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 }
|