From 71a10b7971167c84000b94d514f3619e29325ddb Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Sun, 16 Oct 2022 11:49:29 -0700 Subject: [PATCH] Cache days (abandon) --- package-lock.json | 43 +++++++++++++++++ package.json | 1 + src/components/Timeline.vue | 86 ++++++++++++++++++++++++++++------ src/components/frame/Photo.vue | 14 ++---- 4 files changed, 120 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index adc82140..29d74d53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@nextcloud/paths": "^2.1.0", "@nextcloud/vue": "^7.0.0", "justified-layout": "^4.1.0", + "localforage": "^1.10.0", "moment": "^2.29.4", "path-posix": "^1.0.0", "reflect-metadata": "^0.1.13", @@ -6780,6 +6781,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/immutable": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", @@ -7482,6 +7488,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7528,6 +7542,14 @@ "node": ">=8.9.0" } }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -17243,6 +17265,11 @@ "dev": true, "peer": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "immutable": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", @@ -17749,6 +17776,14 @@ "type-check": "~0.4.0" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "requires": { + "immediate": "~3.0.5" + } + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -17787,6 +17822,14 @@ "json5": "^2.1.2" } }, + "localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "requires": { + "lie": "3.1.1" + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", diff --git a/package.json b/package.json index 06398739..2a8748c3 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@nextcloud/paths": "^2.1.0", "@nextcloud/vue": "^7.0.0", "justified-layout": "^4.1.0", + "localforage": "^1.10.0", "moment": "^2.29.4", "path-posix": "^1.0.0", "reflect-metadata": "^0.1.13", diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue index 539b0528..987a7d5f 100644 --- a/src/components/Timeline.vue +++ b/src/components/Timeline.vue @@ -102,6 +102,7 @@ import moment from 'moment'; import * as dav from "../services/DavRequests"; import * as utils from "../services/Utils"; +import localforage from "localforage"; import justifiedLayout from "justified-layout"; import axios from '@nextcloud/axios' import Folder from "./frame/Folder.vue"; @@ -150,8 +151,6 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { private squareMode = false; /** Header rows for dayId key */ private heads: { [dayid: number]: IHeadRow } = {}; - /** Original days response */ - private days: IDay[] = []; /** Computed row height */ private rowHeight = 100; @@ -215,12 +214,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { // Fit to window this.handleResize(); + // Add scroll listener + (this.$refs.recycler as any).$el.addEventListener('scroll', this.scrollPositionChange, false); + // Get data await this.fetchDays(); - - // Timeline recycler init - (this.$refs.recycler as any).$el.addEventListener('scroll', this.scrollPositionChange, false); - this.scrollPositionChange(); } /** Reset all state */ @@ -230,7 +228,6 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { this.list = []; this.numRows = 0; this.heads = {}; - this.days = []; this.currentStart = 0; this.currentEnd = 0; this.scrollerManager.reset(); @@ -488,9 +485,11 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { let params: any = {}; try { - this.loading++; + // this.loading++; const startState = this.state; + const furl = generateUrl(this.appendQuery(url), params); + let data: IDay[] = []; if (this.$route.name === 'thisday') { data = await dav.getOnThisDayData(); @@ -499,7 +498,12 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { } else if (this.$route.name === 'people' && !this.$route.params.name) { data = await dav.getPeopleData(); } else { - data = (await axios.get(generateUrl(this.appendQuery(url), params))).data; + const foraged = await localforage.getItem(furl); + if (foraged) { + await this.processDays(foraged); + } + data = (await axios.get(furl)).data; + await localforage.setItem(furl, data); } if (this.state !== startState) return; @@ -508,7 +512,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { console.error(err); showError(err?.response?.data?.message || err.message); } finally { - this.loading--; + // this.loading--; } } @@ -590,9 +594,12 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { } // Store globally - this.days = data; - this.list = list; - this.heads = heads; + if (this.list.length) { + this.replaceRows(list); + } else { + this.list = list; + this.heads = heads; + } // Iterate the preload map // Now the inner detail objects are reactive @@ -604,6 +611,57 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { // Fix view height variable await this.scrollerManager.reflow(); + this.scrollPositionChange(); + } + + /** Replace current rows with this one gracefully */ + private replaceRows(rows: IRow[]) { + // Two pointers: match heads + let pOld = 0; + let pNew = 0; + let insert = false; + let remove = false + while (pOld < this.list.length && pNew < rows.length) { + const oldRow = this.list[pOld]; + const newRow = rows[pNew]; + + // Insert new rows + if (insert) { + this.list.splice(pOld, 0, newRow); + pOld++; + pNew++; + if (rows[pNew].type === IRowType.HEAD) { + insert = false; + } + continue; + } + + // Remove old rows + if (remove) { + this.list.splice(pOld, 1); + if (this.list[pOld].type === IRowType.HEAD) { + remove = false; + } + continue; + } + + // Find the next heads + if (newRow.type !== IRowType.HEAD) { pNew++; continue; } + if (oldRow.type !== IRowType.HEAD) { pOld++; continue; } + + if (oldRow.dayId < newRow.dayId) { + // newRow is a new head, start insertion + insert = true; + this.heads[newRow.dayId] = newRow as IHeadRow; + } else if (oldRow.dayId > newRow.dayId) { + // Different head, remove old + remove = true; + } else { + // Same head, continue + pOld++; + pNew++; + } + } } /** Fetch image data for one dayId */ @@ -620,7 +678,7 @@ export default class Timeline extends Mixins(GlobalMixin, UserConfig) { const data = res.data; if (this.state !== startState) return; - const day = this.days.find(d => d.dayid === dayId); + const day = this.heads[dayId].day; day.detail = data; day.count = data.length; this.processDay(day); diff --git a/src/components/frame/Photo.vue b/src/components/frame/Photo.vue index 71967ebf..6d98fa3e 100644 --- a/src/components/frame/Photo.vue +++ b/src/components/frame/Photo.vue @@ -2,7 +2,7 @@
+ @error="error" />
@@ -89,14 +88,9 @@ export default class Photo extends Mixins(GlobalMixin) { return getPreviewUrl(this.data.fileid, this.data.etag, false, size) } - /** Image loaded successfully */ - load() { - this.data.flag |= this.c.FLAG_LOADED; - } - /** Error in loading image */ error(e: any) { - this.data.flag |= (this.c.FLAG_LOADED | this.c.FLAG_LOAD_FAIL); + this.data.flag |= this.c.FLAG_LOAD_FAIL; } /** Clear timers */ @@ -284,7 +278,7 @@ div.img-outer { transition: box-shadow 0.1s ease; .selected > & { box-shadow: 0 0 4px 2px var(--color-primary); } - .p-outer.p-loading > & { display: none; } + .p-outer.placeholder > & { display: none; } .p-outer.error & { object-fit: contain; } } }