Merge pull request #1196 from florianorben/send-on-ctrl
allow messages to be send on ctrl+enter
Этот коммит содержится в:
@@ -8,6 +8,7 @@ const SocketStore = require('../stores/socket_store.jsx');
|
||||
const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const PostStore = require('../stores/post_store.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const Textbox = require('./textbox.jsx');
|
||||
const MsgTyping = require('./msg_typing.jsx');
|
||||
const FileUpload = require('./file_upload.jsx');
|
||||
@@ -27,7 +28,7 @@ export default class CreateComment extends React.Component {
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.commentMsgKeyPress = this.commentMsgKeyPress.bind(this);
|
||||
this.handleUserInput = this.handleUserInput.bind(this);
|
||||
this.handleArrowUp = this.handleArrowUp.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
this.handleUploadStart = this.handleUploadStart.bind(this);
|
||||
this.handleFileUploadComplete = this.handleFileUploadComplete.bind(this);
|
||||
this.handleUploadError = this.handleUploadError.bind(this);
|
||||
@@ -36,6 +37,7 @@ export default class CreateComment extends React.Component {
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.getFileCount = this.getFileCount.bind(this);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
||||
|
||||
PostStore.clearCommentDraftUploads();
|
||||
|
||||
@@ -45,15 +47,23 @@ export default class CreateComment extends React.Component {
|
||||
uploadsInProgress: draft.uploadsInProgress,
|
||||
previews: draft.previews,
|
||||
submitting: false,
|
||||
windowWidth: Utils.windowWidth()
|
||||
windowWidth: Utils.windowWidth(),
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
PreferenceStore.removeChangeListener(this.onPreferenceChange);
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
onPreferenceChange() {
|
||||
this.setState({
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
});
|
||||
}
|
||||
handleResize() {
|
||||
this.setState({windowWidth: Utils.windowWidth()});
|
||||
}
|
||||
@@ -140,10 +150,12 @@ export default class CreateComment extends React.Component {
|
||||
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
||||
}
|
||||
commentMsgKeyPress(e) {
|
||||
if (e.which === 13 && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
ReactDOM.findDOMNode(this.refs.textbox).blur();
|
||||
this.handleSubmit(e);
|
||||
if (this.state.ctrlSend === 'true' && e.ctrlKey || this.state.ctrlSend === 'false') {
|
||||
if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
ReactDOM.findDOMNode(this.refs.textbox).blur();
|
||||
this.handleSubmit(e);
|
||||
}
|
||||
}
|
||||
|
||||
const t = Date.now();
|
||||
@@ -161,7 +173,12 @@ export default class CreateComment extends React.Component {
|
||||
$('.post-right__scroll').perfectScrollbar('update');
|
||||
this.setState({messageText: messageText});
|
||||
}
|
||||
handleArrowUp(e) {
|
||||
handleKeyDown(e) {
|
||||
if (this.state.ctrlSend === 'true' && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
|
||||
this.commentMsgKeyPress(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -313,7 +330,7 @@ export default class CreateComment extends React.Component {
|
||||
<Textbox
|
||||
onUserInput={this.handleUserInput}
|
||||
onKeyPress={this.commentMsgKeyPress}
|
||||
onKeyDown={this.handleArrowUp}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
messageText={this.state.messageText}
|
||||
createMessage='Add a comment...'
|
||||
initialText=''
|
||||
|
||||
@@ -8,6 +8,7 @@ const ChannelStore = require('../stores/channel_store.jsx');
|
||||
const PostStore = require('../stores/post_store.jsx');
|
||||
const UserStore = require('../stores/user_store.jsx');
|
||||
const SocketStore = require('../stores/socket_store.jsx');
|
||||
const PreferenceStore = require('../stores/preference_store.jsx');
|
||||
const MsgTyping = require('./msg_typing.jsx');
|
||||
const Textbox = require('./textbox.jsx');
|
||||
const FileUpload = require('./file_upload.jsx');
|
||||
@@ -36,9 +37,10 @@ export default class CreatePost extends React.Component {
|
||||
this.removePreview = this.removePreview.bind(this);
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.getFileCount = this.getFileCount.bind(this);
|
||||
this.handleArrowUp = this.handleArrowUp.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
this.sendMessage = this.sendMessage.bind(this);
|
||||
this.onPreferenceChange = this.onPreferenceChange.bind(this);
|
||||
|
||||
PostStore.clearDraftUploads();
|
||||
|
||||
@@ -52,8 +54,16 @@ export default class CreatePost extends React.Component {
|
||||
submitting: false,
|
||||
initialText: draft.messageText,
|
||||
windowWidth: Utils.windowWidth(),
|
||||
windowHeight: Utils.windowHeight()
|
||||
windowHeight: Utils.windowHeight(),
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
};
|
||||
|
||||
PreferenceStore.addChangeListener(this.onPreferenceChange);
|
||||
}
|
||||
onPreferenceChange() {
|
||||
this.setState({
|
||||
ctrlSend: PreferenceStore.getPreference(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'send_on_ctrl_enter', {value: 'false'}).value
|
||||
});
|
||||
}
|
||||
handleResize() {
|
||||
this.setState({
|
||||
@@ -201,10 +211,12 @@ export default class CreatePost extends React.Component {
|
||||
);
|
||||
}
|
||||
postMsgKeyPress(e) {
|
||||
if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
ReactDOM.findDOMNode(this.refs.textbox).blur();
|
||||
this.handleSubmit(e);
|
||||
if (this.state.ctrlSend === 'true' && e.ctrlKey || this.state.ctrlSend === 'false') {
|
||||
if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
|
||||
e.preventDefault();
|
||||
ReactDOM.findDOMNode(this.refs.textbox).blur();
|
||||
this.handleSubmit(e);
|
||||
}
|
||||
}
|
||||
|
||||
const t = Date.now();
|
||||
@@ -328,7 +340,12 @@ export default class CreatePost extends React.Component {
|
||||
const draft = PostStore.getDraft(channelId);
|
||||
return draft.previews.length + draft.uploadsInProgress.length;
|
||||
}
|
||||
handleArrowUp(e) {
|
||||
handleKeyDown(e) {
|
||||
if (this.state.ctrlSend === 'true' && e.keyCode === KeyCodes.ENTER && e.ctrlKey === true) {
|
||||
this.postMsgKeyPress(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -393,7 +410,7 @@ export default class CreatePost extends React.Component {
|
||||
<Textbox
|
||||
onUserInput={this.handleUserInput}
|
||||
onKeyPress={this.postMsgKeyPress}
|
||||
onKeyDown={this.handleArrowUp}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onHeightChange={this.resizePostHolder}
|
||||
messageText={this.state.messageText}
|
||||
createMessage='Write a message...'
|
||||
|
||||
@@ -10,6 +10,7 @@ var AppearanceTab = require('./user_settings_appearance.jsx');
|
||||
var DeveloperTab = require('./user_settings_developer.jsx');
|
||||
var IntegrationsTab = require('./user_settings_integrations.jsx');
|
||||
var DisplayTab = require('./user_settings_display.jsx');
|
||||
var AdvancedTab = require('./user_settings_advanced.jsx');
|
||||
|
||||
export default class UserSettings extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -110,6 +111,17 @@ export default class UserSettings extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (this.props.activeTab === 'advanced') {
|
||||
return (
|
||||
<div>
|
||||
<AdvancedTab
|
||||
user={this.state.user}
|
||||
activeSection={this.props.activeSection}
|
||||
updateSection={this.props.updateSection}
|
||||
updateTab={this.props.updateTab}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div/>;
|
||||
|
||||
169
web/react/components/user_settings/user_settings_advanced.jsx
Обычный файл
169
web/react/components/user_settings/user_settings_advanced.jsx
Обычный файл
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
const Client = require('../../utils/client.jsx');
|
||||
const SettingItemMin = require('../setting_item_min.jsx');
|
||||
const SettingItemMax = require('../setting_item_max.jsx');
|
||||
const Constants = require('../../utils/constants.jsx');
|
||||
const PreferenceStore = require('../../stores/preference_store.jsx');
|
||||
|
||||
export default class AdvancedSettingsDisplay extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.updateSection = this.updateSection.bind(this);
|
||||
this.updateSetting = this.updateSetting.bind(this);
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
this.setupInitialState = this.setupInitialState.bind(this);
|
||||
|
||||
this.state = this.setupInitialState();
|
||||
}
|
||||
|
||||
setupInitialState() {
|
||||
const sendOnCtrlEnter = PreferenceStore.getPreference(
|
||||
Constants.Preferences.CATEGORY_ADVANCED_SETTINGS,
|
||||
'send_on_ctrl_enter',
|
||||
{value: 'false'}
|
||||
).value;
|
||||
|
||||
return {
|
||||
settings: {send_on_ctrl_enter: sendOnCtrlEnter}
|
||||
};
|
||||
}
|
||||
|
||||
updateSetting(setting, value) {
|
||||
const settings = this.state.settings;
|
||||
settings[setting] = value;
|
||||
this.setState(settings);
|
||||
}
|
||||
|
||||
handleSubmit(setting) {
|
||||
const preference = PreferenceStore.setPreference(
|
||||
Constants.Preferences.CATEGORY_ADVANCED_SETTINGS,
|
||||
setting,
|
||||
this.state.settings[setting]
|
||||
);
|
||||
|
||||
Client.savePreferences([preference],
|
||||
() => {
|
||||
PreferenceStore.emitChange();
|
||||
this.updateSection('');
|
||||
},
|
||||
(err) => {
|
||||
this.setState({serverError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateSection(section) {
|
||||
this.props.updateSection(section);
|
||||
}
|
||||
|
||||
handleClose() {
|
||||
this.updateSection('');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
$('#user_settings').on('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$('#user_settings').off('hidden.bs.modal', this.handleClose);
|
||||
}
|
||||
|
||||
render() {
|
||||
const serverError = this.state.serverError || null;
|
||||
let ctrlSendSection;
|
||||
|
||||
if (this.props.activeSection === 'advancedCtrlSend') {
|
||||
const ctrlSendActive = [
|
||||
this.state.settings.send_on_ctrl_enter === 'true',
|
||||
this.state.settings.send_on_ctrl_enter === 'false'
|
||||
];
|
||||
|
||||
const inputs = [
|
||||
<div key='ctrlSendSetting'>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
checked={ctrlSendActive[0]}
|
||||
onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'true')}
|
||||
/>
|
||||
{'On'}
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div className='radio'>
|
||||
<label>
|
||||
<input
|
||||
type='radio'
|
||||
checked={ctrlSendActive[1]}
|
||||
onChange={this.updateSetting.bind(this, 'send_on_ctrl_enter', 'false')}
|
||||
/>
|
||||
{'Off'}
|
||||
</label>
|
||||
<br/>
|
||||
</div>
|
||||
<div><br/>{'If enabled \'Enter\' inserts a new line and \'Ctrl + Enter\' submits the message.'}</div>
|
||||
</div>
|
||||
];
|
||||
|
||||
ctrlSendSection = (
|
||||
<SettingItemMax
|
||||
title='Send messages on Ctrl + Enter'
|
||||
inputs={inputs}
|
||||
submit={() => this.handleSubmit('send_on_ctrl_enter')}
|
||||
server_error={serverError}
|
||||
updateSection={(e) => {
|
||||
this.updateSection('');
|
||||
e.preventDefault();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
ctrlSendSection = (
|
||||
<SettingItemMin
|
||||
title='Send messages on Ctrl + Enter'
|
||||
describe={this.state.settings.send_on_ctrl_enter === 'true' ? 'On' : 'Off'}
|
||||
updateSection={() => this.props.updateSection('advancedCtrlSend')}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='modal-header'>
|
||||
<button
|
||||
type='button'
|
||||
className='close'
|
||||
data-dismiss='modal'
|
||||
aria-label='Close'
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
</button>
|
||||
<h4
|
||||
className='modal-title'
|
||||
ref='title'
|
||||
>
|
||||
<i className='modal-back'></i>
|
||||
{'Advanced Settings'}
|
||||
</h4>
|
||||
</div>
|
||||
<div className='user-settings'>
|
||||
<h3 className='tab-header'>{'Advanced Settings'}</h3>
|
||||
<div className='divider-dark first'/>
|
||||
{ctrlSendSection}
|
||||
<div className='divider-dark'/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AdvancedSettingsDisplay.propTypes = {
|
||||
user: React.PropTypes.object,
|
||||
updateSection: React.PropTypes.func,
|
||||
updateTab: React.PropTypes.func,
|
||||
activeSection: React.PropTypes.string
|
||||
};
|
||||
@@ -570,6 +570,7 @@ export default class UserSettingsGeneralTab extends React.Component {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='modal-header'>
|
||||
|
||||
@@ -43,6 +43,7 @@ export default class UserSettingsModal extends React.Component {
|
||||
tabs.push({name: 'integrations', uiName: 'Integrations', icon: 'glyphicon glyphicon-transfer'});
|
||||
}
|
||||
tabs.push({name: 'display', uiName: 'Display', icon: 'glyphicon glyphicon-eye-open'});
|
||||
tabs.push({name: 'advanced', uiName: 'Advanced', icon: 'glyphicon glyphicon-list-alt'});
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -304,7 +304,8 @@ module.exports = {
|
||||
],
|
||||
Preferences: {
|
||||
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show',
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings'
|
||||
CATEGORY_DISPLAY_SETTINGS: 'display_settings',
|
||||
CATEGORY_ADVANCED_SETTINGS: 'advanced_settings'
|
||||
},
|
||||
KeyCodes: {
|
||||
UP: 38,
|
||||
|
||||
Ссылка в новой задаче
Block a user