timeline: join day calls (fix #84)

old-stable24
Varun Patil 2022-10-19 11:22:43 -07:00
parent 40ab7cbf2b
commit 866376c912
2 changed files with 72 additions and 25 deletions

View File

@ -175,6 +175,10 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
/** Set of dayIds for which images loaded */
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 */
private selection = new Map<number, IPhoto>();
@ -244,6 +248,10 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
this.scrollerManager.reset();
this.state = Math.random();
this.loadedDays.clear();
this.fetchDayQueue = [];
window.clearTimeout(this.fetchDayTimer);
window.clearTimeout(this.resizeTimer);
}
/** Recreate everything */
@ -650,22 +658,40 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
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 */
async fetchDay(dayId: number) {
const head = this.heads[dayId];
if (!head) return;
let baseUrl = '/apps/memories/api/days/{dayId}';
const params: any = { dayId };
// Do this in advance to prevent duplicate requests
this.loadedDays.add(dayId);
// Construct URL
const url = generateUrl(this.appendQuery(baseUrl), params);
// 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 {
const startState = this.state;
@ -673,21 +699,39 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) {
const data = res.data;
if (this.state !== startState) return;
// Store cache asynchronously
// Do this regardless of whether the state has
// changed just to be sure
utils.cacheData(url, data);
// Check if the response has any delta
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;
}
// Bin the data into separate days
// It is already sorted in dayid DESC
const dayMap = new Map<number, IPhoto[]>();
for (const photo of data) {
if (!dayMap.get(photo.dayid)) dayMap.set(photo.dayid, []);
dayMap.get(photo.dayid).push(photo);
}
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) {
showError(this.t('memories', 'Failed to load some photos'));
console.error(e);

View File

@ -219,9 +219,12 @@ export async function getCachedData<T>(url: string): Promise<T> {
}
/** Store data in the cache */
export async function cacheData(url: string, data: Object) {
const cache = staticCache || await openCache();
const response = new Response(JSON.stringify(data));
response.headers.set('Content-Type', 'application/json');
await cache.put(url, response);
export function cacheData(url: string, data: Object) {
const str = JSON.stringify(data);
(async () => {
const cache = staticCache || await openCache();
const response = new Response(str);
response.headers.set('Content-Type', 'application/json');
await cache.put(url, response);
})();
}