summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--web/react/components/channel_notifications.jsx8
-rw-r--r--web/react/components/create_post.jsx8
-rw-r--r--web/react/components/post_body.jsx12
-rw-r--r--web/react/components/rhs_comment.jsx11
-rw-r--r--web/react/components/rhs_root_post.jsx14
-rw-r--r--web/react/components/rhs_thread.jsx11
-rw-r--r--web/react/components/setting_item_max.jsx2
-rw-r--r--web/react/components/textbox.jsx9
-rw-r--r--web/react/components/user_settings_notifications.jsx14
-rw-r--r--web/react/package.json3
-rw-r--r--web/react/utils/constants.jsx1
12 files changed, 60 insertions, 35 deletions
diff --git a/README.md b/README.md
index fd695071c..fc6698208 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,7 @@ There are multiple ways to install Mattermost depending on your needs.
#### Development Install
-- [Developer Machine Setup](scripts/README_DEV.md) - Setup your local machine development environment using Docker on Mac OSX or Ubuntu.
+- [Developer Machine Setup](doc/install/dev-setup.md) - Setup your local machine development environment using Docker on Mac OSX or Ubuntu.
#### Production Deployment
diff --git a/web/react/components/channel_notifications.jsx b/web/react/components/channel_notifications.jsx
index 173646597..83067240d 100644
--- a/web/react/components/channel_notifications.jsx
+++ b/web/react/components/channel_notifications.jsx
@@ -162,6 +162,13 @@ export default class ChannelNotifications extends React.Component {
e.preventDefault();
}.bind(this);
+ let curChannel = ChannelStore.get(this.state.channelId);
+ let extraInfo = (<span>These settings will override the global notification settings</span>);
+
+ if (curChannel && curChannel.display_name) {
+ extraInfo = (<span>These settings will override the global notification settings for the <b>{curChannel.display_name}</b> channel</span>);
+ }
+
return (
<SettingItemMax
title='Send desktop notifications'
@@ -169,6 +176,7 @@ export default class ChannelNotifications extends React.Component {
submit={this.handleUpdate}
server_error={serverError}
updateSection={handleUpdateSection}
+ extraInfo={extraInfo}
/>
);
}
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 3d94e8933..871b72a43 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -117,7 +117,6 @@ export default class CreatePost extends React.Component {
Client.createPost(post, channel,
function handlePostSuccess(data) {
- this.resizePostHolder();
AsyncClient.getPosts();
let member = ChannelStore.getMember(channel.id);
@@ -129,7 +128,7 @@ export default class CreatePost extends React.Component {
type: ActionTypes.RECIEVED_POST,
post: data
});
- }.bind(this),
+ },
function handlePostError(err) {
let state = {};
@@ -149,9 +148,6 @@ export default class CreatePost extends React.Component {
);
}
}
- componentDidUpdate() {
- this.resizePostHolder();
- }
postMsgKeyPress(e) {
if (e.which === 13 && !e.shiftKey && !e.altKey) {
e.preventDefault();
@@ -166,7 +162,6 @@ export default class CreatePost extends React.Component {
}
}
handleUserInput(messageText) {
- this.resizePostHolder();
this.setState({messageText: messageText});
let draft = PostStore.getCurrentDraft();
@@ -318,6 +313,7 @@ export default class CreatePost extends React.Component {
<Textbox
onUserInput={this.handleUserInput}
onKeyPress={this.postMsgKeyPress}
+ onHeightChange={this.resizePostHolder}
messageText={this.state.messageText}
createMessage='Write a message...'
channelId={this.state.channelId}
diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx
index 88fb9aec8..d9b8f20ce 100644
--- a/web/react/components/post_body.jsx
+++ b/web/react/components/post_body.jsx
@@ -5,14 +5,26 @@ const FileAttachmentList = require('./file_attachment_list.jsx');
const UserStore = require('../stores/user_store.jsx');
const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx');
+const twemoji = require('twemoji');
export default class PostBody extends React.Component {
constructor(props) {
super(props);
+ this.parseEmojis = this.parseEmojis.bind(this);
+
const linkData = Utils.extractLinks(this.props.post.message);
this.state = {links: linkData.links, message: linkData.text};
}
+ parseEmojis() {
+ twemoji.parse(React.findDOMNode(this), {size: Constants.EMOJI_SIZE});
+ }
+ componentDidMount() {
+ this.parseEmojis();
+ }
+ componentDidUpdate() {
+ this.parseEmojis();
+ }
componentWillReceiveProps(nextProps) {
const linkData = Utils.extractLinks(nextProps.post.message);
this.setState({links: linkData.links, message: linkData.text});
diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx
index 3e2cf04c8..f1a90102c 100644
--- a/web/react/components/rhs_comment.jsx
+++ b/web/react/components/rhs_comment.jsx
@@ -12,12 +12,14 @@ var FileAttachmentList = require('./file_attachment_list.jsx');
var Client = require('../utils/client.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var ActionTypes = Constants.ActionTypes;
+var twemoji = require('twemoji');
export default class RhsComment extends React.Component {
constructor(props) {
super(props);
this.retryComment = this.retryComment.bind(this);
+ this.parseEmojis = this.parseEmojis.bind(this);
this.state = {};
}
@@ -51,6 +53,12 @@ export default class RhsComment extends React.Component {
PostStore.updatePendingPost(post);
this.forceUpdate();
}
+ parseEmojis() {
+ twemoji.parse(React.findDOMNode(this), {size: Constants.EMOJI_SIZE});
+ }
+ componentDidMount() {
+ this.parseEmojis();
+ }
shouldComponentUpdate(nextProps) {
if (!Utils.areStatesEqual(nextProps.post, this.props.post)) {
return true;
@@ -58,6 +66,9 @@ export default class RhsComment extends React.Component {
return false;
}
+ componentDidUpdate() {
+ this.parseEmojis();
+ }
render() {
var post = this.props.post;
diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx
index 5aed5e1b5..83b57b955 100644
--- a/web/react/components/rhs_root_post.jsx
+++ b/web/react/components/rhs_root_post.jsx
@@ -6,12 +6,23 @@ var UserProfile = require('./user_profile.jsx');
var UserStore = require('../stores/user_store.jsx');
var utils = require('../utils/utils.jsx');
var FileAttachmentList = require('./file_attachment_list.jsx');
+var twemoji = require('twemoji');
+var Constants = require('../utils/constants.jsx');
export default class RhsRootPost extends React.Component {
constructor(props) {
super(props);
+
+ this.parseEmojis = this.parseEmojis.bind(this);
+
this.state = {};
}
+ parseEmojis() {
+ twemoji.parse(React.findDOMNode(this), {size: Constants.EMOJI_SIZE});
+ }
+ componentDidMount() {
+ this.parseEmojis();
+ }
shouldComponentUpdate(nextProps) {
if (!utils.areStatesEqual(nextProps.post, this.props.post)) {
return true;
@@ -19,6 +30,9 @@ export default class RhsRootPost extends React.Component {
return false;
}
+ componentDidUpdate() {
+ this.parseEmojis();
+ }
render() {
var post = this.props.post;
var message = utils.textToJsx(post.message);
diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx
index 88e462c92..2f23d80d9 100644
--- a/web/react/components/rhs_thread.jsx
+++ b/web/react/components/rhs_thread.jsx
@@ -18,7 +18,6 @@ export default class RhsThread extends React.Component {
this.onChange = this.onChange.bind(this);
this.onChangeAll = this.onChangeAll.bind(this);
- this.onTimeChange = this.onTimeChange.bind(this);
this.state = this.getStateFromStores();
}
@@ -44,7 +43,6 @@ export default class RhsThread extends React.Component {
componentDidMount() {
PostStore.addSelectedPostChangeListener(this.onChange);
PostStore.addChangeListener(this.onChangeAll);
- UserStore.addStatusesChangeListener(this.onTimeChange);
this.resize();
$(window).resize(function resize() {
this.resize();
@@ -58,7 +56,6 @@ export default class RhsThread extends React.Component {
componentWillUnmount() {
PostStore.removeSelectedPostChangeListener(this.onChange);
PostStore.removeChangeListener(this.onChangeAll);
- UserStore.removeStatusesChangeListener(this.onTimeChange);
}
onChange() {
var newState = this.getStateFromStores();
@@ -96,14 +93,6 @@ export default class RhsThread extends React.Component {
this.setState(newState);
}
}
- onTimeChange() {
- for (var id in this.state.postList.posts) {
- if (!this.refs[id]) {
- continue;
- }
- this.refs[id].forceUpdate();
- }
- }
resize() {
var height = $(window).height() - $('#error_bar').outerHeight() - 100;
$('.post-right__scroll').css('height', height + 'px');
diff --git a/web/react/components/setting_item_max.jsx b/web/react/components/setting_item_max.jsx
index 48c5f160d..b1bab1d48 100644
--- a/web/react/components/setting_item_max.jsx
+++ b/web/react/components/setting_item_max.jsx
@@ -15,7 +15,7 @@ export default class SettingItemMax extends React.Component {
var extraInfo = null;
if (this.props.extraInfo) {
- extraInfo = this.props.extraInfo;
+ extraInfo = (<div className='setting-list__hint'>{this.props.extraInfo}</div>);
}
var submit = '';
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index 0408a262d..b4518fe80 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -121,7 +121,6 @@ export default class Textbox extends React.Component {
}
this.addedMention = false;
this.refs.commands.getSuggestedCommands(nextProps.messageText);
- this.resize();
}
updateMentionTab(mentionText) {
// using setTimeout so dispatch isn't called during an in progress dispatch
@@ -135,7 +134,6 @@ export default class Textbox extends React.Component {
}
handleChange() {
this.props.onUserInput(React.findDOMNode(this.refs.message).value);
- this.resize();
}
handleKeyPress(e) {
const text = React.findDOMNode(this.refs.message).value;
@@ -244,6 +242,8 @@ export default class Textbox extends React.Component {
const e = React.findDOMNode(this.refs.message);
const w = React.findDOMNode(this.refs.wrapper);
+ let prevHeight = $(e).height();
+
const lht = parseInt($(e).css('lineHeight'), 10);
const lines = e.scrollHeight / lht;
let mod = 15;
@@ -259,6 +259,10 @@ export default class Textbox extends React.Component {
$(e).css({height: 'auto', 'overflow-y': 'scroll'}).height(167);
$(w).css({height: 'auto'}).height(167);
}
+
+ if (prevHeight !== $(e).height()) {
+ this.props.onHeightChange();
+ }
}
handleFocus() {
const elm = React.findDOMNode(this.refs.message);
@@ -316,5 +320,6 @@ Textbox.propTypes = {
messageText: React.PropTypes.string.isRequired,
onUserInput: React.PropTypes.func.isRequired,
onKeyPress: React.PropTypes.func.isRequired,
+ onHeightChange: React.PropTypes.func.isRequired,
createMessage: React.PropTypes.string.isRequired
};
diff --git a/web/react/components/user_settings_notifications.jsx b/web/react/components/user_settings_notifications.jsx
index 5fe6bbb4e..33db1a332 100644
--- a/web/react/components/user_settings_notifications.jsx
+++ b/web/react/components/user_settings_notifications.jsx
@@ -1,7 +1,6 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
-var ChannelStore = require('../stores/channel_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var SettingItemMin = require('./setting_item_min.jsx');
var SettingItemMax = require('./setting_item_max.jsx');
@@ -69,11 +68,9 @@ function getNotificationsStateFromStores() {
}
}
- var curChannel = ChannelStore.getCurrent().display_name;
-
return {notifyLevel: desktop, enableEmail: email, soundNeeded: soundNeeded, enableSound: sound,
usernameKey: usernameKey, mentionKey: mentionKey, customKeys: customKeys, customKeysChecked: customKeys.length > 0,
- firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey, curChannel: curChannel};
+ firstNameKey: firstNameKey, allKey: allKey, channelKey: channelKey};
}
export default class NotificationsTab extends React.Component {
@@ -147,12 +144,10 @@ export default class NotificationsTab extends React.Component {
}
componentDidMount() {
UserStore.addChangeListener(this.onListenerChange);
- ChannelStore.addChangeListener(this.onListenerChange);
$('#user_settings').on('hidden.bs.modal', this.handleClose);
}
componentWillUnmount() {
UserStore.removeChangeListener(this.onListenerChange);
- ChannelStore.removeChangeListener(this.onListenerChange);
$('#user_settings').off('hidden.bs.modal', this.handleClose);
this.props.updateSection('');
}
@@ -271,12 +266,6 @@ export default class NotificationsTab extends React.Component {
e.preventDefault();
}.bind(this);
- let extraInfo = (
- <div className='setting-list__hint'>
- These settings will override the global notification settings for the <b>{this.state.curChannel}</b> channel
- </div>
- );
-
desktopSection = (
<SettingItemMax
title='Send desktop notifications'
@@ -284,7 +273,6 @@ export default class NotificationsTab extends React.Component {
submit={this.handleSubmit}
server_error={serverError}
updateSection={handleUpdateDesktopSection}
- extraInfo={extraInfo}
/>
);
} else {
diff --git a/web/react/package.json b/web/react/package.json
index 86563136f..da55dc2b8 100644
--- a/web/react/package.json
+++ b/web/react/package.json
@@ -8,7 +8,8 @@
"keymirror": "^0.1.1",
"object-assign": "^3.0.0",
"react": "^0.13.3",
- "react-zeroclipboard-mixin": "^0.1.0"
+ "react-zeroclipboard-mixin": "^0.1.0",
+ "twemoji": "^1.4.1"
},
"devDependencies": {
"browserify": "^11.0.1",
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 3a6ca1b89..18b7ff59c 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -103,6 +103,7 @@ module.exports = {
MONTHS: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
MAX_DMS: 20,
MAX_POST_LEN: 4000,
+ EMOJI_SIZE: 16,
ONLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path class='online--icon' d='M6,5.487c1.371,0,2.482-1.116,2.482-2.493c0-1.378-1.111-2.495-2.482-2.495S3.518,1.616,3.518,2.994C3.518,4.371,4.629,5.487,6,5.487z M10.452,8.545c-0.101-0.829-0.36-1.968-0.726-2.541C9.475,5.606,8.5,5.5,8.5,5.5S8.43,7.521,6,7.521C3.507,7.521,3.5,5.5,3.5,5.5S2.527,5.606,2.273,6.004C1.908,6.577,1.648,7.716,1.547,8.545C1.521,8.688,1.49,9.082,1.498,9.142c0.161,1.295,2.238,2.322,4.375,2.358C5.916,11.501,5.958,11.501,6,11.501c0.043,0,0.084,0,0.127-0.001c2.076-0.026,4.214-1.063,4.375-2.358C10.509,9.082,10.471,8.696,10.452,8.545z'/></g></g></svg>",
OFFLINE_ICON_SVG: "<svg version='1.1' id='Layer_1' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns#' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:svg='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' sodipodi:docname='TRASH_1_4.svg' inkscape:version='0.48.4 r9939' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='12px' height='12px' viewBox='0 0 12 12' enable-background='new 0 0 12 12' xml:space='preserve'><sodipodi:namedview inkscape:cy='139.7898' inkscape:cx='26.358185' inkscape:zoom='1.18' showguides='true' showgrid='false' id='namedview6' guidetolerance='10' gridtolerance='10' objecttolerance='10' borderopacity='1' bordercolor='#666666' pagecolor='#ffffff' inkscape:current-layer='Layer_1' inkscape:window-maximized='1' inkscape:window-y='-8' inkscape:window-x='-8' inkscape:window-height='705' inkscape:window-width='1366' inkscape:guide-bbox='true' inkscape:pageshadow='2' inkscape:pageopacity='0'><sodipodi:guide position='50.036793,85.991376' orientation='1,0' id='guide2986'></sodipodi:guide><sodipodi:guide position='58.426196,66.216355' orientation='0,1' id='guide3047'></sodipodi:guide></sodipodi:namedview><g><g><path fill='#cccccc' d='M6.002,7.143C5.645,7.363,5.167,7.52,4.502,7.52c-2.493,0-2.5-2.02-2.5-2.02S1.029,5.607,0.775,6.004C0.41,6.577,0.15,7.716,0.049,8.545c-0.025,0.145-0.057,0.537-0.05,0.598c0.162,1.295,2.237,2.321,4.375,2.357c0.043,0.001,0.085,0.001,0.127,0.001c0.043,0,0.084,0,0.127-0.001c1.879-0.023,3.793-0.879,4.263-2h-2.89L6.002,7.143L6.002,7.143z M4.501,5.488c1.372,0,2.483-1.117,2.483-2.494c0-1.378-1.111-2.495-2.483-2.495c-1.371,0-2.481,1.117-2.481,2.495C2.02,4.371,3.13,5.488,4.501,5.488z M7.002,6.5v2h5v-2H7.002z'/></g></g></svg>",
MENU_ICON: "<svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px'width='4px' height='16px' viewBox='0 0 8 32' enable-background='new 0 0 8 32' xml:space='preserve'> <g> <circle cx='4' cy='4.062' r='4'/> <circle cx='4' cy='16' r='4'/> <circle cx='4' cy='28' r='4'/> </g> </svg>",