Преглед изворни кода

fix(web): previous/next asset navigation (#3107)

* fix(web): previous/next asset navigation

* Apply suggestions from code review

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>

* Call setViewingAsset once

* Make code more readable

* Avoid recursive call

* Simplify return statement

* Set position of the bucket to Unknown

---------

Co-authored-by: Thomas <9749173+uhthomas@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
Sergey Kondrikov пре 2 година
родитељ
комит
37edef834e

+ 5 - 0
web/src/lib/models/asset-grid-state.ts

@@ -46,6 +46,11 @@ export class AssetGridState {
    */
   assets: AssetResponseDto[] = [];
 
+  /**
+   * Total assets that have been loaded along with additional data
+   */
+  loadedAssets: Record<string, number> = {};
+
   /**
    * User that owns assets
    */

+ 52 - 20
web/src/lib/stores/asset-interaction.store.ts

@@ -66,30 +66,62 @@ function createAssetInteractionStore() {
     isViewingAssetStoreState.set(isViewing);
   };
 
+  const getNextAsset = async (currentBucketIndex: number, assetId: string): Promise<AssetResponseDto | null> => {
+    const currentBucket = _assetGridState.buckets[currentBucketIndex];
+    const assetIndex = currentBucket.assets.findIndex(({ id }) => id == assetId);
+    if (assetIndex === -1) {
+      return null;
+    }
+
+    if (assetIndex + 1 < currentBucket.assets.length) {
+      return currentBucket.assets[assetIndex + 1];
+    }
+
+    const nextBucketIndex = currentBucketIndex + 1;
+    if (nextBucketIndex >= _assetGridState.buckets.length) {
+      return null;
+    }
+
+    const nextBucket = _assetGridState.buckets[nextBucketIndex];
+    await assetStore.getAssetsByBucket(nextBucket.bucketDate, BucketPosition.Unknown);
+
+    return nextBucket.assets[0] ?? null;
+  };
+
+  const getPrevAsset = async (currentBucketIndex: number, assetId: string): Promise<AssetResponseDto | null> => {
+    const currentBucket = _assetGridState.buckets[currentBucketIndex];
+    const assetIndex = currentBucket.assets.findIndex(({ id }) => id == assetId);
+    if (assetIndex === -1) {
+      return null;
+    }
+
+    if (assetIndex > 0) {
+      return currentBucket.assets[assetIndex - 1];
+    }
+
+    const prevBucketIndex = currentBucketIndex - 1;
+    if (prevBucketIndex < 0) {
+      return null;
+    }
+
+    const prevBucket = _assetGridState.buckets[prevBucketIndex];
+    await assetStore.getAssetsByBucket(prevBucket.bucketDate, BucketPosition.Unknown);
+
+    return prevBucket.assets[prevBucket.assets.length - 1] ?? null;
+  };
+
   const navigateAsset = async (direction: 'next' | 'previous') => {
-    let index = _assetGridState.assets.findIndex(({ id }) => id === _viewingAssetStoreState.id);
-
-    index = direction === 'next' ? index + 1 : index - 1;
-
-    const needMoreAbove = index < 0;
-    const needMoreBelow = index >= _assetGridState.assets.length;
-
-    // Try to load more assets if we're at the end.
-    if (needMoreAbove || needMoreBelow) {
-      for (const bucket of _assetGridState.buckets) {
-        if (bucket.assets.length === 0) {
-          await assetStore.getAssetsByBucket(
-            bucket.bucketDate,
-            needMoreAbove ? BucketPosition.Above : BucketPosition.Below,
-          );
-          navigateAsset(direction);
-          break;
-        }
-      }
+    const currentAssetId = _viewingAssetStoreState.id;
+    const currentBucketIndex = _assetGridState.loadedAssets[currentAssetId];
+    if (currentBucketIndex < 0 || currentBucketIndex >= _assetGridState.buckets.length) {
       return;
     }
 
-    const asset = _assetGridState.assets[index];
+    const asset =
+      direction === 'next'
+        ? await getNextAsset(currentBucketIndex, currentAssetId)
+        : await getPrevAsset(currentBucketIndex, currentAssetId);
+
     if (asset) {
       setViewingAsset(asset);
     }

+ 14 - 0
web/src/lib/stores/assets.store.ts

@@ -30,6 +30,15 @@ function createAssetStore() {
     return height;
   };
 
+  const refreshLoadedAssets = (state: AssetGridState): void => {
+    state.loadedAssets = {};
+    state.buckets.forEach((bucket, bucketIndex) =>
+      bucket.assets.map((asset) => {
+        state.loadedAssets[asset.id] = bucketIndex;
+      }),
+    );
+  };
+
   /**
    * Set initial state
    * @param viewportHeight
@@ -54,6 +63,7 @@ function createAssetStore() {
         position: BucketPosition.Unknown,
       })),
       assets: [],
+      loadedAssets: {},
       userId,
     });
 
@@ -101,6 +111,7 @@ function createAssetStore() {
         state.buckets[bucketIndex].assets = assets;
         state.buckets[bucketIndex].position = position;
         state.assets = state.buckets.flatMap((b) => b.assets);
+        refreshLoadedAssets(state);
         return state;
       });
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -123,6 +134,7 @@ function createAssetStore() {
         _removeBucket(state.buckets[bucketIndex].bucketDate);
       }
       state.assets = state.buckets.flatMap((b) => b.assets);
+      refreshLoadedAssets(state);
       return state;
     });
   };
@@ -132,6 +144,7 @@ function createAssetStore() {
       const bucketIndex = state.buckets.findIndex((b) => b.bucketDate === bucketDate);
       state.buckets.splice(bucketIndex, 1);
       state.assets = state.buckets.flatMap((b) => b.assets);
+      refreshLoadedAssets(state);
       return state;
     });
   };
@@ -180,6 +193,7 @@ function createAssetStore() {
       state.buckets[bucketIndex].assets[assetIndex].isFavorite = isFavorite;
 
       state.assets = state.buckets.flatMap((b) => b.assets);
+      refreshLoadedAssets(state);
       return state;
     });
   };