if (this.props.active) {
classname = classname + " conv-active";
}
- return <li className={classname}>{this.props.buddy}</li>;
+ var label = this.props.buddy.display_name;
+ return <li className={classname} onClick={this.props.onClick}>{this.props.buddy.display_name}</li>;
}
});
render: function() {
var tabs = [];
for (var i = 0 ; i < this.props.tabs.length ; i++) {
- tabs.push(ConversationTab(this.props.tabs[i]));
+ tabs.push(ConversationTab({
+ key: "tab" + i,
+ onClick: this.props.onTabSwitch.bind(null, i),
+ buddy: this.props.tabs[i],
+ active: (this.props.focused_tab == i)
+ }));
}
return <ul className="conv-tabbar">{ tabs }</ul>;
}
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.props.onSendMessage(this.state.text);
this.setState(this.getInitialState());
}
// TODO: close window when Escape pressed?
var messages = [];
for (var i = 0 ; i < this.props.messages.length ; i++) {
var msg = this.props.messages[i];
+ var sender = ((msg.direction == "outgoing") ? "me" : this.props.buddy.display_name) + ":";
+ var senderclass = "sender " + msg.direction;
var msgclass = "msg " + msg.direction;
- messages.push(<p>
+ var msgkey = "msg" + i;
+ messages.push(<p key={msgkey}>
<span className="timestamp">{msg.timestamp}</span>
- <span className="sender">{msg.sender}</span>
+ <span className={senderclass}>{sender}</span>
<span className={msgclass}>{msg.text}</span>
</p>);
}
return <div className="conv-tab-content">
<div className="conv-log">{ messages }</div>
- <ConversationTextEntry />
+ <ConversationTextEntry onSendMessage={this.props.onSendMessage}/>
</div>;
}
});
var Conversations = React.createClass({
+ getInitialState: function() {
+ return {
+ focused_tab: -1, // this is zero-indexed, so -1 means "no tab"
+ conversations: []
+ };
+ },
+ handleTabSwitch: function(focusedTabIndex) {
+ console.log("Switching to tab " + focusedTabIndex);
+ this.setState({ focused_tab: focusedTabIndex,
+ conversations: this.state.conversations
+ });
+ },
+ handleSendMessage: function(message) {
+ // 1) Dispatch IM to buddy
+ var active_convs = this.state.conversations.filter(function(obj) { return obj.visible; });
+ var conv = active_convs[this.state.focused_tab];
+ var contact = window.blist[conv.buddy.id];
+ var params = {
+ proto: contact.proto,
+ account: contact.account,
+ buddy: contact.buddy,
+ message: message
+ };
+ console.log("sending im:", params);
+ window.Dispatcher.send_msg("conversations", "send_im", params);
+ // 2) Append IM to sent messages. Later, I might want to hold off on this until I get the ack
+ // from the server, but for now, this will give the most responsive UI.
+ var now = new Date();
+ // TODO: extract this datetime-formatting thing to some other module
+ // TODO: zero-pad these for values < 10
+ var timestamp = "" + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds();
+ conv.messages.push({
+ direction: "outgoing",
+ timestamp: timestamp,
+ text: message
+ });
+ // It's awkward trying to deep copy the entire messages array. I am a
+ // bad person and am mutating this.state directly, so when done
+ // changing state, call forceUpdate()
+ this.forceUpdate();
+ },
+ getConversation: function(proto, account, buddy) {
+ // Helper function to get a particular entry in the conversations list.
+ // If a conversation already exists, it finds it.
+ // Otherwise, it creates a new one for the specified buddy.
+ var bid = proto + ":" + account + ":" + buddy;
+ for (var i = 0 ; i < this.state.conversations.length ; i++) {
+ if (this.state.conversations[i].buddy.id == bid) {
+ return this.state.conversations[i];
+ }
+ }
+ // None found, construct a new one.
+ var entry = {
+ visible: true,
+ buddy: {id: bid, display_name: buddy},
+ messages: []
+ };
+ this.state.conversations.push(entry);
+ this.forceUpdate();
+ return entry;
+ },
+ handleRecvIm: function(params) {
+ var bid = params.proto + ":" + params.account + ":" + params.buddy;
+ if (this.state.conversations[bid] === undefined) {
+ this.state.conversations[bid] = {
+ visible: true,
+ buddy: {id: bid, display_name: params.buddy},
+ messages: []
+ };
+ }
+ this.getConversation(params.proto, params.account, params.buddy).messages.push({
+ direction: "incoming",
+ timestamp: params.timestamp,
+ text: params.message
+ });
+ this.forceUpdate();
+ },
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"
- } ];
+ var active_convs = this.state.conversations.filter(function(obj) { return obj.visible; });
+
+ // conv_tabs is just an Array of strings with the tab title
+ var conv_tabs = active_convs.map(function(obj) { return obj.buddy; });
+
+ var active_pane_messages = [];
+ var active_buddy = null;
+ if (this.state.focused_tab >= 0 && this.state.focused_tab < active_convs.length) {
+ active_pane_messages = active_convs[this.state.focused_tab].messages;
+ active_buddy = active_convs[this.state.focused_tab].buddy;
+ }
+
return <div className="conversations">
- <ConversationTabBar tabs={conv_tabs} />
- <ConversationTabContent messages={active_pane_messages} />
+ <ConversationTabBar onTabSwitch={this.handleTabSwitch} focused_tab={this.state.focused_tab} tabs={conv_tabs} />
+ <ConversationTabContent onSendMessage={this.handleSendMessage} buddy={active_buddy} messages={active_pane_messages} />
</div>;
}
});