Merge branch 'immich-app:main' into feat-web-set-asset-as-profile-image

This commit is contained in:
faupau
2023-07-06 20:48:35 +02:00
committed by GitHub
103 changed files with 22119 additions and 221 deletions
+69 -29
View File
@@ -1,6 +1,5 @@
import { AssetGridState, BucketPosition } from '$lib/models/asset-grid-state';
import { api, AssetResponseDto } from '@api';
import { sortBy } from 'lodash-es';
import { derived, writable } from 'svelte/store';
import { assetGridState, assetStore } from './assets.store';
@@ -13,6 +12,7 @@ export const assetsInAlbumStoreState = writable<AssetResponseDto[]>([]);
export const selectedAssets = writable<Set<AssetResponseDto>>(new Set());
export const selectedGroup = writable<Set<string>>(new Set());
export const isMultiSelectStoreState = derived(selectedAssets, ($selectedAssets) => $selectedAssets.size > 0);
export const assetSelectionCandidates = writable<Set<AssetResponseDto>>(new Set());
function createAssetInteractionStore() {
let _assetGridState = new AssetGridState();
@@ -20,8 +20,7 @@ function createAssetInteractionStore() {
let _selectedAssets: Set<AssetResponseDto>;
let _selectedGroup: Set<string>;
let _assetsInAlbums: AssetResponseDto[];
let savedAssetLength = 0;
let assetSortedByDate: AssetResponseDto[] = [];
let _assetSelectionCandidates: Set<AssetResponseDto>;
// Subscriber
assetGridState.subscribe((state) => {
@@ -44,6 +43,10 @@ function createAssetInteractionStore() {
_assetsInAlbums = assets;
});
assetSelectionCandidates.subscribe((assets) => {
_assetSelectionCandidates = assets;
});
// Methods
/**
@@ -63,41 +66,64 @@ function createAssetInteractionStore() {
isViewingAssetStoreState.set(isViewing);
};
const navigateAsset = async (direction: 'next' | 'previous') => {
// Flatten and sort the asset by date if there are new assets
if (assetSortedByDate.length === 0 || savedAssetLength !== _assetGridState.assets.length) {
assetSortedByDate = sortBy(_assetGridState.assets, (a) => a.fileCreatedAt);
savedAssetLength = _assetGridState.assets.length;
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;
}
// Find the index of the current asset
const currentIndex = assetSortedByDate.findIndex((a) => a.id === _viewingAssetStoreState.id);
if (assetIndex + 1 < currentBucket.assets.length) {
return currentBucket.assets[assetIndex + 1];
}
// Get the next or previous asset
const nextIndex = direction === 'previous' ? currentIndex + 1 : currentIndex - 1;
const nextBucketIndex = currentBucketIndex + 1;
if (nextBucketIndex >= _assetGridState.buckets.length) {
return null;
}
// Run out of asset, this might be because there is no asset in the next bucket.
if (nextIndex == -1) {
let nextBucket = '';
// Find next bucket that doesn't have all assets loaded
const nextBucket = _assetGridState.buckets[nextBucketIndex];
await assetStore.getAssetsByBucket(nextBucket.bucketDate, BucketPosition.Unknown);
for (const bucket of _assetGridState.buckets) {
if (bucket.assets.length === 0) {
nextBucket = bucket.bucketDate;
break;
}
}
return nextBucket.assets[0] ?? null;
};
if (nextBucket !== '') {
await assetStore.getAssetsByBucket(nextBucket, BucketPosition.Below);
navigateAsset(direction);
}
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') => {
const currentAssetId = _viewingAssetStoreState.id;
const currentBucketIndex = _assetGridState.loadedAssets[currentAssetId];
if (currentBucketIndex < 0 || currentBucketIndex >= _assetGridState.buckets.length) {
return;
}
const nextAsset = assetSortedByDate[nextIndex];
if (nextAsset) {
setViewingAsset(nextAsset);
const asset =
direction === 'next'
? await getNextAsset(currentBucketIndex, currentAssetId)
: await getPrevAsset(currentBucketIndex, currentAssetId);
if (asset) {
setViewingAsset(asset);
}
};
@@ -129,14 +155,26 @@ function createAssetInteractionStore() {
selectedGroup.set(_selectedGroup);
};
const setAssetSelectionCandidates = (assets: AssetResponseDto[]) => {
_assetSelectionCandidates = new Set(assets);
assetSelectionCandidates.set(_assetSelectionCandidates);
};
const clearAssetSelectionCandidates = () => {
_assetSelectionCandidates.clear();
assetSelectionCandidates.set(_assetSelectionCandidates);
};
const clearMultiselect = () => {
_selectedAssets.clear();
_selectedGroup.clear();
_assetSelectionCandidates.clear();
_assetsInAlbums = [];
selectedAssets.set(_selectedAssets);
selectedGroup.set(_selectedGroup);
assetsInAlbumStoreState.set(_assetsInAlbums);
assetSelectionCandidates.set(_assetSelectionCandidates);
};
return {
@@ -148,6 +186,8 @@ function createAssetInteractionStore() {
removeAssetFromMultiselectGroup,
addGroupToMultiselectGroup,
removeGroupFromMultiselectGroup,
setAssetSelectionCandidates,
clearAssetSelectionCandidates,
clearMultiselect,
};
}
+19 -6
View File
@@ -1,6 +1,5 @@
import { AssetGridState, BucketPosition } from '$lib/models/asset-grid-state';
import { api, AssetCountByTimeBucketResponseDto } from '@api';
import { flatMap, sumBy } from 'lodash-es';
import { writable } from 'svelte/store';
/**
@@ -31,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
@@ -55,12 +63,13 @@ function createAssetStore() {
position: BucketPosition.Unknown,
})),
assets: [],
loadedAssets: {},
userId,
});
// Update timeline height based on calculated bucket height
assetGridState.update((state) => {
state.timelineHeight = sumBy(state.buckets, (d) => d.bucketHeight);
state.timelineHeight = state.buckets.reduce((acc, b) => acc + b.bucketHeight, 0);
return state;
});
};
@@ -101,7 +110,8 @@ function createAssetStore() {
const bucketIndex = state.buckets.findIndex((b) => b.bucketDate === bucket);
state.buckets[bucketIndex].assets = assets;
state.buckets[bucketIndex].position = position;
state.assets = flatMap(state.buckets, (b) => b.assets);
state.assets = state.buckets.flatMap((b) => b.assets);
refreshLoadedAssets(state);
return state;
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -123,7 +133,8 @@ function createAssetStore() {
if (state.buckets[bucketIndex].assets.length === 0) {
_removeBucket(state.buckets[bucketIndex].bucketDate);
}
state.assets = flatMap(state.buckets, (b) => b.assets);
state.assets = state.buckets.flatMap((b) => b.assets);
refreshLoadedAssets(state);
return state;
});
};
@@ -132,7 +143,8 @@ function createAssetStore() {
assetGridState.update((state) => {
const bucketIndex = state.buckets.findIndex((b) => b.bucketDate === bucketDate);
state.buckets.splice(bucketIndex, 1);
state.assets = flatMap(state.buckets, (b) => b.assets);
state.assets = state.buckets.flatMap((b) => b.assets);
refreshLoadedAssets(state);
return state;
});
};
@@ -180,7 +192,8 @@ function createAssetStore() {
const assetIndex = state.buckets[bucketIndex].assets.findIndex((a) => a.id === assetId);
state.buckets[bucketIndex].assets[assetIndex].isFavorite = isFavorite;
state.assets = flatMap(state.buckets, (b) => b.assets);
state.assets = state.buckets.flatMap((b) => b.assets);
refreshLoadedAssets(state);
return state;
});
};