PLT-6486 Add an @username button to the profile popover, that puts the username in the post when clicked (#6349)
* PLT-6486 Add an `@username` button to the profile popover, that puts the username in the post when clicked * PLT-6486 Display `@username` mention on the right text area on center or RHS. * Disable @mentions from profile popover on searches, mentions and pinned posts. Fix js errors. * Control undefined post in SearchStore that causes an exception.
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
22fa48f455
Коммит
f740698dbe
@@ -577,3 +577,11 @@ export function postListScrollChange(forceScrollToBottom = false) {
|
|||||||
value: forceScrollToBottom
|
value: forceScrollToBottom
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function emitPopoverMentionKeyClick(isRHS, mentionKey) {
|
||||||
|
AppDispatcher.handleViewAction({
|
||||||
|
type: ActionTypes.POPOVER_MENTION_KEY_CLICK,
|
||||||
|
isRHS,
|
||||||
|
mentionKey
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,9 +11,16 @@ import {OverlayTrigger} from 'react-bootstrap';
|
|||||||
export default class AtMention extends React.PureComponent {
|
export default class AtMention extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
mentionName: PropTypes.string.isRequired,
|
mentionName: PropTypes.string.isRequired,
|
||||||
usersByUsername: PropTypes.object.isRequired
|
usersByUsername: PropTypes.object.isRequired,
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
hasMention: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
isRHS: false,
|
||||||
|
hasMention: false
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@@ -76,6 +83,8 @@ export default class AtMention extends React.PureComponent {
|
|||||||
user={user}
|
user={user}
|
||||||
src={Client4.getProfilePictureUrl(user.id, user.last_picture_update)}
|
src={Client4.getProfilePictureUrl(user.id, user.last_picture_update)}
|
||||||
hide={this.hideProfilePopover}
|
hide={this.hideProfilePopover}
|
||||||
|
isRHS={this.props.isRHS}
|
||||||
|
hasMention={this.props.hasMention}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -617,6 +617,8 @@ export default class CreateComment extends React.Component {
|
|||||||
emojiEnabled={window.mm_config.EnableEmojiPicker === 'true'}
|
emojiEnabled={window.mm_config.EnableEmojiPicker === 'true'}
|
||||||
initialText=''
|
initialText=''
|
||||||
channelId={this.props.channelId}
|
channelId={this.props.channelId}
|
||||||
|
isRHS={true}
|
||||||
|
popoverMentionKeyClick={true}
|
||||||
id='reply_textbox'
|
id='reply_textbox'
|
||||||
ref='textbox'
|
ref='textbox'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -774,6 +774,7 @@ export default class CreatePost extends React.Component {
|
|||||||
emojiEnabled={window.mm_config.EnableEmojiPicker === 'true'}
|
emojiEnabled={window.mm_config.EnableEmojiPicker === 'true'}
|
||||||
createMessage={Utils.localizeMessage('create_post.write', 'Write a message...')}
|
createMessage={Utils.localizeMessage('create_post.write', 'Write a message...')}
|
||||||
channelId={this.state.channelId}
|
channelId={this.state.channelId}
|
||||||
|
popoverMentionKeyClick={true}
|
||||||
id='post_textbox'
|
id='post_textbox'
|
||||||
ref='textbox'
|
ref='textbox'
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ export default class Post extends React.PureComponent {
|
|||||||
status={status}
|
status={status}
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export default class PostBody extends React.PureComponent {
|
|||||||
lastPostCount={this.props.lastPostCount}
|
lastPostCount={this.props.lastPostCount}
|
||||||
post={this.props.post}
|
post={this.props.post}
|
||||||
compactDisplay={this.props.compactDisplay}
|
compactDisplay={this.props.compactDisplay}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ export default class PostHeader extends React.PureComponent {
|
|||||||
displayNameType={this.props.displayNameType}
|
displayNameType={this.props.displayNameType}
|
||||||
status={this.props.status}
|
status={this.props.status}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
let botIndicator;
|
let botIndicator;
|
||||||
|
|||||||
@@ -66,12 +66,24 @@ export default class PostMessageView extends React.PureComponent {
|
|||||||
/**
|
/**
|
||||||
* Set to render post body compactly
|
* Set to render post body compactly
|
||||||
*/
|
*/
|
||||||
compactDisplay: PropTypes.bool
|
compactDisplay: PropTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags if the post_message_view is for the RHS (Reply).
|
||||||
|
*/
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags if the post_message_view is for the RHS (Reply).
|
||||||
|
*/
|
||||||
|
hasMention: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
options: {},
|
options: {},
|
||||||
mentionKeys: []
|
mentionKeys: [],
|
||||||
|
isRHS: false,
|
||||||
|
hasMention: false
|
||||||
};
|
};
|
||||||
|
|
||||||
renderDeletedPost() {
|
renderDeletedPost() {
|
||||||
@@ -116,7 +128,13 @@ export default class PostMessageView extends React.PureComponent {
|
|||||||
processNode: (node) => {
|
processNode: (node) => {
|
||||||
const mentionName = node.attribs[attrib];
|
const mentionName = node.attribs[attrib];
|
||||||
|
|
||||||
return <AtMention mentionName={mentionName}/>;
|
return (
|
||||||
|
<AtMention
|
||||||
|
mentionName={mentionName}
|
||||||
|
isRHS={this.props.isRHS}
|
||||||
|
hasMention={this.props.hasMention}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ export default class ProfilePicture extends React.Component {
|
|||||||
status={this.props.status}
|
status={this.props.status}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
hide={this.hideProfilePopover}
|
hide={this.hideProfilePopover}
|
||||||
|
isRHS={this.props.isRHS}
|
||||||
|
hasMention={this.props.hasMention}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -93,7 +95,9 @@ export default class ProfilePicture extends React.Component {
|
|||||||
|
|
||||||
ProfilePicture.defaultProps = {
|
ProfilePicture.defaultProps = {
|
||||||
width: '36',
|
width: '36',
|
||||||
height: '36'
|
height: '36',
|
||||||
|
isRHS: false,
|
||||||
|
hasMention: false
|
||||||
};
|
};
|
||||||
ProfilePicture.propTypes = {
|
ProfilePicture.propTypes = {
|
||||||
src: PropTypes.string.isRequired,
|
src: PropTypes.string.isRequired,
|
||||||
@@ -101,5 +105,7 @@ ProfilePicture.propTypes = {
|
|||||||
width: PropTypes.string,
|
width: PropTypes.string,
|
||||||
height: PropTypes.string,
|
height: PropTypes.string,
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
isBusy: PropTypes.bool
|
isBusy: PropTypes.bool,
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
hasMention: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export default class ProfilePopover extends React.Component {
|
|||||||
|
|
||||||
this.initWebrtc = this.initWebrtc.bind(this);
|
this.initWebrtc = this.initWebrtc.bind(this);
|
||||||
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
this.handleShowDirectChannel = this.handleShowDirectChannel.bind(this);
|
||||||
|
this.handleMentionKeyClick = this.handleMentionKeyClick.bind(this);
|
||||||
this.state = {
|
this.state = {
|
||||||
currentUserId: UserStore.getCurrentId(),
|
currentUserId: UserStore.getCurrentId(),
|
||||||
loadingDMChannel: -1
|
loadingDMChannel: -1
|
||||||
@@ -103,6 +104,18 @@ export default class ProfilePopover extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleMentionKeyClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!this.props.user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.props.hide) {
|
||||||
|
this.props.hide();
|
||||||
|
}
|
||||||
|
GlobalActions.emitPopoverMentionKeyClick(this.props.isRHS, this.props.user.username);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const popoverProps = Object.assign({}, this.props);
|
const popoverProps = Object.assign({}, this.props);
|
||||||
delete popoverProps.user;
|
delete popoverProps.user;
|
||||||
@@ -110,6 +123,8 @@ export default class ProfilePopover extends React.Component {
|
|||||||
delete popoverProps.status;
|
delete popoverProps.status;
|
||||||
delete popoverProps.isBusy;
|
delete popoverProps.isBusy;
|
||||||
delete popoverProps.hide;
|
delete popoverProps.hide;
|
||||||
|
delete popoverProps.isRHS;
|
||||||
|
delete popoverProps.hasMention;
|
||||||
|
|
||||||
let webrtc;
|
let webrtc;
|
||||||
const userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
const userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
||||||
@@ -179,6 +194,7 @@ export default class ProfilePopover extends React.Component {
|
|||||||
delayShow={Constants.WEBRTC_TIME_DELAY}
|
delayShow={Constants.WEBRTC_TIME_DELAY}
|
||||||
placement='top'
|
placement='top'
|
||||||
overlay={<Tooltip id='fullNameTooltip'>{fullname}</Tooltip>}
|
overlay={<Tooltip id='fullNameTooltip'>{fullname}</Tooltip>}
|
||||||
|
key='user-popover-fullname'
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className='overflow--ellipsis text-nowrap padding-bottom'
|
className='overflow--ellipsis text-nowrap padding-bottom'
|
||||||
@@ -247,10 +263,15 @@ export default class ProfilePopover extends React.Component {
|
|||||||
dataContent.push(webrtc);
|
dataContent.push(webrtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let title = `@${this.props.user.username}`;
|
||||||
|
if (this.props.hasMention) {
|
||||||
|
title = <a onClick={this.handleMentionKeyClick}>{title}</a>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover
|
||||||
{...popoverProps}
|
{...popoverProps}
|
||||||
title={'@' + this.props.user.username}
|
title={title}
|
||||||
id='user-profile-popover'
|
id='user-profile-popover'
|
||||||
>
|
>
|
||||||
{dataContent}
|
{dataContent}
|
||||||
@@ -259,11 +280,18 @@ export default class ProfilePopover extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProfilePopover.defaultProps = {
|
||||||
|
isRHS: false,
|
||||||
|
hasMention: false
|
||||||
|
};
|
||||||
|
|
||||||
ProfilePopover.propTypes = Object.assign({
|
ProfilePopover.propTypes = Object.assign({
|
||||||
src: PropTypes.string.isRequired,
|
src: PropTypes.string.isRequired,
|
||||||
user: PropTypes.object.isRequired,
|
user: PropTypes.object.isRequired,
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
isBusy: PropTypes.bool,
|
isBusy: PropTypes.bool,
|
||||||
hide: PropTypes.func
|
hide: PropTypes.func,
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
hasMention: PropTypes.bool
|
||||||
}, Popover.propTypes);
|
}, Popover.propTypes);
|
||||||
delete ProfilePopover.propTypes.id;
|
delete ProfilePopover.propTypes.id;
|
||||||
|
|||||||
@@ -223,6 +223,8 @@ export default class RhsComment extends React.Component {
|
|||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
status={status}
|
status={status}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -291,6 +293,8 @@ export default class RhsComment extends React.Component {
|
|||||||
height='36'
|
height='36'
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -327,6 +331,8 @@ export default class RhsComment extends React.Component {
|
|||||||
status={status}
|
status={status}
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -447,7 +453,11 @@ export default class RhsComment extends React.Component {
|
|||||||
<div className='post__body' >
|
<div className='post__body' >
|
||||||
<div className={postClass}>
|
<div className={postClass}>
|
||||||
{failedPostOptions}
|
{failedPostOptions}
|
||||||
<PostMessageContainer post={post}/>
|
<PostMessageContainer
|
||||||
|
post={post}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{fileAttachment}
|
{fileAttachment}
|
||||||
<ReactionListContainer post={post}/>
|
<ReactionListContainer post={post}/>
|
||||||
|
|||||||
@@ -256,6 +256,8 @@ export default class RhsRootPost extends React.Component {
|
|||||||
user={user}
|
user={user}
|
||||||
status={this.props.status}
|
status={this.props.status}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
let botIndicator;
|
let botIndicator;
|
||||||
@@ -308,6 +310,8 @@ export default class RhsRootPost extends React.Component {
|
|||||||
height='36'
|
height='36'
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -344,6 +348,8 @@ export default class RhsRootPost extends React.Component {
|
|||||||
status={status}
|
status={status}
|
||||||
user={this.props.user}
|
user={this.props.user}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -417,7 +423,13 @@ export default class RhsRootPost extends React.Component {
|
|||||||
<div className={postClass}>
|
<div className={postClass}>
|
||||||
<PostBodyAdditionalContent
|
<PostBodyAdditionalContent
|
||||||
post={post}
|
post={post}
|
||||||
message={<PostMessageContainer post={post}/>}
|
message={
|
||||||
|
<PostMessageContainer
|
||||||
|
post={post}
|
||||||
|
isRHS={true}
|
||||||
|
hasMention={true}
|
||||||
|
/>
|
||||||
|
}
|
||||||
previewCollapsed={this.props.previewCollapsed}
|
previewCollapsed={this.props.previewCollapsed}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -70,14 +70,25 @@ export default class SuggestionBox extends React.Component {
|
|||||||
/**
|
/**
|
||||||
* Function called when an item is selected
|
* Function called when an item is selected
|
||||||
*/
|
*/
|
||||||
onItemSelected: PropTypes.func
|
onItemSelected: PropTypes.func,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags if the suggestion_box is for the RHS (Reply).
|
||||||
|
*/
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called when @mention is clicked
|
||||||
|
*/
|
||||||
|
popoverMentionKeyClick: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
type: 'input',
|
type: 'input',
|
||||||
listStyle: 'top',
|
listStyle: 'top',
|
||||||
renderDividers: false,
|
renderDividers: false,
|
||||||
completeOnTab: true
|
completeOnTab: true,
|
||||||
|
isRHS: false
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -85,6 +96,7 @@ export default class SuggestionBox extends React.Component {
|
|||||||
|
|
||||||
this.handleBlur = this.handleBlur.bind(this);
|
this.handleBlur = this.handleBlur.bind(this);
|
||||||
|
|
||||||
|
this.handlePopoverMentionKeyClick = this.handlePopoverMentionKeyClick.bind(this);
|
||||||
this.handleCompleteWord = this.handleCompleteWord.bind(this);
|
this.handleCompleteWord = this.handleCompleteWord.bind(this);
|
||||||
this.handleChange = this.handleChange.bind(this);
|
this.handleChange = this.handleChange.bind(this);
|
||||||
this.handleCompositionStart = this.handleCompositionStart.bind(this);
|
this.handleCompositionStart = this.handleCompositionStart.bind(this);
|
||||||
@@ -102,10 +114,16 @@ export default class SuggestionBox extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
if (this.props.popoverMentionKeyClick) {
|
||||||
|
SuggestionStore.addPopoverMentionKeyClickListener(this.props.isRHS, this.handlePopoverMentionKeyClick);
|
||||||
|
}
|
||||||
SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
|
SuggestionStore.addPretextChangedListener(this.suggestionId, this.handlePretextChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
if (this.props.popoverMentionKeyClick) {
|
||||||
|
SuggestionStore.removePopoverMentionKeyClickListener(this.props.isRHS, this.handlePopoverMentionKeyClick);
|
||||||
|
}
|
||||||
SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
|
SuggestionStore.removePretextChangedListener(this.suggestionId, this.handlePretextChanged);
|
||||||
|
|
||||||
SuggestionStore.unregisterSuggestionBox(this.suggestionId);
|
SuggestionStore.unregisterSuggestionBox(this.suggestionId);
|
||||||
@@ -121,7 +139,8 @@ export default class SuggestionBox extends React.Component {
|
|||||||
|
|
||||||
getTextbox() {
|
getTextbox() {
|
||||||
if (this.props.type === 'textarea') {
|
if (this.props.type === 'textarea') {
|
||||||
return this.refs.textbox.getDOMNode();
|
const node = this.refs.textbox.getDOMNode();
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.refs.textbox;
|
return this.refs.textbox;
|
||||||
@@ -179,7 +198,18 @@ export default class SuggestionBox extends React.Component {
|
|||||||
this.composing = false;
|
this.composing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCompleteWord(term, matchedPretext) {
|
handlePopoverMentionKeyClick(mentionKey) {
|
||||||
|
let insertText = '@' + mentionKey;
|
||||||
|
|
||||||
|
// if the current text does not end with a whitespace, then insert a space
|
||||||
|
if (this.refs.textbox.value && (/[^\s]$/).test(this.refs.textbox.value)) {
|
||||||
|
insertText = ' ' + insertText;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleCompleteWord(insertText, '', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCompleteWord(term, matchedPretext, shouldEmitWordSuggestion = true) {
|
||||||
const textbox = this.getTextbox();
|
const textbox = this.getTextbox();
|
||||||
const caret = textbox.selectionEnd;
|
const caret = textbox.selectionEnd;
|
||||||
const text = this.props.value;
|
const text = this.props.value;
|
||||||
@@ -232,8 +262,9 @@ export default class SuggestionBox extends React.Component {
|
|||||||
provider.handleCompleteWord(term, matchedPretext);
|
provider.handleCompleteWord(term, matchedPretext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (shouldEmitWordSuggestion) {
|
||||||
GlobalActions.emitCompleteWordSuggestion(this.suggestionId);
|
GlobalActions.emitCompleteWordSuggestion(this.suggestionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyDown(e) {
|
handleKeyDown(e) {
|
||||||
@@ -288,6 +319,8 @@ export default class SuggestionBox extends React.Component {
|
|||||||
Reflect.deleteProperty(props, 'onChange'); // We use onInput instead of onChange on the actual input
|
Reflect.deleteProperty(props, 'onChange'); // We use onInput instead of onChange on the actual input
|
||||||
Reflect.deleteProperty(props, 'onItemSelected');
|
Reflect.deleteProperty(props, 'onItemSelected');
|
||||||
Reflect.deleteProperty(props, 'completeOnTab');
|
Reflect.deleteProperty(props, 'completeOnTab');
|
||||||
|
Reflect.deleteProperty(props, 'isRHS');
|
||||||
|
Reflect.deleteProperty(props, 'popoverMentionKeyClick');
|
||||||
|
|
||||||
const childProps = {
|
const childProps = {
|
||||||
ref: 'textbox',
|
ref: 'textbox',
|
||||||
|
|||||||
@@ -36,11 +36,15 @@ export default class Textbox extends React.Component {
|
|||||||
supportsCommands: PropTypes.bool.isRequired,
|
supportsCommands: PropTypes.bool.isRequired,
|
||||||
handlePostError: PropTypes.func,
|
handlePostError: PropTypes.func,
|
||||||
suggestionListStyle: PropTypes.string,
|
suggestionListStyle: PropTypes.string,
|
||||||
emojiEnabled: PropTypes.bool
|
emojiEnabled: PropTypes.bool,
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
popoverMentionKeyClick: React.PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
supportsCommands: true
|
supportsCommands: true,
|
||||||
|
isRHS: false,
|
||||||
|
popoverMentionKeyClick: false
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -296,6 +300,8 @@ export default class Textbox extends React.Component {
|
|||||||
channelId={this.props.channelId}
|
channelId={this.props.channelId}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
renderDividers={true}
|
renderDividers={true}
|
||||||
|
isRHS={this.props.isRHS}
|
||||||
|
popoverMentionKeyClick={this.props.popoverMentionKeyClick}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref='preview'
|
ref='preview'
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ export default class UserProfile extends React.Component {
|
|||||||
status={this.props.status}
|
status={this.props.status}
|
||||||
isBusy={this.props.isBusy}
|
isBusy={this.props.isBusy}
|
||||||
hide={this.hideProfilePopover}
|
hide={this.hideProfilePopover}
|
||||||
|
isRHS={this.props.isRHS}
|
||||||
|
hasMention={this.props.hasMention}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -99,7 +101,9 @@ UserProfile.defaultProps = {
|
|||||||
user: {},
|
user: {},
|
||||||
overwriteName: '',
|
overwriteName: '',
|
||||||
overwriteImage: '',
|
overwriteImage: '',
|
||||||
disablePopover: false
|
disablePopover: false,
|
||||||
|
isRHS: false,
|
||||||
|
hasMention: false
|
||||||
};
|
};
|
||||||
UserProfile.propTypes = {
|
UserProfile.propTypes = {
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
@@ -108,5 +112,7 @@ UserProfile.propTypes = {
|
|||||||
disablePopover: PropTypes.bool,
|
disablePopover: PropTypes.bool,
|
||||||
displayNameType: PropTypes.string,
|
displayNameType: PropTypes.string,
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
isBusy: PropTypes.bool
|
isBusy: PropTypes.bool,
|
||||||
|
isRHS: PropTypes.bool,
|
||||||
|
hasMention: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class SearchStoreClass extends EventEmitter {
|
|||||||
|
|
||||||
updatePost(post) {
|
updatePost(post) {
|
||||||
const results = this.getSearchResults();
|
const results = this.getSearchResults();
|
||||||
if (results == null) {
|
if (!post || results == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const ActionTypes = Constants.ActionTypes;
|
|||||||
const COMPLETE_WORD_EVENT = 'complete_word';
|
const COMPLETE_WORD_EVENT = 'complete_word';
|
||||||
const PRETEXT_CHANGED_EVENT = 'pretext_changed';
|
const PRETEXT_CHANGED_EVENT = 'pretext_changed';
|
||||||
const SUGGESTIONS_CHANGED_EVENT = 'suggestions_changed';
|
const SUGGESTIONS_CHANGED_EVENT = 'suggestions_changed';
|
||||||
|
const POPOVER_MENTION_KEY_CLICK_EVENT = 'popover_mention_key_click';
|
||||||
|
|
||||||
class SuggestionStore extends EventEmitter {
|
class SuggestionStore extends EventEmitter {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -27,6 +28,10 @@ class SuggestionStore extends EventEmitter {
|
|||||||
this.removeCompleteWordListener = this.removeCompleteWordListener.bind(this);
|
this.removeCompleteWordListener = this.removeCompleteWordListener.bind(this);
|
||||||
this.emitCompleteWord = this.emitCompleteWord.bind(this);
|
this.emitCompleteWord = this.emitCompleteWord.bind(this);
|
||||||
|
|
||||||
|
this.addPopoverMentionKeyClickListener = this.addPopoverMentionKeyClickListener.bind(this);
|
||||||
|
this.removePopoverMentionKeyClickListener = this.removePopoverMentionKeyClickListener.bind(this);
|
||||||
|
this.emitPopoverMentionKeyClick = this.emitPopoverMentionKeyClick.bind(this);
|
||||||
|
|
||||||
this.handleEventPayload = this.handleEventPayload.bind(this);
|
this.handleEventPayload = this.handleEventPayload.bind(this);
|
||||||
this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
|
this.dispatchToken = AppDispatcher.register(this.handleEventPayload);
|
||||||
|
|
||||||
@@ -71,6 +76,16 @@ class SuggestionStore extends EventEmitter {
|
|||||||
this.emit(COMPLETE_WORD_EVENT + id, term, matchedPretext);
|
this.emit(COMPLETE_WORD_EVENT + id, term, matchedPretext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPopoverMentionKeyClickListener(id, callback) {
|
||||||
|
this.on(POPOVER_MENTION_KEY_CLICK_EVENT + id, callback);
|
||||||
|
}
|
||||||
|
removePopoverMentionKeyClickListener(id, callback) {
|
||||||
|
this.removeListener(POPOVER_MENTION_KEY_CLICK_EVENT + id, callback);
|
||||||
|
}
|
||||||
|
emitPopoverMentionKeyClick(isRHS, mentionKey) {
|
||||||
|
this.emit(POPOVER_MENTION_KEY_CLICK_EVENT + isRHS, mentionKey);
|
||||||
|
}
|
||||||
|
|
||||||
registerSuggestionBox(id) {
|
registerSuggestionBox(id) {
|
||||||
this.suggestions.set(id, {
|
this.suggestions.set(id, {
|
||||||
pretext: '',
|
pretext: '',
|
||||||
@@ -304,6 +319,9 @@ class SuggestionStore extends EventEmitter {
|
|||||||
this.completeWord(id, other.term, other.matchedPretext);
|
this.completeWord(id, other.term, other.matchedPretext);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ActionTypes.POPOVER_MENTION_KEY_CLICK:
|
||||||
|
this.emitPopoverMentionKeyClick(other.isRHS, other.mentionKey);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ export function createDMIntroMessage(channel, centeredIntro) {
|
|||||||
width='50'
|
width='50'
|
||||||
height='50'
|
height='50'
|
||||||
user={teammate}
|
user={teammate}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='channel-intro-profile'>
|
<div className='channel-intro-profile'>
|
||||||
@@ -121,6 +122,7 @@ export function createDMIntroMessage(channel, centeredIntro) {
|
|||||||
<UserProfile
|
<UserProfile
|
||||||
user={teammate}
|
user={teammate}
|
||||||
disablePopover={false}
|
disablePopover={false}
|
||||||
|
hasMention={true}
|
||||||
/>
|
/>
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user