Page MenuHomePhabricator

D19153.diff
No OneTemporary

D19153.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -78,7 +78,7 @@
'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948',
'rsrc/css/application/flag/flag.css' => 'bba8f811',
- 'rsrc/css/application/harbormaster/harbormaster.css' => 'c7e29d9e',
+ 'rsrc/css/application/harbormaster/harbormaster.css' => '5dd4c2de',
'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
'rsrc/css/application/herald/herald.css' => 'cd8d0134',
'rsrc/css/application/maniphest/report.css' => '9b9580b7',
@@ -416,7 +416,7 @@
'rsrc/js/application/drydock/drydock-live-operation-status.js' => '901935ef',
'rsrc/js/application/files/behavior-icon-composer.js' => '8499b6ab',
'rsrc/js/application/files/behavior-launch-icon-composer.js' => '48086888',
- 'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => 'be6974cc',
+ 'rsrc/js/application/harbormaster/behavior-harbormaster-log.js' => '796a8803',
'rsrc/js/application/herald/HeraldRuleEditor.js' => 'dca75c0e',
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
@@ -579,7 +579,7 @@
'font-fontawesome' => 'e838e088',
'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => 'b556a948',
- 'harbormaster-css' => 'c7e29d9e',
+ 'harbormaster-css' => '5dd4c2de',
'herald-css' => 'cd8d0134',
'herald-rule-editor' => 'dca75c0e',
'herald-test-css' => 'a52e323e',
@@ -636,7 +636,7 @@
'javelin-behavior-event-all-day' => 'b41537c9',
'javelin-behavior-fancy-datepicker' => 'ecf4e799',
'javelin-behavior-global-drag-and-drop' => '960f6a39',
- 'javelin-behavior-harbormaster-log' => 'be6974cc',
+ 'javelin-behavior-harbormaster-log' => '796a8803',
'javelin-behavior-herald-rule-editor' => '7ebaeed3',
'javelin-behavior-high-security-warning' => 'a464fe03',
'javelin-behavior-history-install' => '7ee2b591',
@@ -1535,6 +1535,9 @@
'javelin-behavior',
'javelin-quicksand',
),
+ '796a8803' => array(
+ 'javelin-behavior',
+ ),
'7a68dda3' => array(
'owners-path-editor',
'javelin-behavior',
@@ -1889,9 +1892,6 @@
'javelin-util',
'javelin-request',
),
- 'be6974cc' => array(
- 'javelin-behavior',
- ),
'bea6e7f4' => array(
'javelin-install',
'javelin-dom',
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php b/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php
--- a/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildLogRenderController.php
@@ -22,14 +22,14 @@
if ($head_lines === null) {
$head_lines = 8;
}
- $head_lines = min($head_lines, 100);
+ $head_lines = min($head_lines, 1024);
$head_lines = max($head_lines, 0);
$tail_lines = $request->getInt('tail');
if ($tail_lines === null) {
$tail_lines = 16;
}
- $tail_lines = min($tail_lines, 100);
+ $tail_lines = min($tail_lines, 1024);
$tail_lines = max($tail_lines, 0);
$head_offset = $request->getInt('headOffset');
@@ -301,7 +301,6 @@
);
}
-
foreach ($views as $view) {
if ($spacer) {
$spacer['tail'] = $view['viewOffset'];
@@ -360,6 +359,22 @@
}
}
+ if ($log->getLive()) {
+ $last_view = last($views);
+ $last_line = last($last_view['viewData']);
+ if ($last_line) {
+ $last_offset = $last_line['offset'];
+ } else {
+ $last_offset = 0;
+ }
+
+ $last_tail = $last_view['viewOffset'] + $last_view['viewLength'];
+ $show_live = ($last_tail === $log_size);
+ if ($show_live) {
+ $rows[] = $this->renderLiveRow($last_offset);
+ }
+ }
+
$table = phutil_tag(
'table',
array(
@@ -650,25 +665,66 @@
),
$expand_down),
);
- $expand_row = phutil_tag('tr', array(), $expand_cells);
- $expand_table = phutil_tag(
+
+ return $this->renderActionTable($expand_cells);
+ }
+
+ private function renderLiveRow($log_size) {
+ $icon_down = id(new PHUIIconView())
+ ->setIcon('fa-chevron-down');
+
+ $follow = javelin_tag(
+ 'a',
+ array(
+ 'sigil' => 'harbormaster-log-expand harbormaster-log-live',
+ 'meta' => array(
+ 'headOffset' => $log_size,
+ 'head' => 0,
+ 'tail' => 1024,
+ 'live' => true,
+ ),
+ ),
+ array(
+ $icon_down,
+ ' ',
+ pht('Follow Log'),
+ ' ',
+ $icon_down,
+ ));
+
+ $expand_cells = array(
+ phutil_tag(
+ 'td',
+ array(
+ 'class' => 'harbormaster-log-follow',
+ ),
+ $follow),
+ );
+
+ return $this->renderActionTable($expand_cells);
+ }
+
+ private function renderActionTable(array $action_cells) {
+ $action_row = phutil_tag('tr', array(), $action_cells);
+
+ $action_table = phutil_tag(
'table',
array(
'class' => 'harbormaster-log-expand-table',
),
- $expand_row);
+ $action_row);
- $cells = array(
+ $format_cells = array(
phutil_tag('th', array()),
phutil_tag(
'td',
array(
'class' => 'harbormaster-log-expand-cell',
),
- $expand_table),
+ $action_table),
);
- return phutil_tag('tr', array(), $cells);
+ return phutil_tag('tr', array(), $format_cells);
}
}
diff --git a/webroot/rsrc/css/application/harbormaster/harbormaster.css b/webroot/rsrc/css/application/harbormaster/harbormaster.css
--- a/webroot/rsrc/css/application/harbormaster/harbormaster.css
+++ b/webroot/rsrc/css/application/harbormaster/harbormaster.css
@@ -97,7 +97,6 @@
'Helvetica Neue', Helvetica, Arial, sans-serif;
}
-
.harbormaster-log-expand-up {
text-align: right;
width: 50%;
@@ -107,6 +106,14 @@
margin: 0 0 0px 4px;
}
+.harbormaster-log-follow {
+ text-align: center;
+}
+
+.harbormaster-log-follow .phui-icon-view {
+ margin: 0 4px;
+}
+
.harbormaster-log-expand-mid {
text-align: center;
white-space: nowrap;
diff --git a/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js b/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js
--- a/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js
+++ b/webroot/rsrc/js/application/harbormaster/behavior-harbormaster-log.js
@@ -5,6 +5,7 @@
JX.behavior('harbormaster-log', function(config) {
var contentNode = JX.$(config.contentNodeID);
+ var following = false;
JX.DOM.listen(contentNode, 'click', 'harbormaster-log-expand', function(e) {
if (!e.isNormalClick()) {
@@ -13,23 +14,64 @@
e.kill();
- var row = e.getNode('tag:tr');
+ expand(e.getTarget());
+ });
+
+ function expand(node) {
+ var row = JX.DOM.findAbove(node, 'tr');
row = JX.DOM.findAbove(row, 'tr');
- var data = e.getNodeData('harbormaster-log-expand');
+ var data = JX.Stratcom.getData(node);
var uri = new JX.URI(config.renderURI)
.addQueryParams(data);
+ if (data.live) {
+ following = true;
+ }
+
var request = new JX.Request(uri, function(r) {
var result = JX.$H(r.markup).getNode();
var rows = [].slice.apply(result.firstChild.childNodes);
+ // If we're following the bottom of the log, the result always includes
+ // the last line from the previous render. Throw it away, then add the
+ // new data.
+ if (data.live && row.previousSibling) {
+ JX.DOM.remove(row.previousSibling);
+ }
+
JX.DOM.replace(row, rows);
+
+ if (data.live) {
+ // If this was a live follow, scroll the new data into view. This is
+ // probably intensely annoying in practice but seems cool for now.
+ var last_row = rows[rows.length - 1];
+ var tail_pos = JX.$V(last_row).y + JX.Vector.getDim(last_row).y;
+ var view_y = JX.Vector.getViewport().y;
+ JX.DOM.scrollToPosition(null, (tail_pos - view_y) + 32);
+
+ setTimeout(follow, 500);
+ }
});
request.send();
- });
+ }
+
+ function follow() {
+ if (!following) {
+ return;
+ }
+
+ var live;
+ try {
+ live = JX.DOM.find(contentNode, 'a', 'harbormaster-log-live');
+ } catch (e) {
+ return;
+ }
+
+ expand(live);
+ }
function onresponse(r) {
JX.DOM.alterClass(contentNode, 'harbormaster-log-view-loading', false);

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 22, 4:37 PM (18 h, 40 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6775309
Default Alt Text
D19153.diff (8 KB)

Event Timeline