diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -349,6 +349,7 @@ 'DarkConsoleEventPlugin' => 'applications/console/plugin/DarkConsoleEventPlugin.php', 'DarkConsoleEventPluginAPI' => 'applications/console/plugin/event/DarkConsoleEventPluginAPI.php', 'DarkConsolePlugin' => 'applications/console/plugin/DarkConsolePlugin.php', + 'DarkConsoleRealtimePlugin' => 'applications/console/plugin/DarkConsoleRealtimePlugin.php', 'DarkConsoleRequestPlugin' => 'applications/console/plugin/DarkConsoleRequestPlugin.php', 'DarkConsoleServicesPlugin' => 'applications/console/plugin/DarkConsoleServicesPlugin.php', 'DarkConsoleStartupPlugin' => 'applications/console/plugin/DarkConsoleStartupPlugin.php', @@ -5144,6 +5145,7 @@ 'DarkConsoleEventPlugin' => 'DarkConsolePlugin', 'DarkConsoleEventPluginAPI' => 'PhabricatorEventListener', 'DarkConsolePlugin' => 'Phobject', + 'DarkConsoleRealtimePlugin' => 'DarkConsolePlugin', 'DarkConsoleRequestPlugin' => 'DarkConsolePlugin', 'DarkConsoleServicesPlugin' => 'DarkConsolePlugin', 'DarkConsoleStartupPlugin' => 'DarkConsolePlugin', diff --git a/src/applications/console/plugin/DarkConsoleRealtimePlugin.php b/src/applications/console/plugin/DarkConsoleRealtimePlugin.php new file mode 100644 --- /dev/null +++ b/src/applications/console/plugin/DarkConsoleRealtimePlugin.php @@ -0,0 +1,26 @@ + 'dark-console-realtime-log', + 'class' => 'dark-console-log-frame', + )); + } + +} diff --git a/webroot/rsrc/css/aphront/dark-console.css b/webroot/rsrc/css/aphront/dark-console.css --- a/webroot/rsrc/css/aphront/dark-console.css +++ b/webroot/rsrc/css/aphront/dark-console.css @@ -217,3 +217,17 @@ .dark-console-panel-error-details { display: none; } + +.dark-console-log-frame { + height: 500px; + overflow: auto; + background: #303030; + border: 1px solid #202020; + margin: 4px; +} + +.dark-console-log-message { + background: #404040; + padding: 8px; + margin: 2px; +} diff --git a/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js b/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js --- a/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js +++ b/webroot/rsrc/js/application/aphlict/behavior-aphlict-listen.js @@ -114,9 +114,16 @@ config.websocketURI, config.subscriptions); - client - .setHandler(onAphlictMessage) - .start(); + var start_client = function() { + client + .setHandler(onAphlictMessage) + .start(); + }; + + // Don't start the client until other behaviors have had a chance to + // initialize. In particular, we want to capture events into the log for + // the DarkConsole "Realtime" panel. + setTimeout(start_client, 0); JX.Stratcom.listen( 'quicksand-redraw', diff --git a/webroot/rsrc/js/core/darkconsole/DarkLog.js b/webroot/rsrc/js/core/darkconsole/DarkLog.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/core/darkconsole/DarkLog.js @@ -0,0 +1,47 @@ +/** + * @provides phabricator-darklog + * @javelin + */ + +JX.install('DarkLog', { + + construct: function() { + this._messages = []; + }, + + members: { + _node: null, + _messages: null, + + addMessage: function(message) { + var node = message.getNode(); + + this._messages.push(message); + if (this._node) { + this._append([node]); + } + + return this; + }, + + setNode: function(node) { + var nodes = []; + for (var ii = 0; ii < this._messages.length; ii++) { + nodes.push(this._messages[ii].getNode()); + } + + this._node = node; + this._append(nodes); + + return this; + }, + + _append: function(nodes) { + for (var ii = 0; ii < nodes.length; ii++) { + this._node.appendChild(nodes[ii]); + } + } + + } + +}); diff --git a/webroot/rsrc/js/core/darkconsole/DarkMessage.js b/webroot/rsrc/js/core/darkconsole/DarkMessage.js new file mode 100644 --- /dev/null +++ b/webroot/rsrc/js/core/darkconsole/DarkMessage.js @@ -0,0 +1,37 @@ +/** + * @provides phabricator-darkmessage + * @javelin + */ + +JX.install('DarkMessage', { + + construct: function() { + + }, + + members: { + _node: null, + _message: null, + + setMessage: function(message) { + this._message = message; + + JX.DOM.setContent(this.getNode(), message); + + return this; + }, + + getNode: function() { + if (!this._node) { + this._node = JX.$N( + 'div', + { + className: 'dark-console-log-message' + }); + } + + return this._node; + } + } + +}); diff --git a/webroot/rsrc/js/core/behavior-dark-console.js b/webroot/rsrc/js/core/darkconsole/behavior-dark-console.js rename from webroot/rsrc/js/core/behavior-dark-console.js rename to webroot/rsrc/js/core/darkconsole/behavior-dark-console.js --- a/webroot/rsrc/js/core/behavior-dark-console.js +++ b/webroot/rsrc/js/core/darkconsole/behavior-dark-console.js @@ -6,6 +6,8 @@ * javelin-dom * javelin-request * phabricator-keyboard-shortcut + * phabricator-darklog + * phabricator-darkmessage */ JX.behavior('dark-console', function(config, statics) { @@ -246,6 +248,12 @@ var div = JX.$N('div', {className: 'dark-console-panel-core'}, JX.$H(html)); JX.DOM.setContent(statics.el.panel, div); + + var params = { + panel: tclass + }; + + JX.Stratcom.invoke('darkconsole.draw', null, params); } function install_shortcut() { @@ -287,4 +295,70 @@ } add_request(config); + +/* -( Realtime Panel )----------------------------------------------------- */ + + + if (!statics.realtime) { + statics.realtime = true; + + var realtime_log = new JX.DarkLog(); + var realtime_id = 'dark-console-realtime-log'; + + JX.Stratcom.listen('darkconsole.draw', null, function(e) { + var data = e.getData(); + if (data.panel != 'DarkConsoleRealtimePlugin') { + return; + } + + var node = JX.$(realtime_id); + realtime_log.setNode(node); + }); + + // If the panel is initially visible, try rendering. + try { + var node = JX.$(realtime_id); + realtime_log.setNode(node); + } catch (exception) { + // Ignore. + } + + var leader_log = function(event_name, type, is_leader, details) { + var parts = []; + if (is_leader === true) { + parts.push('+'); + } else if (is_leader === false) { + parts.push('-'); + } else { + parts.push('~'); + } + + parts.push('[Leader/' + event_name + ']'); + + if (type) { + parts.push('(' + type + ')'); + } + + if (details) { + parts.push(details); + } + + parts = parts.join(' '); + + var message = new JX.DarkMessage() + .setMessage(parts); + + realtime_log.addMessage(message); + }; + + JX.Leader.listen('onReceiveBroadcast', function(message, is_leader) { + var json = JX.JSON.stringify(message.data); + leader_log('onReceiveBroadcast', message.type, is_leader, json); + }); + + JX.Leader.listen('onBecomeLeader', function() { + leader_log('onBecomeLeader'); + }); + } + });