daily commit

This commit is contained in:
Gene S
2016-01-04 19:12:31 -08:00
parent 20be763dc1
commit f64c8082ff
2 changed files with 182 additions and 103 deletions
+12 -2
View File
@@ -103,6 +103,7 @@ html, body {
.label-as-badge { .label-as-badge {
border-radius: 1em; border-radius: 1em;
margin-left: 1em;
} }
.contactList { .contactList {
@@ -162,6 +163,7 @@ html, body {
overflow-y: scroll; overflow-y: scroll;
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
background-image: url("../ignasi_pattern_s.png");
} }
#messagesView { #messagesView {
@@ -174,6 +176,13 @@ html, body {
box-shadow: 0px -1px 2px rgba(0, 0, 0, 0.15); box-shadow: 0px -1px 2px rgba(0, 0, 0, 0.15);
} }
#dummyView {
display:flex;
align-items: center;
justify-content: center;
background-color: #E9E9FF;
}
.chatBox { .chatBox {
list-style: none; list-style: none;
padding: 1.5em 0 1.5em; padding: 1.5em 0 1.5em;
@@ -200,8 +209,8 @@ html, body {
max-width: 90%; max-width: 90%;
float: right; float: right;
background-color: #EFE; background-color: #EFE;
border: 1px solid #CCC; border: 1px solid #CDC;
border-radius: 2px; border-radius: 4px;
padding: 1em; padding: 1em;
position: relative; position: relative;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.15); box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.15);
@@ -233,6 +242,7 @@ html, body {
.chatBox > li.chatBox-inverted > .chatBox-panel { .chatBox > li.chatBox-inverted > .chatBox-panel {
background-color: #EEF; background-color: #EEF;
border: 1px solid #CCD;
float: left; float: left;
} }
+170 -101
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>React Tutorial</title> <title>Tinode example: chat in react.js</title>
<!-- Not present in the tutorial. Just for basic styling. --> <!-- Not present in the tutorial. Just for basic styling. -->
<link rel="stylesheet" href="css/bootstrap.css" /> <link rel="stylesheet" href="css/bootstrap.css" />
<link rel="stylesheet" href="css/base.css" /> <link rel="stylesheet" href="css/base.css" />
@@ -59,7 +59,17 @@
} }
}) })
/* BEGIN Login */
var LoginForm = React.createClass({ var LoginForm = React.createClass({
getInitialState: function() {
return {login: '', password: ''};
},
handleLoginChange: function(e) {
this.setState({login: e.target.value});
},
handlePasswordChange: function(e) {
this.setState({password: e.target.value});
},
handleSubmit: function(e) { handleSubmit: function(e) {
e.preventDefault(); e.preventDefault();
this.props.onLogin(true); this.props.onLogin(true);
@@ -69,9 +79,17 @@
<div className="container-fluid"> <div className="container-fluid">
<form className="form-signin" onSubmit={this.handleSubmit}> <form className="form-signin" onSubmit={this.handleSubmit}>
<label htmlFor="inputLogin" className="sr-only">Login</label> <label htmlFor="inputLogin" className="sr-only">Login</label>
<input type="text" id="inputLogin" className="form-control" placeholder="Login (alice, bob, carol, dave, frank)" required autofocus /> <input type="text" id="inputLogin" className="form-control"
placeholder="Login (alice, bob, carol, dave, frank)"
value={this.state.login}
onChange={this.handleLoginChange}
required autofocus />
<label htmlFor="inputPassword" className="sr-only">Password</label> <label htmlFor="inputPassword" className="sr-only">Password</label>
<input type="password" id="inputPassword" className="form-control" placeholder="Password (alice123, bob123, ...)" required /> <input type="password" id="inputPassword" className="form-control"
placeholder="Password (alice123, bob123, ...)"
value={this.state.password}
onChange={this.handlePasswordChange}
required />
<br /> <br />
<button className="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> <button className="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form> </form>
@@ -90,7 +108,11 @@
); );
} }
}) })
/* END Login */
/* BEGIN Contact list (list of topics) */
/*Contacts caption */
var CurrentUser = React.createClass({ var CurrentUser = React.createClass({
render: function() { render: function() {
return ( return (
@@ -111,6 +133,7 @@
} }
}); });
/* A form for initiating a new topic */
var NewTopic = React.createClass({ var NewTopic = React.createClass({
render: function() { render: function() {
return ( return (
@@ -131,6 +154,107 @@
{id: "usrDAVE", public: "Dave Goliaphsson", comment: "This is a comment", online: {now: false, when: "30 min ago"}, unread: 0} {id: "usrDAVE", public: "Dave Goliaphsson", comment: "This is a comment", online: {now: false, when: "30 min ago"}, unread: 0}
]; ];
/* A single topic */
var Contact = React.createClass({
handleClick: function() {
this.props.onSelected(this.props.public);
},
render: function() {
return (
<li className="media contact" onClick={this.handleClick}>
<a href="#" className="pull-left"><img src="avatar.svg" className="avatar img-circle" alt="" /></a>
<div className="media-body">
<Contact.Online when={this.props.when} now={this.props.now} />
<strong>{this.props.public}</strong> <Contact.Unread count={this.props.unread} />
<p>{this.props.comment}</p>
</div>
</li>
);
}
});
/* The counter of unread messages in the topic */
Contact.Unread = React.createClass({
render: function() {
var showUnreadBadge = null;
if (this.props.count > 0) {
showUnreadBadge = <span className="label label-primary label-as-badge">{this.props.count}</span>;
}
return (
<span>{showUnreadBadge}</span>
);
}
})
/* Online indicator */
Contact.Online = React.createClass({
render: function() {
var lastOnline = '';
if (!this.props.online && this.props.when) {
lastOnline = this.props.when;
} else {
lastOnline = 'online now';
}
return (
<span className="text-muted pull-right">
<span className={this.props.now ? "online" : "offline"}>&#x25cf;</span> <small className="text-muted">&nbsp;{lastOnline}</small>
</span>
);
}
})
/* List of comtacts/topics */
var ContactList = React.createClass({
getInitialState: function() {
return {selected: false};
},
onSelectionChange: function(sel) {
this.props.onTopicSelected(sel);
},
render: function() {
var contactNodes = this.props.data.map(function(c) {
return (
<Contact public={c.public} comment={c.comment}
unread={c.unread}
when={c.online.when} now={c.online.now}
onSelected={this.onSelectionChange} key={c.id} />
);
}, this);
return (
<div className="contactList">
<ul className="media-list">
{contactNodes}
</ul>
</div>
);
}
});
/* The panel to hold all contacts-related stuff together */
var ContactsView = React.createClass({
getInitialState: function() {
return {contacts: []};
},
componentDidMount: function() {
this.setState({contacts: contactsData});
},
render: function() {
return (
<div className="contactBox">
<CurrentUser />
<div className="container-fluid">
<NewTopic />
<ContactList data={this.state.contacts}
onTopicSelected={this.props.onTopicSelected} />
</div>
</div>
);
}
});
/* END Contact list */
/* BEGIN Conversation panel */
var messagesList = [ var messagesList = [
{ {
id: 1, content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet tellus interdum. \ id: 1, content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam sit amet tellus interdum. \
@@ -159,88 +283,6 @@ dignissim a diam a hendrerit. Nulla vel est ut quam rhoncus pulvinar. In hac hab
{id: 7, content: "OK!", timestamp: new Date(2016, 0, 3, 18, 19, 20, 567), response: true} {id: 7, content: "OK!", timestamp: new Date(2016, 0, 3, 18, 19, 20, 567), response: true}
]; ];
var Contact = React.createClass({
render: function() {
return (
<li className="media contact">
<a href="#" className="pull-left"><img src="avatar.svg" className="avatar img-circle" alt="" /></a>
<div className="media-body">
<Contact.Online when={this.props.when} now={this.props.now} />
<strong>{this.props.public}</strong> <Contact.Unread count={this.props.unread} />
<p>{this.props.comment}</p>
</div>
</li>
);
}
});
Contact.Unread = React.createClass({
render: function() {
var showUnreadBadge;
if (this.props.count > 0) {
showUnreadBadge = <span className="label label-success label-as-badge">{this.props.count}</span>;
}
return (
<span>&nbsp;{showUnreadBadge}</span>
);
}
})
Contact.Online = React.createClass({
render: function() {
var lastOnline = '';
if (!this.props.online && this.props.when) {
lastOnline = this.props.when;
} else {
lastOnline = 'online';
}
return (
<span className="text-muted pull-right">
<span className={this.props.now ? "online" : "offline"}>&#x25cf;</span> <small className="text-muted">&nbsp;{lastOnline}</small>
</span>
);
}
})
var ContactList = React.createClass({
render: function() {
var contactNodes = this.props.data.map(function(c) {
return (
<Contact public={c.public} comment={c.comment} unread={c.unread}
when={c.online.when} now={c.online.now} key={c.id} />
);
});
return (
<div className="contactList">
<ul className="media-list">
{contactNodes}
</ul>
</div>
);
}
});
var ContactsView = React.createClass({
getInitialState: function() {
return {contacts: []};
},
componentDidMount: function() {
this.setState({contacts: contactsData});
},
render: function() {
return (
<div className="contactBox">
<CurrentUser />
<div className="container-fluid">
<NewTopic />
<ContactList data={this.state.contacts} />
</div>
</div>
);
}
});
var Message = React.createClass({ var Message = React.createClass({
getInitialState: function() { getInitialState: function() {
return {received: 0}; return {received: 0};
@@ -263,6 +305,7 @@ dignissim a diam a hendrerit. Nulla vel est ut quam rhoncus pulvinar. In hac hab
} }
}); });
/* Received/read indicator */
Message.ReceivedMarker = React.createClass({ Message.ReceivedMarker = React.createClass({
getInitialState: function() { getInitialState: function() {
return {received: 0}; return {received: 0};
@@ -281,6 +324,7 @@ dignissim a diam a hendrerit. Nulla vel est ut quam rhoncus pulvinar. In hac hab
} }
}); });
/* Send message form */
Message.Send = React.createClass({ Message.Send = React.createClass({
render: function() { render: function() {
return ( return (
@@ -296,6 +340,21 @@ dignissim a diam a hendrerit. Nulla vel est ut quam rhoncus pulvinar. In hac hab
); );
} }
}); });
/* This is just a static page to show when no conversation is selected */
var DummyMessagesView = React.createClass({
render: function() {
return (
<div className="panel panel-default fullHeight">
<div className="panel-body text-center center-block fullHeight" id="dummyView">
<div><img id="logo" alt="logo" src="group.svg" />
<h3>Tinode Demo Chat</h3></div>
</div>
</div>
);
}
});
var MessagesView = React.createClass({ var MessagesView = React.createClass({
getInitialState: function() { getInitialState: function() {
return {messages: []}; return {messages: []};
@@ -310,27 +369,45 @@ dignissim a diam a hendrerit. Nulla vel est ut quam rhoncus pulvinar. In hac hab
); );
}); });
return ( return (
<ul className="chatBox"> <div className="panel panel-default fullViewPort" id="messagesPanel">
{messageNodes} <div className="panel-heading" id="chatTitle">
</ul> <h3 className="panel-title">{this.props.topic}</h3>
</div>
<div className="panel-body" id="messagesView">
<ul className="chatBox">
{messageNodes}
</ul>
</div>
<div className="panel-footer" id="sendMessagePanel">
<Message.Send />
</div>
</div>
); );
} }
}); });
/* END Conversation panel */
var AppView = React.createClass({ var AppView = React.createClass({
getInitialState: function() { getInitialState: function() {
return {loggedIn: false, chatTitle: "Bob Smith"}; return {loggedIn: false, selTopic: ''};
}, },
handleLogin: function(ok) { handleLogin: function(ok) {
this.setState({loggedIn: ok}); this.setState({loggedIn: ok});
}, },
handleTopicSelected: function(topic) {
this.setState({selTopic: topic});
},
render: function() { render: function() {
return ( return (
<div className="fullScreen"> <div className="fullScreen">
<div id="sidebar-wrapper"> <div id="sidebar-wrapper">
<div className="sidebar-nav"> <div className="sidebar-nav">
{this.state.loggedIn ? <ContactsView data={contactsData} /> : <LoginView onLogin={this.handleLogin} />} {this.state.loggedIn ?
<ContactsView data={contactsData}
onTopicSelected={this.handleTopicSelected} /> :
<LoginView onLogin={this.handleLogin} />}
</div> </div>
</div> </div>
@@ -338,17 +415,9 @@ dignissim a diam a hendrerit. Nulla vel est ut quam rhoncus pulvinar. In hac hab
<div className="container-fluid fullHeight"> <div className="container-fluid fullHeight">
<div className="row fullHeight"> <div className="row fullHeight">
<div className="col-lg-12 fullHeight"> <div className="col-lg-12 fullHeight">
<div className="panel panel-default fullViewPort" id="messagesPanel"> {this.state.selTopic ?
<div className="panel-heading" id="chatTitle"> <MessagesView topic={this.state.selTopic} /> :
<h3 className="panel-title">{this.state.chatTitle}</h3> <DummyMessagesView />}
</div>
<div className="panel-body" id="messagesView">
<MessagesView />
</div>
<div className="panel-footer" id="sendMessagePanel">
<Message.Send />
</div>
</div>
</div> </div>
</div> </div>
</div> </div>