Home Reference Source Test Repository

src/content/TimeSummaryManager.js

import summarizer from '5rolli-time-summarizer';
import TimeSummaryLabel from './components/TimeSummaryLabel';
import Renderer from './lib/Renderer';
import {createTime} from './helpers';

const REFRESH_DURATION = 200;

/**
 * @typedef {Object} TimeSummary
 *
 * @property {number} es estimated minutes
 * @property {number} es50 50% estimated minutes
 * @property {number} es90 90% estimated minutes
 * @property {number} spent spent minutes
 */


/**
 * Managemnet TimeSummary on Trello Web Site
 */
export default class TimeSummaryManager {
  /**
   * Constructor
   *
   * @param {DOMNode} targetDOM target DOM for observing
   */
  constructor(targetDOM) {
    this.target = targetDOM;
    this.refreshTimeoutId = null;
    this.renderer = new Renderer(TimeSummaryLabel);
    this.observer = new MutationObserver(mutations => {
      mutations.every(mutation => {
        const {target, addedNodes, removedNodes} = mutation;
        const added = addedNodes.length ? addedNodes[0].classList : null;
        const removed = removedNodes.length ? removedNodes[0].classList : null;
        const hasChange = (
          target.classList.contains('list-card-title')
          || target.classList.contains('list-card')
          || added && added.contains('list-card')
          || removed && removed.contains('list-card')
        );
        if (hasChange) {
          this.refresh();
          return false;
        }
        return true;
      });
    });
  }

  /**
   * Start Observe Trello and attach TimeSummary labels
   */
  start() {
    this.refresh();
    this.observer.observe(this.target, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['class']
    });
  }

  /**
   * Refresh all TimeSummary labels
   */
  refresh() {
    /// FIXME: Maybe creating ListManager and BoardManager is better than this...
    if (this.refreshTimeoutId) {
      this.refreshTimeoutId = null;
      this.refresh();
    } else {
      this.refreshTimeoutId = setTimeout(() => {
        const renderItems = this.getTimeSummaries();
        this.renderer.render(renderItems);
        this.refreshTimeoutId = null;
      }, REFRESH_DURATION);
    }
  }

  /**
   * Return TimeSummary and DOMNode per Trello List
   *
   * @return {{node: DOMNode, props: {timeSummary: TimeSummary}}}
   */
  getTimeSummaries() {
    const listNodes = this.target.querySelectorAll('.list-wrapper > .list');
    return Array.prototype.map.call(listNodes, listNode => {
      // Exclude invisible list
      if (listNode.offsetParent === null) {
        return null;
      }
      const titleNodes = listNode.querySelectorAll('.list-card-title');
      const times = Array.prototype.map.call(titleNodes, title => {
        // Exclude invisible title
        if (title.offsetParent) {
          return createTime(title.text);
        }
        return null;
      }).filter(t => t);
      const timeSummary = summarizer(times);
      return {
        node: listNode.querySelector('.list-header'),
        props: {timeSummary}
      };
    }).filter(t => t);
  }
}