summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md13
-rw-r--r--doc/developer/Setup.md2
-rw-r--r--doc/install/Troubleshooting.md2
-rw-r--r--web/react/components/create_post.jsx26
-rw-r--r--web/react/components/edit_post_modal.jsx36
-rw-r--r--web/react/components/textbox.jsx8
-rw-r--r--web/react/stores/post_store.jsx37
-rw-r--r--web/react/utils/constants.jsx10
8 files changed, 124 insertions, 10 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eac1e61e5..425902cf0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,14 @@
# Mattermost Changelog
-## UNDER DEVELOPMENT - Release v1.1.0
+## UNDER DEVELOPMENT Release v1.2.0
The "UNDER DEVELOPMENT" section of the Mattermost changelog appears in the product's `master` branch to note key changes committed to master and are on their way to the next stable release. When a stable release is pushed the "UNDER DEVELOPMENT" heading is removed from the final changelog of the release.
-- **Final release anticipated:** 2015-10-16
+- **Final release anticipated:** 2015-11-16
+
+## Release v1.1.0
+
+Released: 2015-10-16
### Release Highlights
@@ -44,6 +48,11 @@ Messaging and Notifications
- Fixed bug where System Administrator did not have Team Administrator permissions
- Fixed bug causing scrolling to jump when the right hand sidebar opened and closed
+### Known Issues
+
+- Slack import is unstable due to change in Slack export format
+- Uploading a .flac file breaks the file previewer on iOS
+
### Contributors
Many thanks to our external contributors. In no particular order:
diff --git a/doc/developer/Setup.md b/doc/developer/Setup.md
index afaef4ee4..d806b7a9b 100644
--- a/doc/developer/Setup.md
+++ b/doc/developer/Setup.md
@@ -61,7 +61,7 @@ Any issues? Please let us know on our forums at: http://forum.mattermost.org
3. Reload your bashrc
`source ~/.bashrc`
6. Install Node.js
- 1. Download the newest version of the Node.js sources from https://nodejs.org/download/
+ 1. Download the newest version of the Node.js sources from https://nodejs.org/en/download/
2. Extract the contents of the package and cd into the extracted files
3. Compile and install Node.js
`./configure`
diff --git a/doc/install/Troubleshooting.md b/doc/install/Troubleshooting.md
index 8d82100d8..b87663ab3 100644
--- a/doc/install/Troubleshooting.md
+++ b/doc/install/Troubleshooting.md
@@ -3,5 +3,5 @@
#### Important notes
1. **DO NOT manipulate the Mattermost database**
- - In particular, DO NOT delete data from the database, as this will most likely crash Mattermost in strange ways. Mattermost is designed to archive content continously and generally assumes data is never deleted.
+ - In particular, DO NOT delete data from the database, as Mattermost is designed to stop working if data integrity has been compromised. The system is designed to archive content continously and generally assumes data is never deleted.
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 6203e567a..2581bdcca 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -16,6 +16,7 @@ const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes;
+const KeyCodes = Constants.KeyCodes;
export default class CreatePost extends React.Component {
constructor(props) {
@@ -35,6 +36,7 @@ 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);
PostStore.clearDraftUploads();
@@ -172,7 +174,7 @@ export default class CreatePost extends React.Component {
}
}
postMsgKeyPress(e) {
- if (e.which === 13 && !e.shiftKey && !e.altKey) {
+ if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
e.preventDefault();
ReactDOM.findDOMNode(this.refs.textbox).blur();
this.handleSubmit(e);
@@ -292,6 +294,27 @@ export default class CreatePost extends React.Component {
const draft = PostStore.getDraft(channelId);
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);
+ if (!lastPost) {
+ return;
+ }
+ var type = (lastPost.root_id && lastPost.root_id.length > 0) ? 'Comment' : 'Post';
+
+ AppDispatcher.handleViewAction({
+ type: ActionTypes.RECIEVED_EDIT_POST,
+ refocusId: '#post_textbox',
+ title: type,
+ message: lastPost.message,
+ postId: lastPost.id,
+ channelId: lastPost.channel_id
+ });
+ }
+ }
render() {
let serverError = null;
if (this.state.serverError) {
@@ -336,6 +359,7 @@ export default class CreatePost extends React.Component {
<Textbox
onUserInput={this.handleUserInput}
onKeyPress={this.postMsgKeyPress}
+ onKeyDown={this.handleArrowUp}
onHeightChange={this.resizePostHolder}
messageText={this.state.messageText}
createMessage='Write a message...'
diff --git a/web/react/components/edit_post_modal.jsx b/web/react/components/edit_post_modal.jsx
index 3dbff18cb..90d9696e7 100644
--- a/web/react/components/edit_post_modal.jsx
+++ b/web/react/components/edit_post_modal.jsx
@@ -5,6 +5,7 @@ var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var Textbox = require('./textbox.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
+var PostStore = require('../stores/post_store.jsx');
export default class EditPostModal extends React.Component {
constructor() {
@@ -14,6 +15,7 @@ export default class EditPostModal extends React.Component {
this.handleEditInput = this.handleEditInput.bind(this);
this.handleEditKeyPress = this.handleEditKeyPress.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: ''};
}
@@ -35,16 +37,15 @@ export default class EditPostModal extends React.Component {
Client.updatePost(updatedPost,
function success() {
- AsyncClient.getPosts(this.state.channel_id);
+ AsyncClient.getPosts(updatedPost.channel_id);
window.scrollTo(0, 0);
- }.bind(this),
+ },
function error(err) {
AsyncClient.dispatchError(err, 'updatePost');
}
);
$('#edit_post').modal('hide');
- $(this.state.refocusId).focus();
}
handleEditInput(editMessage) {
this.setState({editText: editMessage});
@@ -59,6 +60,18 @@ export default class EditPostModal extends React.Component {
handleUserInput(e) {
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() {
var self = this;
@@ -68,12 +81,29 @@ export default class EditPostModal extends React.Component {
$(ReactDOM.findDOMNode(this.refs.modal)).on('show.bs.modal', function onShow(e) {
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')});
});
$(ReactDOM.findDOMNode(this.refs.modal)).on('shown.bs.modal', function onShown() {
self.refs.editbox.resize();
+ $('#edit_textbox').get(0).focus();
});
+
+ $(React.findDOMNode(this.refs.modal)).on('hide.bs.modal', function onShown() {
+ if (self.state.refocusId !== '') {
+ setTimeout(() => {
+ $(self.state.refocusId).get(0).focus();
+ });
+ }
+ });
+
+ PostStore.addEditPostListener(this.handleEditPostEvent);
+ }
+ componentWillUnmount() {
+ PostStore.removeEditPostListener(this.handleEditPostEvent);
}
render() {
var error = (<div className='form-group'><br /></div>);
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index d51fb9523..86bb42f62 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -9,6 +9,7 @@ const ErrorStore = require('../stores/error_store.jsx');
const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx');
const ActionTypes = Constants.ActionTypes;
+const KeyCodes = Constants.KeyCodes;
export default class Textbox extends React.Component {
constructor(props) {
@@ -148,8 +149,10 @@ export default class Textbox extends React.Component {
this.doProcessMentions = true;
}
- if (e.keyCode === 8) {
+ if (e.keyCode === KeyCodes.BACKSPACE) {
this.handleBackspace(e);
+ } else if (this.props.onKeyDown) {
+ this.props.onKeyDown(e);
}
}
@@ -318,5 +321,6 @@ Textbox.propTypes = {
onUserInput: React.PropTypes.func.isRequired,
onKeyPress: React.PropTypes.func.isRequired,
onHeightChange: React.PropTypes.func,
- createMessage: React.PropTypes.string.isRequired
+ createMessage: React.PropTypes.string.isRequired,
+ onKeyDown: React.PropTypes.func
};
diff --git a/web/react/stores/post_store.jsx b/web/react/stores/post_store.jsx
index d8da48000..8609d8bbf 100644
--- a/web/react/stores/post_store.jsx
+++ b/web/react/stores/post_store.jsx
@@ -6,6 +6,7 @@ var EventEmitter = require('events').EventEmitter;
var ChannelStore = require('../stores/channel_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
+var UserStore = require('../stores/user_store.jsx');
var Constants = require('../utils/constants.jsx');
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 MENTION_DATA_CHANGE_EVENT = 'mention_data_change';
var ADD_MENTION_EVENT = 'add_mention';
+var EDIT_POST_EVENT = 'edit_post';
class PostStoreClass extends EventEmitter {
constructor() {
@@ -75,6 +77,10 @@ class PostStoreClass extends EventEmitter {
this.clearCommentDraftUploads = this.clearCommentDraftUploads.bind(this);
this.storeLatestUpdate = this.storeLatestUpdate.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() {
this.emit(CHANGE_EVENT);
@@ -148,6 +154,18 @@ class PostStoreClass extends EventEmitter {
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() {
var currentId = ChannelStore.getCurrentId();
@@ -212,6 +230,22 @@ class PostStoreClass extends EventEmitter {
getPosts(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) {
this.pStorePost(post);
this.emitChange();
@@ -446,6 +480,9 @@ PostStore.dispatchToken = AppDispatcher.register(function registry(payload) {
case ActionTypes.RECIEVED_ADD_MENTION:
PostStore.emitAddMention(action.id, action.username);
break;
+ case ActionTypes.RECIEVED_EDIT_POST:
+ PostStore.emitEditPost(action);
+ break;
default:
}
});
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index cee2ec114..b8200db54 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -17,6 +17,7 @@ module.exports = {
RECIEVED_POSTS: null,
RECIEVED_POST: null,
+ RECIEVED_EDIT_POST: null,
RECIEVED_SEARCH: null,
RECIEVED_POST_SELECTED: null,
RECIEVED_MENTION_DATA: null,
@@ -289,5 +290,14 @@ module.exports = {
],
Preferences: {
CATEGORY_DIRECT_CHANNEL_SHOW: 'direct_channel_show'
+ },
+ KeyCodes: {
+ UP: 38,
+ DOWN: 40,
+ LEFT: 37,
+ RIGHT: 39,
+ BACKSPACE: 8,
+ ENTER: 13,
+ ESCAPE: 27
}
};