summaryrefslogtreecommitdiffstats
path: root/web/react
diff options
context:
space:
mode:
Diffstat (limited to 'web/react')
-rw-r--r--web/react/components/access_history_modal.jsx4
-rw-r--r--web/react/components/activity_log_modal.jsx4
-rw-r--r--web/react/components/admin_console/team_analytics.jsx2
-rw-r--r--web/react/components/admin_console/user_item.jsx2
-rw-r--r--web/react/components/channel_header.jsx6
-rw-r--r--web/react/components/channel_invite_modal.jsx11
-rw-r--r--web/react/components/channel_loader.jsx1
-rw-r--r--web/react/components/channel_notifications_modal.jsx2
-rw-r--r--web/react/components/create_comment.jsx2
-rw-r--r--web/react/components/create_post.jsx6
-rw-r--r--web/react/components/delete_channel_modal.jsx4
-rw-r--r--web/react/components/delete_post_modal.jsx2
-rw-r--r--web/react/components/edit_channel_header_modal.jsx54
-rw-r--r--web/react/components/invite_member_modal.jsx21
-rw-r--r--web/react/components/member_list_item.jsx6
-rw-r--r--web/react/components/member_list_team_item.jsx4
-rw-r--r--web/react/components/navbar.jsx2
-rw-r--r--web/react/components/post_attachment_oembed.jsx9
-rw-r--r--web/react/components/post_body.jsx20
-rw-r--r--web/react/components/post_header.jsx2
-rw-r--r--web/react/components/post_info.jsx2
-rw-r--r--web/react/components/posts_view.jsx2
-rw-r--r--web/react/components/providers.json4
-rw-r--r--web/react/components/rhs_root_post.jsx4
-rw-r--r--web/react/components/search_results_item.jsx29
-rw-r--r--web/react/components/sidebar_right.jsx11
-rw-r--r--web/react/components/sidebar_right_menu.jsx2
-rw-r--r--web/react/components/suggestion/command_provider.jsx1
-rw-r--r--web/react/components/suggestion/emoticon_provider.jsx23
-rw-r--r--web/react/components/suggestion/suggestion_box.jsx2
-rw-r--r--web/react/components/team_general_tab.jsx133
-rw-r--r--web/react/components/team_members_modal.jsx4
-rw-r--r--web/react/components/team_signup_with_email.jsx19
-rw-r--r--web/react/components/user_settings/manage_outgoing_hooks.jsx4
-rw-r--r--web/react/components/user_settings/user_settings_modal.jsx4
-rw-r--r--web/react/dispatcher/event_helpers.jsx25
-rw-r--r--web/react/package.json2
-rw-r--r--web/react/pages/channel.jsx12
-rw-r--r--web/react/stores/channel_store.jsx4
-rw-r--r--web/react/stores/preference_store.jsx2
-rw-r--r--web/react/stores/socket_store.jsx10
-rw-r--r--web/react/stores/suggestion_store.jsx15
-rw-r--r--web/react/utils/client.jsx7
-rw-r--r--web/react/utils/constants.jsx4
-rw-r--r--web/react/utils/emoticons.jsx1
-rw-r--r--web/react/utils/markdown.jsx8
-rw-r--r--web/react/utils/utils.jsx3
47 files changed, 328 insertions, 173 deletions
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx
index deef92a54..85c28ca5c 100644
--- a/web/react/components/access_history_modal.jsx
+++ b/web/react/components/access_history_modal.jsx
@@ -32,9 +32,11 @@ export default class AccessHistoryModal extends React.Component {
onShow() {
AsyncClient.getAudits();
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
if ($(window).width() > 768) {
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
+ } else {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
}
}
onHide() {
diff --git a/web/react/components/activity_log_modal.jsx b/web/react/components/activity_log_modal.jsx
index 200f4d724..f5341c0bc 100644
--- a/web/react/components/activity_log_modal.jsx
+++ b/web/react/components/activity_log_modal.jsx
@@ -51,9 +51,11 @@ export default class ActivityLogModal extends React.Component {
onShow() {
AsyncClient.getSessions();
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
if ($(window).width() > 768) {
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
+ } else {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
}
}
onHide() {
diff --git a/web/react/components/admin_console/team_analytics.jsx b/web/react/components/admin_console/team_analytics.jsx
index 6c8e63c83..e28699d3c 100644
--- a/web/react/components/admin_console/team_analytics.jsx
+++ b/web/react/components/admin_console/team_analytics.jsx
@@ -221,7 +221,7 @@ export default class TeamAnalytics extends React.Component {
var openChannelCount = (
<div className='col-sm-3'>
<div className='total-count'>
- <div className='title'>{'Public Groups'}<i className='fa fa-unlock-alt'/></div>
+ <div className='title'>{'Public Channels'}<i className='fa fa-globe'/></div>
<div className='content'>{this.state.channel_open_count == null ? 'Loading...' : this.state.channel_open_count}</div>
</div>
</div>
diff --git a/web/react/components/admin_console/user_item.jsx b/web/react/components/admin_console/user_item.jsx
index bd64564c9..ef0b61460 100644
--- a/web/react/components/admin_console/user_item.jsx
+++ b/web/react/components/admin_console/user_item.jsx
@@ -227,7 +227,6 @@ export default class UserItem extends React.Component {
href='#'
className='dropdown-toggle theme'
type='button'
- id='channel_header_dropdown'
data-toggle='dropdown'
aria-expanded='true'
>
@@ -237,7 +236,6 @@ export default class UserItem extends React.Component {
<ul
className='dropdown-menu member-menu'
role='menu'
- aria-labelledby='channel_header_dropdown'
>
{makeAdmin}
{makeMember}
diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx
index d5a46721e..59ceb038e 100644
--- a/web/react/components/channel_header.jsx
+++ b/web/react/components/channel_header.jsx
@@ -101,9 +101,9 @@ export default class ChannelHeader extends React.Component {
if (user.notify_props && user.notify_props.mention_keys) {
const termKeys = UserStore.getCurrentMentionKeys();
- // if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
- // termKeys.splice(termKeys.indexOf('@all'), 1);
- // }
+ if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) {
+ termKeys.splice(termKeys.indexOf('@all'), 1);
+ }
if (user.notify_props.channel === 'true' && termKeys.indexOf('@channel') !== -1) {
termKeys.splice(termKeys.indexOf('@channel'), 1);
diff --git a/web/react/components/channel_invite_modal.jsx b/web/react/components/channel_invite_modal.jsx
index 56e2e53f9..7dac39942 100644
--- a/web/react/components/channel_invite_modal.jsx
+++ b/web/react/components/channel_invite_modal.jsx
@@ -61,6 +61,9 @@ export default class ChannelInviteModal extends React.Component {
onShow() {
if ($(window).width() > 768) {
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
+ } else {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
}
}
componentDidUpdate(prevProps) {
@@ -103,11 +106,6 @@ export default class ChannelInviteModal extends React.Component {
);
}
render() {
- var maxHeight = 1000;
- if (Utils.windowHeight() <= 1200) {
- maxHeight = Utils.windowHeight() - 300;
- }
-
var inviteError = null;
if (this.state.inviteError) {
inviteError = (<label className='has-error control-label'>{this.state.inviteError}</label>);
@@ -139,11 +137,10 @@ export default class ChannelInviteModal extends React.Component {
onHide={this.props.onHide}
>
<Modal.Header closeButton={true}>
- <Modal.Title>{'Add New Members to '}<span className='name'>{this.props.channel.display_nam}</span></Modal.Title>
+ <Modal.Title>{'Add New Members to '}<span className='name'>{this.props.channel.display_name}</span></Modal.Title>
</Modal.Header>
<Modal.Body
ref='modalBody'
- style={{maxHeight}}
>
{inviteError}
{content}
diff --git a/web/react/components/channel_loader.jsx b/web/react/components/channel_loader.jsx
index 13045d732..0d1d9efd7 100644
--- a/web/react/components/channel_loader.jsx
+++ b/web/react/components/channel_loader.jsx
@@ -70,6 +70,7 @@ export default class ChannelLoader extends React.Component {
Utils.applyTheme(Constants.THEMES.default);
}
+ // if preferences have already been stored in local storage do not wait until preference store change is fired and handled in channel.jsx
const selectedFont = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', {value: Constants.DEFAULT_FONT}).value;
Utils.applyFont(selectedFont);
diff --git a/web/react/components/channel_notifications_modal.jsx b/web/react/components/channel_notifications_modal.jsx
index 887589468..e70d3a634 100644
--- a/web/react/components/channel_notifications_modal.jsx
+++ b/web/react/components/channel_notifications_modal.jsx
@@ -335,7 +335,7 @@ export default class ChannelNotificationsModal extends React.Component {
onHide={this.props.onHide}
>
<Modal.Header closeButton={true}>
- {'Notification Preferences for '}<span className='name'>{this.props.channel.display_name}</span>
+ <Modal.Title>{'Notification Preferences for '}<span className='name'>{this.props.channel.display_name}</span></Modal.Title>
</Modal.Header>
<Modal.Body>
<div className='settings-table'>
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index fac40e895..b0f33eda1 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -362,11 +362,11 @@ export default class CreateComment extends React.Component {
onClick={this.handleSubmit}
/>
{uploadsInProgressText}
+ {preview}
{postError}
{serverError}
</div>
</div>
- {preview}
</form>
);
}
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index f7f63fb92..89e984e27 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -470,13 +470,13 @@ export default class CreatePost extends React.Component {
{tutorialTip}
</div>
<div className={postFooterClassName}>
- {postError}
- {serverError}
- {preview}
<MsgTyping
channelId={this.state.channelId}
parentId=''
/>
+ {preview}
+ {postError}
+ {serverError}
</div>
</div>
</form>
diff --git a/web/react/components/delete_channel_modal.jsx b/web/react/components/delete_channel_modal.jsx
index 99bae962a..1255067fd 100644
--- a/web/react/components/delete_channel_modal.jsx
+++ b/web/react/components/delete_channel_modal.jsx
@@ -39,7 +39,9 @@ export default class DeleteChannelModal extends React.Component {
show={this.props.show}
onHide={this.props.onHide}
>
- <Modal.Header closeButton={true}>{'Confirm DELETE Channel'}</Modal.Header>
+ <Modal.Header closeButton={true}>
+ <h4 className='modal-title'>{'Confirm DELETE Channel'}</h4>
+ </Modal.Header>
<Modal.Body>
{`Are you sure you wish to delete the ${this.props.channel.display_name} ${channelTerm}?`}
</Modal.Body>
diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx
index 5e89a0893..827654e1b 100644
--- a/web/react/components/delete_post_modal.jsx
+++ b/web/react/components/delete_post_modal.jsx
@@ -131,7 +131,7 @@ export default class DeletePostModal extends React.Component {
onHide={this.handleHide}
>
<Modal.Header closeButton={true}>
- {`Confirm ${postTerm} Delete`}
+ <Modal.Title>{`Confirm ${postTerm} Delete`}</Modal.Title>
</Modal.Header>
<Modal.Body>
{`Are you sure you want to delete this ${postTerm.toLowerCase()}?`}
diff --git a/web/react/components/edit_channel_header_modal.jsx b/web/react/components/edit_channel_header_modal.jsx
index 5529a419d..e4817f6e4 100644
--- a/web/react/components/edit_channel_header_modal.jsx
+++ b/web/react/components/edit_channel_header_modal.jsx
@@ -1,8 +1,9 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
import * as Client from '../utils/client.jsx';
-import * as AsyncClient from '../utils/async_client.jsx';
+import Constants from '../utils/constants.jsx';
import * as Utils from '../utils/utils.jsx';
const Modal = ReactBootstrap.Modal;
@@ -11,12 +12,14 @@ export default class EditChannelHeaderModal extends React.Component {
constructor(props) {
super(props);
- this.handleEdit = this.handleEdit.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
this.onShow = this.onShow.bind(this);
this.onHide = this.onHide.bind(this);
this.state = {
+ header: props.channel.header,
serverError: ''
};
}
@@ -27,27 +30,38 @@ export default class EditChannelHeaderModal extends React.Component {
}
}
+ componentWillReceiveProps(nextProps) {
+ if (this.props.channel.header !== nextProps.channel.header) {
+ this.setState({
+ header: nextProps.channel.header
+ });
+ }
+ }
+
componentDidUpdate(prevProps) {
if (this.props.show && !prevProps.show) {
this.onShow();
}
}
- handleEdit() {
- var data = {};
- data.channel_id = this.props.channel.id;
-
- if (data.channel_id.length !== 26) {
- return;
- }
-
- data.channel_header = ReactDOM.findDOMNode(this.refs.textarea).value;
+ handleChange(e) {
+ this.setState({
+ header: e.target.value
+ });
+ }
- Client.updateChannelHeader(data,
- () => {
+ handleSubmit() {
+ Client.updateChannelHeader(
+ this.props.channel.id,
+ this.state.header,
+ (channel) => {
this.setState({serverError: ''});
- AsyncClient.getChannel(this.props.channel.id);
this.onHide();
+
+ AppDispatcher.handleServerAction({
+ type: Constants.ActionTypes.RECIEVED_CHANNEL,
+ channel
+ });
},
(err) => {
if (err.message === 'Invalid channel_header parameter') {
@@ -66,7 +80,8 @@ export default class EditChannelHeaderModal extends React.Component {
onHide() {
this.setState({
- serverError: ''
+ serverError: '',
+ header: this.props.channel.header
});
this.props.onHide();
@@ -84,7 +99,7 @@ export default class EditChannelHeaderModal extends React.Component {
onHide={this.onHide}
>
<Modal.Header closeButton={true}>
- {'Edit Header for ' + this.props.channel.display_name}
+ <Modal.Title>{'Edit Header for ' + this.props.channel.display_name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{'Edit the text appearing next to the channel name in the channel header.'}</p>
@@ -94,7 +109,8 @@ export default class EditChannelHeaderModal extends React.Component {
rows='6'
id='edit_header'
maxLength='1024'
- defaultValue={this.props.channel.header}
+ value={this.state.header}
+ onChange={this.handleChange}
/>
{serverError}
</Modal.Body>
@@ -102,14 +118,14 @@ export default class EditChannelHeaderModal extends React.Component {
<button
type='button'
className='btn btn-default'
- onClick={this.props.onHide}
+ onClick={this.onHide}
>
{'Cancel'}
</button>
<button
type='button'
className='btn btn-primary'
- onClick={this.handleEdit}
+ onClick={this.handleSubmit}
>
{'Save'}
</button>
diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx
index 25a915e22..56bc00a7e 100644
--- a/web/react/components/invite_member_modal.jsx
+++ b/web/react/components/invite_member_modal.jsx
@@ -33,6 +33,7 @@ export default class InviteMemberModal extends React.Component {
firstNameErrors: {},
lastNameErrors: {},
emailEnabled: global.window.mm_config.SendEmailNotifications === 'true',
+ userCreationEnabled: global.window.mm_config.EnableUserCreation === 'true',
showConfirmModal: false,
isSendingEmails: false
};
@@ -143,7 +144,7 @@ export default class InviteMemberModal extends React.Component {
componentDidUpdate(prevProps, prevState) {
if (!prevState.show && this.state.show) {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
if ($(window).width() > 768) {
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
}
@@ -252,7 +253,7 @@ export default class InviteMemberModal extends React.Component {
ref={'first_name' + index}
placeholder='First name'
maxLength='64'
- disabled={!this.state.emailEnabled}
+ disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
/>
{firstNameError}
@@ -266,7 +267,7 @@ export default class InviteMemberModal extends React.Component {
ref={'last_name' + index}
placeholder='Last name'
maxLength='64'
- disabled={!this.state.emailEnabled}
+ disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
/>
{lastNameError}
@@ -285,7 +286,7 @@ export default class InviteMemberModal extends React.Component {
className='form-control'
placeholder='email@domain.com'
maxLength='64'
- disabled={!this.state.emailEnabled}
+ disabled={!this.state.emailEnabled || !this.state.userCreationEnabled}
spellCheck='false'
/>
{emailError}
@@ -303,7 +304,7 @@ export default class InviteMemberModal extends React.Component {
var content = null;
var sendButton = null;
- if (this.state.emailEnabled) {
+ if (this.state.emailEnabled && this.state.userCreationEnabled) {
content = (
<div>
{serverError}
@@ -337,7 +338,7 @@ export default class InviteMemberModal extends React.Component {
{sendButtonLabel}
</button>
);
- } else {
+ } else if (this.state.userCreationEnabled) {
var teamInviteLink = null;
if (currentUser && TeamStore.getCurrent().type === 'O') {
var link = (
@@ -358,10 +359,16 @@ export default class InviteMemberModal extends React.Component {
content = (
<div>
- <p>Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.</p>
+ <p>{'Email is currently disabled for your team, and email invitations cannot be sent. Contact your system administrator to enable email and email invitations.'}</p>
{teamInviteLink}
</div>
);
+ } else {
+ content = (
+ <div>
+ <p>{'User creation has been disabled for your team. Please ask your team administrator for details.'}</p>
+ </div>
+ );
}
return (
diff --git a/web/react/components/member_list_item.jsx b/web/react/components/member_list_item.jsx
index f7f77f48a..a7273f280 100644
--- a/web/react/components/member_list_item.jsx
+++ b/web/react/components/member_list_item.jsx
@@ -78,17 +78,15 @@ export default class MemberListItem extends React.Component {
href='#'
className='dropdown-toggle theme'
type='button'
- id='channel_header_dropdown'
data-toggle='dropdown'
aria-expanded='true'
>
+ <span className='fa fa-pencil'></span>
<span className='text-capitalize'>{member.roles || 'Member'} </span>
- <span className='caret'></span>
</a>
<ul
className='dropdown-menu member-menu'
role='menu'
- aria-labelledby='channel_header_dropdown'
>
{makeAdminOption}
{handleRemoveOption}
@@ -96,7 +94,7 @@ export default class MemberListItem extends React.Component {
</div>
);
} else {
- invite = <div className='member-role text-capitalize'>{member.roles || 'Member'}<span className='caret hidden'></span></div>;
+ invite = <div className='member-role text-capitalize'><span className='fa fa-pencil hidden'></span>{member.roles || 'Member'}</div>;
}
return (
diff --git a/web/react/components/member_list_team_item.jsx b/web/react/components/member_list_team_item.jsx
index 316fad01a..7967c410d 100644
--- a/web/react/components/member_list_team_item.jsx
+++ b/web/react/components/member_list_team_item.jsx
@@ -181,17 +181,15 @@ export default class MemberListTeamItem extends React.Component {
href='#'
className='dropdown-toggle theme'
type='button'
- id='channel_header_dropdown'
data-toggle='dropdown'
aria-expanded='true'
>
+ <span className='fa fa-pencil'></span>
<span>{currentRoles} </span>
- <span className='caret'></span>
</a>
<ul
className='dropdown-menu member-menu'
role='menu'
- aria-labelledby='channel_header_dropdown'
>
{makeAdmin}
{makeMember}
diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx
index 3bdc9efac..ae14fca2f 100644
--- a/web/react/components/navbar.jsx
+++ b/web/react/components/navbar.jsx
@@ -272,7 +272,6 @@ export default class Navbar extends React.Component {
href='#'
className='dropdown-toggle theme'
type='button'
- id='channel_header_dropdown'
data-toggle='dropdown'
aria-expanded='true'
>
@@ -282,7 +281,6 @@ export default class Navbar extends React.Component {
<ul
className='dropdown-menu'
role='menu'
- aria-labelledby='channel_header_dropdown'
>
{viewInfoOption}
{addMembersOption}
diff --git a/web/react/components/post_attachment_oembed.jsx b/web/react/components/post_attachment_oembed.jsx
index 13c32f744..4b12ee95e 100644
--- a/web/react/components/post_attachment_oembed.jsx
+++ b/web/react/components/post_attachment_oembed.jsx
@@ -14,7 +14,14 @@ export default class PostAttachmentOEmbed extends React.Component {
}
componentWillReceiveProps(nextProps) {
- this.fetchData(nextProps.link);
+ if (nextProps.link !== this.props.link) {
+ this.isLoading = false;
+ this.fetchData(nextProps.link);
+ }
+ }
+
+ componentDidMount() {
+ this.fetchData(this.props.link);
}
fetchData(link) {
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 296b9e7d7..dcbe56399 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -214,14 +214,14 @@ export default class PostBody extends React.Component {
}
createYoutubeEmbed(link) {
- const ytRegex = /.*(?:youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|watch\?(?:[a-zA-Z-_]+=[a-zA-Z0-9-_]+&)+v=)([^#\&\?]*).*/;
+ const ytRegex = /(?:http|https):\/\/(?:www\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(\/u\/\w\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^\/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#\&\?]*)/;
const match = link.trim().match(ytRegex);
- if (!match || match[1].length !== 11) {
+ if (!match || match[2].length !== 11) {
return null;
}
- const youtubeId = match[1];
+ const youtubeId = match[2];
const time = this.handleYoutubeTime(link);
function onClick(e) {
@@ -309,7 +309,15 @@ export default class PostBody extends React.Component {
let apostrophe = '';
let name = '...';
if (profile != null) {
- if (profile.username.slice(-1) === 's') {
+ let username = profile.username;
+ if (parentPost.props &&
+ parentPost.props.from_webhook &&
+ parentPost.props.override_username &&
+ global.window.mm_config.EnablePostUsernameOverride === 'true') {
+ username = parentPost.props.override_username;
+ }
+
+ if (username.slice(-1) === 's') {
apostrophe = '\'';
} else {
apostrophe = '\'s';
@@ -317,9 +325,9 @@ export default class PostBody extends React.Component {
name = (
<a
className='theme'
- onClick={Utils.searchForTerm.bind(null, profile.username)}
+ onClick={Utils.searchForTerm.bind(null, username)}
>
- {profile.username}
+ {username}
</a>
);
}
diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx
index 76b3a64be..f18024343 100644
--- a/web/react/components/post_header.jsx
+++ b/web/react/components/post_header.jsx
@@ -34,7 +34,7 @@ export default class PostHeader extends React.Component {
userProfile = (
<UserProfile
userId={''}
- overwriteName={''}
+ overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME}
overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
disablePopover={true}
/>
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index 0b8b07d8c..21683bb01 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -196,7 +196,7 @@ export default class PostInfo extends React.Component {
type='text'
readOnly='true'
ref='permalinkbox'
- className='permalink-text form-control no-resize min-height input-large'
+ className='permalink-text form-control no-resize'
rows='1'
value={permalink}
/>
diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx
index 740ce04f6..b7ac92672 100644
--- a/web/react/components/posts_view.jsx
+++ b/web/react/components/posts_view.jsx
@@ -103,7 +103,7 @@ export default class PostsView extends React.Component {
const prevPostIsComment = Utils.isComment(prevPost);
const postFromWebhook = Boolean(post.props && post.props.from_webhook);
const prevPostFromWebhook = Boolean(prevPost.props && prevPost.props.from_webhook);
- const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPostUserId;
+ const prevPostUserId = Utils.isSystemMessage(prevPost) ? '' : prevPost.user_id;
let prevWebhookName = '';
if (prevPost.props && prevPost.props.override_username) {
prevWebhookName = prevPost.props.override_username;
diff --git a/web/react/components/providers.json b/web/react/components/providers.json
index 33e377a39..b5899c225 100644
--- a/web/react/components/providers.json
+++ b/web/react/components/providers.json
@@ -11,7 +11,7 @@
"https?://soundcloud.com/.*/.*"
],
"name": "SoundCloud",
- "height": 110
+ "height": 140
},
{
"patterns": [
@@ -349,7 +349,7 @@
"https?://vine.co/v/[a-zA-Z0-9]+"
],
"name": "Vine",
- "height": 110
+ "height": 490
},
{
"patterns": [
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 0a37a6803..dd9a793be 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -167,7 +167,7 @@ export default class RhsRootPost extends React.Component {
userProfile = (
<UserProfile
userId={''}
- overwriteName={''}
+ overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME}
overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
disablePopover={true}
/>
@@ -209,7 +209,7 @@ export default class RhsRootPost extends React.Component {
</time>
</li>
<li className='col col__reply'>
- <div className='dropdown'>
+ <div>
{rootOptions}
</div>
</li>
diff --git a/web/react/components/search_results_item.jsx b/web/react/components/search_results_item.jsx
index 1d4983026..f71abf971 100644
--- a/web/react/components/search_results_item.jsx
+++ b/web/react/components/search_results_item.jsx
@@ -8,17 +8,31 @@ import * as EventHelpers from '../dispatcher/event_helpers.jsx';
import * as utils from '../utils/utils.jsx';
import * as TextFormatting from '../utils/text_formatting.jsx';
+import Constants from '../utils/constants.jsx';
+
export default class SearchResultsItem extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
+ this.handleFocusRHSClick = this.handleFocusRHSClick.bind(this);
}
handleClick(e) {
e.preventDefault();
EventHelpers.emitPostFocusEvent(this.props.post.id);
+
+ if ($(window).width() < 768) {
+ $('.sidebar--right').removeClass('move--left');
+ $('.inner__wrap').removeClass('move--left');
+ }
+ }
+
+ handleFocusRHSClick(e) {
+ e.preventDefault();
+
+ EventHelpers.emitPostFocusRightHandSideEvent(this.props.post);
}
render() {
@@ -65,13 +79,24 @@ export default class SearchResultsItem extends React.Component {
className='search-item__jump'
onClick={this.handleClick}
>
- {<i className='fa fa-mail-reply'></i>}
+ {'Jump'}
+ </a>
+ </li>
+ <li>
+ <a
+ href='#'
+ className='comment-icon__container search-item__comment'
+ onClick={this.handleFocusRHSClick}
+ >
+ <span
+ className='comment-icon'
+ dangerouslySetInnerHTML={{__html: Constants.COMMENT_ICON}}
+ />
</a>
</li>
</ul>
<div className='search-item-snippet'>
<span
- onClick={this.handleClick}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
/>
</div>
diff --git a/web/react/components/sidebar_right.jsx b/web/react/components/sidebar_right.jsx
index 22d702369..ac1049da0 100644
--- a/web/react/components/sidebar_right.jsx
+++ b/web/react/components/sidebar_right.jsx
@@ -7,6 +7,8 @@ import SearchStore from '../stores/search_store.jsx';
import PostStore from '../stores/post_store.jsx';
import * as Utils from '../utils/utils.jsx';
+const SIDEBAR_SCROLL_DELAY = 500;
+
export default class SidebarRight extends React.Component {
constructor(props) {
super(props);
@@ -39,8 +41,13 @@ export default class SidebarRight extends React.Component {
PostStore.removeSelectedPostChangeListener(this.onSelectedChange);
SearchStore.removeShowSearchListener(this.onShowSearch);
}
- componentWillUpdate() {
- PostStore.jumpPostsViewSidebarOpen();
+ componentWillUpdate(nextProps, nextState) {
+ const isOpen = this.state.search_visible || this.state.post_right_visible;
+ const willOpen = nextState.search_visible || nextState.post_right_visible;
+
+ if (!isOpen && willOpen) {
+ setTimeout(() => PostStore.jumpPostsViewSidebarOpen(), SIDEBAR_SCROLL_DELAY);
+ }
}
doStrangeThings() {
// We should have a better way to do this stuff
diff --git a/web/react/components/sidebar_right_menu.jsx b/web/react/components/sidebar_right_menu.jsx
index 108b1c1b9..d93d146d8 100644
--- a/web/react/components/sidebar_right_menu.jsx
+++ b/web/react/components/sidebar_right_menu.jsx
@@ -87,7 +87,7 @@ export default class SidebarRightMenu extends React.Component {
);
}
- if (isSystemAdmin) {
+ if (isSystemAdmin && !utils.isMobile()) {
consoleLink = (
<li>
<a
diff --git a/web/react/components/suggestion/command_provider.jsx b/web/react/components/suggestion/command_provider.jsx
index a2a446de2..91d556bb9 100644
--- a/web/react/components/suggestion/command_provider.jsx
+++ b/web/react/components/suggestion/command_provider.jsx
@@ -39,7 +39,6 @@ export default class CommandProvider {
handlePretextChanged(suggestionId, pretext) {
if (pretext.startsWith('/')) {
SuggestionStore.setMatchedPretext(suggestionId, pretext);
- SuggestionStore.setCompleteOnSpace(suggestionId, false);
AsyncClient.getSuggestedCommands(pretext, suggestionId, CommandSuggestion);
}
diff --git a/web/react/components/suggestion/emoticon_provider.jsx b/web/react/components/suggestion/emoticon_provider.jsx
index 7dcb86442..fd470cf21 100644
--- a/web/react/components/suggestion/emoticon_provider.jsx
+++ b/web/react/components/suggestion/emoticon_provider.jsx
@@ -51,23 +51,40 @@ export default class EmoticonProvider {
const text = captured[1];
const partialName = captured[2];
- const terms = [];
const names = [];
for (const emoticon of Emoticons.emoticonMap.keys()) {
if (emoticon.indexOf(partialName) !== -1) {
- terms.push(':' + emoticon + ':');
names.push(emoticon);
- if (terms.length >= MAX_EMOTICON_SUGGESTIONS) {
+ if (names.length >= MAX_EMOTICON_SUGGESTIONS) {
break;
}
}
}
+ // sort the emoticons so that emoticons starting with the entered text come first
+ names.sort((a, b) => {
+ const aPrefix = a.startsWith(partialName);
+ const bPrefix = b.startsWith(partialName);
+
+ if (aPrefix === bPrefix) {
+ return a.localeCompare(b);
+ } else if (aPrefix) {
+ return -1;
+ }
+
+ return 1;
+ });
+
+ const terms = names.map((name) => ':' + name + ':');
+
if (terms.length > 0) {
SuggestionStore.setMatchedPretext(suggestionId, text);
SuggestionStore.addSuggestions(suggestionId, terms, names, EmoticonSuggestion);
+
+ // force the selection to be cleared since the order of elements may have changed
+ SuggestionStore.clearSelection(suggestionId);
}
}
}
diff --git a/web/react/components/suggestion/suggestion_box.jsx b/web/react/components/suggestion/suggestion_box.jsx
index ad8ad1e9e..57a33c24a 100644
--- a/web/react/components/suggestion/suggestion_box.jsx
+++ b/web/react/components/suggestion/suggestion_box.jsx
@@ -94,7 +94,7 @@ export default class SuggestionBox extends React.Component {
} else if (e.which === KeyCodes.DOWN) {
EventHelpers.emitSelectNextSuggestion(this.suggestionId);
e.preventDefault();
- } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB || (e.which === KeyCodes.SPACE && SuggestionStore.shouldCompleteOnSpace(this.suggestionId))) {
+ } else if (e.which === KeyCodes.ENTER || e.which === KeyCodes.TAB) {
EventHelpers.emitCompleteWordSuggestion(this.suggestionId);
e.preventDefault();
} else if (this.props.onKeyDown) {
diff --git a/web/react/components/team_general_tab.jsx b/web/react/components/team_general_tab.jsx
index 03715d585..dc615f2e8 100644
--- a/web/react/components/team_general_tab.jsx
+++ b/web/react/components/team_general_tab.jsx
@@ -12,6 +12,7 @@ export default class GeneralTab extends React.Component {
constructor(props) {
super(props);
+ this.updateSection = this.updateSection.bind(this);
this.handleNameSubmit = this.handleNameSubmit.bind(this);
this.handleInviteIdSubmit = this.handleInviteIdSubmit.bind(this);
this.handleOpenInviteSubmit = this.handleOpenInviteSubmit.bind(this);
@@ -27,11 +28,22 @@ export default class GeneralTab extends React.Component {
this.handleTeamListingRadio = this.handleTeamListingRadio.bind(this);
this.handleGenerateInviteId = this.handleGenerateInviteId.bind(this);
- this.state = {
- name: props.team.display_name,
- invite_id: props.team.invite_id,
- allow_open_invite: props.team.allow_open_invite,
- allow_team_listing: props.team.allow_team_listing,
+ this.state = this.setupInitialState(props);
+ }
+
+ updateSection(section) {
+ this.setState(this.setupInitialState(this.props));
+ this.props.updateSection(section);
+ }
+
+ setupInitialState(props) {
+ const team = props.team;
+
+ return {
+ name: team.display_name,
+ invite_id: team.invite_id,
+ allow_open_invite: team.allow_open_invite,
+ allow_team_listing: team.allow_team_listing,
serverError: '',
clientError: ''
};
@@ -71,7 +83,7 @@ export default class GeneralTab extends React.Component {
(team) => {
TeamStore.saveTeam(team);
TeamStore.emitChange();
- this.props.updateSection('');
+ this.updateSection('');
},
(err) => {
state.serverError = err.message;
@@ -91,7 +103,7 @@ export default class GeneralTab extends React.Component {
(team) => {
TeamStore.saveTeam(team);
TeamStore.emitChange();
- this.props.updateSection('');
+ this.updateSection('');
},
(err) => {
state.serverError = err.message;
@@ -129,7 +141,7 @@ export default class GeneralTab extends React.Component {
(team) => {
TeamStore.saveTeam(team);
TeamStore.emitChange();
- this.props.updateSection('');
+ this.updateSection('');
},
(err) => {
state.serverError = err.message;
@@ -164,7 +176,7 @@ export default class GeneralTab extends React.Component {
(team) => {
TeamStore.saveTeam(team);
TeamStore.emitChange();
- this.props.updateSection('');
+ this.updateSection('');
},
(err) => {
state.serverError = err.message;
@@ -180,8 +192,7 @@ export default class GeneralTab extends React.Component {
}
handleClose() {
- this.setState({clientError: '', serverError: ''});
- this.props.updateSection('');
+ this.updateSection('');
}
componentDidMount() {
@@ -195,36 +206,36 @@ export default class GeneralTab extends React.Component {
onUpdateNameSection(e) {
e.preventDefault();
if (this.props.activeSection === 'name') {
- this.props.updateSection('');
+ this.updateSection('');
} else {
- this.props.updateSection('name');
+ this.updateSection('name');
}
}
onUpdateInviteIdSection(e) {
e.preventDefault();
if (this.props.activeSection === 'invite_id') {
- this.props.updateSection('');
+ this.updateSection('');
} else {
- this.props.updateSection('invite_id');
+ this.updateSection('invite_id');
}
}
onUpdateOpenInviteSection(e) {
e.preventDefault();
if (this.props.activeSection === 'open_invite') {
- this.props.updateSection('');
+ this.updateSection('');
} else {
- this.props.updateSection('open_invite');
+ this.updateSection('open_invite');
}
}
onUpdateTeamListingSection(e) {
e.preventDefault();
if (this.props.activeSection === 'team_listing') {
- this.props.updateSection('');
+ this.updateSection('');
} else {
- this.props.updateSection('team_listing');
+ this.updateSection('team_listing');
}
}
@@ -248,44 +259,59 @@ export default class GeneralTab extends React.Component {
serverError = this.state.serverError;
}
+ const enableTeamListing = global.window.mm_config.EnableTeamListing === 'true';
+
let teamListingSection;
if (this.props.activeSection === 'team_listing') {
- const inputs = [
- <div key='userTeamListingOptions'>
- <div className='radio'>
- <label>
- <input
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, true)}
- />
- {'Yes'}
- </label>
- <br/>
+ const inputs = [];
+ let submitHandle = null;
+
+ if (enableTeamListing) {
+ submitHandle = this.handleTeamListingSubmit;
+
+ inputs.push(
+ <div key='userTeamListingOptions'>
+ <div className='radio'>
+ <label>
+ <input
+ name='userTeamListingOptions'
+ type='radio'
+ defaultChecked={this.state.allow_team_listing}
+ onChange={this.handleTeamListingRadio.bind(this, true)}
+ />
+ {'Yes'}
+ </label>
+ <br/>
+ </div>
+ <div className='radio'>
+ <label>
+ <input
+ ref='teamListingRadioNo'
+ name='userTeamListingOptions'
+ type='radio'
+ defaultChecked={!this.state.allow_team_listing}
+ onChange={this.handleTeamListingRadio.bind(this, false)}
+ />
+ {'No'}
+ </label>
+ <br/>
+ </div>
+ <div><br/>{'Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'}</div>
</div>
- <div className='radio'>
- <label>
- <input
- ref='teamListingRadioNo'
- name='userTeamListingOptions'
- type='radio'
- defaultChecked={!this.state.allow_team_listing}
- onChange={this.handleTeamListingRadio.bind(this, false)}
- />
- {'No'}
- </label>
- <br/>
+ );
+ } else {
+ inputs.push(
+ <div key='userTeamListingOptions'>
+ <div><br/>{'Contact your system administrator to turn on the team directory on the system home page.'}</div>
</div>
- <div><br/>{'Including this team will display the team name from the Team Directory section of the Home Page, and provide a link to the sign-in page.'}</div>
- </div>
- ];
+ );
+ }
teamListingSection = (
<SettingItemMax
title='Include this team in the Team Directory'
inputs={inputs}
- submit={this.handleTeamListingSubmit}
+ submit={submitHandle}
server_error={serverError}
client_error={clientError}
updateSection={this.onUpdateTeamListingSection}
@@ -293,10 +319,15 @@ export default class GeneralTab extends React.Component {
);
} else {
let describe = '';
- if (this.state.allow_team_listing === true) {
- describe = 'Yes';
+
+ if (enableTeamListing) {
+ if (this.state.allow_team_listing === true) {
+ describe = 'Yes';
+ } else {
+ describe = 'No';
+ }
} else {
- describe = 'No';
+ describe = 'Team directory is turned off for this system.';
}
teamListingSection = (
diff --git a/web/react/components/team_members_modal.jsx b/web/react/components/team_members_modal.jsx
index eed4a1f19..27224c283 100644
--- a/web/react/components/team_members_modal.jsx
+++ b/web/react/components/team_members_modal.jsx
@@ -26,9 +26,11 @@ export default class TeamMembersModal extends React.Component {
}
onShow() {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
if ($(window).width() > 768) {
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
+ } else {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
}
}
diff --git a/web/react/components/team_signup_with_email.jsx b/web/react/components/team_signup_with_email.jsx
index 06d6e3934..4150a0013 100644
--- a/web/react/components/team_signup_with_email.jsx
+++ b/web/react/components/team_signup_with_email.jsx
@@ -14,18 +14,19 @@ export default class EmailSignUpPage extends React.Component {
}
handleSubmit(e) {
e.preventDefault();
- var team = {};
- var state = {serverError: ''};
+ const team = {};
+ const state = {serverError: null};
+ let isValid = true;
team.email = ReactDOM.findDOMNode(this.refs.email).value.trim().toLowerCase();
if (!team.email || !Utils.isEmail(team.email)) {
state.emailError = 'Please enter a valid email address';
- state.inValid = true;
+ isValid = false;
} else {
- state.emailError = '';
+ state.emailError = null;
}
- if (state.inValid) {
+ if (!isValid) {
this.setState(state);
return;
}
@@ -45,11 +46,16 @@ export default class EmailSignUpPage extends React.Component {
);
}
render() {
- var serverError = null;
+ let serverError = null;
if (this.state.serverError) {
serverError = <div className='form-group has-error'><label className='control-label'>{this.state.serverError}</label></div>;
}
+ let emailError = null;
+ if (this.state.emailError) {
+ emailError = <div className='form-group has-error'><label className='control-label'>{this.state.emailError}</label></div>;
+ }
+
return (
<form
role='form'
@@ -65,6 +71,7 @@ export default class EmailSignUpPage extends React.Component {
maxLength='128'
spellCheck='false'
/>
+ {emailError}
</div>
<div className='form-group'>
<button
diff --git a/web/react/components/user_settings/manage_outgoing_hooks.jsx b/web/react/components/user_settings/manage_outgoing_hooks.jsx
index 9c88bb819..fdbac9831 100644
--- a/web/react/components/user_settings/manage_outgoing_hooks.jsx
+++ b/web/react/components/user_settings/manage_outgoing_hooks.jsx
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import LoadingScreen from '../loading_screen.jsx';
@@ -188,7 +188,7 @@ export default class ManageOutgoingHooks extends React.Component {
key={hook.id}
className='webhook__item'
>
- <div className='padding-top x2'>
+ <div className='padding-top x2 webhook__url'>
<strong>{'URLs: '}</strong><span className='word-break--all'>{hook.callback_urls.join(', ')}</span>
</div>
{channelDiv}
diff --git a/web/react/components/user_settings/user_settings_modal.jsx b/web/react/components/user_settings/user_settings_modal.jsx
index 97c601b5e..36e1aa217 100644
--- a/web/react/components/user_settings/user_settings_modal.jsx
+++ b/web/react/components/user_settings/user_settings_modal.jsx
@@ -47,9 +47,11 @@ export default class UserSettingsModal extends React.Component {
}
handleShow() {
- $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
if ($(window).width() > 768) {
$(ReactDOM.findDOMNode(this.refs.modalBody)).perfectScrollbar();
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
+ } else {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 50);
}
}
diff --git a/web/react/dispatcher/event_helpers.jsx b/web/react/dispatcher/event_helpers.jsx
index 297555f38..297367ce9 100644
--- a/web/react/dispatcher/event_helpers.jsx
+++ b/web/react/dispatcher/event_helpers.jsx
@@ -8,6 +8,7 @@ import Constants from '../utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
import * as AsyncClient from '../utils/async_client.jsx';
import * as Client from '../utils/client.jsx';
+import * as Utils from '../utils/utils.jsx';
export function emitChannelClickEvent(channel) {
AsyncClient.getChannels(true);
@@ -38,6 +39,30 @@ export function emitPostFocusEvent(postId) {
);
}
+export function emitPostFocusRightHandSideEvent(post) {
+ Client.getPost(
+ post.channel_id,
+ post.id,
+ (data) => {
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_POST_SELECTED,
+ post_list: data
+ });
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECIEVED_SEARCH,
+ results: null
+ });
+ },
+ (err) => {
+ AsyncClient.dispatchError(err, 'getPost');
+ }
+ );
+
+ var postChannel = ChannelStore.get(post.channel_id);
+ Utils.switchChannel(postChannel);
+}
+
export function emitLoadMorePostsEvent() {
const id = ChannelStore.getCurrentId();
loadMorePostsTop(id);
diff --git a/web/react/package.json b/web/react/package.json
index 41b2468af..14b16b4e4 100644
--- a/web/react/package.json
+++ b/web/react/package.json
@@ -7,7 +7,7 @@
"flux": "2.1.1",
"highlight.js": "8.9.1",
"keymirror": "0.1.1",
- "marked": "0.3.5",
+ "marked": "mattermost/marked#cb85e5cc81bc7937dbb73c3c53d9532b1b97e3ca",
"object-assign": "4.0.1",
"twemoji": "1.4.1"
},
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 49f0935a9..2122c729e 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -18,9 +18,20 @@ import RegisterAppModal from '../components/register_app_modal.jsx';
import ImportThemeModal from '../components/user_settings/import_theme_modal.jsx';
import InviteMemberModal from '../components/invite_member_modal.jsx';
+import PreferenceStore from '../stores/preference_store.jsx';
+
+import * as Utils from '../utils/utils.jsx';
import * as AsyncClient from '../utils/async_client.jsx';
import * as EventHelpers from '../dispatcher/event_helpers.jsx';
+import Constants from '../utils/constants.jsx';
+
+function onPreferenceChange() {
+ const selectedFont = PreferenceStore.getPreference(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', {value: Constants.DEFAULT_FONT}).value;
+ Utils.applyFont(selectedFont);
+ PreferenceStore.removeChangeListener(onPreferenceChange);
+}
+
function setupChannelPage(props, team, channel) {
if (props.PostId === '') {
EventHelpers.emitChannelClickEvent(channel);
@@ -28,6 +39,7 @@ function setupChannelPage(props, team, channel) {
EventHelpers.emitPostFocusEvent(props.PostId);
}
+ PreferenceStore.addChangeListener(onPreferenceChange);
AsyncClient.getAllPreferences();
// ChannelLoader must be rendered first
diff --git a/web/react/stores/channel_store.jsx b/web/react/stores/channel_store.jsx
index 0bfde77b4..afc960fcf 100644
--- a/web/react/stores/channel_store.jsx
+++ b/web/react/stores/channel_store.jsx
@@ -317,7 +317,9 @@ ChannelStore.dispatchToken = AppDispatcher.register((payload) => {
case ActionTypes.RECIEVED_CHANNEL:
ChannelStore.pStoreChannel(action.channel);
- ChannelStore.pStoreChannelMember(action.member);
+ if (action.member) {
+ ChannelStore.pStoreChannelMember(action.member);
+ }
currentId = ChannelStore.getCurrentId();
if (currentId) {
ChannelStore.resetCounts(currentId);
diff --git a/web/react/stores/preference_store.jsx b/web/react/stores/preference_store.jsx
index e6a1d8a2b..543129aca 100644
--- a/web/react/stores/preference_store.jsx
+++ b/web/react/stores/preference_store.jsx
@@ -1,4 +1,4 @@
-// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import Constants from '../utils/constants.jsx';
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index ee501c149..24fa79ca6 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -59,13 +59,14 @@ class SocketStoreClass extends EventEmitter {
conn.onopen = () => {
if (this.failCount > 0) {
console.log('websocket re-established connection'); //eslint-disable-line no-console
+
+ if (ErrorStore.getLastError()) {
+ ErrorStore.storeLastError(null);
+ ErrorStore.emitChange();
+ }
}
this.failCount = 0;
- if (ErrorStore.getLastError()) {
- ErrorStore.storeLastError(null);
- ErrorStore.emitChange();
- }
};
conn.onclose = () => {
@@ -228,6 +229,7 @@ function handlePostEditEvent(msg) {
// Store post
const post = JSON.parse(msg.props.post);
PostStore.storePost(post);
+ PostStore.emitChange();
// Update channel state
if (ChannelStore.getCurrentId() === msg.channel_id) {
diff --git a/web/react/stores/suggestion_store.jsx b/web/react/stores/suggestion_store.jsx
index 2250ec234..9cd566c22 100644
--- a/web/react/stores/suggestion_store.jsx
+++ b/web/react/stores/suggestion_store.jsx
@@ -38,7 +38,6 @@ class SuggestionStore extends EventEmitter {
// items: a list of objects backing the terms which may be used in rendering
// components: a list of react components that can be used to render their corresponding item
// selection: the term currently selected by the keyboard
- // completeOnSpace: whether or not space will trigger the term to be autocompleted
this.suggestions = new Map();
}
@@ -79,8 +78,7 @@ class SuggestionStore extends EventEmitter {
terms: [],
items: [],
components: [],
- selection: '',
- completeOnSpace: true
+ selection: ''
});
}
@@ -95,7 +93,6 @@ class SuggestionStore extends EventEmitter {
suggestion.terms = [];
suggestion.items = [];
suggestion.components = [];
- suggestion.completeOnSpace = true;
}
clearSelection(id) {
@@ -120,12 +117,6 @@ class SuggestionStore extends EventEmitter {
suggestion.matchedPretext = matchedPretext;
}
- setCompleteOnSpace(id, completeOnSpace) {
- const suggestion = this.suggestions.get(id);
-
- suggestion.completeOnSpace = completeOnSpace;
- }
-
addSuggestion(id, term, item, component) {
const suggestion = this.suggestions.get(id);
@@ -189,10 +180,6 @@ class SuggestionStore extends EventEmitter {
return this.suggestions.get(id).selection;
}
- shouldCompleteOnSpace(id) {
- return this.suggestions.get(id).completeOnSpace;
- }
-
selectNext(id) {
this.setSelectionByDelta(id, 1);
}
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 09e962161..5d02a8c88 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -590,7 +590,12 @@ export function updateChannel(channel, success, error) {
track('api', 'api_channels_update');
}
-export function updateChannelHeader(data, success, error) {
+export function updateChannelHeader(channelId, header, success, error) {
+ const data = {
+ channel_id: channelId,
+ channel_header: header
+ };
+
$.ajax({
url: '/api/v1/channels/update_header',
dataType: 'json',
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 27cf2b175..d23c18b5d 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -123,6 +123,7 @@ export default {
POST_DELETED: 'deleted',
POST_TYPE_JOIN_LEAVE: 'system_join_leave',
SYSTEM_MESSAGE_PREFIX: 'system_',
+ SYSTEM_MESSAGE_PROFILE_NAME: 'System',
SYSTEM_MESSAGE_PROFILE_IMAGE: '/static/images/logo_compact.png',
RESERVED_TEAM_NAMES: [
'www',
@@ -150,7 +151,7 @@ export default {
],
MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
MAX_DMS: 20,
- MAX_CHANNEL_POPOVER_COUNT: 20,
+ MAX_CHANNEL_POPOVER_COUNT: 100,
DM_CHANNEL: 'D',
OPEN_CHANNEL: 'O',
PRIVATE_CHANNEL: 'P',
@@ -362,7 +363,6 @@ export default {
'Droid Serif': 'font--droid_serif',
'Roboto Slab': 'font--roboto_slab',
Lora: 'font--lora',
- Slabo: 'font--slabo',
Arvo: 'font--arvo',
'Open Sans': 'font--open_sans',
Roboto: 'font--roboto',
diff --git a/web/react/utils/emoticons.jsx b/web/react/utils/emoticons.jsx
index ab04936c0..fa5177232 100644
--- a/web/react/utils/emoticons.jsx
+++ b/web/react/utils/emoticons.jsx
@@ -13,7 +13,6 @@ const emoticonPatterns = {
rage: /(^|\s)(:-?[\[@])(?=$|\s)/g, // :@
frowning: /(^|\s)(:-?\()(?=$|\s)/g, // :(
sob: /(^|\s)(:['’]-?\(|:&#x27;\(|:&#39;\()(?=$|\s)/g, // :`(
- kissing_heart: /(^|\s)(:-?\*)(?=$|\s)/g, // :*
pensive: /(^|\s)(:-?\/)(?=$|\s)/g, // :/
confounded: /(^|\s)(:-?s)(?=$|\s)/gi, // :s
flushed: /(^|\s)(:-?\|)(?=$|\s)/g, // :|
diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx
index d600ef069..826b87d08 100644
--- a/web/react/utils/markdown.jsx
+++ b/web/react/utils/markdown.jsx
@@ -69,13 +69,11 @@ class MattermostInlineLexer extends marked.InlineLexer {
this.rules = Object.assign({}, this.rules);
- // modified version of the regex that doesn't break up words in snake_case,
- // allows for links starting with www, and allows links succounded by parentheses
+ // modified version of the regex that allows for links starting with www and those surrounded by parentheses
// the original is /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/| {2,}\n|$)/
- this.rules.text = /^[\s\S]+?(?:[^\w\/](?=_)|(?=_\W|[\\<!\[*`~]|https?:\/\/|www\.|\(| {2,}\n|$))/;
+ this.rules.text = /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/|www\.|\(| {2,}\n|$)/;
- // modified version of the regex that allows links starting with www and those surrounded
- // by parentheses
+ // modified version of the regex that allows links starting with www and those surrounded by parentheses
// the original is /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/
this.rules.url = /^(\(?(?:https?:\/\/|www\.)[^\s<.][^\s<]*[^<.,:;"'\]\s])/;
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 304713528..fb8b89252 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -300,7 +300,7 @@ export function extractLinks(text) {
Autolinker.link(text, {
replaceFn,
- urls: true,
+ urls: {schemeMatches: true, wwwMatches: true, tldMatches: false},
emails: false,
twitter: false,
phone: false,
@@ -576,6 +576,7 @@ export function applyTheme(theme) {
if (theme.sidebarHeaderTextColor) {
changeCss('.sidebar--left .team__header .header__info, .sidebar--menu .team__header .header__info', 'color:' + theme.sidebarHeaderTextColor, 1);
+ changeCss('.sidebar--left .team__header .navbar-right .dropdown__icon, .sidebar--menu .team__header .navbar-right .dropdown__icon', 'fill:' + theme.sidebarHeaderTextColor, 1);
changeCss('.sidebar--left .team__header .user__name, .sidebar--menu .team__header .user__name', 'color:' + changeOpacity(theme.sidebarHeaderTextColor, 0.8), 1);
changeCss('.sidebar--left .team__header:hover .user__name, .sidebar--menu .team__header:hover .user__name', 'color:' + theme.sidebarHeaderTextColor, 1);
changeCss('.modal .modal-header .modal-title, .modal .modal-header .modal-title .name, .modal .modal-header button.close', 'color:' + theme.sidebarHeaderTextColor, 1);