Merge pull request #168 from ralder/fix-loading-screen-for-ie

[webui] fix loading animation for ie
Этот коммит содержится в:
Joram Wilander
2015-07-14 10:49:51 -04:00
родитель cce492977b 4cc9bfce5c
Коммит 1ba12b0ccf
8 изменённых файлов: 109 добавлений и 140 удалений

24
web/react/components/loading_screen.jsx Обычный файл
Просмотреть файл

@@ -0,0 +1,24 @@
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
module.exports = React.createClass({
displayName: "LoadingScreen",
propTypes: {
position: React.PropTypes.oneOf(['absolute', 'fixed', 'relative', 'static', 'inherit'])
},
getDefaultProps: function() {
return { position: 'relative' };
},
render: function() {
return (
<div className="loading-screen" style={{position: this.props.position}}>
<div className="loading__content">
<h3>Loading</h3>
<div className="round round-1"></div>
<div className="round round-2"></div>
<div className="round round-3"></div>
</div>
</div>
);
}
});

Просмотреть файл

@@ -7,6 +7,7 @@ var client = require('../utils/client.jsx');
var asyncClient = require('../utils/async_client.jsx'); var asyncClient = require('../utils/async_client.jsx');
var UserStore = require('../stores/user_store.jsx'); var UserStore = require('../stores/user_store.jsx');
var ChannelStore = require('../stores/channel_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx');
var LoadingScreen = require('./loading_screen.jsx');
function getStateFromStores() { function getStateFromStores() {
return { return {
@@ -16,6 +17,8 @@ function getStateFromStores() {
} }
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "MoreChannelsModal",
componentDidMount: function() { componentDidMount: function() {
ChannelStore.addMoreChangeListener(this._onChange); ChannelStore.addMoreChangeListener(this._onChange);
$(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) { $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) {
@@ -90,7 +93,7 @@ module.exports = React.createClass({
<p className="more-channel-name">{channel.display_name}</p> <p className="more-channel-name">{channel.display_name}</p>
<p className="more-channel-description">{channel.description}</p> <p className="more-channel-description">{channel.description}</p>
</td> </td>
<td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="pull-right btn btn-primary">Join</button></td> <td className="td--action"><button onClick={outter.handleJoin.bind(outter, channel.id)} className="btn btn-primary">Join</button></td>
</tr> </tr>
) )
})} })}
@@ -100,15 +103,7 @@ module.exports = React.createClass({
<p className="primary-message">No more channels to join</p> <p className="primary-message">No more channels to join</p>
<p className="secondary-message">Click 'Create New Channel' to make a new one</p> <p className="secondary-message">Click 'Create New Channel' to make a new one</p>
</div>) </div>)
: <div ref="loadingscreen" className="loading-screen loading-screen--channel"> : <LoadingScreen /> }
<div className="loading__content">
<h3>Loading</h3>
<div id="round_1" className="round"></div>
<div id="round_2" className="round"></div>
<div id="round_3" className="round"></div>
</div>
</div>
}
{ server_error } { server_error }
</div> </div>
<div className="modal-footer"> <div className="modal-footer">

Просмотреть файл

@@ -10,6 +10,7 @@ var UserStore = require('../stores/user_store.jsx');
var ActionTypes = Constants.ActionTypes; var ActionTypes = Constants.ActionTypes;
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "Post",
componentDidMount: function() { componentDidMount: function() {
$('.modal').on('show.bs.modal', function () { $('.modal').on('show.bs.modal', function () {
$('.modal-body').css('overflow-y', 'auto'); $('.modal-body').css('overflow-y', 'auto');
@@ -19,7 +20,7 @@ module.exports = React.createClass({
handleCommentClick: function(e) { handleCommentClick: function(e) {
e.preventDefault(); e.preventDefault();
data = {}; var data = {};
data.order = [this.props.post.id]; data.order = [this.props.post.id];
data.posts = this.props.posts; data.posts = this.props.posts;
@@ -48,7 +49,6 @@ module.exports = React.createClass({
var commentCount = 0; var commentCount = 0;
var commentRootId = parentPost ? post.root_id : post.id; var commentRootId = parentPost ? post.root_id : post.id;
var rootUser = "";
for (var postId in posts) { for (var postId in posts) {
if (posts[postId].root_id == commentRootId) { if (posts[postId].root_id == commentRootId) {
commentCount += 1; commentCount += 1;
@@ -57,12 +57,7 @@ module.exports = React.createClass({
var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null; var error = this.state.error ? <div className='form-group has-error'><label className='control-label'>{ this.state.error }</label></div> : null;
if (this.props.sameRoot){ var rootUser = this.props.sameRoot ? "same--root" : "other--root";
rootUser = "same--root";
}
else {
rootUser = "other--root";
}
var postType = ""; var postType = "";
if (type != "Post"){ if (type != "Post"){
@@ -83,7 +78,7 @@ module.exports = React.createClass({
<div className="post-profile-img__container"> <div className="post-profile-img__container">
<img className="post-profile-img" src={"/api/v1/users/" + post.user_id + "/image?time=" + timestamp} height="36" width="36" /> <img className="post-profile-img" src={"/api/v1/users/" + post.user_id + "/image?time=" + timestamp} height="36" width="36" />
</div> </div>
: "" } : null }
<div className="post__content"> <div className="post__content">
<PostHeader post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} /> <PostHeader post={post} sameRoot={this.props.sameRoot} commentCount={commentCount} handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} />
<PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} /> <PostBody post={post} sameRoot={this.props.sameRoot} parentPost={parentPost} posts={posts} handleCommentClick={this.handleCommentClick} />

Просмотреть файл

@@ -120,7 +120,7 @@ module.exports = React.createClass({
return ( return (
<div className="post-body"> <div className="post-body">
{ comment } { comment }
<p key={post.Id+"_message"} className={postClass}><span>{inner}</span></p> <p key={post.id+"_message"} className={postClass}><span>{inner}</span></p>
{ filenames && filenames.length > 0 ? { filenames && filenames.length > 0 ?
<div className="post-image__columns"> <div className="post-image__columns">
{ postFiles } { postFiles }

Просмотреть файл

@@ -8,6 +8,7 @@ var UserProfile = require( './user_profile.jsx' );
var AsyncClient = require('../utils/async_client.jsx'); var AsyncClient = require('../utils/async_client.jsx');
var CreatePost = require('./create_post.jsx'); var CreatePost = require('./create_post.jsx');
var Post = require('./post.jsx'); var Post = require('./post.jsx');
var LoadingScreen = require('./loading_screen.jsx');
var SocketStore = require('../stores/socket_store.jsx'); var SocketStore = require('../stores/socket_store.jsx');
var utils = require('../utils/utils.jsx'); var utils = require('../utils/utils.jsx');
var Client = require('../utils/client.jsx'); var Client = require('../utils/client.jsx');
@@ -26,37 +27,8 @@ function getStateFromStores() {
}; };
} }
function changeColor(col, amt) {
var usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
var num = parseInt(col,16);
var r = (num >> 16) + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
var b = ((num >> 8) & 0x00FF) + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
var g = (num & 0x0000FF) + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6);
}
module.exports = React.createClass({ module.exports = React.createClass({
displayName: "PostList",
scrollPosition: 0, scrollPosition: 0,
preventScrollTrigger: false, preventScrollTrigger: false,
gotMorePosts: false, gotMorePosts: false,
@@ -69,7 +41,7 @@ module.exports = React.createClass({
utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;'); utils.changeCss('a.theme', 'color:'+user.props.theme+'; fill:'+user.props.theme+'!important;');
utils.changeCss('div.theme', 'background-color:'+user.props.theme+';'); utils.changeCss('div.theme', 'background-color:'+user.props.theme+';');
utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';'); utils.changeCss('.btn.btn-primary', 'background: ' + user.props.theme+';');
utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + changeColor(user.props.theme, -10) +';'); utils.changeCss('.btn.btn-primary:hover, .btn.btn-primary:active, .btn.btn-primary:focus', 'background: ' + utils.changeColor(user.props.theme, -10) +';');
utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';'); utils.changeCss('.modal .modal-header', 'background: ' + user.props.theme+';');
utils.changeCss('.mention', 'background: ' + user.props.theme+';'); utils.changeCss('.mention', 'background: ' + user.props.theme+';');
utils.changeCss('.mention-link', 'color: ' + user.props.theme+';'); utils.changeCss('.mention-link', 'color: ' + user.props.theme+';');
@@ -161,24 +133,20 @@ module.exports = React.createClass({
$('body').off('click.userpopover'); $('body').off('click.userpopover');
}, },
resize: function() { resize: function() {
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
if (this.gotMorePosts) { if (this.gotMorePosts) {
this.gotMorePosts = false; this.gotMorePosts = false;
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
$(post_holder).scrollTop($(post_holder).scrollTop() + (post_holder.scrollHeight-this.oldScrollHeight) ); $(post_holder).scrollTop($(post_holder).scrollTop() + (post_holder.scrollHeight-this.oldScrollHeight) );
$(post_holder).perfectScrollbar('update');
} else { } else {
var post_holder = $(".post-list-holder-by-time")[0];
this.preventScrollTrigger = true;
if ($("#new_message")[0] && !this.scrolledToNew) { if ($("#new_message")[0] && !this.scrolledToNew) {
$(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63); $(post_holder).scrollTop($(post_holder).scrollTop() + $("#new_message").offset().top - 63);
$(post_holder).perfectScrollbar('update');
this.scrolledToNew = true; this.scrolledToNew = true;
} else { } else {
$(post_holder).scrollTop(post_holder.scrollHeight); $(post_holder).scrollTop(post_holder.scrollHeight);
$(post_holder).perfectScrollbar('update');
} }
} }
$(post_holder).perfectScrollbar('update');
}, },
_onChange: function() { _onChange: function() {
var newState = getStateFromStores(); var newState = getStateFromStores();
@@ -347,7 +315,10 @@ module.exports = React.createClass({
<div className="channel-intro-profile"> <div className="channel-intro-profile">
<strong><UserProfile userId={teammate.id} /></strong> <strong><UserProfile userId={teammate.id} /></strong>
</div> </div>
<p className="channel-intro-text">{"This is the start of your private message history with " + teammate_name + "." }<br/>{"Private messages and files shared here are not shown to people outside this area."}</p> <p className="channel-intro-text">
{"This is the start of your private message history with " + teammate_name + "." }<br/>
{"Private messages and files shared here are not shown to people outside this area."}
</p>
</div> </div>
); );
} else { } else {
@@ -410,7 +381,7 @@ module.exports = React.createClass({
{ channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." } { channel.type === 'P' ? " Only invited members can see this private group." : " Any member can join and read this channel." }
<br/> <br/>
<a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}><i className="fa fa-pencil"></i>Set a description</a> <a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#edit_channel" data-desc={channel.description} data-title={channel.display_name} data-channelid={channel.id}><i className="fa fa-pencil"></i>Set a description</a>
<a className="intro-links" style={userStyle} data-toggle="modal" data-target="#channel_invite"><i className="fa fa-user-plus"></i>Invite others to this {ui_type}</a> <a className="intro-links" href="#" style={userStyle} data-toggle="modal" data-target="#channel_invite"><i className="fa fa-user-plus"></i>Invite others to this {ui_type}</a>
</p> </p>
</div> </div>
); );
@@ -420,37 +391,35 @@ module.exports = React.createClass({
var postCtls = []; var postCtls = [];
if (posts != undefined) { if (posts) {
var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date(); var previousPostDay = posts[order[order.length-1]] ? utils.getDateForUnixTicks(posts[order[order.length-1]].create_at): new Date();
var currentPostDay = new Date(); var currentPostDay;
for (var i = order.length-1; i >= 0; i--) { for (var i = order.length-1; i >= 0; i--) {
var post = posts[order[i]]; var post = posts[order[i]];
var parentPost; var parentPost = post.parent_id ? posts[post.parent_id] : null;
if (post.parent_id) { var sameUser = '';
parentPost = posts[post.parent_id]; var sameRoot = false;
} else { var hideProfilePic = false;
parentPost = null; var prevPost = (i < order.length - 1) ? posts[order[i + 1]] : null;
if (prevPost) {
sameUser = (prevPost.user_id === post.user_id) && (post.create_at - prevPost.create_at <= 1000*60*5) ? "same--user" : "";
sameRoot = utils.isComment(post) && (prevPost.id === post.root_id || prevPost.root_id === post.root_id);
// we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post
hideProfilePic = (prevPost.user_id === post.user_id) && !utils.isComment(prevPost) && !utils.isComment(post);
} }
var sameUser = i < order.length-1 && posts[order[i+1]].user_id === post.user_id && post.create_at - posts[order[i+1]].create_at <= 1000*60*5 ? "same--user" : "";
var sameRoot = i < order.length-1 && post.root_id != "" && (posts[order[i+1]].id === post.root_id || posts[order[i+1]].root_id === post.root_id) ? true : false;
// we only hide the profile pic if the previous post is not a comment, the current post is not a comment, and the previous post was made by the same user as the current post
var hideProfilePic = i < order.length-1 && posts[order[i+1]].user_id === post.user_id && posts[order[i+1]].root_id === '' && post.root_id === '';
// check if it's the last comment in a consecutive string of comments on the same post // check if it's the last comment in a consecutive string of comments on the same post
var isLastComment = false; // it is the last comment if it is last post in the channel or the next post has a different root post
if (utils.isComment(post)) { var isLastComment = utils.isComment(post) && (i === 0 || posts[order[i-1]].root_id != post.root_id);
// it is the last comment if it is last post in the channel or the next post has a different root post
isLastComment = (i === 0 || posts[order[i-1]].root_id != post.root_id);
}
var postCtl = <Post sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id} posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} />; var postCtl = <Post sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id} posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment} />;
currentPostDay = utils.getDateForUnixTicks(post.create_at); currentPostDay = utils.getDateForUnixTicks(post.create_at);
if(currentPostDay.getDate() !== previousPostDay.getDate() || currentPostDay.getMonth() !== previousPostDay.getMonth() || currentPostDay.getFullYear() !== previousPostDay.getFullYear()) { if (currentPostDay.toDateString() != previousPostDay.toDateString()) {
postCtls.push( postCtls.push(
<div className="date-separator"> <div className="date-separator">
<hr className="separator__hr" /> <hr className="separator__hr" />
@@ -469,20 +438,10 @@ module.exports = React.createClass({
); );
} }
postCtls.push(postCtl); postCtls.push(postCtl);
previousPostDay = utils.getDateForUnixTicks(post.create_at); previousPostDay = currentPostDay;
} }
} } else {
else { postCtls.push(<LoadingScreen position="absolute" />);
postCtls.push(
<div ref="loadingscreen" className="loading-screen">
<div className="loading__content">
<h3>Loading</h3>
<div id="round_1" className="round"></div>
<div id="round_2" className="round"></div>
<div id="round_3" className="round"></div>
</div>
</div>
);
} }
return ( return (
@@ -497,5 +456,3 @@ module.exports = React.createClass({
); );
} }
}); });

Просмотреть файл

@@ -782,3 +782,34 @@ module.exports.getHomeLink = function() {
parts[0] = "www"; parts[0] = "www";
return window.location.protocol + "//" + parts.join("."); return window.location.protocol + "//" + parts.join(".");
} }
module.exports.changeColor =function(col, amt) {
var usePound = false;
if (col[0] == "#") {
col = col.slice(1);
usePound = true;
}
var num = parseInt(col,16);
var r = (num >> 16) + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
var b = ((num >> 8) & 0x00FF) + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
var g = (num & 0x0000FF) + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return (usePound?"#":"") + String("000000" + (g | (b << 8) | (r << 16)).toString(16)).slice(-6);
};

Просмотреть файл

@@ -2,13 +2,8 @@
display: table; display: table;
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute; padding: 60px;
@include box-sizing(border-box);
text-align: center; text-align: center;
&.loading-screen--channel {
position: relative;
padding: 4em 0 3.5em;
}
.loading__content { .loading__content {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
@@ -19,11 +14,7 @@
margin: 0 0.2em 0; margin: 0 0.2em 0;
display: inline-block; display: inline-block;
} }
}
}
.loading-screen {
.loading__content {
.round { .round {
background-color: #444; background-color: #444;
width: 4px; width: 4px;
@@ -32,43 +23,18 @@
margin: 0 2px; margin: 0 2px;
opacity: 0.1; opacity: 0.1;
@include border-radius(10px); @include border-radius(10px);
-moz-animation: move 0.75s infinite linear; @include animation(move 0.75s infinite linear);
-webkit-animation: move 0.75s infinite linear;
} }
#round_1 { @for $i from 1 through 3 {
-moz-animation-delay: .2s; .round-#{$i} {
-webkit-animation-delay: .2s; @include animation-delay(.2s*$i);
}
#round_2 {
-moz-animation-delay: .4s;
-webkit-animation-delay: .4s;
}
#round_3 {
-moz-animation-delay: .6s;
-webkit-animation-delay: .6s;
}
@-moz-keyframes move {
0% {
opacity: 1;
} }
100% {
opacity: 0.1;
};
} }
@-webkit-keyframes move { @include keyframes(move) {
0% { from { opacity: 1; }
opacity: 1; to { opacity: 0.1; }
}
100% {
opacity: 0.1;
};
} }
} }
} }

Просмотреть файл

@@ -119,6 +119,7 @@ body.ios {
table-layout: fixed; table-layout: fixed;
width: 100%; width: 100%;
min-height: 100%; min-height: 100%;
height: 100%;
.post-list__content { .post-list__content {
display: table-cell; display: table-cell;
vertical-align: bottom; vertical-align: bottom;