diff options
Diffstat (limited to 'web')
21 files changed, 178 insertions, 72 deletions
diff --git a/web/react/components/confirm_modal.jsx b/web/react/components/confirm_modal.jsx new file mode 100644 index 000000000..3be13cf9b --- /dev/null +++ b/web/react/components/confirm_modal.jsx @@ -0,0 +1,31 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +module.exports = React.createClass({ + handleConfirm: function() { + $('#'+this.props.parent_id).attr('data-confirm', 'true'); + $('#'+this.props.parent_id).modal('hide'); + $('#'+this.props.id).modal('hide'); + }, + render: function() { + return ( + <div className="modal fade" id={this.props.id} tabIndex="-1" role="dialog" aria-hidden="true"> + <div className="modal-dialog"> + <div className="modal-content"> + <div className="modal-header"> + <h4 className="modal-title">{this.props.title}</h4> + </div> + <div className="modal-body"> + {this.props.message} + </div> + <div className="modal-footer"> + <button type="button" className="btn btn-default" data-dismiss="modal">Cancel</button> + <button onClick={this.handleConfirm} type="button" className="btn btn-primary">{this.props.confirm_button}</button> + </div> + </div> + </div> + </div> + ); + } +}); + diff --git a/web/react/components/create_comment.jsx b/web/react/components/create_comment.jsx index 9bcbad079..cb7aa371c 100644 --- a/web/react/components/create_comment.jsx +++ b/web/react/components/create_comment.jsx @@ -145,7 +145,7 @@ module.exports = React.createClass({ onUserInput={this.handleUserInput} onKeyPress={this.commentMsgKeyPress} messageText={this.state.messageText} - createMessage="Create a comment..." + createMessage="Add a comment..." initialText="" id="reply_textbox" ref="textbox" /> diff --git a/web/react/components/create_post.jsx b/web/react/components/create_post.jsx index 191be9bf8..5a0b6f85f 100644 --- a/web/react/components/create_post.jsx +++ b/web/react/components/create_post.jsx @@ -31,9 +31,7 @@ module.exports = React.createClass({ post.message = this.state.messageText; - var repRegex = new RegExp("<br>", "g"); - if (post.message.replace(repRegex, " ").trim().length === 0 - && this.state.previews.length === 0) { + if (post.message.trim().length === 0 && this.state.previews.length === 0) { return; } @@ -250,7 +248,7 @@ module.exports = React.createClass({ onUserInput={this.handleUserInput} onKeyPress={this.postMsgKeyPress} messageText={this.state.messageText} - createMessage="Create a post..." + createMessage="Write a message..." channelId={this.state.channel_id} id="post_textbox" ref="textbox" /> diff --git a/web/react/components/invite_member_modal.jsx b/web/react/components/invite_member_modal.jsx index 1d2bbed84..5980664de 100644 --- a/web/react/components/invite_member_modal.jsx +++ b/web/react/components/invite_member_modal.jsx @@ -4,8 +4,37 @@ var utils = require('../utils/utils.jsx'); var Client =require('../utils/client.jsx'); var UserStore = require('../stores/user_store.jsx'); +var ConfirmModal = require('./confirm_modal.jsx'); module.exports = React.createClass({ + componentDidMount: function() { + var self = this; + $('#invite_member').on('hide.bs.modal', function(e) { + if ($('#invite_member').attr('data-confirm') === 'true') { + $('#invite_member').attr('data-confirm', 'false'); + return; + } + + var not_empty = false; + for (var i = 0; i < self.state.invite_ids.length; i++) { + var index = self.state.invite_ids[i]; + if (self.refs["email"+index].getDOMNode().value.trim() !== '') { + not_empty = true; + break; + } + } + + if (not_empty) { + $('#confirm_invite_modal').modal('show'); + e.preventDefault(); + } + + }); + + $('#invite_member').on('hidden.bs.modal', function() { + self.clearFields(); + }); + }, handleSubmit: function(e) { var invite_ids = this.state.invite_ids; var count = invite_ids.length; @@ -56,22 +85,8 @@ module.exports = React.createClass({ Client.inviteMembers(data, function() { + $(this.refs.modal.getDOMNode()).attr('data-confirm', 'true'); $(this.refs.modal.getDOMNode()).modal('hide'); - for (var i = 0; i < invite_ids.length; i++) { - var index = invite_ids[i]; - this.refs["email"+index].getDOMNode().value = ""; - if (config.AllowInviteNames) { - this.refs["first_name"+index].getDOMNode().value = ""; - this.refs["last_name"+index].getDOMNode().value = ""; - } - } - this.setState({ - invite_ids: [0], - id_count: 0, - email_errors: {}, - first_name_errors: {}, - last_name_errors: {} - }); }.bind(this), function(err) { this.setState({ server_error: err }); @@ -89,6 +104,26 @@ module.exports = React.createClass({ invite_ids.push(count); this.setState({ invite_ids: invite_ids, id_count: count }); }, + clearFields: function() { + var invite_ids = this.state.invite_ids; + + for (var i = 0; i < invite_ids.length; i++) { + var index = invite_ids[i]; + this.refs["email"+index].getDOMNode().value = ""; + if (config.AllowInviteNames) { + this.refs["first_name"+index].getDOMNode().value = ""; + this.refs["last_name"+index].getDOMNode().value = ""; + } + } + + this.setState({ + invite_ids: [0], + id_count: 0, + email_errors: {}, + first_name_errors: {}, + last_name_errors: {} + }); + }, removeInviteFields: function(index) { var invite_ids = this.state.invite_ids; var i = invite_ids.indexOf(index); @@ -147,29 +182,38 @@ module.exports = React.createClass({ var server_error = this.state.server_error ? <div className='form-group has-error'><label className='control-label'>{ this.state.server_error }</label></div> : null; return ( - <div className="modal fade" ref="modal" id="invite_member" tabIndex="-1" role="dialog" aria-hidden="true"> - <div className="modal-dialog"> - <div className="modal-content"> - <div className="modal-header"> - <button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button> - <h4 className="modal-title" id="myModalLabel">Invite New Member</h4> - </div> - <div ref="modalBody" className="modal-body"> - <form role="form"> - { invite_sections } - </form> - { server_error } - <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button> - <br/> - <br/> - <label className='control-label'>People invited automatically join Town Square channel.</label> - </div> - <div className="modal-footer"> - <button type="button" className="btn btn-default" data-dismiss="modal">Close</button> - <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button> - </div> - </div> - </div> + <div> + <div className="modal fade" ref="modal" id="invite_member" tabIndex="-1" role="dialog" aria-hidden="true"> + <div className="modal-dialog"> + <div className="modal-content"> + <div className="modal-header"> + <button type="button" className="close" data-dismiss="modal" aria-label="Close" data-reactid=".5.0.0.0.0"><span aria-hidden="true" data-reactid=".5.0.0.0.0.0">×</span></button> + <h4 className="modal-title" id="myModalLabel">Invite New Member</h4> + </div> + <div ref="modalBody" className="modal-body"> + <form role="form"> + { invite_sections } + </form> + { server_error } + <button type="button" className="btn btn-default" onClick={this.addInviteFields}>Add another</button> + <br/> + <br/> + <label className='control-label'>People invited automatically join Town Square channel.</label> + </div> + <div className="modal-footer"> + <button type="button" className="btn btn-default" data-dismiss="modal">Close</button> + <button onClick={this.handleSubmit} type="button" className="btn btn-primary">Send Invitations</button> + </div> + </div> + </div> + </div> + <ConfirmModal + id="confirm_invite_modal" + parent_id="invite_member" + title="Discard Invitations?" + message="You have unsent invitations, are you sure you want to discard them?" + confirm_button="Yes, Discard" + /> </div> ); } else { diff --git a/web/react/components/login.jsx b/web/react/components/login.jsx index 103a93bc6..65f1da1f8 100644 --- a/web/react/components/login.jsx +++ b/web/react/components/login.jsx @@ -52,20 +52,20 @@ var FindTeamDomain = React.createClass({ <div> <span className="signup-team__name">{ config.SiteName }</span> <br/> - <span className="signup-team__subdomain">Enter your {strings.TeamPlural} domain.</span> + <span className="signup-team__subdomain">Enter your {strings.Team}'s domain.</span> <br/> <br/> </div> <form onSubmit={this.handleSubmit}> <div className={server_error ? 'form-group has-error' : 'form-group'}> { server_error } - <input type="text" className="form-control" name="domain" ref="domain" placeholder="teamdomain" /> + <input type="text" className="form-control" name="domain" ref="domain" placeholder="team domain" /> </div> <div className="form-group"> <button type="submit" className="btn btn-primary">Continue</button> </div> <div> - <span>Don't remember your {strings.TeamPlural} domain? <a href="/find_team">Find it here</a></span> + <span>Don't remember your {strings.Team}'s domain? <a href="/find_team">Find it here</a></span> </div> <br/> <br/> diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index 55fc32c33..3079917ec 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -85,20 +85,22 @@ module.exports = React.createClass({ var postFiles = []; var images = []; if (filenames) { - for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) { + for (var i = 0; i < filenames.length; i++) { var fileInfo = utils.splitFileLocation(filenames[i]); if (Object.keys(fileInfo).length === 0) continue; var type = utils.getFileType(fileInfo.ext); if (type === "image") { - postFiles.push( - <div className="post-image__column" key={filenames[i]}> - <a href="#" onClick={this.handleImageClick} data-img-id={images.length.toString()} data-toggle="modal" data-target={"#" + postImageModalId }><div ref={filenames[i]} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}></div></a> - </div> - ); + if (i < Constants.MAX_DISPLAY_FILES) { + postFiles.push( + <div className="post-image__column" key={filenames[i]}> + <a href="#" onClick={this.handleImageClick} data-img-id={images.length.toString()} data-toggle="modal" data-target={"#" + postImageModalId }><div ref={filenames[i]} className="post__load" style={{backgroundImage: 'url(/static/images/load.gif)'}}></div></a> + </div> + ); + } images.push(filenames[i]); - } else { + } else if (i < Constants.MAX_DISPLAY_FILES) { postFiles.push( <div className="post-image__column custom-file" key={fileInfo.name}> <a href={fileInfo.path+"."+fileInfo.ext} download={fileInfo.name+"."+fileInfo.ext}> diff --git a/web/react/components/textbox.jsx b/web/react/components/textbox.jsx index 45798809f..7a4762e07 100644 --- a/web/react/components/textbox.jsx +++ b/web/react/components/textbox.jsx @@ -204,7 +204,7 @@ module.exports = React.createClass({ // If there is a space after the last @, nothing to do. if (lastSpace > atIndex || lastCharSpace > atIndex) { - this.setState({ mentionText: '-1' }); + this.updateMentionTab('-1', null); return; } diff --git a/web/react/components/user_settings.jsx b/web/react/components/user_settings.jsx index a9c2433f2..7d542a8b7 100644 --- a/web/react/components/user_settings.jsx +++ b/web/react/components/user_settings.jsx @@ -658,9 +658,10 @@ var SecurityTab = React.createClass({ ); } else { var d = new Date(this.props.user.last_password_update); - var hour = d.getHours() < 10 ? "0" + d.getHours() : String(d.getHours()); + var hour = d.getHours() % 12 ? String(d.getHours() % 12) : "12"; var min = d.getMinutes() < 10 ? "0" + d.getMinutes() : String(d.getMinutes()); - var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min; + var timeOfDay = d.getHours() >= 12 ? " pm" : " am"; + var dateStr = "Last updated " + Constants.MONTHS[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + " at " + hour + ":" + min + timeOfDay; passwordSection = ( <SettingItemMin @@ -1056,7 +1057,7 @@ var AppearanceTab = React.createClass({ themeSection = ( <SettingItemMax - title="Theme" + title="Theme Color" inputs={inputs} submit={this.submitTheme} server_error={server_error} @@ -1066,7 +1067,7 @@ var AppearanceTab = React.createClass({ } else { themeSection = ( <SettingItemMin - title="Theme" + title="Theme Color" describe={this.state.theme} updateSection={function(){self.props.updateSection("theme");}} /> diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index 4cb30e1d3..c573e9dbb 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -124,7 +124,7 @@ module.exports = React.createClass({ <div key={name+"_loading"}> <img ref="placeholder" className="loader-image" src="/static/images/load.gif" /> { percentage > 0 ? - <span className="loader-percent" >{"Downloading " + percentage + "%"}</span> + <span className="loader-percent" >{"Previewing " + percentage + "%"}</span> : ""} </div> ); diff --git a/web/react/utils/constants.jsx b/web/react/utils/constants.jsx index 0a3b1db3d..deb07409b 100644 --- a/web/react/utils/constants.jsx +++ b/web/react/utils/constants.jsx @@ -61,6 +61,7 @@ module.exports = { "channel", "internal", "localhost", + "dockerhost", "stag", "post", "cluster", diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx index 628d92342..f358f6c05 100644 --- a/web/react/utils/utils.jsx +++ b/web/react/utils/utils.jsx @@ -28,6 +28,9 @@ module.exports.isTestDomain = function() { if ((/^localhost/).test(window.location.hostname)) return true; + if ((/^dockerhost/).test(window.location.hostname)) + return true; + if ((/^test/).test(window.location.hostname)) return true; @@ -75,8 +78,14 @@ module.exports.getDomainWithOutSub = function() { var parts = window.location.host.split("."); - if (parts.length == 1) - return "localhost:8065"; + if (parts.length == 1) { + if (parts[0].indexOf("dockerhost") > -1) { + return "dockerhost:8065"; + } + else { + return "localhost:8065"; + } + } return parts[1] + "." + parts[2]; } @@ -357,9 +366,6 @@ module.exports.textToJsx = function(text, options) { if (options && options['singleline']) { var repRegex = new RegExp("\n", "g"); text = text.replace(repRegex, " "); - } else { - var repRegex = new RegExp("\n", "g"); - text = text.replace(repRegex, "<br>"); } var searchTerm = "" @@ -383,7 +389,7 @@ module.exports.textToJsx = function(text, options) { implicitKeywords[keywordArray[i]] = true; } - var lines = text.split("<br>"); + var lines = text.split("\n"); var urlMatcher = new LinkifyIt(); for (var i = 0; i < lines.length; i++) { var line = lines[i]; @@ -425,7 +431,7 @@ module.exports.textToJsx = function(text, options) { } else if (trimWord.match(hashRegex)) { var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); - var mClass = trimWord in implicitKeywords ? mentionClass : ""; + var mClass = trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords ? mentionClass : ""; if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) { highlightSearchClass = " search-highlight"; @@ -433,7 +439,7 @@ module.exports.textToJsx = function(text, options) { inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>); - } else if (trimWord in implicitKeywords) { + } else if (trimWord in implicitKeywords || trimWord.toLowerCase() in implicitKeywords) { var suffix = word.match(puncEndRegex); var prefix = word.match(puncStartRegex); diff --git a/web/sass-files/sass/partials/_signup.scss b/web/sass-files/sass/partials/_signup.scss index 11ccc0fc9..8917ebb9f 100644 --- a/web/sass-files/sass/partials/_signup.scss +++ b/web/sass-files/sass/partials/_signup.scss @@ -17,7 +17,7 @@ font-weight: 600; margin-bottom: 0.5em; letter-spacing: -0.5px; - font-size: em(30px); + font-size: em(28px); } h3 { font-weight: 600; @@ -28,6 +28,10 @@ font-size: em(18px); font-weight: 600; margin-bottom: 1em; + &.text--light { + font-weight: 300; + color: #999; + } } p { color: #555; diff --git a/web/static/config/config.js b/web/static/config/config.js index 5a12942c8..82d4bbf70 100644 --- a/web/static/config/config.js +++ b/web/static/config/config.js @@ -35,4 +35,4 @@ var strings = { TeamPlural: "teams", Company: "company", CompanyPlural: "companies" -} +}; diff --git a/web/static/help/configure_links.html b/web/static/help/configure_links.html index 61e64a3b9..be6490192 100644 --- a/web/static/help/configure_links.html +++ b/web/static/help/configure_links.html @@ -1,5 +1,23 @@ <htmL> <body> -<p>update these to your own</p> +<h1>About Mattermost</h1> +<p>Mattermost is a team communication service. It brings team real-time messaging and file sharing into one place, with easy archiving and search, accessible across PCs and phones. +</p> +<p>We built Mattermost to help teams focus on what matters most to them. It works for us, we hope it works for you too. + +Learn more, or download the source code from <a href=http://mattermost.com>http://mattermost.com</a>.</p> + +<h1>How to update this link</h1> +<p>In the source code, search for "config.js" and update the links pointing to this page to whatever policies and product description you prefer. +</p> + +<h1>Join the community</h1> +<p>To take part in the community building Mattermost, please consider sharing comments, feature requests, votes, and contributions. If you like the project, please Tweet about us at <a href=https://twitter.com/mattermosthq>@mattermosthq</a>.</p> + +<p>Here's some links to get started:<br> +<li><a href=http://bit.ly/1dHmQqX>Mattermost source code and install instructions</a></li> +<li><a href=http://bit.ly/1JUDoZ3>Mattermost Feature Request and Voting Site</a> </li> +<li><a href=http://bit.ly/1MH9HKa>Mattermost Issue Tracker for reporting bugs</a></li> +</p> </body> -</html>
\ No newline at end of file +</html> diff --git a/web/static/images/ding.mp3 b/web/static/images/ding.mp3 Binary files differindex b08407e03..bfbd9bb82 100644 --- a/web/static/images/ding.mp3 +++ b/web/static/images/ding.mp3 diff --git a/web/static/images/salamander.jpg b/web/static/images/salamander.jpg Binary files differdeleted file mode 100644 index e841b12d5..000000000 --- a/web/static/images/salamander.jpg +++ /dev/null diff --git a/web/static/images/test.png b/web/static/images/test.png Binary files differindex 7d95d80e5..d8a9513d8 100644 --- a/web/static/images/test.png +++ b/web/static/images/test.png diff --git a/web/static/images/testgif.gif b/web/static/images/testgif.gif Binary files differnew file mode 100755 index 000000000..83d5eed72 --- /dev/null +++ b/web/static/images/testgif.gif diff --git a/web/static/images/testjpg.jpg b/web/static/images/testjpg.jpg Binary files differnew file mode 100755 index 000000000..025c8c581 --- /dev/null +++ b/web/static/images/testjpg.jpg diff --git a/web/static/images/toothless.gif b/web/static/images/toothless.gif Binary files differdeleted file mode 100644 index 8c269c131..000000000 --- a/web/static/images/toothless.gif +++ /dev/null diff --git a/web/templates/signup_team.html b/web/templates/signup_team.html index e2b9bc1ad..f7e277340 100644 --- a/web/templates/signup_team.html +++ b/web/templates/signup_team.html @@ -8,7 +8,8 @@ <div class="col-sm-12"> <div class="signup-team__container"> <img class="signup-team-logo" src="/static/images/logo.png" /> - <h4>{{ .SiteName }} is free for an unlimited time, for unlimited users. </h4> + <h2>All team communication in one place, searchable and accessible anywhere</h2> + <h4 class="text--light">{{ .SiteName }} is free for an unlimited time, for unlimited users </h4 class="text--light"> <div id="signup-team"></div> <a class="signup-team-login" href="/login">or Sign In</a> </div> |