summaryrefslogtreecommitdiffstats
path: root/webapp/components
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components')
-rw-r--r--webapp/components/center_panel.jsx5
-rw-r--r--webapp/components/code_preview.jsx101
-rw-r--r--webapp/components/more_channels.jsx2
-rw-r--r--webapp/components/new_channel_modal.jsx2
-rw-r--r--webapp/components/popover_list_members.jsx1
-rw-r--r--webapp/components/suggestion/command_provider.jsx4
-rw-r--r--webapp/components/suggestion/suggestion_box.jsx3
-rw-r--r--webapp/components/textbox.jsx1
-rw-r--r--webapp/components/user_settings/manage_command_hooks.jsx313
-rw-r--r--webapp/components/user_settings/user_settings_advanced.jsx4
-rw-r--r--webapp/components/user_settings/user_settings_display.jsx2
-rw-r--r--webapp/components/view_image.jsx17
12 files changed, 325 insertions, 130 deletions
diff --git a/webapp/components/center_panel.jsx b/webapp/components/center_panel.jsx
index 17e5e43d9..6c156f2a8 100644
--- a/webapp/components/center_panel.jsx
+++ b/webapp/components/center_panel.jsx
@@ -120,7 +120,10 @@ export default class CenterPanel extends React.Component {
id='app-content'
className='app__content'
>
- <div id='channel-header'>
+ <div
+ id='channel-header'
+ className='channel-header'
+ >
<ChannelHeader
user={this.state.user}
/>
diff --git a/webapp/components/code_preview.jsx b/webapp/components/code_preview.jsx
new file mode 100644
index 000000000..e769ae590
--- /dev/null
+++ b/webapp/components/code_preview.jsx
@@ -0,0 +1,101 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+import * as syntaxHightlighting from 'utils/syntax_hightlighting.jsx';
+import Constants from 'utils/constants.jsx';
+import FileInfoPreview from './file_info_preview.jsx';
+
+import React from 'react';
+
+export default class CodePreview extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.updateStateFromProps = this.updateStateFromProps.bind(this);
+ this.handleReceivedError = this.handleReceivedError.bind(this);
+ this.handleReceivedCode = this.handleReceivedCode.bind(this);
+
+ this.state = {
+ code: '',
+ lang: '',
+ loading: true,
+ success: true
+ };
+ }
+
+ componentDidMount() {
+ this.updateStateFromProps(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (this.props.fileUrl !== nextProps.fileUrl) {
+ this.updateStateFromProps(nextProps);
+ }
+ }
+
+ updateStateFromProps(props) {
+ var usedLanguage = syntaxHightlighting.getLang(props.filename);
+
+ if (!usedLanguage || props.fileInfo.size > Constants.CODE_PREVIEW_MAX_FILE_SIZE) {
+ this.setState({code: '', lang: '', loading: false, success: false});
+ return;
+ }
+
+ this.setState({code: '', lang: usedLanguage, loading: true});
+
+ $.ajax({
+ async: true,
+ url: props.fileUrl,
+ type: 'GET',
+ error: this.handleReceivedError,
+ success: this.handleReceivedCode
+ });
+ }
+
+ handleReceivedCode(data) {
+ const parsed = syntaxHightlighting.formatCode(this.state.lang, data, this.props.filename);
+ this.setState({code: parsed, loading: false, success: true});
+ }
+
+ handleReceivedError() {
+ this.setState({loading: false, success: false});
+ }
+
+ static support(filename) {
+ return typeof syntaxHightlighting.getLang(filename) !== 'undefined';
+ }
+
+ render() {
+ if (this.state.loading) {
+ return (
+ <div className='view-image__loading'>
+ <img
+ className='loader-image'
+ src='/static/images/load.gif'
+ />
+ </div>
+ );
+ }
+
+ if (!this.state.success) {
+ return (
+ <FileInfoPreview
+ filename={this.props.filename}
+ fileUrl={this.props.fileUrl}
+ fileInfo={this.props.fileInfo}
+ formatMessage={this.props.formatMessage}
+ />
+ );
+ }
+
+ return <div dangerouslySetInnerHTML={{__html: this.state.code}}/>;
+ }
+}
+
+CodePreview.propTypes = {
+ filename: React.PropTypes.string.isRequired,
+ fileUrl: React.PropTypes.string.isRequired,
+ fileInfo: React.PropTypes.object.isRequired,
+ formatMessage: React.PropTypes.func.isRequired
+};
diff --git a/webapp/components/more_channels.jsx b/webapp/components/more_channels.jsx
index fc047eaff..d0eeec1ef 100644
--- a/webapp/components/more_channels.jsx
+++ b/webapp/components/more_channels.jsx
@@ -160,7 +160,7 @@ export default class MoreChannels extends React.Component {
return (
<div
- className='modal fade'
+ className='modal fade more-channel__modal'
id='more_channels'
ref='modal'
tabIndex='-1'
diff --git a/webapp/components/new_channel_modal.jsx b/webapp/components/new_channel_modal.jsx
index 628687f27..f69eeed49 100644
--- a/webapp/components/new_channel_modal.jsx
+++ b/webapp/components/new_channel_modal.jsx
@@ -38,7 +38,7 @@ class NewChannelModal extends React.Component {
}
componentDidMount() {
if (Utils.isBrowserIE()) {
- $('body').addClass('browser--IE');
+ $('body').addClass('browser--ie');
}
}
handleSubmit(e) {
diff --git a/webapp/components/popover_list_members.jsx b/webapp/components/popover_list_members.jsx
index 81760eb6e..7d048019c 100644
--- a/webapp/components/popover_list_members.jsx
+++ b/webapp/components/popover_list_members.jsx
@@ -158,6 +158,7 @@ export default class PopoverListMembers extends React.Component {
<Popover
ref='memebersPopover'
title={title}
+ className='member-list__popover'
id='member-list-popover'
>
<div className='more-modal__list'>{popoverHtml}</div>
diff --git a/webapp/components/suggestion/command_provider.jsx b/webapp/components/suggestion/command_provider.jsx
index 36860fa66..204f52483 100644
--- a/webapp/components/suggestion/command_provider.jsx
+++ b/webapp/components/suggestion/command_provider.jsx
@@ -37,9 +37,9 @@ CommandSuggestion.propTypes = {
};
export default class CommandProvider {
- handlePretextChanged(suggestionId, pretext) {
+ handlePretextChanged(suggestionId, pretext, channelId) {
if (pretext.startsWith('/')) {
- AsyncClient.getSuggestedCommands(pretext, suggestionId, CommandSuggestion);
+ AsyncClient.getSuggestedCommands(pretext, channelId, suggestionId, CommandSuggestion);
}
}
}
diff --git a/webapp/components/suggestion/suggestion_box.jsx b/webapp/components/suggestion/suggestion_box.jsx
index e3ec63194..97c6c6cd9 100644
--- a/webapp/components/suggestion/suggestion_box.jsx
+++ b/webapp/components/suggestion/suggestion_box.jsx
@@ -111,7 +111,7 @@ export default class SuggestionBox extends React.Component {
handlePretextChanged(pretext) {
for (const provider of this.props.providers) {
- provider.handlePretextChanged(this.suggestionId, pretext);
+ provider.handlePretextChanged(this.suggestionId, pretext, this.props.channelId);
}
}
@@ -160,6 +160,7 @@ SuggestionBox.propTypes = {
value: React.PropTypes.string.isRequired,
onUserInput: React.PropTypes.func,
providers: React.PropTypes.arrayOf(React.PropTypes.object),
+ channelId: React.PropTypes.string,
// explicitly name any input event handlers we override and need to manually call
onChange: React.PropTypes.func,
diff --git a/webapp/components/textbox.jsx b/webapp/components/textbox.jsx
index 1a395072e..952026ed5 100644
--- a/webapp/components/textbox.jsx
+++ b/webapp/components/textbox.jsx
@@ -224,6 +224,7 @@ export default class Textbox extends React.Component {
style={{visibility: this.state.preview ? 'hidden' : 'visible'}}
listComponent={SuggestionList}
providers={this.suggestionProviders}
+ channelId={this.props.channelId}
/>
<div
ref='preview'
diff --git a/webapp/components/user_settings/manage_command_hooks.jsx b/webapp/components/user_settings/manage_command_hooks.jsx
index ce353ad64..9703664cc 100644
--- a/webapp/components/user_settings/manage_command_hooks.jsx
+++ b/webapp/components/user_settings/manage_command_hooks.jsx
@@ -4,9 +4,13 @@
import LoadingScreen from '../loading_screen.jsx';
import * as Client from 'utils/client.jsx';
+import * as Utils from 'utils/utils.jsx';
+import Constants from 'utils/constants.jsx';
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'react-intl';
+const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
+
const holders = defineMessages({
requestTypePost: {
id: 'user.settings.cmds.request_type_post',
@@ -59,6 +63,7 @@ export default class ManageCommandCmds extends React.Component {
this.getCmds = this.getCmds.bind(this);
this.addNewCmd = this.addNewCmd.bind(this);
this.emptyCmd = this.emptyCmd.bind(this);
+ this.updateExternalManagement = this.updateExternalManagement.bind(this);
this.updateTrigger = this.updateTrigger.bind(this);
this.updateURL = this.updateURL.bind(this);
this.updateMethod = this.updateMethod.bind(this);
@@ -99,7 +104,7 @@ export default class ManageCommandCmds extends React.Component {
addNewCmd(e) {
e.preventDefault();
- if (this.state.cmd.trigger === '' || this.state.cmd.url === '') {
+ if (this.state.cmd.url === '' || (this.state.cmd.trigger === '' && !this.state.external_management)) {
return;
}
@@ -189,6 +194,12 @@ export default class ManageCommandCmds extends React.Component {
);
}
+ updateExternalManagement(e) {
+ var cmd = this.state.cmd;
+ cmd.external_management = e.target.checked;
+ this.setState(cmd);
+ }
+
updateTrigger(e) {
var cmd = this.state.cmd;
cmd.trigger = e.target.value;
@@ -270,11 +281,26 @@ export default class ManageCommandCmds extends React.Component {
);
}
+ let slashCommandAutocompleteDiv;
+ if (Utils.isFeatureEnabled(PreReleaseFeatures.SLASHCMD_AUTOCMP)) {
+ slashCommandAutocompleteDiv = (
+ <div className='padding-top x2'>
+ <strong>
+ <FormattedMessage
+ id='user.settings.cmds.external_management'
+ defaultMessage='External management: '
+ />
+ </strong><span className='word-break--all'>{cmd.external_management ? this.props.intl.formatMessage(holders.autocompleteYes) : this.props.intl.formatMessage(holders.autocompleteNo)}</span>
+ </div>
+ );
+ }
+
cmds.push(
<div
key={cmd.id}
className='webhook__item webcmd__item'
>
+ {slashCommandAutocompleteDiv}
{triggerDiv}
<div className='padding-top x2 webcmd__url'>
<strong>
@@ -416,43 +442,115 @@ export default class ManageCommandCmds extends React.Component {
</div>
);
- const disableButton = this.state.cmd.trigger === '' || this.state.cmd.url === '';
+ const disableButton = this.state.cmd.url === '' || (this.state.cmd.trigger === '' && !this.state.external_management);
- return (
- <div key='addCommandCmd'>
- <FormattedHTMLMessage
- id='user.settings.cmds.add_desc'
- defaultMessage='Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name “Joe Smith”. Please see <a href="http://docs.mattermost.com/developer/slash-commands.html">Slash commands documentation</a> for detailed instructions. View all slash commands configured on this team below.'
- />
- <div><label className='control-label padding-top x2'>
- <FormattedMessage
- id='user.settings.cmds.add_new'
- defaultMessage='Add a new command'
- />
- </label></div>
- <div className='padding-top divider-light'></div>
- <div className='padding-top'>
+ let triggerInput;
+ if (!this.state.cmd.external_management) {
+ triggerInput = (
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.trigger'
+ defaultMessage='Command Trigger Word: '
+ />
+ </label>
+ <div className='padding-top'>
+ <input
+ ref='trigger'
+ className='form-control'
+ value={this.state.cmd.trigger}
+ onChange={this.updateTrigger}
+ placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.cmds.trigger_desc'
+ defaultMessage='Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug'
+ />
+ </div>
+ </div>
+ );
+ }
+ let slashCommandAutocompleteCheckbox;
+ if (Utils.isFeatureEnabled(PreReleaseFeatures.SLASHCMD_AUTOCMP)) {
+ slashCommandAutocompleteCheckbox = (
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.external_management'
+ defaultMessage='External management: '
+ />
+ </label>
+ <div className='padding-top'>
+ <div className='checkbox'>
+ <label>
+ <input
+ type='checkbox'
+ checked={this.state.cmd.external_management}
+ onChange={this.updateExternalManagement}
+ />
+ <FormattedMessage
+ id='user.settings.cmds.slashCmd_autocmp'
+ defaultMessage='Enable external application to offer autocomplete'
+ />
+ </label>
+ </div>
+ </div>
+ </div>
+
+ );
+ }
+
+ let autoCompleteSettings;
+ if (!this.state.cmd.external_management) {
+ autoCompleteSettings = (
+ <div>
<div className='padding-top x2'>
<label className='control-label'>
<FormattedMessage
- id='user.settings.cmds.trigger'
- defaultMessage='Command Trigger Word: '
+ id='user.settings.cmds.auto_complete'
+ defaultMessage='Autocomplete: '
+ />
+ </label>
+ <div className='padding-top'>
+ <div className='checkbox'>
+ <label>
+ <input
+ type='checkbox'
+ checked={this.state.cmd.auto_complete}
+ onChange={this.updateAutoComplete}
+ />
+ <FormattedMessage
+ id='user.settings.cmds.auto_complete_help'
+ defaultMessage=' Show this command in the autocomplete list.'
+ />
+ </label>
+ </div>
+ </div>
+ </div>
+
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.auto_complete_hint'
+ defaultMessage='Autocomplete Hint: '
/>
</label>
<div className='padding-top'>
<input
- ref='trigger'
+ ref='autoCompleteHint'
className='form-control'
- value={this.state.cmd.trigger}
- onChange={this.updateTrigger}
- placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
+ value={this.state.cmd.auto_complete_hint}
+ onChange={this.updateAutoCompleteHint}
+ placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
/>
</div>
<div className='padding-top'>
<FormattedMessage
- id='user.settings.cmds.trigger_desc'
- defaultMessage='Examples: /patient, /client, /employee Reserved: /echo, /join, /logout, /me, /shrug'
+ id='user.settings.cmds.auto_complete_hint_desc'
+ defaultMessage='Optional hint in the autocomplete list about parameters needed for command.'
/>
</div>
</div>
@@ -460,6 +558,76 @@ export default class ManageCommandCmds extends React.Component {
<div className='padding-top x2'>
<label className='control-label'>
<FormattedMessage
+ id='user.settings.cmds.auto_complete_desc'
+ defaultMessage='Autocomplete Description: '
+ />
+ </label>
+ <div className='padding-top'>
+ <input
+ ref='autoCompleteDesc'
+ className='form-control'
+ value={this.state.cmd.auto_complete_desc}
+ onChange={this.updateAutoCompleteDesc}
+ placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.cmds.auto_complete_desc_desc'
+ defaultMessage='Optional short description of slash command for the autocomplete list.'
+ />
+ </div>
+ </div>
+
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
+ id='user.settings.cmds.display_name'
+ defaultMessage='Descriptive Label: '
+ />
+ </label>
+ <div className='padding-top'>
+ <input
+ ref='displayName'
+ className='form-control'
+ value={this.state.cmd.display_name}
+ onChange={this.updateDisplayName}
+ placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
+ />
+ </div>
+ <div className='padding-top'>
+ <FormattedMessage
+ id='user.settings.cmds.cmd_display_name'
+ defaultMessage='Brief description of slash command to show in listings.'
+ />
+ </div>
+ {addError}
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div key='addCommandCmd'>
+ <FormattedHTMLMessage
+ id='user.settings.cmds.add_desc'
+ defaultMessage='Create slash commands to send events to external integrations and receive a response. For example typing `/patient Joe Smith` could bring back search results from your internal health records management system for the name “Joe Smith”. Please see <a href="http://docs.mattermost.com/developer/slash-commands.html">Slash commands documentation</a> for detailed instructions. View all slash commands configured on this team below.'
+ />
+ <div><label className='control-label padding-top x2'>
+ <FormattedMessage
+ id='user.settings.cmds.add_new'
+ defaultMessage='Add a new command'
+ />
+ </label></div>
+ <div className='padding-top divider-light'></div>
+ <div className='padding-top'>
+
+ {slashCommandAutocompleteCheckbox}
+ {triggerInput}
+
+ <div className='padding-top x2'>
+ <label className='control-label'>
+ <FormattedMessage
id='user.settings.cmds.url'
defaultMessage='Request URL: '
/>
@@ -560,102 +728,7 @@ export default class ManageCommandCmds extends React.Component {
</div>
</div>
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete'
- defaultMessage='Autocomplete: '
- />
- </label>
- <div className='padding-top'>
- <div className='checkbox'>
- <label>
- <input
- type='checkbox'
- checked={this.state.cmd.auto_complete}
- onChange={this.updateAutoComplete}
- />
- <FormattedMessage
- id='user.settings.cmds.auto_complete_help'
- defaultMessage=' Show this command in the autocomplete list.'
- />
- </label>
- </div>
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint'
- defaultMessage='Autocomplete Hint: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='autoCompleteHint'
- className='form-control'
- value={this.state.cmd.auto_complete_hint}
- onChange={this.updateAutoCompleteHint}
- placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_hint_desc'
- defaultMessage='Optional hint in the autocomplete list about parameters needed for command.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc'
- defaultMessage='Autocomplete Description: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='autoCompleteDesc'
- className='form-control'
- value={this.state.cmd.auto_complete_desc}
- onChange={this.updateAutoCompleteDesc}
- placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.auto_complete_desc_desc'
- defaultMessage='Optional short description of slash command for the autocomplete list.'
- />
- </div>
- </div>
-
- <div className='padding-top x2'>
- <label className='control-label'>
- <FormattedMessage
- id='user.settings.cmds.display_name'
- defaultMessage='Descriptive Label: '
- />
- </label>
- <div className='padding-top'>
- <input
- ref='displayName'
- className='form-control'
- value={this.state.cmd.display_name}
- onChange={this.updateDisplayName}
- placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
- />
- </div>
- <div className='padding-top'>
- <FormattedMessage
- id='user.settings.cmds.cmd_display_name'
- defaultMessage='Brief description of slash command to show in listings.'
- />
- </div>
- {addError}
- </div>
+ {autoCompleteSettings}
<div className='padding-top x2 padding-bottom'>
<a
diff --git a/webapp/components/user_settings/user_settings_advanced.jsx b/webapp/components/user_settings/user_settings_advanced.jsx
index 7c496f57b..40897e8c9 100644
--- a/webapp/components/user_settings/user_settings_advanced.jsx
+++ b/webapp/components/user_settings/user_settings_advanced.jsx
@@ -51,6 +51,10 @@ const holders = defineMessages({
EMBED_TOGGLE: {
id: 'user.settings.advance.embed_toggle',
defaultMessage: 'Show toggle for all embed previews'
+ },
+ SLASHCMD_AUTOCMP: {
+ id: 'user.settings.advance.slashCmd_autocmp',
+ defaultMessage: 'Enable external application to offer slash command autocomplete'
}
});
diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx
index 58d4493cb..3299588f7 100644
--- a/webapp/components/user_settings/user_settings_display.jsx
+++ b/webapp/components/user_settings/user_settings_display.jsx
@@ -195,7 +195,7 @@ export default class UserSettingsDisplay extends React.Component {
const showUsername = (
<FormattedMessage
id='user.settings.display.showUsername'
- defaultMessage='Show username (team default)'
+ defaultMessage='Show username (default)'
/>
);
const showNickname = (
diff --git a/webapp/components/view_image.jsx b/webapp/components/view_image.jsx
index e739fca30..7572f88ae 100644
--- a/webapp/components/view_image.jsx
+++ b/webapp/components/view_image.jsx
@@ -7,6 +7,7 @@ import * as Client from 'utils/client.jsx';
import * as Utils from 'utils/utils.jsx';
import AudioVideoPreview from './audio_video_preview.jsx';
import Constants from 'utils/constants.jsx';
+import CodePreview from './code_preview.jsx';
import FileInfoPreview from './file_info_preview.jsx';
import FileStore from 'stores/file_store.jsx';
import ViewImagePopoverBar from './view_image_popover_bar.jsx';
@@ -254,6 +255,15 @@ class ViewImageModal extends React.Component {
formatMessage={this.props.intl.formatMessage}
/>
);
+ } else if (CodePreview.support(filename)) {
+ content = (
+ <CodePreview
+ filename={filename}
+ fileUrl={fileUrl}
+ fileInfo={fileInfo}
+ formatMessage={this.props.intl.formatMessage}
+ />
+ );
} else {
content = (
<FileInfoPreview
@@ -311,18 +321,19 @@ class ViewImageModal extends React.Component {
<Modal
show={this.props.show}
onHide={this.props.onModalDismissed}
- className='image_modal'
+ className='modal-image'
dialogClassName='modal-image'
>
<Modal.Body
- modalClassName='image-body'
+ modalClassName='modal-image__body'
onClick={this.props.onModalDismissed}
>
<div
- className={'image-wrapper'}
+ className={'modal-image__wrapper'}
onClick={this.props.onModalDismissed}
>
<div
+ className='modal-back'
onMouseEnter={this.onMouseEnterImage}
onMouseLeave={this.onMouseLeaveImage}
onClick={(e) => e.stopPropagation()}