mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +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' =>
|
||||
array(
|
||||
'core.pkg.css' => 'd82d2f53',
|
||||
'core.pkg.js' => '4af4aa9d',
|
||||
'core.pkg.js' => '0627d27e',
|
||||
'darkconsole.pkg.js' => 'ca8671ce',
|
||||
'differential.pkg.css' => '4a93db37',
|
||||
'differential.pkg.js' => 'eca39a2c',
|
||||
|
@ -336,11 +336,11 @@ return array(
|
|||
'rsrc/image/texture/table_header_tall.png' => 'd56b434f',
|
||||
'rsrc/js/application/aphlict/Aphlict.js' => '08be8878',
|
||||
'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/config/behavior-reorder-fields.js' => '938aed89',
|
||||
'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/countdown/timer.js' => '361e3ed3',
|
||||
'rsrc/js/application/dashboard/behavior-dashboard-async-panel.js' => '469c0d9e',
|
||||
|
@ -528,7 +528,7 @@ return array(
|
|||
'javelin-aphlict' => '08be8878',
|
||||
'javelin-behavior' => '8a3ed18b',
|
||||
'javelin-behavior-aphlict-dropdown' => '2a2dba85',
|
||||
'javelin-behavior-aphlict-listen' => 'acda9f51',
|
||||
'javelin-behavior-aphlict-listen' => '1da67f34',
|
||||
'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884',
|
||||
'javelin-behavior-aphront-crop' => 'b98fc918',
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea' => '4a11ea9c',
|
||||
|
@ -540,7 +540,7 @@ return array(
|
|||
'javelin-behavior-boards-filter' => '22f113af',
|
||||
'javelin-behavior-config-reorder-fields' => '938aed89',
|
||||
'javelin-behavior-conpherence-menu' => '7ee23816',
|
||||
'javelin-behavior-conpherence-pontificate' => '53f6f2dd',
|
||||
'javelin-behavior-conpherence-pontificate' => 'd83a949c',
|
||||
'javelin-behavior-conpherence-widget-pane' => '40b1ff90',
|
||||
'javelin-behavior-countdown-timer' => '361e3ed3',
|
||||
'javelin-behavior-dark-console' => 'e9fdb5e5',
|
||||
|
@ -956,6 +956,18 @@ return array(
|
|||
1 => 'javelin-util',
|
||||
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' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
|
@ -1192,14 +1204,6 @@ return array(
|
|||
1 => 'javelin-dom',
|
||||
2 => 'phabricator-prefab',
|
||||
),
|
||||
'53f6f2dd' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'javelin-workflow',
|
||||
4 => 'javelin-stratcom',
|
||||
),
|
||||
'54b612ba' =>
|
||||
array(
|
||||
0 => 'javelin-color',
|
||||
|
@ -1608,18 +1612,6 @@ return array(
|
|||
1 => 'javelin-dom',
|
||||
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' =>
|
||||
array(
|
||||
0 => 'javelin-install',
|
||||
|
@ -1877,6 +1869,14 @@ return array(
|
|||
3 => 'javelin-dom',
|
||||
4 => 'phabricator-keyboard-shortcut',
|
||||
),
|
||||
'd83a949c' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-util',
|
||||
3 => 'javelin-workflow',
|
||||
4 => 'javelin-stratcom',
|
||||
),
|
||||
'd8e135db' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
|
|
|
@ -9,4 +9,5 @@ final class ConpherenceUpdateActions extends ConpherenceConstants {
|
|||
const REMOVE_PERSON = 'remove_person';
|
||||
const NOTIFICATIONS = 'notifications';
|
||||
const ADD_STATUS = 'add_status';
|
||||
const LOAD = 'load';
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ final class ConpherenceUpdateController
|
|||
->executeOne();
|
||||
|
||||
$action = $request->getStr('action', ConpherenceUpdateActions::METADATA);
|
||||
|
||||
$latest_transaction_id = null;
|
||||
$response_mode = $request->isAjax() ? 'ajax' : 'redirect';
|
||||
$error_view = null;
|
||||
|
@ -38,7 +39,7 @@ final class ConpherenceUpdateController
|
|||
$errors = array();
|
||||
$delete_draft = false;
|
||||
$xactions = array();
|
||||
if ($request->isFormPost()) {
|
||||
if ($request->isFormPost() || ($action == ConpherenceUpdateActions::LOAD)) {
|
||||
$editor = id(new ConpherenceEditor())
|
||||
->setContinueOnNoEffect($request->isContinueRequest())
|
||||
->setContentSourceFromRequest($request)
|
||||
|
@ -114,10 +115,16 @@ final class ConpherenceUpdateController
|
|||
'That was a non-update. Try cancel.');
|
||||
}
|
||||
break;
|
||||
case ConpherenceUpdateActions::LOAD:
|
||||
$updated = false;
|
||||
$response_mode = 'ajax';
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown action: '.$action);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($xactions || ($action == ConpherenceUpdateActions::LOAD)) {
|
||||
if ($xactions) {
|
||||
try {
|
||||
$xactions = $editor->applyTransactions($conpherence, $xactions);
|
||||
|
@ -132,6 +139,8 @@ final class ConpherenceUpdateController
|
|||
->setCancelURI($this->getApplicationURI($conpherence_id.'/'))
|
||||
->setException($ex);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($response_mode) {
|
||||
case 'ajax':
|
||||
$latest_transaction_id = $request->getInt('latest_transaction_id');
|
||||
|
@ -266,6 +275,7 @@ final class ConpherenceUpdateController
|
|||
$need_transactions = false;
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
case ConpherenceUpdateActions::LOAD:
|
||||
$need_transactions = true;
|
||||
break;
|
||||
case ConpherenceUpdateActions::MESSAGE:
|
||||
|
|
|
@ -97,6 +97,7 @@ final class ConpherenceViewController extends
|
|||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
'pageObjects' => array($conpherence->getPHID()),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -156,7 +157,11 @@ final class ConpherenceViewController extends
|
|||
'type' => 'hidden',
|
||||
'name' => 'latest_transaction_id',
|
||||
'value' => $latest_transaction_id,
|
||||
'sigil' => 'latest-transaction-id'
|
||||
'sigil' => 'latest-transaction-id',
|
||||
'meta' => array(
|
||||
'threadPHID' => $conpherence->getPHID(),
|
||||
'threadID' => $conpherence->getID(),
|
||||
),
|
||||
),
|
||||
''))
|
||||
->render();
|
||||
|
|
|
@ -306,6 +306,17 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
$participant->save();
|
||||
}
|
||||
|
||||
if ($xactions) {
|
||||
$data = array(
|
||||
'type' => 'message',
|
||||
'threadPHID' => $object->getPHID(),
|
||||
'messageID' => last($xactions)->getID(),
|
||||
'subscribers' => array($object->getPHID()),
|
||||
);
|
||||
|
||||
PhabricatorNotificationClient::tryToPostMessage($data);
|
||||
}
|
||||
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,9 +112,7 @@ final class PhabricatorFeedStoryPublisher {
|
|||
}
|
||||
|
||||
$this->insertNotifications($chrono_key);
|
||||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||
$this->sendNotification($chrono_key);
|
||||
}
|
||||
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'FeedPublisherWorker',
|
||||
|
@ -181,11 +179,7 @@ final class PhabricatorFeedStoryPublisher {
|
|||
'subscribers' => $this->subscribedPHIDs,
|
||||
);
|
||||
|
||||
try {
|
||||
PhabricatorNotificationClient::postMessage($data);
|
||||
} catch (Exception $ex) {
|
||||
// Ignore, these are not critical.
|
||||
}
|
||||
PhabricatorNotificationClient::tryToPostMessage($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,7 +25,20 @@ final class PhabricatorNotificationClient {
|
|||
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');
|
||||
|
||||
id(new HTTPSFuture($server_uri, json_encode($data)))
|
||||
|
|
|
@ -27,19 +27,13 @@ JX.behavior('aphlict-listen', function(config) {
|
|||
.start();
|
||||
}
|
||||
|
||||
// Respond to a notification from the Aphlict notification server. We send
|
||||
// a request to Phabricator to get notification details.
|
||||
function onaphlictmessage(type, message) {
|
||||
switch (type) {
|
||||
case 'error':
|
||||
new JX.Notification()
|
||||
.setContent('(Aphlict) ' + message)
|
||||
.alterClassName('jx-notification-error', true)
|
||||
.setDuration(0)
|
||||
.show();
|
||||
break;
|
||||
JX.Stratcom.listen('aphlict-receive-message', null, function(e) {
|
||||
var message = e.getData();
|
||||
|
||||
if (message.type != 'notification') {
|
||||
return;
|
||||
}
|
||||
|
||||
case 'receive':
|
||||
var request = new JX.Request(
|
||||
'/notification/individual/',
|
||||
onnotification);
|
||||
|
@ -53,6 +47,23 @@ JX.behavior('aphlict-listen', function(config) {
|
|||
.setPriority(250);
|
||||
|
||||
JX.Router.getInstance().queue(routable);
|
||||
});
|
||||
|
||||
|
||||
// Respond to a notification from the Aphlict notification server. We send
|
||||
// a request to Phabricator to get notification details.
|
||||
function onaphlictmessage(type, message) {
|
||||
switch (type) {
|
||||
case 'error':
|
||||
new JX.Notification()
|
||||
.setContent('(Aphlict) ' + message)
|
||||
.alterClassName('jx-notification-error', true)
|
||||
.setDuration(0)
|
||||
.show();
|
||||
break;
|
||||
|
||||
case 'receive':
|
||||
JX.Stratcom.invoke('aphlict-receive-message', null, message);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -8,6 +8,52 @@
|
|||
*/
|
||||
|
||||
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) {
|
||||
e.kill();
|
||||
|
||||
|
|
Loading…
Reference in a new issue