diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -8,7 +8,7 @@ return array( 'names' => array( 'core.pkg.css' => 'ed3d6355', - 'core.pkg.js' => '616511ac', + 'core.pkg.js' => 'ac41c400', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => 'bb338e4b', 'differential.pkg.js' => '895b8d62', @@ -348,10 +348,10 @@ 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/calendar/event-all-day.js' => 'ca5fa62a', 'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', - 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'de397217', + 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'b7342ddb', 'rsrc/js/application/conpherence/behavior-drag-and-drop-photo.js' => 'cf86d16a', - 'rsrc/js/application/conpherence/behavior-durable-column.js' => '61252a27', - 'rsrc/js/application/conpherence/behavior-menu.js' => 'eb61cb03', + 'rsrc/js/application/conpherence/behavior-durable-column.js' => '16c695bf', + 'rsrc/js/application/conpherence/behavior-menu.js' => '4351c4a0', 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', 'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3', 'rsrc/js/application/conpherence/behavior-widget-pane.js' => '93568464', @@ -516,7 +516,7 @@ 'conpherence-menu-css' => 'f389e048', 'conpherence-message-pane-css' => '0e75feef', 'conpherence-notification-css' => 'd208f806', - 'conpherence-thread-manager' => 'de397217', + 'conpherence-thread-manager' => 'b7342ddb', 'conpherence-transaction-css' => '42a457f6', 'conpherence-update-css' => '1099a660', 'conpherence-widget-pane-css' => '2af42ebe', @@ -557,7 +557,7 @@ 'javelin-behavior-choose-control' => '6153c708', 'javelin-behavior-config-reorder-fields' => '14a827de', 'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', - 'javelin-behavior-conpherence-menu' => 'eb61cb03', + 'javelin-behavior-conpherence-menu' => '4351c4a0', 'javelin-behavior-conpherence-pontificate' => '21ba5861', 'javelin-behavior-conpherence-widget-pane' => '93568464', 'javelin-behavior-countdown-timer' => 'e4cc26b3', @@ -584,7 +584,7 @@ 'javelin-behavior-diffusion-locate-file' => '6d3e1947', 'javelin-behavior-diffusion-pull-lastmodified' => '2b228192', 'javelin-behavior-doorkeeper-tag' => 'e5822781', - 'javelin-behavior-durable-column' => '61252a27', + 'javelin-behavior-durable-column' => '16c695bf', 'javelin-behavior-error-log' => '6882e80a', 'javelin-behavior-event-all-day' => 'ca5fa62a', 'javelin-behavior-fancy-datepicker' => '5c0f680f', @@ -930,6 +930,16 @@ 'javelin-request', 'javelin-util', ), + '16c695bf' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-stratcom', + 'javelin-behavior-device', + 'javelin-scrollbar', + 'javelin-quicksand', + 'phabricator-keyboard-shortcut', + 'conpherence-thread-manager', + ), '1ad0a787' => array( 'javelin-install', 'javelin-reactor', @@ -1073,6 +1083,20 @@ 'javelin-dom', 'javelin-request', ), + '4351c4a0' => array( + 'javelin-behavior', + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-workflow', + 'javelin-behavior-device', + 'javelin-history', + 'javelin-vector', + 'javelin-scrollbar', + 'phabricator-title', + 'phabricator-shaped-request', + 'conpherence-thread-manager', + ), '44168bad' => array( 'javelin-behavior', 'javelin-dom', @@ -1256,16 +1280,6 @@ 'javelin-stratcom', 'javelin-dom', ), - '61252a27' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-stratcom', - 'javelin-behavior-device', - 'javelin-scrollbar', - 'javelin-quicksand', - 'phabricator-keyboard-shortcut', - 'conpherence-thread-manager', - ), '6153c708' => array( 'javelin-behavior', 'javelin-stratcom', @@ -1705,6 +1719,17 @@ 'javelin-dom', 'javelin-util', ), + 'b7342ddb' => array( + 'javelin-dom', + 'javelin-util', + 'javelin-stratcom', + 'javelin-install', + 'javelin-aphlict', + 'javelin-workflow', + 'javelin-router', + 'javelin-behavior-device', + 'javelin-vector', + ), 'ba4fa35c' => array( 'javelin-behavior', 'javelin-dom', @@ -1832,17 +1857,6 @@ 'javelin-typeahead-ondemand-source', 'javelin-dom', ), - 'de397217' => array( - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-install', - 'javelin-aphlict', - 'javelin-workflow', - 'javelin-router', - 'javelin-behavior-device', - 'javelin-vector', - ), 'df5e11d2' => array( 'javelin-install', ), @@ -1920,20 +1934,6 @@ 'phabricator-phtize', 'javelin-dom', ), - 'eb61cb03' => array( - 'javelin-behavior', - 'javelin-dom', - 'javelin-util', - 'javelin-stratcom', - 'javelin-workflow', - 'javelin-behavior-device', - 'javelin-history', - 'javelin-vector', - 'javelin-scrollbar', - 'phabricator-title', - 'phabricator-shaped-request', - 'conpherence-thread-manager', - ), 'efe49472' => array( 'javelin-install', 'javelin-util', diff --git a/src/applications/conpherence/ConpherenceTransactionRenderer.php b/src/applications/conpherence/ConpherenceTransactionRenderer.php --- a/src/applications/conpherence/ConpherenceTransactionRenderer.php +++ b/src/applications/conpherence/ConpherenceTransactionRenderer.php @@ -90,6 +90,7 @@ if ($previous_day != $current_day) { $date_marker_transaction->setDateCreated( $transaction->getDateCreated()); + $date_marker_transaction->setID($previous_transaction->getID()); $rendered_transactions[] = $date_marker_transaction_view->render(); } } @@ -144,6 +145,15 @@ ), ), pht('Show Older Messages')); + $oldscrollbutton = javelin_tag( + 'div', + array( + 'sigil' => 'conpherence-transaction-view', + 'meta' => array( + 'id' => $oldest_transaction_id - 0.5, + ), + ), + $oldscrollbutton); } $newscrollbutton = ''; @@ -160,6 +170,15 @@ ), ), pht('Show Newer Messages')); + $newscrollbutton = javelin_tag( + 'div', + array( + 'sigil' => 'conpherence-transaction-view', + 'meta' => array( + 'id' => $newest_transaction_id + 0.5, + ), + ), + $newscrollbutton); } return hsprintf( diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -79,7 +79,7 @@ if ($before_transaction_id || $after_transaction_id) { $header = null; $form = null; - $content = array('messages' => $messages); + $content = array('transactions' => $messages); } else { $policy_objects = id(new PhabricatorPolicyQuery()) ->setViewer($user) @@ -89,7 +89,7 @@ $form = $this->renderFormContent(); $content = array( 'header' => $header, - 'messages' => $messages, + 'transactions' => $messages, 'form' => $form, ); } diff --git a/src/applications/conpherence/view/ConpherenceTransactionView.php b/src/applications/conpherence/view/ConpherenceTransactionView.php --- a/src/applications/conpherence/view/ConpherenceTransactionView.php +++ b/src/applications/conpherence/view/ConpherenceTransactionView.php @@ -103,10 +103,14 @@ $transaction = $this->getConpherenceTransaction(); switch ($transaction->getTransactionType()) { case ConpherenceTransactionType::TYPE_DATE_MARKER: - return phutil_tag( + return javelin_tag( 'div', array( 'class' => 'conpherence-transaction-view date-marker', + 'sigil' => 'conpherence-transaction-view', + 'meta' => array( + 'id' => $transaction->getID() + 0.5, + ), ), array( phutil_tag( @@ -134,11 +138,15 @@ 'conpherence-transaction-header grouped', array($actions, $info)); - return phutil_tag( + return javelin_tag( 'div', array( 'class' => 'conpherence-transaction-view '.$classes, 'id' => $transaction_id, + 'sigil' => 'conpherence-transaction-view', + 'meta' => array( + 'id' => $transaction->getID(), + ), ), array( $image, diff --git a/webroot/rsrc/js/application/conpherence/ConpherenceThreadManager.js b/webroot/rsrc/js/application/conpherence/ConpherenceThreadManager.js --- a/webroot/rsrc/js/application/conpherence/ConpherenceThreadManager.js +++ b/webroot/rsrc/js/application/conpherence/ConpherenceThreadManager.js @@ -27,6 +27,8 @@ _loadedThreadID: null, _loadedThreadPHID: null, _latestTransactionID: null, + _transactionIDMap: null, + _transactionCache: null, _canEditLoadedThread: null, _updating: null, _minimalDisplay: false, @@ -83,6 +85,59 @@ return this; }, + _updateTransactionIDMap: function(transactions) { + var loaded_id = this.getLoadedThreadID(); + if (!this._transactionIDMap[loaded_id]) { + this._transactionIDMap[this._loadedThreadID] = {}; + } + var loaded_transaction_ids = this._transactionIDMap[loaded_id]; + var transaction; + for (var ii = 0; ii < transactions.length; ii++) { + transaction = transactions[ii]; + loaded_transaction_ids[JX.Stratcom.getData(transaction).id] = 1; + } + this._transactionIDMap[this._loadedThreadID] = loaded_transaction_ids; + return this; + }, + + _updateTransactionCache: function(transactions) { + var transaction; + for (var ii = 0; ii < transactions.length; ii++) { + transaction = transactions[ii]; + this._transactionCache[JX.Stratcom.getData(transaction).id] = + transaction; + } + return this; + }, + + _getLoadedTransactions: function() { + var loaded_id = this.getLoadedThreadID(); + var loaded_tx_ids = JX.keys(this._transactionIDMap[loaded_id]); + loaded_tx_ids.sort(function (a, b) { + var x = parseFloat(a); + var y = parseFloat(b); + if (x > y) { + return 1; + } + if (x < y) { + return -1; + } + return 0; + }); + var transactions = []; + for (var ii = 0; ii < loaded_tx_ids.length; ii++) { + transactions.push(this._transactionCache[loaded_tx_ids[ii]]); + } + return transactions; + }, + + _deleteTransactionCaches: function(id) { + delete this._transactionCache[id]; + delete this._transactionIDMap[this._loadedThreadID][id]; + + return this; + }, + setCanEditLoadedThread: function(bool) { this._canEditLoadedThread = bool; return this; @@ -151,6 +206,10 @@ }, start: function() { + + this._transactionIDMap = {}; + this._transactionCache = {}; + JX.Stratcom.listen( 'aphlict-server-message', null, @@ -206,11 +265,9 @@ new JX.Workflow(this._getMoreMessagesURI(), data) .setHandler(JX.bind(this, function(r) { + this._deleteTransactionCaches(JX.Stratcom.getData(node).id); JX.DOM.remove(node); - var messages = JX.$H(r.messages); - JX.DOM.prependContent( - this._messagesRootCallback(), - messages); + this._updateTransactions(r); })).start(); })); JX.Stratcom.listen( @@ -228,11 +285,9 @@ new JX.Workflow(this._getMoreMessagesURI(), data) .setHandler(JX.bind(this, function(r) { + this._deleteTransactionCaches(JX.Stratcom.getData(node).id); JX.DOM.remove(node); - var messages = JX.$H(r.messages); - JX.DOM.appendContent( - this._messagesRootCallback(), - JX.$H(messages)); + this._updateTransactions(r); })).start(); })); }, @@ -254,7 +309,9 @@ return true; }, - _markUpdated: function(r) { + _updateDOM: function(r) { + this._updateTransactions(r); + this._updating.knownID = r.latest_transaction_id; this._latestTransactionID = r.latest_transaction_id; JX.Stratcom.invoke( @@ -263,6 +320,16 @@ r.aphlictDropdownData); }, + _updateTransactions: function(r) { + var new_transactions = JX.$H(r.transactions).getFragment().childNodes; + this._updateTransactionIDMap(new_transactions); + this._updateTransactionCache(new_transactions); + + var transactions = this._getLoadedTransactions(); + + JX.DOM.setContent(this._messagesRootCallback(), transactions); + }, + _updateThread: function() { var params = this._getParams({ action: 'load', @@ -272,8 +339,7 @@ .setData(params) .setHandler(JX.bind(this, function(r) { if (this._shouldUpdateDOM(r)) { - this._markUpdated(r); - + this._updateDOM(r); this._didUpdateThreadCallback(r); } })); @@ -306,8 +372,7 @@ .setData(params) .setHandler(JX.bind(this, function(r) { if (this._shouldUpdateDOM(r)) { - this._markUpdated(r); - + this._updateDOM(r); this._didUpdateWorkflowCallback(r); } })); @@ -357,6 +422,13 @@ r.aphlictDropdownData); this._didLoadThreadCallback(r); + var messages_root = this._messagesRootCallback(); + var messages = JX.DOM.scry( + messages_root, + 'div', + 'conpherence-transaction-view'); + this._updateTransactionIDMap(messages); + this._updateTransactionCache(messages); if (force_reload) { JX.Stratcom.invoke('hashchange'); @@ -383,8 +455,7 @@ var workflow = JX.Workflow.newFromForm(form, params, keep_enabled) .setHandler(JX.bind(this, function(r) { if (this._shouldUpdateDOM(r)) { - this._markUpdated(r); - + this._updateDOM(r); this._didSendMessageCallback(r); } else if (r.non_update) { this._didSendMessageCallback(r, true); diff --git a/webroot/rsrc/js/application/conpherence/behavior-durable-column.js b/webroot/rsrc/js/application/conpherence/behavior-durable-column.js --- a/webroot/rsrc/js/application/conpherence/behavior-durable-column.js +++ b/webroot/rsrc/js/application/conpherence/behavior-durable-column.js @@ -159,7 +159,6 @@ return; } var messages = _getColumnMessagesNode(); - JX.DOM.appendContent(messages, JX.$H(r.transactions)); scrollbar.scrollTo(messages.scrollHeight); }); @@ -168,7 +167,6 @@ }); threadManager.setDidUpdateWorkflowCallback(function(r) { var messages = _getColumnMessagesNode(); - JX.DOM.appendContent(messages, JX.$H(r.transactions)); scrollbar.scrollTo(messages.scrollHeight); JX.DOM.setContent(_getColumnTitleNode(), r.conpherence_title); }); diff --git a/webroot/rsrc/js/application/conpherence/behavior-menu.js b/webroot/rsrc/js/application/conpherence/behavior-menu.js --- a/webroot/rsrc/js/application/conpherence/behavior-menu.js +++ b/webroot/rsrc/js/application/conpherence/behavior-menu.js @@ -36,7 +36,7 @@ }); threadManager.setDidLoadThreadCallback(function(r) { var header = JX.$H(r.header); - var messages = JX.$H(r.messages); + var messages = JX.$H(r.transactions); var form = JX.$H(r.form); var root = JX.DOM.find(document, 'div', 'conpherence-layout'); var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane'); @@ -51,7 +51,6 @@ }); threadManager.setDidUpdateThreadCallback(function(r) { - JX.DOM.appendContent(scrollbar.getContentNode(), JX.$H(r.transactions)); _scrollMessageWindow(); }); @@ -73,14 +72,13 @@ } catch (ex) { // Ignore; maybe no files widget } - JX.DOM.appendContent(scrollbar.getContentNode(), JX.$H(r.transactions)); - _scrollMessageWindow(); - if (fileWidget) { JX.DOM.setContent( fileWidget, JX.$H(r.file_widget)); } + + _scrollMessageWindow(); textarea.value = ''; } markThreadLoading(false);