view src/app/analysis-item/analysis-item.component.ts @ 509:041468f553e1 tip master

Merge pull request #57 from LucasThompson/fix/session-stack-max-call-stack Fix accidental recursion in PersistentStack
author Lucas Thompson <LucasThompson@users.noreply.github.com>
date Mon, 27 Nov 2017 11:04:30 +0000
parents ccce2c09502e
children
line wrap: on
line source
/**
 * Created by lucast on 21/03/2017.
 */
import {
  ChangeDetectionStrategy,
  Component,
  Input,
  OnDestroy,
  OnInit,
  Output,
  EventEmitter
} from '@angular/core';
import {naivePagingMapper} from '../visualisations/WavesJunk';
import {OnSeekHandler} from '../playhead/PlayHeadHelpers';
import {
  defaultColourGenerator,
  HigherLevelFeatureShape
} from '../visualisations/FeatureUtilities';
import {
  RenderLoopService,
  TaskRemover
} from '../services/render-loop/render-loop.service';
import {DomSanitizer} from '@angular/platform-browser';
import {
  isExtractedAnalysisItem,
  isLoadedRootAudioItem,
  isPendingAnalysisItem,
  isPendingRootAudioItem,
  Item,
  RootAudioItem
} from './AnalysisItem';

@Component({
  selector: 'ugly-analysis-item',
  templateUrl: './analysis-item.component.html',
  styleUrls: ['./analysis-item.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AnalysisItemComponent implements OnInit, OnDestroy {

  // TODO should be TimelineTimeContext?
  @Input() set timeline(timeline: Timeline) {
    this.mTimeline = timeline;
    this.resetRemoveAnimation();
  }

  get timeline(): Timeline {
    return this.mTimeline;
  }

  @Input() set isActive(isActive: boolean) {
    this.removeAnimation();
    this.mIsActive = isActive;
    if (isActive) {
      this.resetRemoveAnimation();
    }
  }

  get isActive() {
    return this.mIsActive;
  }

  @Input() item: Item;
  @Input() contentWidth: number;
  @Input() onSeek: OnSeekHandler;
  @Output() remove: EventEmitter<Item>;
  // TODO move / re-think - naivePagingMapper feels like a big ol' bodge
  private removeAnimation: TaskRemover;
  private hasProgressOnInit = false;
  private mIsActive: boolean;
  private mTimeline: Timeline;

  constructor(private renderLoop: RenderLoopService,
              private sanitizer: DomSanitizer) {
    this.remove = new EventEmitter<Item>();
  }

  ngOnInit(): void {
    this.resetRemoveAnimation();
    this.hasProgressOnInit = this.item.progress != null;
  }

  isLoading(): boolean {
    return this.hasProgressOnInit && this.item.progress < 100;
  }

  isAudioItem(): boolean {
    return this.item && isLoadedRootAudioItem(this.item);
  }

  isPending(): boolean {
    return this.item &&
      !isLoadedRootAudioItem(this.item) && !isExtractedAnalysisItem(this.item) &&
      (isPendingAnalysisItem(this.item) || isPendingRootAudioItem(this.item));
  }

  getFeatureShape(): HigherLevelFeatureShape | null {
    return !isPendingRootAudioItem(this.item) &&
    isExtractedAnalysisItem(this.item) ? this.item.shape : null;
  }

  getDuration(): number | null {
    if (isLoadedRootAudioItem(this.item)) {
      return this.item.audioData.duration;
    }
    if (isExtractedAnalysisItem(this.item)) {
      return this.item.parent.audioData.duration;
    }
  }

  getNextColour(): string {
    return defaultColourGenerator.next().value;
  }

  ngOnDestroy(): void {
    this.removeAnimation();
  }

  private sanitize(url: string) {
    return this.sanitizer.bypassSecurityTrustUrl(url);
  }

  private generateFilename(item: RootAudioItem): string {
    // TODO this is too brittle, and will often produce the wrong result
    // i.e. audio/mpeg results in .mpeg, when .mp3 is likely desired
    const mimeParts = item.mimeType ? item.mimeType.split('/') : [];
    const extension = mimeParts.length === 2 ? mimeParts[1] : '';
    return `${item.title}.${extension}`;
  }

  private resetRemoveAnimation(): void {
    if (this.removeAnimation) {
      this.removeAnimation();
    }
    const createPagingTask = () => {
      const pagingMapper = naivePagingMapper(this.timeline);
      return this.renderLoop.addPlayingTask(currentTime => {
        pagingMapper(currentTime);
      });
    };
    // only add a pager to audio items, it can drive the feature items
    const remover = this.timeline && this.isAudioItem() ?
      createPagingTask() : () => {};
    this.removeAnimation = () => {
      remover();
      this.removeAnimation = () => {};
    };
  }
}