diff --git a/frontend/src/app.js b/frontend/src/app.js index f7b9717f4..17fe75e13 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -111,6 +111,19 @@ const router = new Router({ routes: Routes, mode: "history", saveScrollPosition: true, + scrollBehavior: (to, from, savedPosition) => { + if (savedPosition) { + return new Promise((resolve) => { + Notify.ajaxWait().then(() => { + setTimeout(() => { + resolve(savedPosition); + }, 200); + }); + }); + } else { + return { x: 0, y: 0 }; + } + }, }); router.beforeEach((to, from, next) => { diff --git a/frontend/src/common/notify.js b/frontend/src/common/notify.js index 9f707fe8b..53314673a 100644 --- a/frontend/src/common/notify.js +++ b/frontend/src/common/notify.js @@ -31,6 +31,9 @@ https://docs.photoprism.org/developer-guide/ import Event from "pubsub-js"; import { $gettext } from "./vm"; +let ajaxPending = 0; +let ajaxCallbacks = []; + const Notify = { info: function (message) { Event.publish("notify.info", { message }); @@ -49,10 +52,34 @@ const Notify = { Event.publish("session.logout", { message }); }, ajaxStart: function () { + ajaxPending++; Event.publish("ajax.start"); }, ajaxEnd: function () { + ajaxPending--; Event.publish("ajax.end"); + + if (!this.ajaxBusy()) { + ajaxCallbacks.forEach((resolve) => { + resolve(); + }); + } + }, + ajaxBusy: function () { + if (ajaxPending < 0) { + ajaxPending = 0; + } + + return ajaxPending > 0; + }, + ajaxWait: function () { + return new Promise((resolve) => { + if (this.ajaxBusy()) { + ajaxCallbacks.push(resolve); + } else { + resolve(); + } + }); }, blockUI: function () { const el = document.getElementById("busy-overlay"); diff --git a/frontend/src/pages/albums.vue b/frontend/src/pages/albums.vue index d82969a1a..d6276c632 100644 --- a/frontend/src/pages/albums.vue +++ b/frontend/src/pages/albums.vue @@ -283,6 +283,19 @@ export default { } }, methods: { + searchCount() { + const offset = parseInt(window.localStorage.getItem("albums_offset")); + + if(this.offset > 0 || !offset) { + return this.batchSize; + } + + return offset + this.batchSize; + }, + setOffset(offset) { + this.offset = offset; + window.localStorage.setItem("albums_offset", offset); + }, share(album) { this.model = album; this.dialog.share = true; @@ -382,19 +395,19 @@ export default { Object.assign(params, this.staticFilter); } - Album.search(params).then(response => { - this.results = this.dirty ? response.models : this.results.concat(response.models); + Album.search(params).then(resp => { + this.results = this.dirty ? resp.models : this.results.concat(resp.models); - this.scrollDisabled = (response.models.length < count); + this.scrollDisabled = (resp.count < resp.limit); if (this.scrollDisabled) { - this.offset = offset; + this.setOffset(resp.offset); if (this.results.length > 1) { this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} albums loaded"), {n: this.results.length})); } } else { - this.offset = offset + count; + this.setOffset(resp.offset + resp.limit); this.page++; this.$nextTick(() => { @@ -434,7 +447,7 @@ export default { }, searchParams() { const params = { - count: this.batchSize, + count: this.searchCount(), offset: this.offset, }; @@ -464,12 +477,11 @@ export default { const params = this.searchParams(); - Album.search(params).then(response => { - this.offset = this.batchSize; + Album.search(params).then(resp => { + this.offset = resp.limit; + this.results = resp.models; - this.results = response.models; - - this.scrollDisabled = (response.models.length < this.batchSize); + this.scrollDisabled = (resp.count < resp.limit); if (this.scrollDisabled) { if (!this.results.length) { diff --git a/frontend/src/pages/labels.vue b/frontend/src/pages/labels.vue index 6b076152a..a0a048eda 100644 --- a/frontend/src/pages/labels.vue +++ b/frontend/src/pages/labels.vue @@ -196,6 +196,19 @@ export default { } }, methods: { + searchCount() { + const offset = parseInt(window.localStorage.getItem("labels_offset")); + + if(this.offset > 0 || !offset) { + return this.batchSize; + } + + return offset + this.batchSize; + }, + setOffset(offset) { + this.offset = offset; + window.localStorage.setItem("labels_offset", offset); + }, selectRange(rangeEnd, models) { if (!models || !models[rangeEnd] || !(models[rangeEnd] instanceof RestModel)) { console.warn("selectRange() - invalid arguments:", rangeEnd, models); @@ -332,18 +345,18 @@ export default { Object.assign(params, this.staticFilter); } - Label.search(params).then(response => { - this.results = this.dirty ? response.models : this.results.concat(response.models); + Label.search(params).then(resp => { + this.results = this.dirty ? resp.models : this.results.concat(resp.models); - this.scrollDisabled = (response.models.length < count); + this.scrollDisabled = (resp.count < resp.limit); if (this.scrollDisabled) { - this.offset = offset; + this.setOffset(resp.offset); if (this.results.length > 1) { this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} labels loaded"), {n: this.results.length})); } } else { - this.offset = offset + count; + this.setOffset(resp.offset + resp.limit); this.page++; this.$nextTick(() => { @@ -383,7 +396,7 @@ export default { }, searchParams() { const params = { - count: this.batchSize, + count: this.searchCount(), offset: this.offset, }; @@ -421,12 +434,11 @@ export default { const params = this.searchParams(); - Label.search(params).then(response => { - this.offset = this.batchSize; + Label.search(params).then(resp => { + this.offset = resp.limit; + this.results = resp.models; - this.results = response.models; - - this.scrollDisabled = (response.models.length < this.batchSize); + this.scrollDisabled = (resp.count < resp.limit); if (this.scrollDisabled) { this.$notify.info(this.$gettextInterpolate(this.$gettext("%{n} labels found"), {n: this.results.length})); diff --git a/frontend/src/share.js b/frontend/src/share.js index 91d92cc30..d0830c5ab 100644 --- a/frontend/src/share.js +++ b/frontend/src/share.js @@ -110,6 +110,19 @@ const router = new Router({ routes: Routes, mode: "history", saveScrollPosition: true, + scrollBehavior: (to, from, savedPosition) => { + if (savedPosition) { + return new Promise((resolve) => { + Notify.ajaxWait().then(() => { + setTimeout(() => { + resolve(savedPosition); + }, 200); + }); + }); + } else { + return { x: 0, y: 0 }; + } + }, }); router.beforeEach((to, from, next) => { diff --git a/frontend/src/share/albums.vue b/frontend/src/share/albums.vue index 02dcbe09c..c78f5a4e6 100644 --- a/frontend/src/share/albums.vue +++ b/frontend/src/share/albums.vue @@ -195,6 +195,19 @@ export default { } }, methods: { + searchCount() { + const offset = parseInt(window.localStorage.getItem("share_albums_offset")); + + if(this.offset > 0 || !offset) { + return this.batchSize; + } + + return offset + this.batchSize; + }, + setOffset(offset) { + this.offset = offset; + window.localStorage.setItem("share_albums_offset", offset); + }, showUpload() { Event.publish("dialog.upload"); }, @@ -282,19 +295,19 @@ export default { Object.assign(params, this.staticFilter); } - Album.search(params).then(response => { - this.results = this.dirty ? response.models : this.results.concat(response.models); + Album.search(params).then(resp => { + this.results = this.dirty ? resp.models : this.results.concat(resp.models); - this.scrollDisabled = (response.models.length < count); + this.scrollDisabled = (resp.count < resp.limit); if (this.scrollDisabled) { - this.offset = offset; + this.setOffset(resp.offset); if (this.results.length > 1) { this.$notify.info(this.$gettextInterpolate(this.$gettext("All %{n} albums loaded"), {n: this.results.length})); } } else { - this.offset = offset + count; + this.setOffset(resp.offset + resp.limit); this.page++; this.$nextTick(() => { @@ -334,7 +347,7 @@ export default { }, searchParams() { const params = { - count: this.batchSize, + count: this.searchCount(), offset: this.offset, }; @@ -364,12 +377,11 @@ export default { const params = this.searchParams(); - Album.search(params).then(response => { - this.offset = this.batchSize; + Album.search(params).then(resp => { + this.offset = resp.limit; + this.results = resp.models; - this.results = response.models; - - this.scrollDisabled = (response.models.length < this.batchSize); + this.scrollDisabled = (resp.count < resp.limit); if (this.scrollDisabled) { if (!this.results.length) {