github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/ui/app/components/streaming-file.js (about)

     1  import Component from '@ember/component';
     2  import { run } from '@ember/runloop';
     3  import { task } from 'ember-concurrency';
     4  import WindowResizable from 'nomad-ui/mixins/window-resizable';
     5  
     6  export default Component.extend(WindowResizable, {
     7    tagName: 'pre',
     8    classNames: ['cli-window'],
     9    'data-test-log-cli': true,
    10  
    11    mode: 'streaming', // head, tail, streaming
    12    isStreaming: true,
    13    logger: null,
    14  
    15    didReceiveAttrs() {
    16      if (!this.logger) {
    17        return;
    18      }
    19  
    20      run.scheduleOnce('actions', () => {
    21        switch (this.mode) {
    22          case 'head':
    23            this.head.perform();
    24            break;
    25          case 'tail':
    26            this.tail.perform();
    27            break;
    28          case 'streaming':
    29            if (this.isStreaming) {
    30              this.stream.perform();
    31            } else {
    32              this.logger.stop();
    33            }
    34            break;
    35        }
    36      });
    37    },
    38  
    39    didInsertElement() {
    40      this.fillAvailableHeight();
    41    },
    42  
    43    windowResizeHandler() {
    44      run.once(this, this.fillAvailableHeight);
    45    },
    46  
    47    fillAvailableHeight() {
    48      // This math is arbitrary and far from bulletproof, but the UX
    49      // of having the log window fill available height is worth the hack.
    50      const margins = 30; // Account for padding and margin on either side of the CLI
    51      const cliWindow = this.element;
    52      cliWindow.style.height = `${window.innerHeight - cliWindow.offsetTop - margins}px`;
    53    },
    54  
    55    head: task(function*() {
    56      yield this.get('logger.gotoHead').perform();
    57      run.scheduleOnce('afterRender', () => {
    58        this.element.scrollTop = 0;
    59      });
    60    }),
    61  
    62    tail: task(function*() {
    63      yield this.get('logger.gotoTail').perform();
    64      run.scheduleOnce('afterRender', () => {
    65        const cliWindow = this.element;
    66        cliWindow.scrollTop = cliWindow.scrollHeight;
    67      });
    68    }),
    69  
    70    synchronizeScrollPosition(force = false) {
    71      const cliWindow = this.element;
    72      if (cliWindow.scrollHeight - cliWindow.scrollTop < 10 || force) {
    73        // If the window is approximately scrolled to the bottom, follow the log
    74        cliWindow.scrollTop = cliWindow.scrollHeight;
    75      }
    76    },
    77  
    78    stream: task(function*() {
    79      // Force the scroll position to the bottom of the window when starting streaming
    80      this.logger.one('tick', () => {
    81        run.scheduleOnce('afterRender', () => this.synchronizeScrollPosition(true));
    82      });
    83  
    84      // Follow the log if the scroll position is near the bottom of the cli window
    85      this.logger.on('tick', this, 'scheduleScrollSynchronization');
    86  
    87      yield this.logger.startStreaming();
    88      this.logger.off('tick', this, 'scheduleScrollSynchronization');
    89    }),
    90  
    91    scheduleScrollSynchronization() {
    92      run.scheduleOnce('afterRender', () => this.synchronizeScrollPosition());
    93    },
    94  
    95    willDestroy() {
    96      this.logger.stop();
    97    },
    98  });