diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index 67224f387a..3e14f353a1 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -74,6 +74,12 @@ color: @at-blue; } + &-menuIconStack--wrapper { + &:hover { + color: @at-blue; + } + } + &-row { display: flex; diff --git a/awx/ui/client/features/output/api.events.service.js b/awx/ui/client/features/output/api.events.service.js index 8db532cc25..7b7571fa72 100644 --- a/awx/ui/client/features/output/api.events.service.js +++ b/awx/ui/client/features/output/api.events.service.js @@ -5,8 +5,8 @@ import { } from './constants'; const BASE_PARAMS = { - order_by: OUTPUT_ORDER_BY, page_size: OUTPUT_PAGE_SIZE, + order_by: OUTPUT_ORDER_BY, }; const merge = (...objs) => _.merge({}, ...objs); @@ -20,12 +20,6 @@ function JobEventsApiService ($http, $q) { this.cache = {}; }; - this.clearCache = () => { - Object.keys(this.cache).forEach(key => { - delete this.cache[key]; - }); - }; - this.fetch = () => this.getLast() .then(results => { this.cache.last = results; @@ -33,20 +27,31 @@ function JobEventsApiService ($http, $q) { return this; }); + this.clearCache = () => { + Object.keys(this.cache).forEach(key => { + delete this.cache[key]; + }); + }; + + this.pushMaxCounter = events => { + const maxCounter = Math.max(...events.map(({ counter }) => counter)); + + if (maxCounter > this.state.maxCounter) { + this.state.maxCounter = maxCounter; + } + + return maxCounter; + }; + this.getFirst = () => { - const page = 1; - const params = merge(this.params, { page }); + const params = merge(this.params, { page: 1 }); return $http.get(this.endpoint, { params }) .then(({ data }) => { const { results, count } = data; - const maxCounter = Math.max(...results.map(({ counter }) => counter)); this.state.count = count; - - if (maxCounter > this.state.maxCounter) { - this.state.maxCounter = maxCounter; - } + this.pushMaxCounter(results); return results; }); @@ -62,13 +67,9 @@ function JobEventsApiService ($http, $q) { return $http.get(this.endpoint, { params }) .then(({ data }) => { const { results, count } = data; - const maxCounter = Math.max(...results.map(({ counter }) => counter)); this.state.count = count; - - if (maxCounter > this.state.maxCounter) { - this.state.maxCounter = maxCounter; - } + this.pushMaxCounter(results); return results; }); @@ -84,7 +85,6 @@ function JobEventsApiService ($http, $q) { return $http.get(this.endpoint, { params }) .then(({ data }) => { const { results, count } = data; - const maxCounter = Math.max(...results.map(({ counter }) => counter)); let rotated = results; @@ -97,10 +97,7 @@ function JobEventsApiService ($http, $q) { } this.state.count = count; - - if (maxCounter > this.state.maxCounter) { - this.state.maxCounter = maxCounter; - } + this.pushMaxCounter(results); return rotated; }); @@ -119,11 +116,8 @@ function JobEventsApiService ($http, $q) { return $http.get(this.endpoint, { params }) .then(({ data }) => { const { results } = data; - const maxCounter = Math.max(...results.map(({ counter }) => counter)); - if (maxCounter > this.state.maxCounter) { - this.state.maxCounter = maxCounter; - } + this.pushMaxCounter(results); return results; }); diff --git a/awx/ui/client/features/output/index.controller.js b/awx/ui/client/features/output/index.controller.js index 8532bf4416..5c71a1b861 100644 --- a/awx/ui/client/features/output/index.controller.js +++ b/awx/ui/client/features/output/index.controller.js @@ -22,9 +22,6 @@ const bufferState = [0, 0]; // [length, count] const listeners = []; const rx = []; -let following = false; -let attach = true; - function bufferInit () { rx.length = 0; @@ -57,59 +54,91 @@ function bufferEmpty (min, max) { return removed; } +let attached = false; +let noframes = false; +let isOnLastPage = false; + function onFrames (events) { - if (!following) { + if (noframes) { + return $q.resolve(); + } + + if (!attached) { const minCounter = Math.min(...events.map(({ counter }) => counter)); - // attachment range - const max = slide.getTailCounter() + 1; - const min = Math.max(1, slide.getHeadCounter(), max - 50); - if (minCounter > max || minCounter < min) { + if (minCounter > slide.getTailCounter() + 1) { return $q.resolve(); } - if (!attach) { - return $q.resolve(); - } + attached = true; + } - follow(); + if (vm.isInFollowMode) { + vm.isFollowing = true; } const capacity = slide.getCapacity(); + if (capacity <= 0 && !isOnLastPage) { + attached = false; + + return $q.resolve(); + } + return slide.popBack(events.length - capacity) .then(() => slide.pushFront(events)) .then(() => { - scroll.setScrollPosition(scroll.getScrollHeight()); + if (vm.isFollowing && scroll.isBeyondLowerThreshold()) { + scroll.scrollToBottom(); + } return $q.resolve(); }); } function first () { - unfollow(); scroll.pause(); + unfollow(); - return slide.getFirst() + attached = false; + noframes = true; + isOnLastPage = false; + + slide.getFirst() .then(() => { scroll.resume(); + noframes = false; return $q.resolve(); }); } function next () { + if (vm.isFollowing) { + return $q.resolve(); + } + scroll.pause(); return slide.getNext() + .then(() => { + isOnLastPage = slide.isOnLastPage(); + if (isOnLastPage) { + stream.setMissingCounterThreshold(slide.getTailCounter() + 1); + if (scroll.isBeyondLowerThreshold()) { + scroll.scrollToBottom(); + follow(); + } + } + }) .finally(() => scroll.resume()); } function previous () { - unfollow(); scroll.pause(); const initialPosition = scroll.getScrollPosition(); + isOnLastPage = false; return slide.getPrevious() .then(popHeight => { @@ -121,6 +150,22 @@ function previous () { .finally(() => scroll.resume()); } +function menuLast () { + if (vm.isFollowing) { + unfollow(); + + return $q.resolve(); + } + + if (isOnLastPage) { + scroll.scrollToBottom(); + + return $q.resolve(); + } + + return last(); +} + function last () { scroll.pause(); @@ -129,7 +174,8 @@ function last () { stream.setMissingCounterThreshold(slide.getTailCounter() + 1); scroll.setScrollPosition(scroll.getScrollHeight()); - attach = true; + isOnLastPage = true; + follow(); scroll.resume(); return $q.resolve(); @@ -141,28 +187,21 @@ function down () { } function up () { - if (following) { - unfollow(); - } else { - scroll.moveUp(); - } + scroll.moveUp(); } function follow () { - scroll.pause(); - scroll.hide(); + isOnLastPage = slide.isOnLastPage(); - following = true; - vm.isFollowing = following; + if (resource.model.get('event_processing_finished')) return; + if (!isOnLastPage) return; + + vm.isInFollowMode = true; } function unfollow () { - attach = false; - following = false; - vm.isFollowing = following; - - scroll.unhide(); - scroll.resume(); + vm.isInFollowMode = false; + vm.isFollowing = false; } function togglePanelExpand () { @@ -276,6 +315,13 @@ function reloadState (params) { return $state.transitionTo($state.current, params, { inherit: false, location: 'replace' }); } +function getMaxCounter () { + const apiMax = resource.events.getMaxCounter(); + const wsMax = stream.getMaxCounter(); + + return Math.max(apiMax, wsMax); +} + function OutputIndexController ( _$compile_, _$q_, @@ -318,9 +364,10 @@ function OutputIndexController ( vm.togglePanelExpand = togglePanelExpand; // Stdout Navigation - vm.menu = { last, first, down, up }; + vm.menu = { last: menuLast, first, down, up }; vm.isMenuExpanded = true; - vm.isFollowing = following; + vm.isFollowing = false; + vm.isInFollowMode = false; vm.toggleMenuExpand = toggleMenuExpand; vm.toggleLineExpand = toggleLineExpand; vm.showHostDetails = showHostDetails; @@ -330,10 +377,21 @@ function OutputIndexController ( bufferInit(); status.init(resource); - slide.init(render, resource.events, scroll); - + slide.init(render, resource.events, scroll, { getMaxCounter }); render.init({ compile, toggles: vm.toggleLineEnabled }); - scroll.init({ previous, next }); + + scroll.init({ + next, + previous, + onLeaveLower () { + unfollow(); + return $q.resolve(); + }, + onEnterLower () { + follow(); + return $q.resolve(); + }, + }); stream.init({ bufferAdd, diff --git a/awx/ui/client/features/output/index.view.html b/awx/ui/client/features/output/index.view.html index c588f2b375..511da3c6dc 100644 --- a/awx/ui/client/features/output/index.view.html +++ b/awx/ui/client/features/output/index.view.html @@ -24,10 +24,9 @@ -