mirror of
https://github.com/immich-app/immich.git
synced 2025-06-20 17:03:14 +02:00
minimize sorting
This commit is contained in:
parent
c9728a107e
commit
fa216b55d7
1 changed files with 70 additions and 29 deletions
|
@ -24,11 +24,13 @@ import { SvelteSet } from 'svelte/reactivity';
|
||||||
import { get, writable, type Unsubscriber } from 'svelte/store';
|
import { get, writable, type Unsubscriber } from 'svelte/store';
|
||||||
import { handleError } from '../utils/handle-error';
|
import { handleError } from '../utils/handle-error';
|
||||||
import { websocketEvents } from './websocket';
|
import { websocketEvents } from './websocket';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
TIMELINE: { INTERSECTION_EXPAND_TOP, INTERSECTION_EXPAND_BOTTOM },
|
TIMELINE: { INTERSECTION_EXPAND_TOP, INTERSECTION_EXPAND_BOTTOM },
|
||||||
} = TUNABLES;
|
} = TUNABLES;
|
||||||
|
|
||||||
type AssetApiGetTimeBucketsRequest = Parameters<typeof getTimeBuckets>[0];
|
type AssetApiGetTimeBucketsRequest = Parameters<typeof getTimeBuckets>[0];
|
||||||
|
|
||||||
export type AssetStoreOptions = Omit<AssetApiGetTimeBucketsRequest, 'size'> & {
|
export type AssetStoreOptions = Omit<AssetApiGetTimeBucketsRequest, 'size'> & {
|
||||||
timelineAlbumId?: string;
|
timelineAlbumId?: string;
|
||||||
deferInit?: boolean;
|
deferInit?: boolean;
|
||||||
|
@ -85,6 +87,7 @@ export type TimelineAsset = {
|
||||||
country: string | null;
|
country: string | null;
|
||||||
people: string[];
|
people: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
class IntersectingAsset {
|
class IntersectingAsset {
|
||||||
// --- public ---
|
// --- public ---
|
||||||
readonly #group: AssetDateGroup;
|
readonly #group: AssetDateGroup;
|
||||||
|
@ -116,9 +119,11 @@ class IntersectingAsset {
|
||||||
this.asset = asset;
|
this.asset = asset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssetOperation = (asset: TimelineAsset) => { remove: boolean };
|
type AssetOperation = (asset: TimelineAsset) => { remove: boolean };
|
||||||
|
|
||||||
type MoveAsset = { asset: TimelineAsset; year: number; month: number };
|
type MoveAsset = { asset: TimelineAsset; year: number; month: number };
|
||||||
|
|
||||||
export class AssetDateGroup {
|
export class AssetDateGroup {
|
||||||
// --- public
|
// --- public
|
||||||
readonly bucket: AssetBucket;
|
readonly bucket: AssetBucket;
|
||||||
|
@ -161,6 +166,7 @@ export class AssetDateGroup {
|
||||||
getFirstAsset() {
|
getFirstAsset() {
|
||||||
return this.intersetingAssets[0]?.asset;
|
return this.intersetingAssets[0]?.asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRandomAsset() {
|
getRandomAsset() {
|
||||||
const random = Math.floor(Math.random() * this.intersetingAssets.length);
|
const random = Math.floor(Math.random() * this.intersetingAssets.length);
|
||||||
return this.intersetingAssets[random];
|
return this.intersetingAssets[random];
|
||||||
|
@ -238,6 +244,7 @@ export interface Viewport {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ViewportXY = Viewport & {
|
export type ViewportXY = Viewport & {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
|
@ -245,23 +252,47 @@ export type ViewportXY = Viewport & {
|
||||||
|
|
||||||
class AddContext {
|
class AddContext {
|
||||||
lookupCache: {
|
lookupCache: {
|
||||||
[dayOfMonth: number]: AssetDateGroup;
|
[year: number]: { [month: number]: { [day: number]: AssetDateGroup } };
|
||||||
} = {};
|
} = {};
|
||||||
unprocessedAssets: TimelineAsset[] = [];
|
unprocessedAssets: TimelineAsset[] = [];
|
||||||
changedDateGroups = new Set<AssetDateGroup>();
|
changedDateGroups = new Set<AssetDateGroup>();
|
||||||
newDateGroups = new Set<AssetDateGroup>();
|
newDateGroups = new Set<AssetDateGroup>();
|
||||||
sort(bucket: AssetBucket, sortOrder: AssetOrder = AssetOrder.Desc) {
|
|
||||||
|
getDateGroup(year: number, month: number, day: number): AssetDateGroup | undefined {
|
||||||
|
return this.lookupCache[year]?.[month]?.[day];
|
||||||
|
}
|
||||||
|
|
||||||
|
setDateGroup(dateGroup: AssetDateGroup, year: number, month: number, day: number) {
|
||||||
|
if (!this.lookupCache[year]) {
|
||||||
|
this.lookupCache[year] = {};
|
||||||
|
}
|
||||||
|
if (!this.lookupCache[year][month]) {
|
||||||
|
this.lookupCache[year][month] = {};
|
||||||
|
}
|
||||||
|
this.lookupCache[year][month][day] = dateGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
get existingDateGroups() {
|
||||||
|
return this.changedDateGroups.difference(this.newDateGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
get updatedBuckets() {
|
||||||
|
const updated = new Set<AssetBucket>();
|
||||||
for (const group of this.changedDateGroups) {
|
for (const group of this.changedDateGroups) {
|
||||||
group.sortAssets(sortOrder);
|
updated.add(group.bucket);
|
||||||
}
|
}
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
get bucketsWithNewDateGroups() {
|
||||||
|
const updated = new Set<AssetBucket>();
|
||||||
for (const group of this.newDateGroups) {
|
for (const group of this.newDateGroups) {
|
||||||
group.sortAssets(sortOrder);
|
updated.add(group.bucket);
|
||||||
}
|
|
||||||
if (this.newDateGroups.size > 0) {
|
|
||||||
bucket.sortDateGroups();
|
|
||||||
}
|
}
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AssetBucket {
|
export class AssetBucket {
|
||||||
// --- public ---
|
// --- public ---
|
||||||
#intersecting: boolean = $state(false);
|
#intersecting: boolean = $state(false);
|
||||||
|
@ -326,6 +357,7 @@ export class AssetBucket {
|
||||||
this.handleLoadError,
|
this.handleLoadError,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
set intersecting(newValue: boolean) {
|
set intersecting(newValue: boolean) {
|
||||||
const old = this.#intersecting;
|
const old = this.#intersecting;
|
||||||
if (old !== newValue) {
|
if (old !== newValue) {
|
||||||
|
@ -449,7 +481,14 @@ export class AssetBucket {
|
||||||
this.addTimelineAsset(timelineAsset, addContext);
|
this.addTimelineAsset(timelineAsset, addContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
addContext.sort(this, this.#sortOrder);
|
for (const group of addContext.existingDateGroups) {
|
||||||
|
group.sortAssets(this.#sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addContext.newDateGroups.size > 0) {
|
||||||
|
this.sortDateGroups();
|
||||||
|
}
|
||||||
|
|
||||||
return addContext.unprocessedAssets;
|
return addContext.unprocessedAssets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,32 +501,29 @@ export class AssetBucket {
|
||||||
|
|
||||||
if (this.month === month && this.year === year) {
|
if (this.month === month && this.year === year) {
|
||||||
const day = date.get('day');
|
const day = date.get('day');
|
||||||
let dateGroup: AssetDateGroup | undefined = addContext.lookupCache[day];
|
let dateGroup = addContext.getDateGroup(year, month, day);
|
||||||
if (!dateGroup) {
|
if (!dateGroup) {
|
||||||
dateGroup = this.findDateGroupByDay(day);
|
dateGroup = this.findDateGroupByDay(day);
|
||||||
if (dateGroup) {
|
if (dateGroup) {
|
||||||
addContext.lookupCache[day] = dateGroup;
|
addContext.setDateGroup(dateGroup, year, month, day);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dateGroup) {
|
if (dateGroup) {
|
||||||
const intersectingAsset = new IntersectingAsset(dateGroup, timelineAsset);
|
const intersectingAsset = new IntersectingAsset(dateGroup, timelineAsset);
|
||||||
if (dateGroup.intersetingAssets.some((a) => a.id === id)) {
|
|
||||||
console.error(`Ignoring attempt to add duplicate asset ${id} to ${dateGroup.groupTitle}`);
|
|
||||||
} else {
|
|
||||||
dateGroup.intersetingAssets.push(intersectingAsset);
|
dateGroup.intersetingAssets.push(intersectingAsset);
|
||||||
addContext.changedDateGroups.add(dateGroup);
|
addContext.changedDateGroups.add(dateGroup);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
dateGroup = new AssetDateGroup(this, this.dateGroups.length, date, day);
|
dateGroup = new AssetDateGroup(this, this.dateGroups.length, date, day);
|
||||||
dateGroup.intersetingAssets.push(new IntersectingAsset(dateGroup, timelineAsset));
|
dateGroup.intersetingAssets.push(new IntersectingAsset(dateGroup, timelineAsset));
|
||||||
this.dateGroups.push(dateGroup);
|
this.dateGroups.push(dateGroup);
|
||||||
addContext.lookupCache[day] = dateGroup;
|
addContext.setDateGroup(dateGroup, year, month, day);
|
||||||
addContext.newDateGroups.add(dateGroup);
|
addContext.newDateGroups.add(dateGroup);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addContext.unprocessedAssets.push(timelineAsset);
|
addContext.unprocessedAssets.push(timelineAsset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getRandomDateGroup() {
|
getRandomDateGroup() {
|
||||||
const random = Math.floor(Math.random() * this.dateGroups.length);
|
const random = Math.floor(Math.random() * this.dateGroups.length);
|
||||||
return this.dateGroups[random];
|
return this.dateGroups[random];
|
||||||
|
@ -536,6 +572,7 @@ export class AssetBucket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get bucketHeight() {
|
get bucketHeight() {
|
||||||
return this.#bucketHeight;
|
return this.#bucketHeight;
|
||||||
}
|
}
|
||||||
|
@ -1031,6 +1068,7 @@ export class AssetStore {
|
||||||
rowWidth: Math.floor(viewportWidth),
|
rowWidth: Math.floor(viewportWidth),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateGeometry(bucket: AssetBucket, invalidateHeight: boolean) {
|
#updateGeometry(bucket: AssetBucket, invalidateHeight: boolean) {
|
||||||
if (invalidateHeight) {
|
if (invalidateHeight) {
|
||||||
bucket.isBucketHeightActual = false;
|
bucket.isBucketHeightActual = false;
|
||||||
|
@ -1183,9 +1221,9 @@ export class AssetStore {
|
||||||
if (assets.length === 0) {
|
if (assets.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const updatedBuckets = new Set<AssetBucket>();
|
|
||||||
const updatedDateGroups = new Set<AssetDateGroup>();
|
|
||||||
|
|
||||||
|
const addContext = new AddContext();
|
||||||
|
const bucketCount = this.buckets.length;
|
||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
const utc = DateTime.fromISO(asset.localDateTime).toUTC().startOf('month');
|
const utc = DateTime.fromISO(asset.localDateTime).toUTC().startOf('month');
|
||||||
const year = utc.get('year');
|
const year = utc.get('year');
|
||||||
|
@ -1196,21 +1234,24 @@ export class AssetStore {
|
||||||
bucket = new AssetBucket(this, utc, 1, this.#options.order);
|
bucket = new AssetBucket(this, utc, 1, this.#options.order);
|
||||||
this.buckets.push(bucket);
|
this.buckets.push(bucket);
|
||||||
}
|
}
|
||||||
const addContext = new AddContext();
|
|
||||||
bucket.addTimelineAsset(asset, addContext);
|
bucket.addTimelineAsset(asset, addContext);
|
||||||
addContext.sort(bucket, this.#options.order);
|
|
||||||
updatedBuckets.add(bucket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.buckets.length !== bucketCount) {
|
||||||
this.buckets.sort((a, b) => {
|
this.buckets.sort((a, b) => {
|
||||||
return a.year === b.year ? b.month - a.month : b.year - a.year;
|
return a.year === b.year ? b.month - a.month : b.year - a.year;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const dateGroup of updatedDateGroups) {
|
|
||||||
dateGroup.sortAssets(this.#options.order);
|
|
||||||
}
|
}
|
||||||
for (const bucket of updatedBuckets) {
|
|
||||||
|
for (const group of addContext.existingDateGroups) {
|
||||||
|
group.sortAssets(this.#options.order);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const bucket of addContext.bucketsWithNewDateGroups) {
|
||||||
bucket.sortDateGroups();
|
bucket.sortDateGroups();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const bucket of addContext.updatedBuckets) {
|
||||||
this.#updateGeometry(bucket, true);
|
this.#updateGeometry(bucket, true);
|
||||||
}
|
}
|
||||||
this.updateIntersections();
|
this.updateIntersections();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue