summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/context.go15
-rw-r--r--api/templates/error.html2
-rw-r--r--api/templates/signup_team_body.html3
-rw-r--r--api/templates/welcome_body.html12
-rw-r--r--api/templates/welcome_subject.html2
-rw-r--r--api/user.go15
-rw-r--r--web/react/components/error_bar.jsx85
-rw-r--r--web/react/components/post_info.jsx16
-rw-r--r--web/react/components/signup_user_complete.jsx2
-rw-r--r--web/react/components/team_signup_password_page.jsx2
-rw-r--r--web/react/components/textbox.jsx53
-rw-r--r--web/react/components/user_settings/user_settings_appearance.jsx2
-rw-r--r--web/react/pages/channel.jsx6
-rw-r--r--web/react/stores/error_store.jsx2
-rw-r--r--web/react/stores/socket_store.jsx42
-rw-r--r--web/react/utils/client.jsx2
-rw-r--r--web/react/utils/constants.jsx28
-rw-r--r--web/react/utils/utils.jsx4
-rw-r--r--web/sass-files/sass/partials/_markdown.scss38
-rw-r--r--web/sass-files/sass/partials/_mentions.scss4
-rw-r--r--web/sass-files/sass/partials/_post.scss7
-rw-r--r--web/sass-files/sass/partials/_responsive.scss2
-rw-r--r--web/sass-files/sass/partials/_videos.scss10
23 files changed, 198 insertions, 156 deletions
diff --git a/api/context.go b/api/context.go
index 9a276a1a1..f123d8702 100644
--- a/api/context.go
+++ b/api/context.go
@@ -456,18 +456,13 @@ func IsPrivateIpAddress(ipAddress string) bool {
}
func RenderWebError(err *model.AppError, w http.ResponseWriter, r *http.Request) {
-
- protocol := GetProtocol(r)
- SiteURL := protocol + "://" + r.Host
-
- m := make(map[string]string)
- m["Message"] = err.Message
- m["Details"] = err.DetailedError
- m["SiteName"] = utils.Cfg.TeamSettings.SiteName
- m["SiteURL"] = SiteURL
+ props := make(map[string]string)
+ props["Message"] = err.Message
+ props["Details"] = err.DetailedError
+ props["SiteURL"] = GetProtocol(r) + "://" + r.Host
w.WriteHeader(err.StatusCode)
- ServerTemplates.ExecuteTemplate(w, "error.html", m)
+ ServerTemplates.ExecuteTemplate(w, "error.html", Page{Props: props, ClientProps: utils.ClientProperties})
}
func Handle404(w http.ResponseWriter, r *http.Request) {
diff --git a/api/templates/error.html b/api/templates/error.html
index 760578896..6b643556e 100644
--- a/api/templates/error.html
+++ b/api/templates/error.html
@@ -23,7 +23,7 @@
<div class="error__container">
<div class="error__icon"><i class="fa fa-exclamation-triangle"></i></div>
<h2>{{ .ClientProps.SiteName }} needs your help:</h2>
- <p>{{.Message}}</p>
+ <p>{{ .Props.Message }}</p>
<a href="{{.Props.SiteURL}}">Go back to team site</a>
</div>
</div>
diff --git a/api/templates/signup_team_body.html b/api/templates/signup_team_body.html
index f5c0e62b0..e6ffb3a5b 100644
--- a/api/templates/signup_team_body.html
+++ b/api/templates/signup_team_body.html
@@ -22,9 +22,6 @@
<a href="{{.Props.Link}}" style="background: #2389D7; border-radius: 3px; color: #fff; border: none; outline: none; min-width: 200px; padding: 15px 25px; font-size: 14px; font-family: inherit; cursor: pointer; -webkit-appearance: none;text-decoration: none;">Set up your team</a>
</p>
{{ .ClientProps.SiteName }} is one place for all your team communication, searchable and available anywhere.<br>You'll get more out of {{ .ClientProps.SiteName }} when your team is in constant communication--let's get them on board.<br></p>
- <p>
- Learn more by <a href="{{.Props.TourUrl}}" style="text-decoration: none; color:#2389D7;">taking a tour</a>
- </p>
</td>
</tr>
<tr>
diff --git a/api/templates/welcome_body.html b/api/templates/welcome_body.html
index c75e14c6a..5fe3450b7 100644
--- a/api/templates/welcome_body.html
+++ b/api/templates/welcome_body.html
@@ -17,15 +17,9 @@
<table border="0" cellpadding="0" cellspacing="0" style="padding: 20px 50px 0; text-align: center; margin: 0 auto">
<tr>
<td style="border-bottom: 1px solid #ddd; padding: 0 0 20px;">
- <h2 style="font-weight: normal; margin-top: 10px;">You joined the {{.Props.TeamDisplayName}} team at {{.ClientProps.SiteName}}!</h2>
- <p>Please let me know if you have any questions.<br>Enjoy your stay at <a href="{{.Props.TeamURL}}">{{.ClientProps.SiteName}}</a>.</p>
- </td>
- </tr>
- <tr>
- <td style="color: #999; padding-top: 20px; line-height: 25px; font-size: 13px;">
- Any questions at all, mail us any time: <a href="mailto:{{.ClientProps.FeedbackEmail}}" style="text-decoration: none; color:#2389D7;">{{.ClientProps.FeedbackEmail}}</a>.<br>
- Best wishes,<br>
- The {{.ClientProps.SiteName}} Team<br>
+ <h2 style="font-weight: normal; margin-top: 10px;">You can sign-in to your new team from the web address:</h2>
+ <a href="{{.Props.TeamURL}}">{{.Props.TeamURL}}</a>
+ <p>Mattermost lets you share messages and files from your PC or phone, with instant search and archiving.</p>
</td>
</tr>
</table>
diff --git a/api/templates/welcome_subject.html b/api/templates/welcome_subject.html
index 2214f7a38..c3b70ef20 100644
--- a/api/templates/welcome_subject.html
+++ b/api/templates/welcome_subject.html
@@ -1 +1 @@
-{{define "welcome_subject"}}Welcome to {{ .ClientProps.SiteName }}{{end}} \ No newline at end of file
+{{define "welcome_subject"}}You joined {{ .Props.TeamDisplayName }}{{end}}
diff --git a/api/user.go b/api/user.go
index 3cce3cdd3..9ed4404f1 100644
--- a/api/user.go
+++ b/api/user.go
@@ -198,7 +198,7 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
l4g.Error("Encountered an issue joining default channels user_id=%s, team_id=%s, err=%v", ruser.Id, ruser.TeamId, err)
}
- //fireAndForgetWelcomeEmail(ruser.FirstName, ruser.Email, team.Name, c.TeamURL+"/channels/town-square")
+ fireAndForgetWelcomeEmail(ruser.Email, team.DisplayName, c.GetTeamURLFromTeam(team))
if user.EmailVerified {
if cresult := <-Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
l4g.Error("Failed to set email verified err=%v", cresult.Err)
@@ -218,17 +218,13 @@ func CreateUser(c *Context, team *model.Team, user *model.User) *model.User {
}
}
-func fireAndForgetWelcomeEmail(name, email, teamDisplayName, link, siteURL string) {
+func fireAndForgetWelcomeEmail(email, teamDisplayName, teamURL string) {
go func() {
subjectPage := NewServerTemplatePage("welcome_subject")
- subjectPage.Props["SiteURL"] = siteURL
+ subjectPage.Props["TeamDisplayName"] = teamDisplayName
bodyPage := NewServerTemplatePage("welcome_body")
- bodyPage.Props["SiteURL"] = siteURL
- bodyPage.Props["Nickname"] = name
- bodyPage.Props["TeamDisplayName"] = teamDisplayName
- bodyPage.Props["FeedbackName"] = utils.Cfg.EmailSettings.FeedbackName
- bodyPage.Props["TeamURL"] = link
+ bodyPage.Props["TeamURL"] = teamURL
if err := utils.SendMail(email, subjectPage.Render(), bodyPage.Render()); err != nil {
l4g.Error("Failed to send welcome email successfully err=%v", err)
@@ -821,6 +817,9 @@ func uploadProfileImage(c *Context, w http.ResponseWriter, r *http.Request) {
Srv.Store.User().UpdateLastPictureUpdate(c.Session.UserId)
c.LogAudit("")
+
+ // write something as the response since jQuery expects a json response
+ w.Write([]byte("true"))
}
func updateUser(c *Context, w http.ResponseWriter, r *http.Request) {
diff --git a/web/react/components/error_bar.jsx b/web/react/components/error_bar.jsx
index 87d94a41d..05726e860 100644
--- a/web/react/components/error_bar.jsx
+++ b/web/react/components/error_bar.jsx
@@ -2,10 +2,6 @@
// See License.txt for license information.
var ErrorStore = require('../stores/error_store.jsx');
-var utils = require('../utils/utils.jsx');
-var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
-var Constants = require('../utils/constants.jsx');
-var ActionTypes = Constants.ActionTypes;
export default class ErrorBar extends React.Component {
constructor() {
@@ -13,70 +9,79 @@ export default class ErrorBar extends React.Component {
this.onErrorChange = this.onErrorChange.bind(this);
this.handleClose = this.handleClose.bind(this);
+ this.prevTimer = null;
- this.state = this.getStateFromStores();
- if (this.state.message) {
- setTimeout(this.handleClose, 10000);
+ this.state = ErrorStore.getLastError();
+ if (this.state && this.state.message) {
+ this.prevTimer = setTimeout(this.handleClose, 10000);
}
}
- getStateFromStores() {
- var error = ErrorStore.getLastError();
- if (!error || error.message === 'There appears to be a problem with your internet connection') {
- return {message: null};
- }
- return {message: error.message};
- }
componentDidMount() {
ErrorStore.addChangeListener(this.onErrorChange);
$('body').css('padding-top', $(React.findDOMNode(this)).outerHeight());
- $(window).resize(function onResize() {
- if (this.state.message) {
+ $(window).resize(() => {
+ if (this.state && this.state.message) {
$('body').css('padding-top', $(React.findDOMNode(this)).outerHeight());
}
- }.bind(this));
+ });
}
+
componentWillUnmount() {
ErrorStore.removeChangeListener(this.onErrorChange);
}
+
onErrorChange() {
- var newState = this.getStateFromStores();
- if (!utils.areStatesEqual(newState, this.state)) {
- if (newState.message) {
- setTimeout(this.handleClose, 10000);
- }
+ var newState = ErrorStore.getLastError();
+
+ if (this.prevTimer != null) {
+ clearInterval(this.prevTimer);
+ this.prevTimer = null;
+ }
+ if (newState) {
this.setState(newState);
+ this.prevTimer = setTimeout(this.handleClose, 10000);
+ } else {
+ this.setState({message: null});
}
}
+
handleClose(e) {
if (e) {
e.preventDefault();
}
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECIEVED_ERROR,
- err: null
- });
+ ErrorStore.storeLastError(null);
+ ErrorStore.emitChange();
$('body').css('padding-top', '0');
}
+
render() {
- if (this.state.message) {
- return (
- <div className='error-bar'>
- <span>{this.state.message}</span>
- <a
- href='#'
- className='error-bar__close'
- onClick={this.handleClose}
- >
- &times;
- </a>
- </div>
- );
+ if (!this.state) {
+ return <div/>;
+ }
+
+ if (!this.state.message) {
+ return <div/>;
+ }
+
+ if (this.state.connErrorCount < 7) {
+ return <div/>;
}
- return <div/>;
+ return (
+ <div className='error-bar'>
+ <span>{this.state.message}</span>
+ <a
+ href='#'
+ className='error-bar__close'
+ onClick={this.handleClose}
+ >
+ &times;
+ </a>
+ </div>
+ );
}
}
diff --git a/web/react/components/post_info.jsx b/web/react/components/post_info.jsx
index c38edf6a2..824e7ef39 100644
--- a/web/react/components/post_info.jsx
+++ b/web/react/components/post_info.jsx
@@ -5,6 +5,8 @@ var UserStore = require('../stores/user_store.jsx');
var utils = require('../utils/utils.jsx');
var Constants = require('../utils/constants.jsx');
+var Tooltip = ReactBootstrap.Tooltip;
+var OverlayTrigger = ReactBootstrap.OverlayTrigger;
export default class PostInfo extends React.Component {
constructor(props) {
@@ -148,15 +150,19 @@ export default class PostInfo extends React.Component {
var dropdown = this.createDropdown();
+ let tooltip = <Tooltip>{utils.displayDate(post.create_at)} at ${utils.displayTime(post.create_at)}</Tooltip>;
+
return (
<ul className='post-header post-info'>
<li className='post-header-col'>
- <time
- className='post-profile-time'
- title={`${utils.displayDate(post.create_at)} at ${utils.displayTime(post.create_at)}`}
+ <OverlayTrigger
+ placement='top'
+ overlay={tooltip}
>
- {utils.displayDateTime(post.create_at)}
- </time>
+ <time className='post-profile-time'>
+ {utils.displayDateTime(post.create_at)}
+ </time>
+ </OverlayTrigger>
</li>
<li className='post-header-col post-header__reply'>
<div className='dropdown'>
diff --git a/web/react/components/signup_user_complete.jsx b/web/react/components/signup_user_complete.jsx
index 8311747ee..495159efc 100644
--- a/web/react/components/signup_user_complete.jsx
+++ b/web/react/components/signup_user_complete.jsx
@@ -84,7 +84,7 @@ export default class SignupUserComplete extends React.Component {
if (this.props.hash > 0) {
BrowserStore.setGlobalItem(this.props.hash, JSON.stringify({wizard: 'finished'}));
}
- window.location.href = '/';
+ window.location.href = '/' + this.props.teamName + '/channels/town-square';
}.bind(this),
function emailLoginFailure(err) {
if (err.message === 'Login failed because email address has not been verified') {
diff --git a/web/react/components/team_signup_password_page.jsx b/web/react/components/team_signup_password_page.jsx
index b26d9f6ce..105e4817a 100644
--- a/web/react/components/team_signup_password_page.jsx
+++ b/web/react/components/team_signup_password_page.jsx
@@ -53,7 +53,7 @@ export default class TeamSignupPasswordPage extends React.Component {
props.state.wizard = 'finished';
props.updateParent(props.state, true);
- window.location.href = '/';
+ window.location.href = '/' + teamSignup.team.name + '/channels/town-square';
}.bind(this),
function loginFail(err) {
if (err.message === 'Login failed because email address has not been verified') {
diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx
index ea8126bec..5f5316013 100644
--- a/web/react/components/textbox.jsx
+++ b/web/react/components/textbox.jsx
@@ -5,7 +5,6 @@ const AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
const PostStore = require('../stores/post_store.jsx');
const CommandList = require('./command_list.jsx');
const ErrorStore = require('../stores/error_store.jsx');
-const AsyncClient = require('../utils/async_client.jsx');
const Utils = require('../utils/utils.jsx');
const Constants = require('../utils/constants.jsx');
@@ -18,7 +17,6 @@ export default class Textbox extends React.Component {
this.getStateFromStores = this.getStateFromStores.bind(this);
this.onListenerChange = this.onListenerChange.bind(this);
this.onRecievedError = this.onRecievedError.bind(this);
- this.onTimerInterrupt = this.onTimerInterrupt.bind(this);
this.updateMentionTab = this.updateMentionTab.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
@@ -35,8 +33,7 @@ export default class Textbox extends React.Component {
this.state = {
mentionText: '-1',
mentions: [],
- connection: '',
- timerInterrupt: null
+ connection: ''
};
this.caret = -1;
@@ -44,6 +41,7 @@ export default class Textbox extends React.Component {
this.doProcessMentions = false;
this.mentions = [];
}
+
getStateFromStores() {
const error = ErrorStore.getLastError();
@@ -53,6 +51,7 @@ export default class Textbox extends React.Component {
return {message: null};
}
+
componentDidMount() {
PostStore.addAddMentionListener(this.onListenerChange);
ErrorStore.addChangeListener(this.onRecievedError);
@@ -60,46 +59,28 @@ export default class Textbox extends React.Component {
this.resize();
this.updateMentionTab(null);
}
+
componentWillUnmount() {
PostStore.removeAddMentionListener(this.onListenerChange);
ErrorStore.removeChangeListener(this.onRecievedError);
}
+
onListenerChange(id, username) {
if (id === this.props.id) {
this.addMention(username);
}
}
- onRecievedError() {
- const errorState = this.getStateFromStores();
- if (this.state.timerInterrupt !== null) {
- window.clearInterval(this.state.timerInterrupt);
- this.setState({timerInterrupt: null});
- }
+ onRecievedError() {
+ const errorState = ErrorStore.getLastError();
- if (errorState.message === 'There appears to be a problem with your internet connection') {
+ if (errorState && errorState.connErrorCount > 0) {
this.setState({connection: 'bad-connection'});
- const timerInterrupt = window.setInterval(this.onTimerInterrupt, 5000);
- this.setState({timerInterrupt: timerInterrupt});
} else {
this.setState({connection: ''});
}
}
- onTimerInterrupt() {
- // Since these should only happen when you have no connection and slightly briefly after any
- // performance hit should not matter
- if (this.state.connection === 'bad-connection') {
- AppDispatcher.handleServerAction({
- type: ActionTypes.RECIEVED_ERROR,
- err: null
- });
-
- AsyncClient.updateLastViewedAt();
- }
- window.clearInterval(this.state.timerInterrupt);
- this.setState({timerInterrupt: null});
- }
componentDidUpdate() {
if (this.caret >= 0) {
Utils.setCaretPosition(React.findDOMNode(this.refs.message), this.caret);
@@ -111,6 +92,7 @@ export default class Textbox extends React.Component {
}
this.resize();
}
+
componentWillReceiveProps(nextProps) {
if (!this.addedMention) {
this.checkForNewMention(nextProps.messageText);
@@ -122,19 +104,22 @@ export default class Textbox extends React.Component {
this.addedMention = false;
this.refs.commands.getSuggestedCommands(nextProps.messageText);
}
+
updateMentionTab(mentionText) {
// using setTimeout so dispatch isn't called during an in progress dispatch
- setTimeout(function updateMentionTabAfterTimeout() {
+ setTimeout(() => {
AppDispatcher.handleViewAction({
type: ActionTypes.RECIEVED_MENTION_DATA,
id: this.props.id,
mention_text: mentionText
});
- }.bind(this), 1);
+ }, 1);
}
+
handleChange() {
this.props.onUserInput(React.findDOMNode(this.refs.message).value);
}
+
handleKeyPress(e) {
const text = React.findDOMNode(this.refs.message).value;
@@ -157,6 +142,7 @@ export default class Textbox extends React.Component {
this.props.onKeyPress(e);
}
+
handleKeyDown(e) {
if (Utils.getSelectedText(React.findDOMNode(this.refs.message)) !== '') {
this.doProcessMentions = true;
@@ -166,6 +152,7 @@ export default class Textbox extends React.Component {
this.handleBackspace(e);
}
}
+
handleBackspace() {
const text = React.findDOMNode(this.refs.message).value;
if (text.indexOf('/') === 0) {
@@ -185,6 +172,7 @@ export default class Textbox extends React.Component {
this.doProcessMentions = true;
}
}
+
checkForNewMention(text) {
const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
@@ -211,6 +199,7 @@ export default class Textbox extends React.Component {
const name = preText.substring(atIndex + 1, preText.length).toLowerCase();
this.updateMentionTab(name);
}
+
addMention(name) {
const caret = Utils.getCaretPosition(React.findDOMNode(this.refs.message));
@@ -233,11 +222,13 @@ export default class Textbox extends React.Component {
this.props.onUserInput(`${prefix}@${name} ${suffix}`);
}
+
addCommand(cmd) {
const elm = React.findDOMNode(this.refs.message);
elm.value = cmd;
this.handleChange();
}
+
resize() {
const e = React.findDOMNode(this.refs.message);
const w = React.findDOMNode(this.refs.wrapper);
@@ -264,21 +255,25 @@ export default class Textbox extends React.Component {
this.props.onHeightChange();
}
}
+
handleFocus() {
const elm = React.findDOMNode(this.refs.message);
if (elm.title === elm.value) {
elm.value = '';
}
}
+
handleBlur() {
const elm = React.findDOMNode(this.refs.message);
if (elm.value === '') {
elm.value = elm.title;
}
}
+
handlePaste() {
this.doProcessMentions = true;
}
+
render() {
return (
<div
diff --git a/web/react/components/user_settings/user_settings_appearance.jsx b/web/react/components/user_settings/user_settings_appearance.jsx
index 7617f04d1..4372069e7 100644
--- a/web/react/components/user_settings/user_settings_appearance.jsx
+++ b/web/react/components/user_settings/user_settings_appearance.jsx
@@ -81,6 +81,8 @@ export default class UserSettingsAppearance extends React.Component {
$('#user_settings').off('hidden.bs.modal', this.handleClose);
this.props.updateTab('general');
+ $('.ps-container.modal-body').scrollTop(0);
+ $('.ps-container.modal-body').perfectScrollbar('update');
$('#user_settings').modal('hide');
},
(err) => {
diff --git a/web/react/pages/channel.jsx b/web/react/pages/channel.jsx
index 07207c556..74259194a 100644
--- a/web/react/pages/channel.jsx
+++ b/web/react/pages/channel.jsx
@@ -26,6 +26,7 @@ var ChannelInviteModal = require('../components/channel_invite_modal.jsx');
var TeamMembersModal = require('../components/team_members.jsx');
var DirectChannelModal = require('../components/more_direct_channels.jsx');
var ErrorBar = require('../components/error_bar.jsx');
+var ErrorStore = require('../stores/error_store.jsx');
var ChannelLoader = require('../components/channel_loader.jsx');
var MentionList = require('../components/mention_list.jsx');
var ChannelInfoModal = require('../components/channel_info_modal.jsx');
@@ -234,6 +235,11 @@ function setupChannelPage(props) {
<RegisterAppModal />,
document.getElementById('register_app_modal')
);
+
+ if (global.window.config.SendEmailNotifications === 'false') {
+ ErrorStore.storeLastError({message: 'Preview Mode: Email notifications have not been configured'});
+ ErrorStore.emitChange();
+ }
}
global.window.setup_channel_page = setupChannelPage;
diff --git a/web/react/stores/error_store.jsx b/web/react/stores/error_store.jsx
index 597c88cff..ece7d8522 100644
--- a/web/react/stores/error_store.jsx
+++ b/web/react/stores/error_store.jsx
@@ -48,7 +48,7 @@ class ErrorStoreClass extends EventEmitter {
var ErrorStore = new ErrorStoreClass();
-ErrorStore.dispatchToken = AppDispatcher.register(function registry(payload) {
+ErrorStore.dispatchToken = AppDispatcher.register((payload) => {
var action = payload.action;
switch (action.type) {
case ActionTypes.RECIEVED_ERROR:
diff --git a/web/react/stores/socket_store.jsx b/web/react/stores/socket_store.jsx
index ae74059d1..1d853f979 100644
--- a/web/react/stores/socket_store.jsx
+++ b/web/react/stores/socket_store.jsx
@@ -3,6 +3,7 @@
var AppDispatcher = require('../dispatcher/app_dispatcher.jsx');
var UserStore = require('./user_store.jsx');
+var ErrorStore = require('./error_store.jsx');
var EventEmitter = require('events').EventEmitter;
var Constants = require('../utils/constants.jsx');
@@ -21,6 +22,7 @@ class SocketStoreClass extends EventEmitter {
this.addChangeListener = this.addChangeListener.bind(this);
this.removeChangeListener = this.removeChangeListener.bind(this);
this.sendMessage = this.sendMessage.bind(this);
+ this.failCount = 0;
this.initialize();
}
@@ -37,27 +39,43 @@ class SocketStoreClass extends EventEmitter {
protocol = 'wss://';
}
var connUrl = protocol + location.host + '/api/v1/websocket';
- console.log('connecting to ' + connUrl); //eslint-disable-line no-console
+ if (this.failCount === 0) {
+ console.log('websocket connecting to ' + connUrl); //eslint-disable-line no-console
+ }
conn = new WebSocket(connUrl);
- conn.onclose = function closeConn(evt) {
- console.log('websocket closed'); //eslint-disable-line no-console
- console.log(evt); //eslint-disable-line no-console
+ conn.onopen = () => {
+ if (this.failCount > 0) {
+ console.log('websocket re-established connection'); //eslint-disable-line no-console
+ }
+
+ this.failCount = 0;
+ ErrorStore.storeLastError(null);
+ ErrorStore.emitChange();
+ };
+
+ conn.onclose = () => {
conn = null;
setTimeout(
- function reconnect() {
+ () => {
this.initialize();
- }.bind(this),
+ },
3000
);
- }.bind(this);
+ };
+
+ conn.onerror = (evt) => {
+ if (this.failCount === 0) {
+ console.log('websocket error ' + evt); //eslint-disable-line no-console
+ }
+
+ this.failCount = this.failCount + 1;
- conn.onerror = function connError(evt) {
- console.log('websocket error'); //eslint-disable-line no-console
- console.log(evt); //eslint-disable-line no-console
+ ErrorStore.storeLastError({connErrorCount: this.failCount, message: 'We cannot reach the Mattermost service. The service may be down or misconfigured. Please contact an administrator to make sure the WebSocket port is configured properly.'});
+ ErrorStore.emitChange();
};
- conn.onmessage = function connMessage(evt) {
+ conn.onmessage = (evt) => {
AppDispatcher.handleServerAction({
type: ActionTypes.RECIEVED_MSG,
msg: JSON.parse(evt.data)
@@ -86,7 +104,7 @@ class SocketStoreClass extends EventEmitter {
var SocketStore = new SocketStoreClass();
-SocketStore.dispatchToken = AppDispatcher.register(function registry(payload) {
+SocketStore.dispatchToken = AppDispatcher.register((payload) => {
var action = payload.action;
switch (action.type) {
diff --git a/web/react/utils/client.jsx b/web/react/utils/client.jsx
index 4effa7307..715e26197 100644
--- a/web/react/utils/client.jsx
+++ b/web/react/utils/client.jsx
@@ -27,7 +27,7 @@ function handleError(methodName, xhr, status, err) {
msg = 'error in ' + methodName + ' status=' + status + ' statusCode=' + xhr.status + ' err=' + err;
if (xhr.status === 0) {
- e = {message: 'There appears to be a problem with your internet connection'};
+ e = {message: 'There appears to be a problem with your internet connection', connErrorCount: 1};
} else {
e = {message: 'We received an unexpected status code from the server (' + xhr.status + ')'};
}
diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx
index 90af9beda..8c9e1ee85 100644
--- a/web/react/utils/constants.jsx
+++ b/web/react/utils/constants.jsx
@@ -182,7 +182,7 @@ module.exports = {
},
{
id: 'sidebarText',
- uiName: 'Sidebar text color'
+ uiName: 'Sidebar Text'
},
{
id: 'sidebarHeaderBg',
@@ -190,51 +190,51 @@ module.exports = {
},
{
id: 'sidebarHeaderTextColor',
- uiName: 'Sidebar Header text color'
+ uiName: 'Sidebar Header Text'
},
{
id: 'sidebarUnreadText',
- uiName: 'Sidebar unread text color'
+ uiName: 'Sidebar Unread Text'
},
{
id: 'sidebarTextHoverBg',
- uiName: 'Sidebar text hover BG'
+ uiName: 'Sidebar Text Hover BG'
},
{
id: 'sidebarTextHoverColor',
- uiName: 'Sidebar text hover color'
+ uiName: 'Sidebar Text Hover Color'
},
{
id: 'sidebarTextActiveBg',
- uiName: 'Sidebar text active BG'
+ uiName: 'Sidebar Text Active BG'
},
{
id: 'sidebarTextActiveColor',
- uiName: 'Sidebar text active color'
+ uiName: 'Sidebar Text Active Color'
},
{
id: 'onlineIndicator',
- uiName: 'Online indicator'
+ uiName: 'Online Indicator'
},
{
id: 'mentionBj',
- uiName: 'Mention jewel BG'
+ uiName: 'Mention Jewel BG'
},
{
id: 'mentionColor',
- uiName: 'Mention jewel text color'
+ uiName: 'Mention Jewel Text'
},
{
id: 'centerChannelBg',
- uiName: 'Center channel BG'
+ uiName: 'Center Channel BG'
},
{
id: 'centerChannelColor',
- uiName: 'Center channel text color'
+ uiName: 'Center Channel Text'
},
{
id: 'linkColor',
- uiName: 'Link color'
+ uiName: 'Link Color'
},
{
id: 'buttonBg',
@@ -243,7 +243,7 @@ module.exports = {
{
id: 'buttonColor',
- uiName: 'Button Color'
+ uiName: 'Button Text'
}
]
};
diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 50438c6cf..d45240f9a 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -314,7 +314,6 @@ function getYoutubeEmbed(link) {
$('.video-type.' + youtubeId).html('Youtube - ');
$('.video-uploader.' + youtubeId).html(metadata.channelTitle);
$('.video-title.' + youtubeId).find('a').html(metadata.title);
- $('.post-list-holder-by-time').scrollTop($('.post-list-holder-by-time')[0].scrollHeight);
}
if (global.window.config.GoogleDeveloperKey) {
@@ -616,8 +615,9 @@ export function applyTheme(theme) {
}
if (theme.centerChannelColor) {
- changeCss('.app__content', 'color:' + theme.centerChannelColor, 2);
+ changeCss('.app__content, .post-create__container .post-create-body .btn-file', 'color:' + theme.centerChannelColor, 1);
changeCss('#post-create', 'color:' + theme.centerChannelColor, 2);
+ changeCss('.post-body hr', 'background:' + theme.centerChannelColor, 1);
changeCss('.channel-header .heading', 'color:' + theme.centerChannelColor, 1);
changeCss('.markdown__table tbody tr:nth-child(2n)', 'background:' + changeOpacity(theme.centerChannelColor, 0.07), 1);
changeCss('.channel-header__info>div.dropdown .header-dropdown__icon', 'color:' + changeOpacity(theme.centerChannelColor, 0.8), 1);
diff --git a/web/sass-files/sass/partials/_markdown.scss b/web/sass-files/sass/partials/_markdown.scss
index de92e9d20..122586354 100644
--- a/web/sass-files/sass/partials/_markdown.scss
+++ b/web/sass-files/sass/partials/_markdown.scss
@@ -2,10 +2,20 @@
font-weight: 700;
}
.markdown__paragraph-inline {
- display: inline;
- + .markdown__paragraph-inline {
- margin-left: 4px;
- }
+ display: inline;
+ + .markdown__paragraph-inline {
+ margin-left: 4px;
+ }
+}
+.post-body {
+ hr {
+ height: 4px;
+ padding: 0;
+ margin: 15px 0 16px;
+ background-color: #e7e7e7;
+ border: 0 none;
+ @include opacity(0.2);
+ }
}
.markdown__table {
background: #fff;
@@ -21,6 +31,26 @@
}
}
}
+blockquote {
+ border: none;
+ position: relative;
+ font-size: 16px;
+ padding: 10px 10px 10px 38px;
+ margin-bottom: 0;
+ &:before {
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-style: normal;
+ display: inline-block;
+ text-decoration: inherit;
+ content: "\f10d";
+ left: 8px;
+ top: 5px;
+ position: absolute;
+ font-size: 20px;
+ @include opacity(0.6);
+ }
+}
pre {
border: none;
background-color: #f7f7f7;
diff --git a/web/sass-files/sass/partials/_mentions.scss b/web/sass-files/sass/partials/_mentions.scss
index a86cb8a73..83cdde53b 100644
--- a/web/sass-files/sass/partials/_mentions.scss
+++ b/web/sass-files/sass/partials/_mentions.scss
@@ -66,8 +66,4 @@
.mention-highlight {
background-color:#fff2bb;
color: #333;
-}
-
-.mention-link {
- color:$primary-color;
} \ No newline at end of file
diff --git a/web/sass-files/sass/partials/_post.scss b/web/sass-files/sass/partials/_post.scss
index e362e8f7a..a1958af3b 100644
--- a/web/sass-files/sass/partials/_post.scss
+++ b/web/sass-files/sass/partials/_post.scss
@@ -185,7 +185,7 @@ body.ios {
.post-create__container {
form {
width: 100%;
- padding: 0 1em;
+ padding: 10px 1em 0 1em;
margin: 0;
}
.post-create-body {
@@ -229,12 +229,13 @@ body.ios {
right: 0;
position: absolute;
top: 1px;
- color: #999;
+ color: #444;
+ @include opacity(0.5);
@include single-transition(all, 0.15s);
font-size: 16px;
padding: 7px 9px 6px;
&:hover, &:active {
- color: #444;
+ @include opacity(0.9);
box-shadow: none;
}
}
diff --git a/web/sass-files/sass/partials/_responsive.scss b/web/sass-files/sass/partials/_responsive.scss
index d29c653ff..e0b35d0bf 100644
--- a/web/sass-files/sass/partials/_responsive.scss
+++ b/web/sass-files/sass/partials/_responsive.scss
@@ -417,7 +417,7 @@
padding: 0 1em;
}
form {
- padding: 0;
+ padding: 10px 0 0 0;
}
.post-create-body {
padding-bottom: 10px;
diff --git a/web/sass-files/sass/partials/_videos.scss b/web/sass-files/sass/partials/_videos.scss
index de18aa08a..9e1ce29b7 100644
--- a/web/sass-files/sass/partials/_videos.scss
+++ b/web/sass-files/sass/partials/_videos.scss
@@ -1,6 +1,7 @@
.video-div {
position:relative;
max-width: 480px;
+ margin-bottom: 8px;
.video-thumbnail {
max-width: 100%;
height: auto;
@@ -19,18 +20,15 @@
}
.video-type {
- color:grey;
+ @include opacity(0.8);
font-size:15px;
- font-weight:200;
margin:0px;
padding:0px;
}
.video-uploader {
- font-size:15px;
- margin-top:3px;
- margin-bottom:0px;
- padding:0px;
+ font-size: 13px;
+ margin: 0 0 15px;
}
.video-title {