timeline: join day calls (fix #84)
parent
40ab7cbf2b
commit
866376c912
|
@ -175,6 +175,10 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
||||||
|
|
||||||
/** Set of dayIds for which images loaded */
|
/** Set of dayIds for which images loaded */
|
||||||
private loadedDays = new Set<number>();
|
private loadedDays = new Set<number>();
|
||||||
|
/** Days to load in the next call */
|
||||||
|
private fetchDayQueue = [] as number[];
|
||||||
|
/** Timer to load day call */
|
||||||
|
private fetchDayTimer = null as number | null;
|
||||||
/** Set of selected file ids */
|
/** Set of selected file ids */
|
||||||
private selection = new Map<number, IPhoto>();
|
private selection = new Map<number, IPhoto>();
|
||||||
|
|
||||||
|
@ -244,6 +248,10 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
||||||
this.scrollerManager.reset();
|
this.scrollerManager.reset();
|
||||||
this.state = Math.random();
|
this.state = Math.random();
|
||||||
this.loadedDays.clear();
|
this.loadedDays.clear();
|
||||||
|
this.fetchDayQueue = [];
|
||||||
|
window.clearTimeout(this.fetchDayTimer);
|
||||||
|
window.clearTimeout(this.resizeTimer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Recreate everything */
|
/** Recreate everything */
|
||||||
|
@ -650,22 +658,40 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
||||||
this.scrollPositionChange();
|
this.scrollPositionChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** API url for Day call */
|
||||||
|
private getDayUrl(dayId: number|string) {
|
||||||
|
let baseUrl = '/apps/memories/api/days/{dayId}';
|
||||||
|
const params: any = { dayId };
|
||||||
|
return generateUrl(this.appendQuery(baseUrl), params);
|
||||||
|
}
|
||||||
|
|
||||||
/** Fetch image data for one dayId */
|
/** Fetch image data for one dayId */
|
||||||
async fetchDay(dayId: number) {
|
async fetchDay(dayId: number) {
|
||||||
const head = this.heads[dayId];
|
const head = this.heads[dayId];
|
||||||
if (!head) return;
|
if (!head) return;
|
||||||
|
|
||||||
let baseUrl = '/apps/memories/api/days/{dayId}';
|
|
||||||
const params: any = { dayId };
|
|
||||||
|
|
||||||
// Do this in advance to prevent duplicate requests
|
// Do this in advance to prevent duplicate requests
|
||||||
this.loadedDays.add(dayId);
|
this.loadedDays.add(dayId);
|
||||||
|
|
||||||
// Construct URL
|
|
||||||
const url = generateUrl(this.appendQuery(baseUrl), params);
|
|
||||||
|
|
||||||
// Look for cache
|
// Look for cache
|
||||||
this.processDay(dayId, await utils.getCachedData(url));
|
this.processDay(dayId, await utils.getCachedData(this.getDayUrl(dayId)));
|
||||||
|
|
||||||
|
// Aggregate fetch requests
|
||||||
|
this.fetchDayQueue.push(dayId);
|
||||||
|
if (!this.fetchDayTimer) {
|
||||||
|
this.fetchDayTimer = window.setTimeout(() => {
|
||||||
|
this.fetchDayTimer = null;
|
||||||
|
this.fetchDayExpire();
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchDayExpire() {
|
||||||
|
if (this.fetchDayQueue.length === 0) return;
|
||||||
|
|
||||||
|
// Construct URL
|
||||||
|
const url = this.getDayUrl(this.fetchDayQueue.join(','));
|
||||||
|
this.fetchDayQueue = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const startState = this.state;
|
const startState = this.state;
|
||||||
|
@ -673,21 +699,39 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
|
||||||
const data = res.data;
|
const data = res.data;
|
||||||
if (this.state !== startState) return;
|
if (this.state !== startState) return;
|
||||||
|
|
||||||
// Store cache asynchronously
|
// Bin the data into separate days
|
||||||
// Do this regardless of whether the state has
|
// It is already sorted in dayid DESC
|
||||||
// changed just to be sure
|
const dayMap = new Map<number, IPhoto[]>();
|
||||||
utils.cacheData(url, data);
|
for (const photo of data) {
|
||||||
|
if (!dayMap.get(photo.dayid)) dayMap.set(photo.dayid, []);
|
||||||
// Check if the response has any delta
|
dayMap.get(photo.dayid).push(photo);
|
||||||
if (head.day.detail?.length) {
|
|
||||||
if (head.day.detail.length === data.length &&
|
|
||||||
head.day.detail.every((p, i) => p.fileid === data[i].fileid && p.etag === data[i].etag)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.processDay(dayId, data);
|
// Store cache asynchronously
|
||||||
|
// Do this regardless of whether the state has
|
||||||
|
// changed since the data is already fetched
|
||||||
|
//
|
||||||
|
// These loops cannot be combined because processDay
|
||||||
|
// creates circular references which cannot be stringified
|
||||||
|
for (const [dayId, photos] of dayMap) {
|
||||||
|
utils.cacheData(this.getDayUrl(dayId), photos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each day as needed
|
||||||
|
for (const [dayId, photos] of dayMap) {
|
||||||
|
// Check if the response has any delta
|
||||||
|
const head = this.heads[dayId];
|
||||||
|
if (head.day.detail?.length) {
|
||||||
|
if (head.day.detail.length === photos.length &&
|
||||||
|
head.day.detail.every((p, i) => p.fileid === photos[i].fileid && p.etag === photos[i].etag)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass ahead
|
||||||
|
this.processDay(dayId, photos);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showError(this.t('memories', 'Failed to load some photos'));
|
showError(this.t('memories', 'Failed to load some photos'));
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
@ -219,9 +219,12 @@ export async function getCachedData<T>(url: string): Promise<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Store data in the cache */
|
/** Store data in the cache */
|
||||||
export async function cacheData(url: string, data: Object) {
|
export function cacheData(url: string, data: Object) {
|
||||||
const cache = staticCache || await openCache();
|
const str = JSON.stringify(data);
|
||||||
const response = new Response(JSON.stringify(data));
|
(async () => {
|
||||||
response.headers.set('Content-Type', 'application/json');
|
const cache = staticCache || await openCache();
|
||||||
await cache.put(url, response);
|
const response = new Response(str);
|
||||||
|
response.headers.set('Content-Type', 'application/json');
|
||||||
|
await cache.put(url, response);
|
||||||
|
})();
|
||||||
}
|
}
|
Loading…
Reference in New Issue