mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
Make Conpherence threads update in real time, very roughly
Summary: Ref T4083. This needs some work (mostly in the Conpherence JS itself), but is sort of functional. In particular: - On thread pages, add the thread as a `pageObject`. - After updating a thread, send a new "message" event to the server. - Share a little more event posting code. - In the browser, use event dispatch to respond to events. - Add a listener for the new event type. - Update conpherence threads (this part is really yucky). Test Plan: With multiple browser windows / browsers open, posted a message to a thread, and saw it update everywhere. Reviewers: joshuaspence Reviewed By: joshuaspence Subscribers: chad, epriestley Maniphest Tasks: T4083 Differential Revision: https://secure.phabricator.com/D9486
This commit is contained in:
parent
84d259cea2
commit
4bc561f17b
9 changed files with 152 additions and 61 deletions
|
@ -8,7 +8,7 @@ return array(
|
||||||
'names' =>
|
'names' =>
|
||||||
array(
|
array(
|
||||||
'core.pkg.css' => 'd82d2f53',
|
'core.pkg.css' => 'd82d2f53',
|
||||||
'core.pkg.js' => '4af4aa9d',
|
'core.pkg.js' => '0627d27e',
|
||||||
'darkconsole.pkg.js' => 'ca8671ce',
|
'darkconsole.pkg.js' => 'ca8671ce',
|
||||||
'differential.pkg.css' => '4a93db37',
|
'differential.pkg.css' => '4a93db37',
|
||||||
'differential.pkg.js' => 'eca39a2c',
|
'differential.pkg.js' => 'eca39a2c',
|
||||||
|
@ -336,11 +336,11 @@ return array(
|
||||||
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
||||||
'rsrc/js/application/aphlict/Aphlict.js' => '08be8878',
|
'rsrc/js/application/aphlict/Aphlict.js' => '08be8878',
|
||||||
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '2a2dba85',
|
'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '2a2dba85',
|
||||||
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'acda9f51',
|
'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => '1da67f34',
|
||||||
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
|
'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18',
|
||||||
'rsrc/js/application/config/behavior-reorder-fields.js' => '938aed89',
|
'rsrc/js/application/config/behavior-reorder-fields.js' => '938aed89',
|
||||||
'rsrc/js/application/conpherence/behavior-menu.js' => '7ee23816',
|
'rsrc/js/application/conpherence/behavior-menu.js' => '7ee23816',
|
||||||
'rsrc/js/application/conpherence/behavior-pontificate.js' => '53f6f2dd',
|
'rsrc/js/application/conpherence/behavior-pontificate.js' => 'd83a949c',
|
||||||
'rsrc/js/application/conpherence/behavior-widget-pane.js' => '40b1ff90',
|
'rsrc/js/application/conpherence/behavior-widget-pane.js' => '40b1ff90',
|
||||||
'rsrc/js/application/countdown/timer.js' => '361e3ed3',
|
'rsrc/js/application/countdown/timer.js' => '361e3ed3',
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
|
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
|
||||||
|
@ -528,7 +528,7 @@ return array(
|
||||||
'javelin-aphlict' => '08be8878',
|
'javelin-aphlict' => '08be8878',
|
||||||
'javelin-behavior' => '8a3ed18b',
|
'javelin-behavior' => '8a3ed18b',
|
||||||
'javelin-behavior-aphlict-dropdown' => '2a2dba85',
|
'javelin-behavior-aphlict-dropdown' => '2a2dba85',
|
||||||
'javelin-behavior-aphlict-listen' => 'acda9f51',
|
'javelin-behavior-aphlict-listen' => '1da67f34',
|
||||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||||
'javelin-behavior-aphront-crop' => 'b98fc918',
|
'javelin-behavior-aphront-crop' => 'b98fc918',
|
||||||
'javelin-behavior-aphront-drag-and-drop-textarea' => '4a11ea9c',
|
'javelin-behavior-aphront-drag-and-drop-textarea' => '4a11ea9c',
|
||||||
|
@ -540,7 +540,7 @@ return array(
|
||||||
'javelin-behavior-boards-filter' => '22f113af',
|
'javelin-behavior-boards-filter' => '22f113af',
|
||||||
'javelin-behavior-config-reorder-fields' => '938aed89',
|
'javelin-behavior-config-reorder-fields' => '938aed89',
|
||||||
'javelin-behavior-conpherence-menu' => '7ee23816',
|
'javelin-behavior-conpherence-menu' => '7ee23816',
|
||||||
'javelin-behavior-conpherence-pontificate' => '53f6f2dd',
|
'javelin-behavior-conpherence-pontificate' => 'd83a949c',
|
||||||
'javelin-behavior-conpherence-widget-pane' => '40b1ff90',
|
'javelin-behavior-conpherence-widget-pane' => '40b1ff90',
|
||||||
'javelin-behavior-countdown-timer' => '361e3ed3',
|
'javelin-behavior-countdown-timer' => '361e3ed3',
|
||||||
'javelin-behavior-dark-console' => 'e9fdb5e5',
|
'javelin-behavior-dark-console' => 'e9fdb5e5',
|
||||||
|
@ -956,6 +956,18 @@ return array(
|
||||||
1 => 'javelin-util',
|
1 => 'javelin-util',
|
||||||
2 => 'phabricator-keyboard-shortcut-manager',
|
2 => 'phabricator-keyboard-shortcut-manager',
|
||||||
),
|
),
|
||||||
|
'1da67f34' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-behavior',
|
||||||
|
1 => 'javelin-aphlict',
|
||||||
|
2 => 'javelin-stratcom',
|
||||||
|
3 => 'javelin-request',
|
||||||
|
4 => 'javelin-uri',
|
||||||
|
5 => 'javelin-dom',
|
||||||
|
6 => 'javelin-json',
|
||||||
|
7 => 'javelin-router',
|
||||||
|
8 => 'phabricator-notification',
|
||||||
|
),
|
||||||
'1e1c8a59' =>
|
'1e1c8a59' =>
|
||||||
array(
|
array(
|
||||||
0 => 'javelin-behavior',
|
0 => 'javelin-behavior',
|
||||||
|
@ -1192,14 +1204,6 @@ return array(
|
||||||
1 => 'javelin-dom',
|
1 => 'javelin-dom',
|
||||||
2 => 'phabricator-prefab',
|
2 => 'phabricator-prefab',
|
||||||
),
|
),
|
||||||
'53f6f2dd' =>
|
|
||||||
array(
|
|
||||||
0 => 'javelin-behavior',
|
|
||||||
1 => 'javelin-dom',
|
|
||||||
2 => 'javelin-util',
|
|
||||||
3 => 'javelin-workflow',
|
|
||||||
4 => 'javelin-stratcom',
|
|
||||||
),
|
|
||||||
'54b612ba' =>
|
'54b612ba' =>
|
||||||
array(
|
array(
|
||||||
0 => 'javelin-color',
|
0 => 'javelin-color',
|
||||||
|
@ -1608,18 +1612,6 @@ return array(
|
||||||
1 => 'javelin-dom',
|
1 => 'javelin-dom',
|
||||||
2 => 'javelin-stratcom',
|
2 => 'javelin-stratcom',
|
||||||
),
|
),
|
||||||
'acda9f51' =>
|
|
||||||
array(
|
|
||||||
0 => 'javelin-behavior',
|
|
||||||
1 => 'javelin-aphlict',
|
|
||||||
2 => 'javelin-stratcom',
|
|
||||||
3 => 'javelin-request',
|
|
||||||
4 => 'javelin-uri',
|
|
||||||
5 => 'javelin-dom',
|
|
||||||
6 => 'javelin-json',
|
|
||||||
7 => 'javelin-router',
|
|
||||||
8 => 'phabricator-notification',
|
|
||||||
),
|
|
||||||
'ad7a69ca' =>
|
'ad7a69ca' =>
|
||||||
array(
|
array(
|
||||||
0 => 'javelin-install',
|
0 => 'javelin-install',
|
||||||
|
@ -1877,6 +1869,14 @@ return array(
|
||||||
3 => 'javelin-dom',
|
3 => 'javelin-dom',
|
||||||
4 => 'phabricator-keyboard-shortcut',
|
4 => 'phabricator-keyboard-shortcut',
|
||||||
),
|
),
|
||||||
|
'd83a949c' =>
|
||||||
|
array(
|
||||||
|
0 => 'javelin-behavior',
|
||||||
|
1 => 'javelin-dom',
|
||||||
|
2 => 'javelin-util',
|
||||||
|
3 => 'javelin-workflow',
|
||||||
|
4 => 'javelin-stratcom',
|
||||||
|
),
|
||||||
'd8e135db' =>
|
'd8e135db' =>
|
||||||
array(
|
array(
|
||||||
0 => 'javelin-behavior',
|
0 => 'javelin-behavior',
|
||||||
|
|
|
@ -9,4 +9,5 @@ final class ConpherenceUpdateActions extends ConpherenceConstants {
|
||||||
const REMOVE_PERSON = 'remove_person';
|
const REMOVE_PERSON = 'remove_person';
|
||||||
const NOTIFICATIONS = 'notifications';
|
const NOTIFICATIONS = 'notifications';
|
||||||
const ADD_STATUS = 'add_status';
|
const ADD_STATUS = 'add_status';
|
||||||
|
const LOAD = 'load';
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ final class ConpherenceUpdateController
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
$action = $request->getStr('action', ConpherenceUpdateActions::METADATA);
|
$action = $request->getStr('action', ConpherenceUpdateActions::METADATA);
|
||||||
|
|
||||||
$latest_transaction_id = null;
|
$latest_transaction_id = null;
|
||||||
$response_mode = $request->isAjax() ? 'ajax' : 'redirect';
|
$response_mode = $request->isAjax() ? 'ajax' : 'redirect';
|
||||||
$error_view = null;
|
$error_view = null;
|
||||||
|
@ -38,7 +39,7 @@ final class ConpherenceUpdateController
|
||||||
$errors = array();
|
$errors = array();
|
||||||
$delete_draft = false;
|
$delete_draft = false;
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost() || ($action == ConpherenceUpdateActions::LOAD)) {
|
||||||
$editor = id(new ConpherenceEditor())
|
$editor = id(new ConpherenceEditor())
|
||||||
->setContinueOnNoEffect($request->isContinueRequest())
|
->setContinueOnNoEffect($request->isContinueRequest())
|
||||||
->setContentSourceFromRequest($request)
|
->setContentSourceFromRequest($request)
|
||||||
|
@ -114,24 +115,32 @@ final class ConpherenceUpdateController
|
||||||
'That was a non-update. Try cancel.');
|
'That was a non-update. Try cancel.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ConpherenceUpdateActions::LOAD:
|
||||||
|
$updated = false;
|
||||||
|
$response_mode = 'ajax';
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Unknown action: '.$action);
|
throw new Exception('Unknown action: '.$action);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ($xactions) {
|
|
||||||
try {
|
if ($xactions || ($action == ConpherenceUpdateActions::LOAD)) {
|
||||||
$xactions = $editor->applyTransactions($conpherence, $xactions);
|
if ($xactions) {
|
||||||
if ($delete_draft) {
|
try {
|
||||||
$draft = PhabricatorDraft::newFromUserAndKey(
|
$xactions = $editor->applyTransactions($conpherence, $xactions);
|
||||||
$user,
|
if ($delete_draft) {
|
||||||
$conpherence->getPHID());
|
$draft = PhabricatorDraft::newFromUserAndKey(
|
||||||
$draft->delete();
|
$user,
|
||||||
|
$conpherence->getPHID());
|
||||||
|
$draft->delete();
|
||||||
|
}
|
||||||
|
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
||||||
|
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
||||||
|
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
|
||||||
|
->setException($ex);
|
||||||
}
|
}
|
||||||
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
|
|
||||||
return id(new PhabricatorApplicationTransactionNoEffectResponse())
|
|
||||||
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
|
|
||||||
->setException($ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($response_mode) {
|
switch ($response_mode) {
|
||||||
case 'ajax':
|
case 'ajax':
|
||||||
$latest_transaction_id = $request->getInt('latest_transaction_id');
|
$latest_transaction_id = $request->getInt('latest_transaction_id');
|
||||||
|
@ -266,6 +275,7 @@ final class ConpherenceUpdateController
|
||||||
$need_transactions = false;
|
$need_transactions = false;
|
||||||
switch ($action) {
|
switch ($action) {
|
||||||
case ConpherenceUpdateActions::METADATA:
|
case ConpherenceUpdateActions::METADATA:
|
||||||
|
case ConpherenceUpdateActions::LOAD:
|
||||||
$need_transactions = true;
|
$need_transactions = true;
|
||||||
break;
|
break;
|
||||||
case ConpherenceUpdateActions::MESSAGE:
|
case ConpherenceUpdateActions::MESSAGE:
|
||||||
|
|
|
@ -97,6 +97,7 @@ final class ConpherenceViewController extends
|
||||||
array(
|
array(
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'device' => true,
|
'device' => true,
|
||||||
|
'pageObjects' => array($conpherence->getPHID()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +157,11 @@ final class ConpherenceViewController extends
|
||||||
'type' => 'hidden',
|
'type' => 'hidden',
|
||||||
'name' => 'latest_transaction_id',
|
'name' => 'latest_transaction_id',
|
||||||
'value' => $latest_transaction_id,
|
'value' => $latest_transaction_id,
|
||||||
'sigil' => 'latest-transaction-id'
|
'sigil' => 'latest-transaction-id',
|
||||||
|
'meta' => array(
|
||||||
|
'threadPHID' => $conpherence->getPHID(),
|
||||||
|
'threadID' => $conpherence->getID(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
''))
|
''))
|
||||||
->render();
|
->render();
|
||||||
|
|
|
@ -306,6 +306,17 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||||
$participant->save();
|
$participant->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($xactions) {
|
||||||
|
$data = array(
|
||||||
|
'type' => 'message',
|
||||||
|
'threadPHID' => $object->getPHID(),
|
||||||
|
'messageID' => last($xactions)->getID(),
|
||||||
|
'subscribers' => array($object->getPHID()),
|
||||||
|
);
|
||||||
|
|
||||||
|
PhabricatorNotificationClient::tryToPostMessage($data);
|
||||||
|
}
|
||||||
|
|
||||||
return $xactions;
|
return $xactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,9 +112,7 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->insertNotifications($chrono_key);
|
$this->insertNotifications($chrono_key);
|
||||||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
$this->sendNotification($chrono_key);
|
||||||
$this->sendNotification($chrono_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
PhabricatorWorker::scheduleTask(
|
PhabricatorWorker::scheduleTask(
|
||||||
'FeedPublisherWorker',
|
'FeedPublisherWorker',
|
||||||
|
@ -181,11 +179,7 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
'subscribers' => $this->subscribedPHIDs,
|
'subscribers' => $this->subscribedPHIDs,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
PhabricatorNotificationClient::tryToPostMessage($data);
|
||||||
PhabricatorNotificationClient::postMessage($data);
|
|
||||||
} catch (Exception $ex) {
|
|
||||||
// Ignore, these are not critical.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,7 +25,20 @@ final class PhabricatorNotificationClient {
|
||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function postMessage(array $data) {
|
public static function tryToPostMessage(array $data) {
|
||||||
|
if (!PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
self::postMessage($data);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
// Just ignore any issues here.
|
||||||
|
phlog($ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function postMessage(array $data) {
|
||||||
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
|
$server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri');
|
||||||
|
|
||||||
id(new HTTPSFuture($server_uri, json_encode($data)))
|
id(new HTTPSFuture($server_uri, json_encode($data)))
|
||||||
|
|
|
@ -27,6 +27,29 @@ JX.behavior('aphlict-listen', function(config) {
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JX.Stratcom.listen('aphlict-receive-message', null, function(e) {
|
||||||
|
var message = e.getData();
|
||||||
|
|
||||||
|
if (message.type != 'notification') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new JX.Request(
|
||||||
|
'/notification/individual/',
|
||||||
|
onnotification);
|
||||||
|
|
||||||
|
var routable = request
|
||||||
|
.addData({key: message.key})
|
||||||
|
.getRoutable();
|
||||||
|
|
||||||
|
routable
|
||||||
|
.setType('notification')
|
||||||
|
.setPriority(250);
|
||||||
|
|
||||||
|
JX.Router.getInstance().queue(routable);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Respond to a notification from the Aphlict notification server. We send
|
// Respond to a notification from the Aphlict notification server. We send
|
||||||
// a request to Phabricator to get notification details.
|
// a request to Phabricator to get notification details.
|
||||||
function onaphlictmessage(type, message) {
|
function onaphlictmessage(type, message) {
|
||||||
|
@ -40,19 +63,7 @@ JX.behavior('aphlict-listen', function(config) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'receive':
|
case 'receive':
|
||||||
var request = new JX.Request(
|
JX.Stratcom.invoke('aphlict-receive-message', null, message);
|
||||||
'/notification/individual/',
|
|
||||||
onnotification);
|
|
||||||
|
|
||||||
var routable = request
|
|
||||||
.addData({key: message.key})
|
|
||||||
.getRoutable();
|
|
||||||
|
|
||||||
routable
|
|
||||||
.setType('notification')
|
|
||||||
.setPriority(250);
|
|
||||||
|
|
||||||
JX.Router.getInstance().queue(routable);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -8,6 +8,52 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('conpherence-pontificate', function(config) {
|
JX.behavior('conpherence-pontificate', function(config) {
|
||||||
|
|
||||||
|
JX.Stratcom.listen('aphlict-receive-message', null, function(e) {
|
||||||
|
var message = e.getData();
|
||||||
|
|
||||||
|
if (message.type != 'message') {
|
||||||
|
// Not a message event.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is really, really gross.
|
||||||
|
var infonode = JX.DOM.find(document, 'input', 'latest-transaction-id');
|
||||||
|
var data = JX.Stratcom.getData(infonode);
|
||||||
|
|
||||||
|
var latest_id = infonode.value;
|
||||||
|
var thread_phid = data.threadPHID;
|
||||||
|
var thread_id = data.threadID;
|
||||||
|
|
||||||
|
if (message.threadPHID != thread_phid) {
|
||||||
|
// Message event for some thread other than the visible one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.messageID <= latest_id) {
|
||||||
|
// Message event for something we already know about.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
action: 'load',
|
||||||
|
latest_transaction_id: latest_id
|
||||||
|
};
|
||||||
|
|
||||||
|
new JX.Workflow('/conpherence/update/' + thread_id + '/')
|
||||||
|
.setData(params)
|
||||||
|
.setHandler(function(r) {
|
||||||
|
var messages = JX.DOM.find(document, 'div', 'conpherence-messages');
|
||||||
|
JX.DOM.appendContent(messages, JX.$H(r.transactions));
|
||||||
|
messages.scrollTop = messages.scrollHeight;
|
||||||
|
|
||||||
|
// TODO: Continued grossness from above.
|
||||||
|
infonode.value = r.latest_transaction_id;
|
||||||
|
})
|
||||||
|
.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
var onsubmit = function(e) {
|
var onsubmit = function(e) {
|
||||||
e.kill();
|
e.kill();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue