summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/react/components/create_comment.jsx14
-rw-r--r--web/react/components/create_post.jsx15
-rw-r--r--web/react/components/signup_user_complete.jsx2
-rw-r--r--web/react/components/team_signup_username_page.jsx2
-rw-r--r--web/react/components/time_since.jsx3
-rw-r--r--web/react/components/user_settings/user_settings_general.jsx1
-rw-r--r--web/react/stores/socket_store.jsx24
-rw-r--r--web/react/utils/constants.jsx5
-rw-r--r--web/react/utils/text_formatting.jsx8
-rw-r--r--web/react/utils/utils.jsx15
-rw-r--r--web/templates/head.html6
11 files changed, 73 insertions, 22 deletions
diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx
index 9e7c67515..709485991 100644
--- a/web/react/components/create_comment.jsx
+++ b/web/react/components/create_comment.jsx
@@ -59,6 +59,7 @@ class CreateComment extends React.Component {
this.getFileCount = this.getFileCount.bind(this);
this.handleResize = this.handleResize.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
+ this.focusTextbox = this.focusTextbox.bind(this);
PostStore.clearCommentDraftUploads();
@@ -76,7 +77,7 @@ class CreateComment extends React.Component {
PreferenceStore.addChangeListener(this.onPreferenceChange);
window.addEventListener('resize', this.handleResize);
- this.refs.textbox.focus();
+ this.focusTextbox();
}
componentWillUnmount() {
PreferenceStore.removeChangeListener(this.onPreferenceChange);
@@ -99,7 +100,7 @@ class CreateComment extends React.Component {
}
if (prevProps.rootId !== this.props.rootId) {
- this.refs.textbox.focus();
+ this.focusTextbox();
}
}
handleSubmit(e) {
@@ -226,7 +227,7 @@ class CreateComment extends React.Component {
}
}
handleUploadClick() {
- this.refs.textbox.focus();
+ this.focusTextbox();
}
handleUploadStart(clientIds) {
let draft = PostStore.getCommentDraft(this.props.rootId);
@@ -238,7 +239,7 @@ class CreateComment extends React.Component {
// this is a bit redundant with the code that sets focus when the file input is clicked,
// but this also resets the focus after a drag and drop
- this.refs.textbox.focus();
+ this.focusTextbox();
}
handleFileUploadComplete(filenames, clientIds) {
let draft = PostStore.getCommentDraft(this.props.rootId);
@@ -306,6 +307,11 @@ class CreateComment extends React.Component {
getFileCount() {
return this.state.previews.length + this.state.uploadsInProgress.length;
}
+ focusTextbox() {
+ if (!Utils.isMobile()) {
+ this.refs.textbox.focus();
+ }
+ }
render() {
let serverError = null;
if (this.state.serverError) {
diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx
index 6ea80cd13..ecabdaee6 100644
--- a/web/react/components/create_post.jsx
+++ b/web/react/components/create_post.jsx
@@ -63,6 +63,7 @@ class CreatePost extends React.Component {
this.getFileCount = this.getFileCount.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.sendMessage = this.sendMessage.bind(this);
+ this.focusTextbox = this.focusTextbox.bind(this);
PostStore.clearDraftUploads();
@@ -193,6 +194,11 @@ class CreatePost extends React.Component {
}
);
}
+ focusTextbox() {
+ if (!Utils.isMobile()) {
+ this.refs.textbox.focus();
+ }
+ }
postMsgKeyPress(e) {
if (this.state.ctrlSend && e.ctrlKey || !this.state.ctrlSend) {
if (e.which === KeyCodes.ENTER && !e.shiftKey && !e.altKey) {
@@ -216,7 +222,7 @@ class CreatePost extends React.Component {
PostStore.storeCurrentDraft(draft);
}
handleUploadClick() {
- this.refs.textbox.focus();
+ this.focusTextbox();
}
handleUploadStart(clientIds, channelId) {
const draft = PostStore.getDraft(channelId);
@@ -228,7 +234,7 @@ class CreatePost extends React.Component {
// this is a bit redundant with the code that sets focus when the file input is clicked,
// but this also resets the focus after a drag and drop
- this.refs.textbox.focus();
+ this.focusTextbox();
}
handleFileUploadComplete(filenames, clientIds, channelId) {
const draft = PostStore.getDraft(channelId);
@@ -305,11 +311,12 @@ class CreatePost extends React.Component {
componentDidMount() {
ChannelStore.addChangeListener(this.onChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
- this.refs.textbox.focus();
+
+ this.focusTextbox();
}
componentDidUpdate(prevProps, prevState) {
if (prevState.channelId !== this.state.channelId) {
- this.refs.textbox.focus();
+ this.focusTextbox();
}
}
componentWillUnmount() {
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 98a832542..672213d1a 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -303,7 +303,7 @@ class SignupUserComplete extends React.Component {
ref='name'
className='form-control'
placeholder=''
- maxLength='128'
+ maxLength={Constants.MAX_USERNAME_LENGTH}
spellCheck='false'
/>
{nameError}
diff --git a/web/react/components/team_signup_username_page.jsx b/web/react/components/team_signup_username_page.jsx
index a7332975d..0fa9cb103 100644
--- a/web/react/components/team_signup_username_page.jsx
+++ b/web/react/components/team_signup_username_page.jsx
@@ -115,7 +115,7 @@ class TeamSignupUsernamePage extends React.Component {
className='form-control'
placeholder=''
defaultValue={this.props.state.user.username}
- maxLength='128'
+ maxLength={Constants.MAX_USERNAME_LENGTH}
spellCheck='false'
/>
{nameHelpText}
diff --git a/web/react/components/time_since.jsx b/web/react/components/time_since.jsx
index ba8dbffcc..1560d2469 100644
--- a/web/react/components/time_since.jsx
+++ b/web/react/components/time_since.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
import Constants from '../utils/constants.jsx';
+import * as Utils from '../utils/utils.jsx';
import {FormattedRelative, FormattedDate} from 'mm-intl';
@@ -24,7 +25,7 @@ export default class TimeSince extends React.Component {
if (this.props.sameUser) {
return (
<time className='post__time'>
- <FormattedRelative value={this.props.eventTime} />
+ {Utils.displayTimeFormatted(this.props.eventTime)}
</time>
);
}
diff --git a/web/react/components/user_settings/user_settings_general.jsx b/web/react/components/user_settings/user_settings_general.jsx
index f20b4b807..cd229775f 100644
--- a/web/react/components/user_settings/user_settings_general.jsx
+++ b/web/react/components/user_settings/user_settings_general.jsx
@@ -514,6 +514,7 @@ class UserSettingsGeneralTab extends React.Component {
<label className='col-sm-5 control-label'>{usernameLabel}</label>
<div className='col-sm-7'>
<input
+ maxLength={Constants.MAX_USERNAME_LENGTH}
className='form-control'
type='text'
onChange={this.updateUsername}
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index 9c3270f68..bc2bdbe64 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -28,10 +28,13 @@ class SocketStoreClass extends EventEmitter {
this.addChangeListener = this.addChangeListener.bind(this);
this.removeChangeListener = this.removeChangeListener.bind(this);
this.sendMessage = this.sendMessage.bind(this);
+ this.close = this.close.bind(this);
+
this.failCount = 0;
this.initialize();
}
+
initialize() {
if (!UserStore.getCurrentId()) {
return;
@@ -106,15 +109,19 @@ class SocketStoreClass extends EventEmitter {
};
}
}
+
emitChange(msg) {
this.emit(CHANGE_EVENT, msg);
}
+
addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
}
+
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
+
handleMessage(msg) {
switch (msg.action) {
case SocketEvents.POSTED:
@@ -153,6 +160,7 @@ class SocketStoreClass extends EventEmitter {
default:
}
}
+
sendMessage(msg) {
if (conn && conn.readyState === WebSocket.OPEN) {
conn.send(JSON.stringify(msg));
@@ -161,9 +169,16 @@ class SocketStoreClass extends EventEmitter {
this.initialize();
}
}
+
setTranslations(messages) {
this.translations = messages;
}
+
+ close() {
+ if (conn && conn.readyState === WebSocket.OPEN) {
+ conn.close();
+ }
+ }
}
function handleNewPostEvent(msg, translations) {
@@ -305,12 +320,5 @@ function handlePreferenceChangedEvent(msg) {
var SocketStore = new SocketStoreClass();
-/*SocketStore.dispatchToken = AppDispatcher.register((payload) => {
- var action = payload.action;
-
- switch (action.type) {
- default:
- }
- });*/
-
export default SocketStore;
+window.SocketStore = SocketStore;
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index c1bd41b88..d78776aa3 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -464,8 +464,9 @@ export default {
},
OVERLAY_TIME_DELAY: 400,
MIN_USERNAME_LENGTH: 3,
- MAX_USERNAME_LENGTH: 15,
+ MAX_USERNAME_LENGTH: 64,
MIN_PASSWORD_LENGTH: 5,
MAX_PASSWORD_LENGTH: 50,
- TIME_SINCE_UPDATE_INTERVAL: 30000
+ TIME_SINCE_UPDATE_INTERVAL: 30000,
+ MIN_HASHTAG_LINK_LENGTH: 3
};
diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx
index e837ded53..dae2252a6 100644
--- a/web/react/utils/text_formatting.jsx
+++ b/web/react/utils/text_formatting.jsx
@@ -248,8 +248,14 @@ function autolinkHashtags(text, tokens) {
const index = tokens.size;
const alias = `MM_HASHTAG${index}`;
+ let value = hashtag;
+
+ if (hashtag.length > Constants.MIN_HASHTAG_LINK_LENGTH) {
+ value = `<a class='mention-link' href='#' data-hashtag='${hashtag}'>${hashtag}</a>`;
+ }
+
tokens.set(alias, {
- value: `<a class='mention-link' href='#' data-hashtag='${hashtag}'>${hashtag}</a>`,
+ value,
originalText: hashtag
});
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 6bb7baa64..4beec8d64 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -14,6 +14,8 @@ import * as AsyncClient from './async_client.jsx';
import * as client from './client.jsx';
import Autolinker from 'autolinker';
+import {FormattedTime} from 'mm-intl';
+
export function isEmail(email) {
// writing a regex to match all valid email addresses is really, really hard (see http://stackoverflow.com/a/201378)
// so we just do a simple check and rely on a verification email to tell if it's a real address
@@ -245,6 +247,19 @@ export function displayTime(ticks, utc) {
return hours + ':' + minutes + ampm + timezone;
}
+export function displayTimeFormatted(ticks) {
+ const useMilitaryTime = PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time');
+
+ return (
+ <FormattedTime
+ value={ticks}
+ hour='numeric'
+ minute='numeric'
+ hour12={!useMilitaryTime}
+ />
+ );
+}
+
export function displayDateTime(ticks) {
var seconds = Math.floor((Date.now() - ticks) / 1000);
diff --git a/web/templates/head.html b/web/templates/head.html
index b1ec905b5..da65e1779 100644
--- a/web/templates/head.html
+++ b/web/templates/head.html
@@ -122,6 +122,12 @@
}
});
});
+
+ $(window).on('beforeunload', function(){
+ if (window.SocketStore) {
+ SocketStore.close();
+ }
+ });
</script>
<script>