annotate src/app/notebook-feed/notebook-feed.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 2142e7820706
children
rev   line source
dev@171 1 /**
dev@171 2 * Created by lucast on 21/03/2017.
dev@171 3 */
dev@232 4 import {
dev@232 5 ChangeDetectionStrategy,
dev@285 6 ChangeDetectorRef,
dev@456 7 Component, EventEmitter,
dev@285 8 Inject,
dev@285 9 Input,
dev@456 10 OnDestroy, Output
dev@236 11 } from '@angular/core';
dev@289 12 import Waves from 'waves-ui-piper';
dev@350 13 import {
dev@350 14 getRootUri,
dev@460 15 isLoadedRootAudioItem,
dev@350 16 Item
dev@460 17 } from '../analysis-item/AnalysisItem';
dev@285 18 import {Observable} from 'rxjs/Observable';
dev@285 19 import {Dimension} from '../app.module';
dev@285 20 import {Subscription} from 'rxjs/Subscription';
dev@348 21 import {OnSeekHandler} from '../playhead/PlayHeadHelpers';
dev@464 22 import {AudioPlayerService} from '../services/audio-player/audio-player.service';
dev@171 23
dev@171 24 @Component({
dev@171 25 selector: 'ugly-notebook-feed',
dev@171 26 templateUrl: './notebook-feed.component.html',
dev@232 27 styleUrls: ['./notebook-feed.component.css'],
dev@232 28 changeDetection: ChangeDetectionStrategy.OnPush
dev@171 29 })
dev@285 30 export class NotebookFeedComponent implements OnDestroy {
dev@464 31 @Input() set analyses(analyses: Item[]) {
dev@464 32 const front = analyses[0];
dev@464 33 if (analyses !== this.mAnalyses) {
dev@464 34 if (front && getRootUri(front) !== this.currentAudioUri) {
dev@464 35 this.audioService.unload();
dev@464 36 this.audioService.loadAudioFromUri(getRootUri(front));
dev@469 37 } else if (!front) {
dev@469 38 this.audioService.unload();
dev@464 39 }
dev@464 40 }
dev@464 41 this.mAnalyses = analyses;
dev@464 42 if (front) {
dev@464 43 this.currentAudioUri = this.getCurrentAudioUri();
dev@470 44 } else {
dev@470 45 this.currentAudioUri = '';
dev@464 46 }
dev@171 47 }
dev@464 48
dev@464 49 get analyses(): Item[] {
dev@464 50 return this.mAnalyses;
dev@464 51 }
dev@464 52
dev@348 53 @Input() onSeek: OnSeekHandler;
dev@456 54 @Output() removeItem: EventEmitter<Item>;
dev@171 55
dev@285 56 private resizeSubscription: Subscription;
dev@285 57 private width: number;
dev@285 58 private lastWidth: number;
dev@282 59 private timelines: Map<string, Timeline>;
dev@464 60 private mAnalyses: Item[];
dev@464 61 private currentAudioUri: string;
dev@181 62
dev@285 63 constructor(
dev@285 64 private ref: ChangeDetectorRef,
dev@464 65 @Inject('DimensionObservable') private onResize: Observable<Dimension>,
dev@464 66 private audioService: AudioPlayerService
dev@285 67 ) {
dev@456 68 this.removeItem = new EventEmitter<Item>();
dev@282 69 this.timelines = new Map();
dev@285 70 this.onResize.subscribe(dim => {
dev@285 71 this.lastWidth = this.width;
dev@285 72 this.width = dim.width;
dev@285 73 });
dev@285 74
dev@285 75 // the use of requestAnimationFrame here is to leave the dom updates
dev@285 76 // to a time convenient for the browser, and avoid a cascade / waterfall
dev@285 77 // of DOM changes for rapid resize events in the event handler above.
dev@285 78 // ..I'm not convinced this is particularly beneficial here // TODO
dev@285 79 const triggerChangeDetectionOnResize = () => {
dev@285 80 requestAnimationFrame(triggerChangeDetectionOnResize);
dev@285 81 if (this.width !== this.lastWidth) {
dev@285 82 ref.markForCheck(); // only trigger change detection if width changed
dev@285 83 }
dev@285 84 };
dev@285 85 requestAnimationFrame(triggerChangeDetectionOnResize);
dev@282 86 }
dev@282 87
dev@350 88 getOrCreateTimeline(item: Item): Timeline | void {
dev@282 89 if (!item.hasSharedTimeline) {
dev@282 90 return;
dev@282 91 }
dev@350 92 const uri = getRootUri(item);
dev@350 93 if (this.timelines.has(uri)) {
dev@350 94 return this.timelines.get(uri);
dev@282 95 } else {
dev@282 96 const timeline = new Waves.core.Timeline();
dev@350 97 this.timelines.set(uri, timeline);
dev@282 98 return timeline;
dev@282 99 }
dev@181 100 }
dev@285 101
dev@350 102 isAudioItem(item: Item): boolean {
dev@460 103 return isLoadedRootAudioItem(item);
dev@348 104 }
dev@348 105
dev@350 106 isActiveItem(item: Item): boolean {
dev@464 107 return this.getCurrentAudioUri() === getRootUri(item);
dev@350 108 }
dev@350 109
dev@350 110 getOnSeekForItem(item: Item): (timeSeconds: number) => any {
dev@348 111 return this.isActiveItem(item) ? this.onSeek : () => {};
dev@348 112 }
dev@348 113
dev@285 114 ngOnDestroy(): void {
dev@285 115 if (this.resizeSubscription) {
dev@285 116 this.resizeSubscription.unsubscribe();
dev@285 117 }
dev@285 118 }
dev@464 119
dev@464 120 private getCurrentAudioUri(): string {
dev@464 121 if (this.analyses.length === 0) {
dev@464 122 return '';
dev@464 123 }
dev@464 124 try {
dev@464 125 return getRootUri(this.analyses[0]);
dev@464 126 } catch (e) {
dev@464 127 return '';
dev@464 128 }
dev@464 129 }
dev@171 130 }