From d8c177a9e84769318963c96666575649980fd553 Mon Sep 17 00:00:00 2001
From: Xiao Gui <xgui3783@gmail.com>
Date: Fri, 2 Jun 2023 12:20:48 +0200
Subject: [PATCH] fix: wireframe querying

---
 .../core/space/boundingBox.directive.ts       |   3 +-
 src/features/category-acc.directive.ts        |   3 +-
 src/features/entry/entry.component.ts         | 159 ++++++++++--------
 src/features/voi-bbox.directive.ts            |   2 +-
 .../viewerCmp/viewerCmp.component.ts          |  11 ++
 .../viewerCmp/viewerCmp.template.html         |   4 +-
 6 files changed, 110 insertions(+), 72 deletions(-)

diff --git a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
index 72d0e2e66..ed1aa7731 100644
--- a/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
+++ b/src/atlasComponents/sapiViews/core/space/boundingBox.directive.ts
@@ -1,4 +1,4 @@
-import { Directive, Input, OnChanges } from "@angular/core";
+import { Directive, Input, OnChanges, Output } from "@angular/core";
 import { BehaviorSubject, Observable } from "rxjs";
 import { distinctUntilChanged } from "rxjs/operators";
 import { BoundingBox, SxplrTemplate, SxplrAtlas } from "src/atlasComponents/sapi/sxplrTypes"
@@ -60,6 +60,7 @@ export class SapiViewsCoreSpaceBoundingBox implements OnChanges{
     bbox: null
   })
 
+  @Output('sxplr-sapiviews-core-space-boundingbox-changed')
   public bbox$: Observable<{
     atlas: SxplrAtlas
     space: SxplrTemplate
diff --git a/src/features/category-acc.directive.ts b/src/features/category-acc.directive.ts
index cddba9e69..73156e7af 100644
--- a/src/features/category-acc.directive.ts
+++ b/src/features/category-acc.directive.ts
@@ -118,7 +118,8 @@ export class CategoryAccDirective implements AfterContentInit, OnDestroy {
           return this.datasource
         }),
       )
-    })
+    }),
+    shareReplay(1),
   )
 
   constructor(){
diff --git a/src/features/entry/entry.component.ts b/src/features/entry/entry.component.ts
index 979fea404..722fd8e99 100644
--- a/src/features/entry/entry.component.ts
+++ b/src/features/entry/entry.component.ts
@@ -1,13 +1,13 @@
 import { AfterViewInit, Component, OnDestroy, QueryList, ViewChildren } from '@angular/core';
 import { select, Store } from '@ngrx/store';
-import { distinctUntilChanged, map, scan, shareReplay, switchMap, tap } from 'rxjs/operators';
+import { debounceTime, distinctUntilChanged, map, scan, shareReplay, switchMap, withLatestFrom } from 'rxjs/operators';
 import { IDS, SAPI } from 'src/atlasComponents/sapi';
 import { Feature } from 'src/atlasComponents/sapi/sxplrTypes';
 import { FeatureBase } from '../base';
 import * as userInteraction from "src/state/userInteraction"
 import { atlasSelection } from 'src/state';
 import { CategoryAccDirective } from "../category-acc.directive"
-import { BehaviorSubject, combineLatest, concat, merge, of, Subscription } from 'rxjs';
+import { combineLatest, concat, forkJoin, merge, of, Subject, Subscription } from 'rxjs';
 import { DsExhausted, IsAlreadyPulling, PulledDataSource } from 'src/util/pullable';
 import { TranslatedFeature } from '../list/list.directive';
 
@@ -33,56 +33,63 @@ const categoryAcc = <T extends Record<string, unknown>>(categories: T[]) => {
 })
 export class EntryComponent extends FeatureBase implements AfterViewInit, OnDestroy {
 
-  private _features$ = new BehaviorSubject<TranslatedFeature[]>([])
-  features$ = this._features$.pipe(
-    shareReplay(1)
-  )
-
   @ViewChildren(CategoryAccDirective)
   catAccDirs: QueryList<CategoryAccDirective>
 
-  public busyTallying$ = new BehaviorSubject<boolean>(false)
-  public totals$ = new BehaviorSubject<number>(null)
-
   constructor(private sapi: SAPI, private store: Store) {
     super()
   }
 
   #subscriptions: Subscription[] = []
+  #catAccDirs = new Subject<CategoryAccDirective[]>()
+  features$ = this.#catAccDirs.pipe(
+    switchMap(dirs => concat(
+      of([] as TranslatedFeature[]),
+      merge(...dirs.map((dir, idx) =>
+        dir.datasource$.pipe(
+          switchMap(ds =>  ds.data$),
+          map(val => ({ val, idx }))
+        ))
+      ).pipe(
+        map(({ idx, val }) => ({ [idx.toString()]: val })),
+        scan((acc, curr) => ({ ...acc, ...curr })),
+        map(record => Object.values(record).flatMap(v => v))
+      )
+    )),
+    shareReplay(1),
+  )
 
-  private _busy$ = new BehaviorSubject<boolean>(false)
-  busy$ = this._busy$.pipe(
+  busy$ = this.#catAccDirs.pipe(
+    switchMap(dirs => combineLatest(
+      dirs.map(dir => dir.isBusy$)
+    )),
+    map(flags => flags.some(flag => flag)),
+    distinctUntilChanged(),
     shareReplay(1)
   )
 
-  ngOnDestroy(): void {
-    while (this.#subscriptions.length > 0) this.#subscriptions.pop().unsubscribe()
-  }
-  ngAfterViewInit(): void {
-    const catAccDirs$ = merge(
-      of(null),
-      this.catAccDirs.changes
-    ).pipe(
-      map(() => Array.from(this.catAccDirs))
-    )
-    this.#subscriptions.push(
-      catAccDirs$.pipe(
-        switchMap(dirs => combineLatest(
-          dirs.map(dir => dir.isBusy$)
-        )),
-        map(flags => flags.some(flag => flag)),
-        distinctUntilChanged(),
-      ).subscribe(value => {
-        this._busy$.next(value)
-      }),
-      catAccDirs$.pipe(
-        tap(() => this.busyTallying$.next(true)),
-        switchMap(catArrDirs => merge(
-          ...catArrDirs.map((dir, idx) => dir.total$.pipe(
-            map(val => ({ idx, val }))
-          ))
-        )),
-        
+  public busyTallying$ = this.#catAccDirs.pipe(
+    switchMap(arr => concat(
+      of(true),
+      forkJoin(
+        arr.map(dir => dir.total$)
+      ).pipe(
+        map(() => false)
+      )
+    )),
+    shareReplay(1)
+  )
+
+  public totals$ = this.#catAccDirs.pipe(
+    switchMap(arr => concat(
+      of(0),
+      merge(
+        ...arr.map((dir, idx) =>
+          dir.total$.pipe(
+            map(val => ({ val, idx }))
+          )
+        )
+      ).pipe(
         map(({ idx, val }) => ({ [idx.toString()]: val })),
         scan((acc, curr) => ({ ...acc, ...curr })),
         map(record => {
@@ -92,11 +99,47 @@ export class EntryComponent extends FeatureBase implements AfterViewInit, OnDest
           }
           return tally
         }),
-        tap(num => {
-          this.busyTallying$.next(false)
-          this.totals$.next(num)
-        }),
-      ).subscribe(),
+      )
+    ))
+  )
+
+  ngOnDestroy(): void {
+    while (this.#subscriptions.length > 0) this.#subscriptions.pop().unsubscribe()
+  }
+  ngAfterViewInit(): void {
+    this.#subscriptions.push(
+      merge(
+        of(null),
+        this.catAccDirs.changes
+      ).pipe(
+        map(() => Array.from(this.catAccDirs))
+      ).subscribe(dirs => this.#catAccDirs.next(dirs)),
+
+      this.#pullAll.pipe(
+        debounceTime(320),
+        withLatestFrom(this.#catAccDirs),
+        switchMap(([_, dirs]) => combineLatest(dirs.map(dir => dir.datasource$))),
+      ).subscribe(async dss => {
+        await Promise.all(
+          dss.map(async ds => {
+            // eslint-disable-next-line no-constant-condition
+            while (true) {
+              try {
+                await ds.pull()
+              } catch (e) {
+                if (e instanceof DsExhausted) {
+                  console.log('exhausted')
+                  break
+                }
+                if (e instanceof IsAlreadyPulling ) {
+                  continue
+                }
+                throw e
+              }
+            }
+          })
+        )
+      })
     )
   }
 
@@ -167,28 +210,8 @@ export class EntryComponent extends FeatureBase implements AfterViewInit, OnDest
     }
   }
 
-  async pullAll(){
-    const dss = Array.from(this.catAccDirs).map(catAcc => catAcc.datasource)
-
-    this._features$.next([])
-    await Promise.all(
-      dss.map(async ds => {
-        // eslint-disable-next-line no-constant-condition
-        while (true) {
-          try {
-            await ds.pull()
-          } catch (e) {
-            if (e instanceof DsExhausted) {
-              break
-            }
-            if (e instanceof IsAlreadyPulling ) {
-              continue
-            }
-            throw e
-          }
-        }
-      })
-    )
-    this._features$.next(dss.flatMap(ds => ds.finalValue))
+  #pullAll = new Subject()
+  pullAll(){
+    this.#pullAll.next(null)
   }
 }
diff --git a/src/features/voi-bbox.directive.ts b/src/features/voi-bbox.directive.ts
index d6ed2e0aa..f150225b9 100644
--- a/src/features/voi-bbox.directive.ts
+++ b/src/features/voi-bbox.directive.ts
@@ -47,7 +47,7 @@ export class VoiBboxDirective implements OnDestroy {
   
   @Input()
   set features(feats: Feature[]){
-    this.#voiFeatures = feats.filter(isVoiData)
+    this.#voiFeatures = (feats || []).filter(isVoiData)
     this.#features$.next(this.#voiFeatures)
   }
   get features(): VoiFeature[]{
diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts
index 136f0baf8..2dbd534db 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.component.ts
+++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts
@@ -14,6 +14,7 @@ import { atlasAppearance, atlasSelection, userInteraction } from "src/state";
 import { SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
 import { EntryComponent } from "src/features/entry/entry.component";
 import { TFace, TSandsPoint, getCoord } from "src/util/types";
+import { wait } from "src/util/fn";
 
 @Component({
   selector: 'iav-cmp-viewer-container',
@@ -438,4 +439,14 @@ export class ViewerCmp implements OnDestroy {
       })
     )
   }
+
+  @ViewChild('voiFeatureEntryCmp')
+  voiFeatureEntryCmp: EntryComponent
+
+  async pullAllVoi(){
+    if (this.voiFeatureEntryCmp){
+      await wait(320)
+      this.voiFeatureEntryCmp.pullAll()
+    }
+  }
 }
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 2ec0d9559..459fb19e8 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -1124,6 +1124,7 @@
     class="sxplr-pe-all mat-elevation-z8"
     [template]="view.selectedTemplate"
     [bbox]="bbox.bbox$ | async | getProperty : 'bbox'"
+    [attr.data-feature-length]="((voiFeatureEntryCmp.features$ | async) || []).length"
     #voiFeatureEntryCmp="featureEntryCmp">
   </sxplr-feature-entry>
 
@@ -1141,7 +1142,7 @@
     iav-switch
     [iav-switch-state]="false"
     #voiSwitch="iavSwitch"
-    (iav-switch-event)="$event && voiFeatureEntryCmp.pullAll()"
+    (iav-switch-event)="$event && pullAllVoi()"
     (click)="voiSwitch.toggle()">
 
     <ng-template [ngIf]="voiSwitch.switchState$ | async" [ngIfElse]="chevronCollapseTmpl">
@@ -1173,6 +1174,7 @@
 
 <div
   sxplr-sapiviews-core-space-boundingbox
+  (sxplr-sapiviews-core-space-boundingbox-changed)="pullAllVoi()"
   [sxplr-sapiviews-core-space-boundingbox-atlas]="selectedAtlas$ | async"
   [sxplr-sapiviews-core-space-boundingbox-space]="templateSelected$ | async"
   [sxplr-sapiviews-core-space-boundingbox-spec]="viewerCtx$ | async | nehubaVCtxToBbox"
-- 
GitLab