changeset 464:50f61d1945db

Hook up some buttons for navigating history (undo / redo). Some refactoring to allow for the audio player to get updated as a consequence of a state change (the audio related to the current top of the stack is used).
author Lucas Thompson <dev@lucas.im>
date Fri, 30 Jun 2017 14:01:22 +0100
parents c9c6b01e9b4f
children caef9a302bec
files src/app/app.component.html src/app/app.component.ts src/app/notebook-feed/notebook-feed.component.ts
diffstat 3 files changed, 64 insertions(+), 56 deletions(-) [+]
line wrap: on
line diff
--- a/src/app/app.component.html	Fri Jun 30 13:59:51 2017 +0100
+++ b/src/app/app.component.html	Fri Jun 30 14:01:22 2017 +0100
@@ -17,10 +17,15 @@
       <ugly-audio-file-open
         (fileOpened)="onFileOpened($event); tray.close()"
       ></ugly-audio-file-open>
-      <!-- menu opens when trigger button is clicked -->
       <button md-icon-button (click)="tray.toggle()">
         <md-icon>extension</md-icon>
       </button>
+      <button md-icon-button (click)="analyses.stepBack()">
+        <md-icon>undo</md-icon>
+      </button>
+      <button md-icon-button (click)="analyses.stepForward()">
+        <md-icon>redo</md-icon>
+      </button>
     </md-toolbar>
   </div>
 
@@ -35,7 +40,6 @@
     <ugly-notebook-feed
       (removeItem)="removeItem($event)"
       [analyses]="analyses.toIterable()"
-      [rootAudioUri]="rootAudioItem.uri"
       [onSeek]="onSeek"></ugly-notebook-feed>
   </div>
 </div>
--- a/src/app/app.component.ts	Fri Jun 30 13:59:51 2017 +0100
+++ b/src/app/app.component.ts	Fri Jun 30 14:01:22 2017 +0100
@@ -19,7 +19,7 @@
   isLoadedRootAudioItem,
   Item,
   RootAudioItem,
-  LoadedRootAudioItem
+  getRootAudioItem
 } from './analysis-item/AnalysisItem';
 import {OnSeekHandler} from './playhead/PlayHeadHelpers';
 import {UrlResourceLifetimeManager} from './app.module';
@@ -38,7 +38,6 @@
   private analyses: PersistentStack<Item>; // TODO some immutable state container describing entire session
   private nRecordings: number; // TODO user control for naming a recording
   private countingId: number; // TODO improve uniquely identifying items
-  private rootAudioItem: LoadedRootAudioItem;
   private onSeek: OnSeekHandler;
 
   constructor(private audioService: AudioPlayerService,
@@ -53,7 +52,6 @@
     this.nRecordings = 0;
     this.countingId = 0;
     this.onSeek = (time) => this.audioService.seekTo(time);
-    this.rootAudioItem = {} as any; // TODO eugh
 
     iconRegistry.addSvgIcon(
       'duck',
@@ -67,11 +65,12 @@
           this.analyses.shift();
           this.canExtract = false;
         } else {
-          this.rootAudioItem.audioData = (resource as AudioResource).samples;
-          if (this.rootAudioItem.audioData) {
+          const audioData = (resource as AudioResource).samples;
+          if (audioData) {
+            const rootAudio = getRootAudioItem(this.analyses.get(0));
             this.canExtract = true;
             const currentRootIndex = this.analyses.findIndex(val => {
-              return isLoadedRootAudioItem(val) && val.uri === this.rootAudioItem.uri;
+              return isPendingRootAudioItem(val) && val.uri === rootAudio.uri;
             });
             if (currentRootIndex !== -1) {
               this.analyses.set(
@@ -79,7 +78,7 @@
                 Object.assign(
                   {},
                   this.analyses.get(currentRootIndex),
-                  {audioData: this.rootAudioItem.audioData}
+                  {audioData}
                 )
               );
             }
@@ -130,7 +129,6 @@
       mimeType: file.type,
       isExportable: createExportableItem
     } as RootAudioItem;
-    this.rootAudioItem = pending as LoadedRootAudioItem; // TODO this is silly
 
     // TODO re-ordering of items for display
     // , one alternative is a Angular Pipe / Filter for use in the Template
@@ -144,19 +142,24 @@
 
     this.canExtract = false;
 
-    const placeholderCard: AnalysisItem = {
-      parent: this.rootAudioItem,
-      hasSharedTimeline: true,
-      extractorKey: outputInfo.extractorKey,
-      outputId: outputInfo.outputId,
-      title: outputInfo.name,
-      description: outputInfo.outputId,
-      id: `${++this.countingId}`,
-      progress: 0
-    };
-    this.analyses.unshift(placeholderCard);
-    this.sendExtractionRequest(placeholderCard);
-    return placeholderCard.id;
+    const rootAudio = getRootAudioItem(this.analyses.get(0));
+
+    if (isLoadedRootAudioItem(rootAudio)) {
+      const placeholderCard: AnalysisItem = {
+        parent: rootAudio,
+        hasSharedTimeline: true,
+        extractorKey: outputInfo.extractorKey,
+        outputId: outputInfo.outputId,
+        title: outputInfo.name,
+        description: outputInfo.outputId,
+        id: `${++this.countingId}`,
+        progress: 0
+      };
+      this.analyses.unshift(placeholderCard);
+      this.sendExtractionRequest(placeholderCard);
+      return placeholderCard.id;
+    }
+    throw new Error('Cannot extract. No audio loaded');
   }
 
   removeItem(item: Item): void {
@@ -170,30 +173,6 @@
         return toRemove;
       }, []);
     this.analyses.remove(...indicesToRemove);
-    if (isPendingRootAudioItem(item)) {
-      if (this.rootAudioItem.uri === item.uri) {
-        this.audioService.unload();
-        const topItem = this.analyses.get(0);
-        const nullRootAudio: LoadedRootAudioItem = {uri: ''} as any; // TODO eugh
-
-        if (topItem) {
-          if (isPendingAnalysisItem(topItem)) {
-            this.rootAudioItem = topItem.parent as LoadedRootAudioItem;
-          } else if (isPendingRootAudioItem(topItem)) {
-            this.rootAudioItem = topItem as LoadedRootAudioItem;
-          } else {
-           this.rootAudioItem = nullRootAudio;
-          }
-        } else {
-          this.rootAudioItem = nullRootAudio;
-        }
-        if (this.rootAudioItem) {
-          this.audioService.loadAudioFromUri(this.rootAudioItem.uri);
-        }
-      } else {
-        this.resourceManager.revokeUrlToResource(item.uri);
-      }
-    }
   }
 
   ngOnDestroy(): void {
--- a/src/app/notebook-feed/notebook-feed.component.ts	Fri Jun 30 13:59:51 2017 +0100
+++ b/src/app/notebook-feed/notebook-feed.component.ts	Fri Jun 30 14:01:22 2017 +0100
@@ -19,6 +19,7 @@
 import {Dimension} from '../app.module';
 import {Subscription} from 'rxjs/Subscription';
 import {OnSeekHandler} from '../playhead/PlayHeadHelpers';
+import {AudioPlayerService} from '../services/audio-player/audio-player.service';
 
 @Component({
   selector: 'ugly-notebook-feed',
@@ -27,25 +28,38 @@
   changeDetection: ChangeDetectionStrategy.OnPush
 })
 export class NotebookFeedComponent implements OnDestroy {
-  @Input() analyses: Item[];
-  @Input() set rootAudioUri(uri: string) {
-    this._rootAudioUri = uri;
+  @Input() set analyses(analyses: Item[]) {
+    const front = analyses[0];
+    if (analyses !== this.mAnalyses) {
+      if (front && getRootUri(front) !== this.currentAudioUri) {
+        this.audioService.unload();
+        this.audioService.loadAudioFromUri(getRootUri(front));
+      }
+    }
+    this.mAnalyses = analyses;
+    if (front) {
+      this.currentAudioUri = this.getCurrentAudioUri();
+    }
   }
+
+  get analyses(): Item[] {
+    return this.mAnalyses;
+  }
+
   @Input() onSeek: OnSeekHandler;
   @Output() removeItem: EventEmitter<Item>;
 
-  get rootAudioUri(): string {
-    return this._rootAudioUri;
-  }
-  private _rootAudioUri: string;
   private resizeSubscription: Subscription;
   private width: number;
   private lastWidth: number;
   private timelines: Map<string, Timeline>;
+  private mAnalyses: Item[];
+  private currentAudioUri: string;
 
   constructor(
     private ref: ChangeDetectorRef,
-    @Inject('DimensionObservable') private onResize: Observable<Dimension>
+    @Inject('DimensionObservable') private onResize: Observable<Dimension>,
+    private audioService: AudioPlayerService
   ) {
     this.removeItem = new EventEmitter<Item>();
     this.timelines = new Map();
@@ -86,7 +100,7 @@
   }
 
   isActiveItem(item: Item): boolean {
-    return this.rootAudioUri === getRootUri(item);
+    return this.getCurrentAudioUri() === getRootUri(item);
   }
 
   getOnSeekForItem(item: Item): (timeSeconds: number) => any {
@@ -98,4 +112,15 @@
       this.resizeSubscription.unsubscribe();
     }
   }
+
+  private getCurrentAudioUri(): string {
+    if (this.analyses.length === 0) {
+      return '';
+    }
+    try {
+      return getRootUri(this.analyses[0]);
+    } catch (e) {
+      return '';
+    }
+  }
 }