@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

After Aphlict reconnects, ask the server to replay recent messages

Summary:
Fixes T12563. If we've ever seen an "open", mark all future connections as reconnects. When we reconnect, replay recent history.

(Until duplicate messages (T12564) are handled better this may cause some notification duplication.)

Also emit a reconnect event (for T12566) but don't use it yet.

Test Plan: {F4912044}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12563

Differential Revision: https://secure.phabricator.com/D17708

+130 -29
+25 -25
resources/celerity/map.php
··· 10 10 'conpherence.pkg.css' => 'a34d59bd', 11 11 'conpherence.pkg.js' => '5f86c17d', 12 12 'core.pkg.css' => '959330a2', 13 - 'core.pkg.js' => 'cb50c410', 14 - 'darkconsole.pkg.js' => 'a2faee86', 13 + 'core.pkg.js' => '1cedf416', 14 + 'darkconsole.pkg.js' => '31272f61', 15 15 'differential.pkg.css' => '90b30783', 16 16 'differential.pkg.js' => 'ddfeb49b', 17 17 'diffusion.pkg.css' => '91c5d3a6', ··· 20 20 'maniphest.pkg.css' => '4845691a', 21 21 'maniphest.pkg.js' => '5ab2753f', 22 22 'rsrc/css/aphront/aphront-bars.css' => '231ac33c', 23 - 'rsrc/css/aphront/dark-console.css' => 'e7c6e44d', 23 + 'rsrc/css/aphront/dark-console.css' => '53798a6d', 24 24 'rsrc/css/aphront/dialog-view.css' => '685c7e2d', 25 25 'rsrc/css/aphront/list-filter-view.css' => '5d6f0526', 26 26 'rsrc/css/aphront/multi-column.css' => '84cc6640', ··· 361 361 'rsrc/image/texture/table_header.png' => '5c433037', 362 362 'rsrc/image/texture/table_header_hover.png' => '038ec3b9', 363 363 'rsrc/image/texture/table_header_tall.png' => 'd56b434f', 364 - 'rsrc/js/application/aphlict/Aphlict.js' => 'ce5f793f', 364 + 'rsrc/js/application/aphlict/Aphlict.js' => '7cacce98', 365 365 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'caade6f2', 366 366 'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'd82b1ff9', 367 367 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '5e2634b9', ··· 522 522 'rsrc/js/core/behavior-workflow.js' => '0a3f3021', 523 523 'rsrc/js/core/darkconsole/DarkLog.js' => 'c8e1ffe3', 524 524 'rsrc/js/core/darkconsole/DarkMessage.js' => 'c48cccdd', 525 - 'rsrc/js/core/darkconsole/behavior-dark-console.js' => '698614f9', 525 + 'rsrc/js/core/darkconsole/behavior-dark-console.js' => '2a228a94', 526 526 'rsrc/js/core/phtize.js' => 'd254d646', 527 527 'rsrc/js/phui/behavior-phui-dropdown-menu.js' => 'b95d6f7d', 528 528 'rsrc/js/phui/behavior-phui-file-upload.js' => 'b003d4fb', ··· 538 538 'symbols' => array( 539 539 'almanac-css' => 'dbb9b3af', 540 540 'aphront-bars' => '231ac33c', 541 - 'aphront-dark-console-css' => 'e7c6e44d', 541 + 'aphront-dark-console-css' => '53798a6d', 542 542 'aphront-dialog-view-css' => '685c7e2d', 543 543 'aphront-list-filter-view-css' => '5d6f0526', 544 544 'aphront-multi-column-view-css' => '84cc6640', ··· 583 583 'herald-rule-editor' => 'd6a7e717', 584 584 'herald-test-css' => 'a52e323e', 585 585 'inline-comment-summary-css' => '51efda3a', 586 - 'javelin-aphlict' => 'ce5f793f', 586 + 'javelin-aphlict' => '7cacce98', 587 587 'javelin-behavior' => '61cbc29a', 588 588 'javelin-behavior-aphlict-dropdown' => 'caade6f2', 589 589 'javelin-behavior-aphlict-listen' => 'd82b1ff9', ··· 605 605 'javelin-behavior-conpherence-pontificate' => '55616e04', 606 606 'javelin-behavior-conpherence-search' => '9bbf3762', 607 607 'javelin-behavior-countdown-timer' => 'e4cc26b3', 608 - 'javelin-behavior-dark-console' => '698614f9', 608 + 'javelin-behavior-dark-console' => '2a228a94', 609 609 'javelin-behavior-dashboard-async-panel' => '469c0d9e', 610 610 'javelin-behavior-dashboard-move-panels' => '408bf173', 611 611 'javelin-behavior-dashboard-query-panel-select' => '453c5375', ··· 1089 1089 'javelin-install', 1090 1090 'javelin-util', 1091 1091 ), 1092 + '2a228a94' => array( 1093 + 'javelin-behavior', 1094 + 'javelin-stratcom', 1095 + 'javelin-util', 1096 + 'javelin-dom', 1097 + 'javelin-request', 1098 + 'phabricator-keyboard-shortcut', 1099 + 'phabricator-darklog', 1100 + 'phabricator-darkmessage', 1101 + ), 1092 1102 '2b8de964' => array( 1093 1103 'javelin-install', 1094 1104 'javelin-util', ··· 1381 1391 '6882e80a' => array( 1382 1392 'javelin-dom', 1383 1393 ), 1384 - '698614f9' => array( 1385 - 'javelin-behavior', 1386 - 'javelin-stratcom', 1387 - 'javelin-util', 1388 - 'javelin-dom', 1389 - 'javelin-request', 1390 - 'phabricator-keyboard-shortcut', 1391 - 'phabricator-darklog', 1392 - 'phabricator-darkmessage', 1393 - ), 1394 1394 '69adf288' => array( 1395 1395 'javelin-install', 1396 1396 ), ··· 1467 1467 '7a68dda3' => array( 1468 1468 'owners-path-editor', 1469 1469 'javelin-behavior', 1470 + ), 1471 + '7cacce98' => array( 1472 + 'javelin-install', 1473 + 'javelin-util', 1474 + 'javelin-websocket', 1475 + 'javelin-leader', 1476 + 'javelin-json', 1470 1477 ), 1471 1478 '7cbe244b' => array( 1472 1479 'javelin-install', ··· 2013 2020 ), 2014 2021 'cd2b9b77' => array( 2015 2022 'phui-oi-list-view-css', 2016 - ), 2017 - 'ce5f793f' => array( 2018 - 'javelin-install', 2019 - 'javelin-util', 2020 - 'javelin-websocket', 2021 - 'javelin-leader', 2022 - 'javelin-json', 2023 2023 ), 2024 2024 'd0c516d5' => array( 2025 2025 'javelin-behavior',
+12
src/applications/console/plugin/DarkConsoleRealtimePlugin.php
··· 23 23 )); 24 24 25 25 $reconnect_label = pht('Reconnect'); 26 + $replay_label = pht('Replay'); 26 27 27 28 $buttons = phutil_tag( 28 29 'div', ··· 40 41 'action' => 'reconnect', 41 42 'label' => $reconnect_label, 42 43 )), 44 + id(new PHUIButtonView()) 45 + ->setIcon('fa-backward') 46 + ->setColor(PHUIButtonView::GREY) 47 + ->setText($replay_label) 48 + ->addSigil('dark-console-realtime-action') 49 + ->setMetadata( 50 + array( 51 + 'action' => 'replay', 52 + 'label' => $replay_label, 53 + )), 43 54 )); 44 55 45 56 return phutil_tag( 46 57 'div', 47 58 array( 59 + 'class' => 'dark-console-realtime', 48 60 ), 49 61 array( 50 62 $buttons,
+5
support/aphlict/server/aphlict_server.js
··· 197 197 admin_server.setClientServers(aphlict_clients); 198 198 admin_server.setPeerList(peer_list); 199 199 } 200 + 201 + for (ii = 0; ii < aphlict_clients.length; ii++) { 202 + var client_server = aphlict_clients[ii]; 203 + client_server.setAdminServers(aphlict_admins); 204 + }
+36
support/aphlict/server/lib/AphlictClientServer.js
··· 16 16 17 17 this._server = server; 18 18 this._lists = {}; 19 + this._adminServers = []; 19 20 }, 20 21 21 22 properties: { 22 23 logger: null, 24 + adminServers: null 23 25 }, 24 26 25 27 members: { ··· 31 33 this._lists[instance] = new JX.AphlictListenerList(instance); 32 34 } 33 35 return this._lists[instance]; 36 + }, 37 + 38 + getHistory: function(age) { 39 + var results = []; 40 + 41 + var servers = this.getAdminServers(); 42 + for (var ii = 0; ii < servers.length; ii++) { 43 + var messages = servers[ii].getHistory(age); 44 + for (var jj = 0; jj < messages.length; jj++) { 45 + results.push(messages[jj]); 46 + } 47 + } 48 + 49 + return results; 34 50 }, 35 51 36 52 log: function() { ··· 115 131 'Unsubscribed from: %s', 116 132 JSON.stringify(message.data)); 117 133 listener.unsubscribe(message.data); 134 + break; 135 + 136 + case 'replay': 137 + var age = message.data.age || 60000; 138 + var min_age = (new Date().getTime() - age); 139 + 140 + var old_messages = self.getHistory(min_age); 141 + for (var ii = 0; ii < old_messages.length; ii++) { 142 + var old_message = old_messages[ii]; 143 + 144 + if (!listener.isSubscribedToAny(old_message.subscribers)) { 145 + continue; 146 + } 147 + 148 + try { 149 + listener.writeMessage(old_message); 150 + } catch (error) { 151 + break; 152 + } 153 + } 118 154 break; 119 155 120 156 default:
+4
webroot/rsrc/css/aphront/dark-console.css
··· 231 231 padding: 8px; 232 232 margin: 2px; 233 233 } 234 + 235 + .dark-console-realtime .button { 236 + margin-right: 8px; 237 + }
+45 -4
webroot/rsrc/js/application/aphlict/Aphlict.js
··· 40 40 _socket: null, 41 41 _subscriptions: null, 42 42 _status: null, 43 + _isReconnect: false, 43 44 44 45 start: function() { 45 46 JX.Leader.listen('onBecomeLeader', JX.bind(this, this._lead)); ··· 94 95 }, 95 96 96 97 _open: function() { 98 + // If this is a reconnect, ask the server to replay recent messages 99 + // after other tabs have had a chance to subscribe. Do this before we 100 + // broadcast that the connection status is now open. 101 + if (this._isReconnect) { 102 + setTimeout(JX.bind(this, this._reconnect), 100); 103 + } 104 + 97 105 this._broadcastStatus('open'); 98 106 JX.Leader.broadcast(null, {type: 'aphlict.getsubscribers'}); 99 107 }, 100 108 109 + _reconnect: function() { 110 + this.replay(); 111 + 112 + JX.Leader.broadcast(null, {type: 'aphlict.reconnect', data: null}); 113 + }, 114 + 115 + replay: function() { 116 + var replay = { 117 + age: 60000 118 + }; 119 + 120 + JX.Leader.broadcast(null, {type: 'aphlict.replay', data: replay}); 121 + }, 122 + 101 123 _close: function() { 102 124 this._broadcastStatus('closed'); 103 125 }, ··· 131 153 132 154 case 'aphlict.subscribe': 133 155 if (is_leader) { 134 - this._write({ 135 - command: 'subscribe', 136 - data: message.data 137 - }); 156 + this._writeCommand('subscribe', message.data); 157 + } 158 + break; 159 + 160 + case 'aphlict.replay': 161 + if (is_leader) { 162 + this._writeCommand('replay', message.data); 138 163 } 139 164 break; 140 165 ··· 147 172 148 173 _setStatus: function(status) { 149 174 this._status = status; 175 + 176 + // If we've ever seen an open connection, any new connection we make 177 + // is a reconnect and should replay history. 178 + if (status == 'open') { 179 + this._isReconnect = true; 180 + } 181 + 150 182 this.invoke('didChangeStatus'); 151 183 }, 152 184 153 185 _write: function(message) { 154 186 this._socket.send(JX.JSON.stringify(message)); 187 + }, 188 + 189 + _writeCommand: function(command, message) { 190 + var frame = { 191 + command: command, 192 + data: message 193 + }; 194 + 195 + return this._write(frame); 155 196 } 156 197 157 198 },
+3
webroot/rsrc/js/core/darkconsole/behavior-dark-console.js
··· 392 392 ws.reconnect(); 393 393 } 394 394 break; 395 + case 'replay': 396 + JX.Aphlict.getInstance().replay(); 397 + break; 395 398 } 396 399 397 400 });