PLT-74: Enable Up Arrow keyboard shortcut to edit your last message

Этот коммит содержится в:
Florian Orben
2015-10-15 02:13:48 +02:00
родитель c890e21cef
Коммит 551b07960e
5 изменённых файлов: 97 добавлений и 3 удалений

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

@@ -16,6 +16,7 @@ const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx'); const Constants = require('../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes; const ActionTypes = Constants.ActionTypes;
const KeyCodes = Constants.KeyCodes;
export default class CreatePost extends React.Component { export default class CreatePost extends React.Component {
constructor(props) { constructor(props) {
@@ -35,6 +36,7 @@ export default class CreatePost extends React.Component {
this.removePreview = this.removePreview.bind(this); this.removePreview = this.removePreview.bind(this);
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
this.getFileCount = this.getFileCount.bind(this); this.getFileCount = this.getFileCount.bind(this);
this.handleArrowUp = this.handleArrowUp.bind(this);
PostStore.clearDraftUploads(); PostStore.clearDraftUploads();
@@ -172,7 +174,7 @@ export default class CreatePost extends React.Component {
} }
} }
postMsgKeyPress(e) { postMsgKeyPress(e) {
if (e.which === 13 && !e.shiftKey && !e.altKey) { if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
e.preventDefault(); e.preventDefault();
ReactDOM.findDOMNode(this.refs.textbox).blur(); ReactDOM.findDOMNode(this.refs.textbox).blur();
this.handleSubmit(e); this.handleSubmit(e);
@@ -292,6 +294,24 @@ export default class CreatePost extends React.Component {
const draft = PostStore.getDraft(channelId); const draft = PostStore.getDraft(channelId);
return draft.previews.length + draft.uploadsInProgress.length; return draft.previews.length + draft.uploadsInProgress.length;
} }
handleArrowUp(e) {
if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
e.preventDefault();
const channelId = ChannelStore.getCurrentId();
const lastPost = PostStore.getCurrentUsersLatestPost(channelId);
var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post';
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_EDIT_POST,
refoucsId: '#post_textbox',
title: type,
message: lastPost.message,
lastPostId: lastPost.id,
channelId: lastPost.channel_id
});
}
}
render() { render() {
let serverError = null; let serverError = null;
if (this.state.serverError) { if (this.state.serverError) {
@@ -336,6 +356,7 @@ export default class CreatePost extends React.Component {
<Textbox <Textbox
onUserInput={this.handleUserInput} onUserInput={this.handleUserInput}
onKeyPress={this.postMsgKeyPress} onKeyPress={this.postMsgKeyPress}
onKeyDown={this.handleArrowUp}
onHeightChange={this.resizePostHolder} onHeightChange={this.resizePostHolder}
messageText={this.state.messageText} messageText={this.state.messageText}
createMessage='Write a message...' createMessage='Write a message...'

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

@@ -5,6 +5,7 @@ var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var Textbox = require('./textbox.jsx'); var Textbox = require('./textbox.jsx');
var BrowserStore = require('../stores/browser_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx');
var PostStore = require('../stores/post_store.jsx');
export default class EditPostModal extends React.Component { export default class EditPostModal extends React.Component {
constructor() { constructor() {
@@ -14,6 +15,7 @@ export default class EditPostModal extends React.Component {
this.handleEditInput = this.handleEditInput.bind(this); this.handleEditInput = this.handleEditInput.bind(this);
this.handleEditKeyPress = this.handleEditKeyPress.bind(this); this.handleEditKeyPress = this.handleEditKeyPress.bind(this);
this.handleUserInput = this.handleUserInput.bind(this); this.handleUserInput = this.handleUserInput.bind(this);
this.handleEditPostEvent = this.handleEditPostEvent.bind(this);
this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''}; this.state = {editText: '', title: '', post_id: '', channel_id: '', comments: 0, refocusId: ''};
} }
@@ -59,6 +61,18 @@ export default class EditPostModal extends React.Component {
handleUserInput(e) { handleUserInput(e) {
this.setState({editText: e.target.value}); this.setState({editText: e.target.value});
} }
handleEditPostEvent(options) {
this.setState({
editText: options.message || '',
title: options.title || '',
post_id: options.postId || '',
channel_id: options.channelId || '',
comments: options.comments || 0,
refocusId: options.refocusId || ''
});
$(React.findDOMNode(this.refs.modal)).modal('show');
}
componentDidMount() { componentDidMount() {
var self = this; var self = this;
@@ -68,12 +82,20 @@ export default class EditPostModal extends React.Component {
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) { $(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
var button = e.relatedTarget; var button = e.relatedTarget;
if (!button) {
return;
}
self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refoucsid')}); self.setState({editText: $(button).attr('data-message'), title: $(button).attr('data-title'), channel_id: $(button).attr('data-channelid'), post_id: $(button).attr('data-postid'), comments: $(button).attr('data-comments'), refocusId: $(button).attr('data-refoucsid')});
}); });
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() { $(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
self.refs.editbox.resize(); self.refs.editbox.resize();
}); });
PostStore.addEditPostListener(this.handleEditPostEvent);
}
componentWillUnmount() {
PostStore.removeEditPostListener(this.handleEditPostEvent);
} }
render() { render() {
var error = (<div className='form-group'><br /></div>); var error = (<div className='form-group'><br /></div>);

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

@@ -9,6 +9,7 @@ const ErrorStore = require('../stores/error_store.jsx');
const Utils = require('../utils/utils.jsx'); const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx'); const Constants = require('../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes; const ActionTypes = Constants.ActionTypes;
const KeyCodes = Constants.KeyCodes;
export default class Textbox extends React.Component { export default class Textbox extends React.Component {
constructor(props) { constructor(props) {
@@ -148,8 +149,10 @@ export default class Textbox extends React.Component {
this.doProcessMentions = true; this.doProcessMentions = true;
} }
if (e.keyCode === 8) { if (e.keyCode === KeyCodes.BACKSPACE) {
this.handleBackspace(e); this.handleBackspace(e);
} else if (this.props.onKeyDown) {
this.props.onKeyDown(e);
} }
} }
@@ -318,5 +321,6 @@ Textbox.propTypes = {
onUserInput: React.PropTypes.func.isRequired, onUserInput: React.PropTypes.func.isRequired,
onKeyPress: React.PropTypes.func.isRequired, onKeyPress: React.PropTypes.func.isRequired,
onHeightChange: React.PropTypes.func, onHeightChange: React.PropTypes.func,
createMessage: React.PropTypes.string.isRequired createMessage: React.PropTypes.string.isRequired,
onKeyDown: React.PropTypes.func
}; };

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

@@ -6,6 +6,7 @@ var EventEmitter = require('events').EventEmitter;
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx'); var BrowserStore = require('../stores/browser_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var Constants = require('../utils/constants.jsx'); var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
@@ -16,6 +17,7 @@ var SEARCH_TERM_CHANGE_EVENT = 'search_term_change';
var SELECTED_POST_CHANGE_EVENT = 'selected_post_change'; var SELECTED_POST_CHANGE_EVENT = 'selected_post_change';
var MENTION_DATA_CHANGE_EVENT = 'mention_data_change'; var MENTION_DATA_CHANGE_EVENT = 'mention_data_change';
var ADD_MENTION_EVENT = 'add_mention'; var ADD_MENTION_EVENT = 'add_mention';
var EDIT_POST_EVENT = 'edit_post';
class PostStoreClass extends EventEmitter { class PostStoreClass extends EventEmitter {
constructor() { constructor() {
@@ -75,6 +77,10 @@ class PostStoreClass extends EventEmitter {
this.clearCommentDraftUploads = this.clearCommentDraftUploads.bind(this); this.clearCommentDraftUploads = this.clearCommentDraftUploads.bind(this);
this.storeLatestUpdate = this.storeLatestUpdate.bind(this); this.storeLatestUpdate = this.storeLatestUpdate.bind(this);
this.getLatestUpdate = this.getLatestUpdate.bind(this); this.getLatestUpdate = this.getLatestUpdate.bind(this);
this.emitEditPost = this.emitEditPost.bind(this);
this.addEditPostListener = this.addEditPostListener.bind(this);
this.removeEditPostListener = this.removeEditPostListener.bind(this);
this.getCurrentUsersLatestPost = this.getCurrentUsersLatestPost.bind(this);
} }
emitChange() { emitChange() {
this.emit(CHANGE_EVENT); this.emit(CHANGE_EVENT);
@@ -148,6 +154,18 @@ class PostStoreClass extends EventEmitter {
this.removeListener(ADD_MENTION_EVENT, callback); this.removeListener(ADD_MENTION_EVENT, callback);
} }
emitEditPost(post) {
this.emit(EDIT_POST_EVENT, post);
}
addEditPostListener(callback) {
this.on(EDIT_POST_EVENT, callback);
}
removeEditPostListener(callback) {
this.removeListener(EDIT_POST_EVENT, callback);
}
getCurrentPosts() { getCurrentPosts() {
var currentId = ChannelStore.getCurrentId(); var currentId = ChannelStore.getCurrentId();
@@ -212,6 +230,22 @@ class PostStoreClass extends EventEmitter {
getPosts(channelId) { getPosts(channelId) {
return BrowserStore.getItem('posts_' + channelId); return BrowserStore.getItem('posts_' + channelId);
} }
getCurrentUsersLatestPost(channelId) {
const userId = UserStore.getCurrentId();
var postList = makePostListNonNull(this.getPosts(channelId));
var i = 0;
var len = postList.order.length;
var lastPost = null;
for (i; i < len; i++) {
if (postList.posts[postList.order[i]].user_id === userId) {
lastPost = postList.posts[postList.order[i]];
break;
}
}
return lastPost;
}
storePost(post) { storePost(post) {
this.pStorePost(post); this.pStorePost(post);
this.emitChange(); this.emitChange();
@@ -446,6 +480,9 @@ PostStore.dispatchToken = AppDispatcher.register(function registry(payload) {
case ActionTypes.RECIEVED_ADD_MENTION: case ActionTypes.RECIEVED_ADD_MENTION:
PostStore.emitAddMention(action.id, action.username); PostStore.emitAddMention(action.id, action.username);
break; break;
case ActionTypes.RECIEVED_EDIT_POST:
PostStore.emitEditPost(action);
break;
default: default:
} }
}); });

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

@@ -17,6 +17,7 @@ module.exports = {
RECIEVED_POSTS: null, RECIEVED_POSTS: null,
RECIEVED_POST: null, RECIEVED_POST: null,
RECIEVED_EDIT_POST: null,
RECIEVED_SEARCH: null, RECIEVED_SEARCH: null,
RECIEVED_POST_SELECTED: null, RECIEVED_POST_SELECTED: null,
RECIEVED_MENTION_DATA: null, RECIEVED_MENTION_DATA: null,
@@ -289,5 +290,14 @@ module.exports = {
], ],
Preferences: { Preferences: {
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show' CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show'
},
KeyCodes: {
UP: 38,
DOWN: 40,
LEFT: 37,
RIGHT: 39,
BACKSPACE: 8,
ENTER: 13,
ESCAPE: 27
} }
}; };