BuddhaNexus/buddhanexus-frontend

View on GitHub
src/views/textview/text-view-left.js

Summary

Maintainability
C
1 day
Test Coverage
import { customElement, html, LitElement, property } from 'lit-element';
import { removeDuplicates } from '../utility/views-common';
import { getFileTextAndParallels } from '../../api/actions';

import sharedDataViewStyles from '../data/data-view-shared.styles';
import styles from './text-view-table.styles';
import { LeftSegmentContainer } from './LeftSegment';
import { C_HIGHLIGHTED_SEGMENT, C_SELECTED_SEGMENT } from './text-view';

@customElement('text-view-left')
export class TextViewLeft extends LitElement {
  @property({ type: String }) fileName;
  @property({ type: Array }) limitCollection;
  @property({ type: Number }) quoteLength;
  @property({ type: Number }) cooccurance;
  @property({ type: Number }) currentPosition = 0;
  @property({ type: Object }) leftTextData;
  @property({ type: Number }) score;
  @property({ type: Array }) multiLingualMode;
  @property({ type: String }) leftActiveSegment;
  @property({ type: Boolean }) showSegmentNumbers;
  @property({ type: String }) segmentDisplaySide;
  @property({ type: String }) transMethod;
  // Local variables
  @property({ type: String }) veryShortText = false;
  @property({ type: String }) reachedEndText = false;
  @property({ type: Array }) textLeft = [];
  @property({ type: Object }) parallels = {};
  @property({ type: String }) noEndlessScrolling = false;
  @property({ type: String }) fetchError;
  @property({ type: String }) fetchLoading = true;
  @property({ type: Number }) currentPage = 0;
  @property({ type: Object }) textLeftBySegNr = {};
  @property({ type: Boolean }) addedSegmentObservers = false;

  static get styles() {
    return [sharedDataViewStyles, styles];
  }

  firstUpdated() {
    if (this.leftTextData) {
      this.handleLeftTextDataChanged();
    } else if (this.leftActiveSegment === undefined) {
      this.fetchNewText();
    } else {
      this.leftTextData = { selectedParallels: [this.leftActiveSegment] };
    }
  }

  updated(_changedProperties) {
    _changedProperties.forEach(async (oldValue, propName) => {
      if (propName === 'fileName' && !this.fetchLoading) {
        if (!('leftTextData' in _changedProperties)) {
          this.handleFilenameChanged();
        }
      }

      if (propName === 'leftTextData') {
        this.handleLeftTextDataChanged();
      }
      if (propName === 'leftActiveSegment') {
        this.noEndlessScrolling = false;
      }

      const fileChanged = [
        'score',
        'cooccurance',
        'quoteLength',
        'limitCollection',
        'multiLingualMode',
      ].includes(propName);
      if (fileChanged && !this.fetchLoading) {
        this.noEndlessScrolling = true;
        await this.fetchNewText();
      }

      if (propName === 'textLeft' && oldValue) {
        this.scrollAfterEndlessReload();
        this.addedSegmentObservers = false;
        await this.addSegmentObservers();
      }

      if (propName === 'currentPage' && !this.fetchLoading) {
        await this.fetchNewText();
        this.scrollAfterEndlessReload();
      }
    });
  }

  handleLeftTextDataChanged() {
    this.addedSegmentObservers = false;
    this.noEndlessScrolling = false;
    this.parallels = {};
    this.textLeft = [];
    this.leftActiveSegment = this.leftTextData.selectedParallels[0];
    this.currentPage = 0;
    this.veryShortText = false;
    this.reachedEndText = false;
    this.fetchNewText();
  }

  handleFilenameChanged() {
    this.addedSegmentObservers = false;
    this.noEndlessScrolling = true;
    this.textLeft = [];
    this.parallels = {};
    this.leftActiveSegment = undefined;
    this.currentPage = 0;
    this.veryShortText = false;
    this.reachedEndText = false;
    this.fetchNewText();
  }

  async fetchNewText() {
    this.fetchLoading = true;
    const { textleft, parallels, error } = await getFileTextAndParallels({
      fileName: this.fileName,
      limit_collection: this.limitCollection,
      score: this.score,
      par_length: this.quoteLength,
      co_occ: this.cooccurance,
      active_segment: this.leftActiveSegment,
      multi_lingual: this.multiLingualMode,
    });
    if (textleft.length !== 800) {
      if (this.leftActiveSegment) {
        this.reachedEndText = true;
      } else {
        this.veryShortText = true;
      }
    }
    this.textLeft = removeDuplicates(textleft, 'segnr');
    this.textLeftBySegNr = {};
    this.textLeft.forEach(
      ({ segnr, parallel_ids }) => (this.textLeftBySegNr[segnr] = parallel_ids)
    );
    this.parallels = {};
    if (parallels.length >= 1) {
      for (let i = 0; i <= parallels.length; i++) {
        if (parallels[i]) {
          this.parallels[parallels[i].id] = parallels[i];
        }
      }
    }
    this.fetchError = error;
    this.fetchLoading = false;
    this.addedSegmentObservers = false;
  }

  scrollAfterEndlessReload() {
    if (this.noEndlessScrolling) {
      return;
    }
    const activeSegment = this.shadowRoot.getElementById(
      this.leftActiveSegment
    );
    if (!activeSegment) {
      return;
    }
    const rootEl = document.querySelector('html');
    const rootElScroll = rootEl.scrollTop;
    activeSegment.scrollIntoView({
      // depends on direction of scrolling (downwards/upwards)
      block: this.currentPosition > 400 ? 'end' : 'start',
      inline: 'nearest',
    });
    rootEl.scrollTop = rootElScroll;
    this.noEndlessScrolling = true;
  }

  incrementPage() {
    this.currentPage = this.currentPage + 1;
  }

  async addSegmentObservers() {
    if (this.addedSegmentObservers) {
      return;
    }
    const targets = this.shadowRoot.querySelectorAll('.left-segment');
    if (
      targets.length === 0 ||
      this.veryShortText ||
      this.addedSegmentObservers
    ) {
      return;
    }
    const observer = new IntersectionObserver(
      entries => {
        for (let i = 0; i < entries.length; i++) {
          let entry = entries[i];
          if (!entry.isIntersecting) {
            continue;
          }
          // when an intersecting element is observed, we need to allow scrolling!
          this.noEndlessScrolling = false;
          this.leftActiveSegment = entry.target.id;
          this.incrementPage();
          this.currentPosition = parseInt(entry.target.getAttribute('number'));
          observer.unobserve(entry.target);
          break;
        }
      },
      {
        root: this.shadowRoot.querySelector('#left-text-column'),
      }
    );
    if (
      this.leftActiveSegment !== undefined && // ### delete me?
      this.leftActiveSegment !== targets[0].id
    ) {
      observer.observe(targets[0]);
    }
    if (!this.reachedEndText) {
      observer.observe(targets[targets.length - 1]);
    }
    this.addedSegmentObservers = true;
  }

  handleSegmentClick(e) {
    this.shadowRoot
      .querySelectorAll(`.${C_SELECTED_SEGMENT}`)
      .forEach(el => el.classList.remove(C_SELECTED_SEGMENT));
    this.shadowRoot
      .querySelectorAll(`.${C_HIGHLIGHTED_SEGMENT}`)
      .forEach(el => el.classList.remove(C_HIGHLIGHTED_SEGMENT));
    if (e) {
      this.displayParallels(e.target);
    }
  }

  displayParallels(element) {
    if (!element) {
      return;
    }

    let selectedWord = element;
    let selectedSegment = element.parentElement;
    if (!selectedSegment) {
      return;
    }

    if (selectedSegment.classList.contains('chinese-verse')) {
      selectedSegment = selectedSegment.parentElement;
    }

    selectedWord.classList.add(C_HIGHLIGHTED_SEGMENT);
    const position = selectedWord.getAttribute('position');
    const segnr = selectedSegment.id;
    const parallels = this.textLeftBySegNr[segnr]
      .map(parallel => {
        if (this.parallels[parallel]) return parallel;
      })
      .filter(el => el != null);
    this.dispatchEvent(
      new CustomEvent('active-segment-changed', {
        bubbles: true,
        composed: true,
        detail: {
          activeSegment: segnr,
          position: position,
          selectedParallels: parallels,
          limitCollection: this.limitCollection,
          rightMode: false,
        },
      })
    );
  }

  filterParallels(parallelIds) {
    if (parallelIds.length >= 1) {
      return parallelIds.map(id => this.parallels[id]).filter(el => el != null);
    } else {
      return [];
    }
  }

  render() {
    return html`
      ${this.fetchLoading
        ? html`
            <bn-loading-spinner></bn-loading-spinner>
          `
        : null}
      ${this.textLeft.map(({ parallel_ids, segnr, segtext }, i) =>
        LeftSegmentContainer({
          segmentNr: segnr,
          segText: segtext,
          current_parallels: this.filterParallels(parallel_ids),
          number: i - 1,
          onClick: this.handleSegmentClick,
          leftTextData: this.leftTextData,
          currentSegment: this.leftActiveSegment,
          currentPosition: this.currentPosition,
          showSegmentNumbers: this.showSegmentNumbers,
          segmentDisplaySide: this.segmentDisplaySide,
          transMethod: this.transMethod,
        })
      )}
    `;
  }
}