Этот коммит содержится в:
=Corey Hulen
2016-03-17 09:51:45 -07:00
родитель 074f15f761 128204b47a
Коммит 5273559ca1
90 изменённых файлов: 2766 добавлений и 1650 удалений

Просмотреть файл

@@ -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}
/>

101
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
};

Просмотреть файл

@@ -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'

Просмотреть файл

@@ -38,7 +38,7 @@ class NewChannelModal extends React.Component {
}
componentDidMount() {
if (Utils.isBrowserIE()) {
$('body').addClass('browser--IE');
$('body').addClass('browser--ie');
}
}
handleSubmit(e) {

Просмотреть файл

@@ -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>

Просмотреть файл

@@ -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);
}
}
}

Просмотреть файл

@@ -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,

Просмотреть файл

@@ -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'

Просмотреть файл

@@ -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,7 +442,170 @@ 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);
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.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>
</div>
);
}
return (
<div key='addCommandCmd'>
@@ -433,29 +622,8 @@ export default class ManageCommandCmds extends React.Component {
<div className='padding-top divider-light'></div>
<div className='padding-top'>
<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>
{slashCommandAutocompleteCheckbox}
{triggerInput}
<div className='padding-top x2'>
<label className='control-label'>
@@ -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

Просмотреть файл

@@ -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'
}
});

Просмотреть файл

@@ -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 = (

Просмотреть файл

@@ -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()}