diff options
Diffstat (limited to 'web/react')
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)(:['’]-?\(|:'\(|:'\()(?=$|\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); |