From: Drew Fisher <drew.m.fisher@gmail.com>
Date: Wed, 14 May 2014 08:06:38 +0000 (-0700)
Subject: A mostly-working three-panel page
X-Git-Url: http://git.zarvox.org/shortlog/%7B%7B%20url_for%28%27main.signup_page%27%29%20%7D%7D?a=commitdiff_plain;h=a70f6c380988ca8c1ef37bf3b16b868f235953e3;p=imoo.git

A mostly-working three-panel page

I think I'm finally getting the hang of React.
---

diff --git a/reactornado/dispatcher.js b/reactornado/dispatcher.js
index cca1a46..4667757 100644
--- a/reactornado/dispatcher.js
+++ b/reactornado/dispatcher.js
@@ -36,7 +36,7 @@ var Dispatcher = {
             console.log(url);
             window.blist[bid].bicon_url = url;
         }
-        window.buddy_list.onChange(window.blist);
+        window.reactor.refs.buddy_list.onChange(window.blist);
     },
     handle_conversations_message: function(method, params) {
         console.log("conversations:", method, params);
diff --git a/reactornado/index.html b/reactornado/index.html
index bc028bb..994bd01 100644
--- a/reactornado/index.html
+++ b/reactornado/index.html
@@ -11,11 +11,5 @@
 		<script type="text/javascript" src="main.js"></script>
 	</head>
 	<body>
-		<!-- A div for the initial login form -->
-		<div id="loginform"></div>
-		<!-- A div for the buddy list -->
-		<div id="buddy_list"></div>
-		<!-- A div for the tabbed chat view -->
-		<div id="chats"></div>
 	</body>
 </html>
diff --git a/reactornado/main.js b/reactornado/main.js
index 02a8176..3c2147a 100644
--- a/reactornado/main.js
+++ b/reactornado/main.js
@@ -21,14 +21,9 @@ window.onload = function () {
     window.ws_conn = ws;
 
     // Attach React-rendered components
-    React.renderComponent(
-        window.widgets.LoginForm(),
-        document.getElementById('loginform')
-    );
-
-    window.buddy_list = React.renderComponent(
-        window.widgets.BuddyList(),
-        document.getElementById('buddy_list')
+    window.reactor = React.renderComponent(
+        window.widgets.ReactContainer(),
+        document.body
     );
     console.log("loaded");
 };
diff --git a/reactornado/style.css b/reactornado/style.css
index f4bebfe..e8b865b 100644
--- a/reactornado/style.css
+++ b/reactornado/style.css
@@ -4,8 +4,11 @@ body {
 	min-height: 100vh;
 }
 
-.form {
-	float: left;
+#react-container {
+	height: 100vh;
+	width: 100vw;
+	display: -webkit-flex;
+	-webkit-flex-direction: row;
 }
 
 .form > label {
@@ -16,13 +19,15 @@ body {
 	margin-bottom: 20px;
 }
 
-#buddy_list {
-	float: right;
+.buddy_list {
+	min-width: 20vw;
+	max-height: 100vh;
+	overflow: auto;
 }
 
 /* buddy list things */
 .buddy {
-	width: 200px;
+	min-width: 200px;
 	height: 48px;
 	font-size: small;
 }
@@ -50,3 +55,59 @@ body {
 	white-space: nowrap;
 	width: 100%;
 }
+
+/* Things related to the conversation tabbar, backlog, and text input */
+
+.conversations {
+	-webkit-flex: 1;
+	min-height: 100vh;
+	width: 80%;
+	min-height: 100vh;
+	display: -webkit-flex;
+	-webkit-flex-direction: column;
+	//width: 80%;
+}
+
+.conv-tabbar {
+	display: block;
+	margin: 0px;
+}
+
+.conv-tab {
+	display: inline-block;
+	border-width: 2px 2px 0px 2px;
+	border-color: black;
+	border-style: solid;
+	outline: 2px;
+}
+
+.conv-active {
+	background-color: #f80;
+}
+
+.conv-tab-content {
+	display: -webkit-flex;
+	-webkit-flex-direction: column;
+	-webkit-flex: 1;
+	width: 100%;
+}
+
+/* Pack the individual chat lines tightly */
+.conv-log {
+	-webkit-flex: 1;
+}
+.conv-log > p {
+	margin: 0px;
+	padding: 0px;
+}
+
+.conv-input-container {
+	width: 100%;
+}
+
+.conv-input {
+	letter-spacing: normal;
+	word-spacing: normal;
+	word-wrap: break-word;
+	width: 95%;
+}
diff --git a/reactornado/widgets.js b/reactornado/widgets.js
index a1202dd..4ced81c 100644
--- a/reactornado/widgets.js
+++ b/reactornado/widgets.js
@@ -33,6 +33,12 @@ var LoginForm = React.createClass({
     handleLogin: function () {
         console.log("Imma log in now");
         window.Dispatcher.send_msg("accounts", "create_account", this.state);
+        this.setState(this.getInitialState());
+    },
+    checkSubmit: function(e) {
+        if (e && e.keyCode == 13) {
+            this.handleLogin();
+        }
     },
 
     render: function () {
@@ -42,8 +48,8 @@ var LoginForm = React.createClass({
             <option value="prpl-jabber">Jabber</option>
             <option value="prpl-aim">AIM</option>
         </select></label>
-        <label>Username:<input type="text" id="username" valueLink={this.linkState('username')} /></label>
-        <label>Password:<input type="password" id="password" valueLink={this.linkState('password')} /></label>
+        <label>Username:<input type="text" id="username" valueLink={this.linkState('username')} onKeyPress={this.checkSubmit} /></label>
+        <label>Password:<input type="password" id="password" valueLink={this.linkState('password')} onKeyPress={this.checkSubmit} /></label>
         <input type="button" id="login" value="Login" onClick={this.handleLogin} />
         </form>
         );
@@ -96,11 +102,102 @@ var BuddyList = React.createClass({
     }
 });
 
+var ConversationTab = React.createClass({
+    render: function() {
+        var classname = "conv-tab";
+        if (this.props.active) {
+            classname = classname + " conv-active";
+        }
+        return <li className={classname}>{this.props.buddy}</li>;
+    }
+});
+
+var ConversationTabBar = React.createClass({
+    render: function() {
+        var tabs = [];
+        for (var i = 0 ; i < this.props.tabs.length ; i++) {
+            tabs.push(ConversationTab(this.props.tabs[i]));
+        }
+        return <ul className="conv-tabbar">{ tabs }</ul>;
+    }
+});
+
+var ConversationTextEntry = React.createClass({
+    mixins: [React.addons.LinkedStateMixin],
+    getInitialState: function() {
+        return {"text": ""};
+    },
+    checkSubmit: function(e) {
+        if (e && e.keyCode == 13) {
+            // TODO: do this only if Shift not currently pressed? (awkward...)
+            e.preventDefault();
+            // TODO: do something with this.state.text - window.Dispatcher.send_im?
+            this.setState(this.getInitialState());
+        }
+        // TODO: close window when Escape pressed?
+        // TODO: change tabs when Tab pressed?
+    },
+    render: function() {
+        return <footer className="conv-input-container"><textarea className="conv-input" rows="4" valueLink={this.linkState('text')} onKeyPress={this.checkSubmit} ></textarea></footer>;
+    }
+});
+
+var ConversationTabContent = React.createClass({
+    render: function() {
+        var messages = [];
+        for (var i = 0 ; i < this.props.messages.length ; i++) {
+            var msg = this.props.messages[i];
+            var msgclass = "msg " + msg.direction;
+            messages.push(<p>
+                <span className="timestamp">{msg.timestamp}</span>
+                <span className="sender">{msg.sender}</span>
+                <span className={msgclass}>{msg.text}</span>
+                </p>);
+        }
+        return <div className="conv-tab-content">
+                <div className="conv-log">{ messages }</div>
+                <ConversationTextEntry />
+               </div>;
+    }
+});
+
+var Conversations = React.createClass({
+    render: function() {
+        // TODO: pull data from this.state and provide a method to handle
+        // dispatcher-sent messages
+        var conv_tabs = [ {"active": true, "buddy": "Drew Fisher"},
+                          {"active": false, "buddy": "Kate Harrison"},
+                          {"active": false, "buddy": "Matt Mullins"}
+               ];
+        var active_pane_messages = [ {"direction": "incoming",
+                               "sender": "nobody",
+                               "timestamp": "10:41",
+                               "text": "this is a test message"
+                              } ];
+        return <div className="conversations">
+                <ConversationTabBar tabs={conv_tabs} />
+                <ConversationTabContent messages={active_pane_messages} />
+               </div>;
+    }
+});
+
+var ReactContainer = React.createClass({
+    render: function() {
+        return <div id="react-container">
+                 <LoginForm ref="login_form" />
+                 <Conversations ref="conversations" />
+                 <BuddyList ref="buddy_list" />
+               </div>;
+    }
+});
+
 // integration for now:
 window.widgets = {
     "List": List,
     "ListItem": ListItem,
     "BuddyListItem": BuddyListItem,
     "LoginForm": LoginForm,
-    "BuddyList": BuddyList
+    "BuddyList": BuddyList,
+    "Conversations": Conversations,
+    "ReactContainer": ReactContainer
 };