From 86775b88e4b8afc3a044ed2d1947ed249eb1c33b Mon Sep 17 00:00:00 2001 From: Drew Fisher Date: Sat, 17 May 2014 00:20:15 -0700 Subject: [PATCH] frontend: many things - Allow switching between tabs with click - Sending messages works and appends to chat log - Make IMs received propagate to conversation tabs --- reactornado/dispatcher.js | 7 +-- reactornado/widgets.js | 125 ++++++++++++++++++++++++++++++++------ 2 files changed, 107 insertions(+), 25 deletions(-) diff --git a/reactornado/dispatcher.js b/reactornado/dispatcher.js index 4667757..b22b0d6 100644 --- a/reactornado/dispatcher.js +++ b/reactornado/dispatcher.js @@ -53,12 +53,7 @@ var Dispatcher = { } if (method == "recv_im") { - window.conversations[bid].visible = true; - window.conversations[bid].messages.push({ - message: params.message, - timestamp: params.timestamp, - sender: "them" - }); + window.reactor.refs.conversations.handleRecvIm(params); } else if (method == "buddy_typing_state") { window.conversations[bid].type_state = params.state; } diff --git a/reactornado/widgets.js b/reactornado/widgets.js index a1dafc4..cb3e325 100644 --- a/reactornado/widgets.js +++ b/reactornado/widgets.js @@ -89,7 +89,8 @@ var ConversationTab = React.createClass({ if (this.props.active) { classname = classname + " conv-active"; } - return
  • {this.props.buddy}
  • ; + var label = this.props.buddy.display_name; + return
  • {this.props.buddy.display_name}
  • ; } }); @@ -97,7 +98,12 @@ 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])); + 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 ; } @@ -112,7 +118,7 @@ var ConversationTextEntry = React.createClass({ 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? @@ -128,36 +134,117 @@ var ConversationTabContent = React.createClass({ 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(

    + var msgkey = "msg" + i; + messages.push(

    {msg.timestamp} - {msg.sender} + {sender} {msg.text}

    ); } return
    { messages }
    - +
    ; } }); 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
    - - + +
    ; } }); -- 2.39.5