annotate src/app/notebook-feed/notebook-feed.component.ts @ 469:3a76205e06b6

Unload audio when stack is empty.
author Lucas Thompson <dev@lucas.im>
date Fri, 30 Jun 2017 15:56:43 +0100
parents 50f61d1945db
children 2142e7820706
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@464 44 }
dev@171 45 }
dev@464 46
dev@464 47 get analyses(): Item[] {
dev@464 48 return this.mAnalyses;
dev@464 49 }
dev@464 50
dev@348 51 @Input() onSeek: OnSeekHandler;
dev@456 52 @Output() removeItem: EventEmitter<Item>;
dev@171 53
dev@285 54 private resizeSubscription: Subscription;
dev@285 55 private width: number;
dev@285 56 private lastWidth: number;
dev@282 57 private timelines: Map<string, Timeline>;
dev@464 58 private mAnalyses: Item[];
dev@464 59 private currentAudioUri: string;
dev@181 60
dev@285 61 constructor(
dev@285 62 private ref: ChangeDetectorRef,
dev@464 63 @Inject('DimensionObservable') private onResize: Observable<Dimension>,
dev@464 64 private audioService: AudioPlayerService
dev@285 65 ) {
dev@456 66 this.removeItem = new EventEmitter<Item>();
dev@282 67 this.timelines = new Map();
dev@285 68 this.onResize.subscribe(dim => {
dev@285 69 this.lastWidth = this.width;
dev@285 70 this.width = dim.width;
dev@285 71 });
dev@285 72
dev@285 73 // the use of requestAnimationFrame here is to leave the dom updates
dev@285 74 // to a time convenient for the browser, and avoid a cascade / waterfall
dev@285 75 // of DOM changes for rapid resize events in the event handler above.
dev@285 76 // ..I'm not convinced this is particularly beneficial here // TODO
dev@285 77 const triggerChangeDetectionOnResize = () => {
dev@285 78 requestAnimationFrame(triggerChangeDetectionOnResize);
dev@285 79 if (this.width !== this.lastWidth) {
dev@285 80 ref.markForCheck(); // only trigger change detection if width changed
dev@285 81 }
dev@285 82 };
dev@285 83 requestAnimationFrame(triggerChangeDetectionOnResize);
dev@282 84 }
dev@282 85
dev@350 86 getOrCreateTimeline(item: Item): Timeline | void {
dev@282 87 if (!item.hasSharedTimeline) {
dev@282 88 return;
dev@282 89 }
dev@350 90 const uri = getRootUri(item);
dev@350 91 if (this.timelines.has(uri)) {
dev@350 92 return this.timelines.get(uri);
dev@282 93 } else {
dev@282 94 const timeline = new Waves.core.Timeline();
dev@350 95 this.timelines.set(uri, timeline);
dev@282 96 return timeline;
dev@282 97 }
dev@181 98 }
dev@285 99
dev@350 100 isAudioItem(item: Item): boolean {
dev@460 101 return isLoadedRootAudioItem(item);
dev@348 102 }
dev@348 103
dev@350 104 isActiveItem(item: Item): boolean {
dev@464 105 return this.getCurrentAudioUri() === getRootUri(item);
dev@350 106 }
dev@350 107
dev@350 108 getOnSeekForItem(item: Item): (timeSeconds: number) => any {
dev@348 109 return this.isActiveItem(item) ? this.onSeek : () => {};
dev@348 110 }
dev@348 111
dev@285 112 ngOnDestroy(): void {
dev@285 113 if (this.resizeSubscription) {
dev@285 114 this.resizeSubscription.unsubscribe();
dev@285 115 }
dev@285 116 }
dev@464 117
dev@464 118 private getCurrentAudioUri(): string {
dev@464 119 if (this.analyses.length === 0) {
dev@464 120 return '';
dev@464 121 }
dev@464 122 try {
dev@464 123 return getRootUri(this.analyses[0]);
dev@464 124 } catch (e) {
dev@464 125 return '';
dev@464 126 }
dev@464 127 }
dev@171 128 }