1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Conpherence - change message rendering logic to eradicate possibility of duplicates

Summary:
Fixes T6713. Before this diff, we would update the DOM when various requests came back, but the logic to erase race conditions proved too tricky for me to get right. Instead, change the algorithm up and keep a set of transaction ids around per thread. When its time to update the transactions, sort the list of ids and just render the whole darn set again.

To make this work, this ends up adding transacton ids to fake transactons like "show older" and date markers. This is able to work by using a float sort and giving these transactions ids that are .5 from being an integer and in the right place numerically.

Test Plan: for durable column, clicked show older and it worked. sent a message and it worked. for main view, clicked show older and it worked. sent a message and it worked.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T6713

Differential Revision: https://secure.phabricator.com/D12819
This commit is contained in:
Bob Trahan 2015-05-13 11:06:54 -07:00
parent 20b7308dfb
commit 6c049d06ce
7 changed files with 162 additions and 68 deletions

View file

@ -8,7 +8,7 @@
return array( return array(
'names' => array( 'names' => array(
'core.pkg.css' => 'ed3d6355', 'core.pkg.css' => 'ed3d6355',
'core.pkg.js' => '616511ac', 'core.pkg.js' => 'ac41c400',
'darkconsole.pkg.js' => 'e7393ebb', 'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => 'bb338e4b', 'differential.pkg.css' => 'bb338e4b',
'differential.pkg.js' => '895b8d62', 'differential.pkg.js' => '895b8d62',
@ -348,10 +348,10 @@ return array(
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
'rsrc/js/application/calendar/event-all-day.js' => 'ca5fa62a', 'rsrc/js/application/calendar/event-all-day.js' => 'ca5fa62a',
'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', '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-drag-and-drop-photo.js' => 'cf86d16a',
'rsrc/js/application/conpherence/behavior-durable-column.js' => '61252a27', 'rsrc/js/application/conpherence/behavior-durable-column.js' => '16c695bf',
'rsrc/js/application/conpherence/behavior-menu.js' => 'eb61cb03', 'rsrc/js/application/conpherence/behavior-menu.js' => '4351c4a0',
'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861',
'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3', 'rsrc/js/application/conpherence/behavior-quicksand-blacklist.js' => '7927a7d3',
'rsrc/js/application/conpherence/behavior-widget-pane.js' => '93568464', 'rsrc/js/application/conpherence/behavior-widget-pane.js' => '93568464',
@ -516,7 +516,7 @@ return array(
'conpherence-menu-css' => 'f389e048', 'conpherence-menu-css' => 'f389e048',
'conpherence-message-pane-css' => '0e75feef', 'conpherence-message-pane-css' => '0e75feef',
'conpherence-notification-css' => 'd208f806', 'conpherence-notification-css' => 'd208f806',
'conpherence-thread-manager' => 'de397217', 'conpherence-thread-manager' => 'b7342ddb',
'conpherence-transaction-css' => '42a457f6', 'conpherence-transaction-css' => '42a457f6',
'conpherence-update-css' => '1099a660', 'conpherence-update-css' => '1099a660',
'conpherence-widget-pane-css' => '2af42ebe', 'conpherence-widget-pane-css' => '2af42ebe',
@ -557,7 +557,7 @@ return array(
'javelin-behavior-choose-control' => '6153c708', 'javelin-behavior-choose-control' => '6153c708',
'javelin-behavior-config-reorder-fields' => '14a827de', 'javelin-behavior-config-reorder-fields' => '14a827de',
'javelin-behavior-conpherence-drag-and-drop-photo' => 'cf86d16a', '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-pontificate' => '21ba5861',
'javelin-behavior-conpherence-widget-pane' => '93568464', 'javelin-behavior-conpherence-widget-pane' => '93568464',
'javelin-behavior-countdown-timer' => 'e4cc26b3', 'javelin-behavior-countdown-timer' => 'e4cc26b3',
@ -584,7 +584,7 @@ return array(
'javelin-behavior-diffusion-locate-file' => '6d3e1947', 'javelin-behavior-diffusion-locate-file' => '6d3e1947',
'javelin-behavior-diffusion-pull-lastmodified' => '2b228192', 'javelin-behavior-diffusion-pull-lastmodified' => '2b228192',
'javelin-behavior-doorkeeper-tag' => 'e5822781', 'javelin-behavior-doorkeeper-tag' => 'e5822781',
'javelin-behavior-durable-column' => '61252a27', 'javelin-behavior-durable-column' => '16c695bf',
'javelin-behavior-error-log' => '6882e80a', 'javelin-behavior-error-log' => '6882e80a',
'javelin-behavior-event-all-day' => 'ca5fa62a', 'javelin-behavior-event-all-day' => 'ca5fa62a',
'javelin-behavior-fancy-datepicker' => '5c0f680f', 'javelin-behavior-fancy-datepicker' => '5c0f680f',
@ -930,6 +930,16 @@ return array(
'javelin-request', 'javelin-request',
'javelin-util', '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( '1ad0a787' => array(
'javelin-install', 'javelin-install',
'javelin-reactor', 'javelin-reactor',
@ -1073,6 +1083,20 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-request', '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( '44168bad' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1256,16 +1280,6 @@ return array(
'javelin-stratcom', 'javelin-stratcom',
'javelin-dom', '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( '6153c708' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -1705,6 +1719,17 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-util', '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( 'ba4fa35c' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1832,17 +1857,6 @@ return array(
'javelin-typeahead-ondemand-source', 'javelin-typeahead-ondemand-source',
'javelin-dom', '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( 'df5e11d2' => array(
'javelin-install', 'javelin-install',
), ),
@ -1920,20 +1934,6 @@ return array(
'phabricator-phtize', 'phabricator-phtize',
'javelin-dom', '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( 'efe49472' => array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',

View file

@ -90,6 +90,7 @@ final class ConpherenceTransactionRenderer {
if ($previous_day != $current_day) { if ($previous_day != $current_day) {
$date_marker_transaction->setDateCreated( $date_marker_transaction->setDateCreated(
$transaction->getDateCreated()); $transaction->getDateCreated());
$date_marker_transaction->setID($previous_transaction->getID());
$rendered_transactions[] = $date_marker_transaction_view->render(); $rendered_transactions[] = $date_marker_transaction_view->render();
} }
} }
@ -144,6 +145,15 @@ final class ConpherenceTransactionRenderer {
), ),
), ),
pht('Show Older Messages')); pht('Show Older Messages'));
$oldscrollbutton = javelin_tag(
'div',
array(
'sigil' => 'conpherence-transaction-view',
'meta' => array(
'id' => $oldest_transaction_id - 0.5,
),
),
$oldscrollbutton);
} }
$newscrollbutton = ''; $newscrollbutton = '';
@ -160,6 +170,15 @@ final class ConpherenceTransactionRenderer {
), ),
), ),
pht('Show Newer Messages')); pht('Show Newer Messages'));
$newscrollbutton = javelin_tag(
'div',
array(
'sigil' => 'conpherence-transaction-view',
'meta' => array(
'id' => $newest_transaction_id + 0.5,
),
),
$newscrollbutton);
} }
return hsprintf( return hsprintf(

View file

@ -79,7 +79,7 @@ final class ConpherenceViewController extends
if ($before_transaction_id || $after_transaction_id) { if ($before_transaction_id || $after_transaction_id) {
$header = null; $header = null;
$form = null; $form = null;
$content = array('messages' => $messages); $content = array('transactions' => $messages);
} else { } else {
$policy_objects = id(new PhabricatorPolicyQuery()) $policy_objects = id(new PhabricatorPolicyQuery())
->setViewer($user) ->setViewer($user)
@ -89,7 +89,7 @@ final class ConpherenceViewController extends
$form = $this->renderFormContent(); $form = $this->renderFormContent();
$content = array( $content = array(
'header' => $header, 'header' => $header,
'messages' => $messages, 'transactions' => $messages,
'form' => $form, 'form' => $form,
); );
} }

View file

@ -103,10 +103,14 @@ final class ConpherenceTransactionView extends AphrontView {
$transaction = $this->getConpherenceTransaction(); $transaction = $this->getConpherenceTransaction();
switch ($transaction->getTransactionType()) { switch ($transaction->getTransactionType()) {
case ConpherenceTransactionType::TYPE_DATE_MARKER: case ConpherenceTransactionType::TYPE_DATE_MARKER:
return phutil_tag( return javelin_tag(
'div', 'div',
array( array(
'class' => 'conpherence-transaction-view date-marker', 'class' => 'conpherence-transaction-view date-marker',
'sigil' => 'conpherence-transaction-view',
'meta' => array(
'id' => $transaction->getID() + 0.5,
),
), ),
array( array(
phutil_tag( phutil_tag(
@ -134,11 +138,15 @@ final class ConpherenceTransactionView extends AphrontView {
'conpherence-transaction-header grouped', 'conpherence-transaction-header grouped',
array($actions, $info)); array($actions, $info));
return phutil_tag( return javelin_tag(
'div', 'div',
array( array(
'class' => 'conpherence-transaction-view '.$classes, 'class' => 'conpherence-transaction-view '.$classes,
'id' => $transaction_id, 'id' => $transaction_id,
'sigil' => 'conpherence-transaction-view',
'meta' => array(
'id' => $transaction->getID(),
),
), ),
array( array(
$image, $image,

View file

@ -27,6 +27,8 @@ JX.install('ConpherenceThreadManager', {
_loadedThreadID: null, _loadedThreadID: null,
_loadedThreadPHID: null, _loadedThreadPHID: null,
_latestTransactionID: null, _latestTransactionID: null,
_transactionIDMap: null,
_transactionCache: null,
_canEditLoadedThread: null, _canEditLoadedThread: null,
_updating: null, _updating: null,
_minimalDisplay: false, _minimalDisplay: false,
@ -83,6 +85,59 @@ JX.install('ConpherenceThreadManager', {
return this; 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) { setCanEditLoadedThread: function(bool) {
this._canEditLoadedThread = bool; this._canEditLoadedThread = bool;
return this; return this;
@ -151,6 +206,10 @@ JX.install('ConpherenceThreadManager', {
}, },
start: function() { start: function() {
this._transactionIDMap = {};
this._transactionCache = {};
JX.Stratcom.listen( JX.Stratcom.listen(
'aphlict-server-message', 'aphlict-server-message',
null, null,
@ -206,11 +265,9 @@ JX.install('ConpherenceThreadManager', {
new JX.Workflow(this._getMoreMessagesURI(), data) new JX.Workflow(this._getMoreMessagesURI(), data)
.setHandler(JX.bind(this, function(r) { .setHandler(JX.bind(this, function(r) {
this._deleteTransactionCaches(JX.Stratcom.getData(node).id);
JX.DOM.remove(node); JX.DOM.remove(node);
var messages = JX.$H(r.messages); this._updateTransactions(r);
JX.DOM.prependContent(
this._messagesRootCallback(),
messages);
})).start(); })).start();
})); }));
JX.Stratcom.listen( JX.Stratcom.listen(
@ -228,11 +285,9 @@ JX.install('ConpherenceThreadManager', {
new JX.Workflow(this._getMoreMessagesURI(), data) new JX.Workflow(this._getMoreMessagesURI(), data)
.setHandler(JX.bind(this, function(r) { .setHandler(JX.bind(this, function(r) {
this._deleteTransactionCaches(JX.Stratcom.getData(node).id);
JX.DOM.remove(node); JX.DOM.remove(node);
var messages = JX.$H(r.messages); this._updateTransactions(r);
JX.DOM.appendContent(
this._messagesRootCallback(),
JX.$H(messages));
})).start(); })).start();
})); }));
}, },
@ -254,7 +309,9 @@ JX.install('ConpherenceThreadManager', {
return true; return true;
}, },
_markUpdated: function(r) { _updateDOM: function(r) {
this._updateTransactions(r);
this._updating.knownID = r.latest_transaction_id; this._updating.knownID = r.latest_transaction_id;
this._latestTransactionID = r.latest_transaction_id; this._latestTransactionID = r.latest_transaction_id;
JX.Stratcom.invoke( JX.Stratcom.invoke(
@ -263,6 +320,16 @@ JX.install('ConpherenceThreadManager', {
r.aphlictDropdownData); 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() { _updateThread: function() {
var params = this._getParams({ var params = this._getParams({
action: 'load', action: 'load',
@ -272,8 +339,7 @@ JX.install('ConpherenceThreadManager', {
.setData(params) .setData(params)
.setHandler(JX.bind(this, function(r) { .setHandler(JX.bind(this, function(r) {
if (this._shouldUpdateDOM(r)) { if (this._shouldUpdateDOM(r)) {
this._markUpdated(r); this._updateDOM(r);
this._didUpdateThreadCallback(r); this._didUpdateThreadCallback(r);
} }
})); }));
@ -306,8 +372,7 @@ JX.install('ConpherenceThreadManager', {
.setData(params) .setData(params)
.setHandler(JX.bind(this, function(r) { .setHandler(JX.bind(this, function(r) {
if (this._shouldUpdateDOM(r)) { if (this._shouldUpdateDOM(r)) {
this._markUpdated(r); this._updateDOM(r);
this._didUpdateWorkflowCallback(r); this._didUpdateWorkflowCallback(r);
} }
})); }));
@ -357,6 +422,13 @@ JX.install('ConpherenceThreadManager', {
r.aphlictDropdownData); r.aphlictDropdownData);
this._didLoadThreadCallback(r); 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) { if (force_reload) {
JX.Stratcom.invoke('hashchange'); JX.Stratcom.invoke('hashchange');
@ -383,8 +455,7 @@ JX.install('ConpherenceThreadManager', {
var workflow = JX.Workflow.newFromForm(form, params, keep_enabled) var workflow = JX.Workflow.newFromForm(form, params, keep_enabled)
.setHandler(JX.bind(this, function(r) { .setHandler(JX.bind(this, function(r) {
if (this._shouldUpdateDOM(r)) { if (this._shouldUpdateDOM(r)) {
this._markUpdated(r); this._updateDOM(r);
this._didSendMessageCallback(r); this._didSendMessageCallback(r);
} else if (r.non_update) { } else if (r.non_update) {
this._didSendMessageCallback(r, true); this._didSendMessageCallback(r, true);

View file

@ -159,7 +159,6 @@ JX.behavior('durable-column', function(config, statics) {
return; return;
} }
var messages = _getColumnMessagesNode(); var messages = _getColumnMessagesNode();
JX.DOM.appendContent(messages, JX.$H(r.transactions));
scrollbar.scrollTo(messages.scrollHeight); scrollbar.scrollTo(messages.scrollHeight);
}); });
@ -168,7 +167,6 @@ JX.behavior('durable-column', function(config, statics) {
}); });
threadManager.setDidUpdateWorkflowCallback(function(r) { threadManager.setDidUpdateWorkflowCallback(function(r) {
var messages = _getColumnMessagesNode(); var messages = _getColumnMessagesNode();
JX.DOM.appendContent(messages, JX.$H(r.transactions));
scrollbar.scrollTo(messages.scrollHeight); scrollbar.scrollTo(messages.scrollHeight);
JX.DOM.setContent(_getColumnTitleNode(), r.conpherence_title); JX.DOM.setContent(_getColumnTitleNode(), r.conpherence_title);
}); });

View file

@ -36,7 +36,7 @@ JX.behavior('conpherence-menu', function(config) {
}); });
threadManager.setDidLoadThreadCallback(function(r) { threadManager.setDidLoadThreadCallback(function(r) {
var header = JX.$H(r.header); 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 form = JX.$H(r.form);
var root = JX.DOM.find(document, 'div', 'conpherence-layout'); var root = JX.DOM.find(document, 'div', 'conpherence-layout');
var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane'); var header_root = JX.DOM.find(root, 'div', 'conpherence-header-pane');
@ -51,7 +51,6 @@ JX.behavior('conpherence-menu', function(config) {
}); });
threadManager.setDidUpdateThreadCallback(function(r) { threadManager.setDidUpdateThreadCallback(function(r) {
JX.DOM.appendContent(scrollbar.getContentNode(), JX.$H(r.transactions));
_scrollMessageWindow(); _scrollMessageWindow();
}); });
@ -73,14 +72,13 @@ JX.behavior('conpherence-menu', function(config) {
} catch (ex) { } catch (ex) {
// Ignore; maybe no files widget // Ignore; maybe no files widget
} }
JX.DOM.appendContent(scrollbar.getContentNode(), JX.$H(r.transactions));
_scrollMessageWindow();
if (fileWidget) { if (fileWidget) {
JX.DOM.setContent( JX.DOM.setContent(
fileWidget, fileWidget,
JX.$H(r.file_widget)); JX.$H(r.file_widget));
} }
_scrollMessageWindow();
textarea.value = ''; textarea.value = '';
} }
markThreadLoading(false); markThreadLoading(false);