From 5f6b54247deb7614b4bfd422852b1f2a352da9c2 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Fri, 8 May 2026 17:20:24 +0100 Subject: [PATCH 1/3] Start the UI transition from ScanList to Gallery --- webapp/src/components/appContent.vue | 8 +- .../galleryCard.vue} | 52 +++++------ .../openSeadragonViewer.vue | 0 .../scanViewer.vue | 0 ...scanListContent.vue => galleryContent.vue} | 87 +++++++++---------- 5 files changed, 71 insertions(+), 76 deletions(-) rename webapp/src/components/tabContentComponents/{scanListComponents/scanCard.vue => galleryComponents/galleryCard.vue} (77%) rename webapp/src/components/tabContentComponents/{scanListComponents => galleryComponents}/openSeadragonViewer.vue (100%) rename webapp/src/components/tabContentComponents/{scanListComponents => galleryComponents}/scanViewer.vue (100%) rename webapp/src/components/tabContentComponents/{scanListContent.vue => galleryContent.vue} (74%) diff --git a/webapp/src/components/appContent.vue b/webapp/src/components/appContent.vue index 29af3e5e..9ddf17ae 100644 --- a/webapp/src/components/appContent.vue +++ b/webapp/src/components/appContent.vue @@ -85,7 +85,7 @@ import aboutContent from "./tabContentComponents/aboutContent.vue"; import controlContent from "./tabContentComponents/controlContent.vue"; import loggingContent from "./tabContentComponents/loggingContent.vue"; import powerContent from "./tabContentComponents/powerContent.vue"; -import scanListContent from "./tabContentComponents/scanListContent.vue"; +import galleryContent from "./tabContentComponents/galleryContent.vue"; import settingsContent from "./tabContentComponents/settingsContent.vue"; import slideScanContent from "./tabContentComponents/slideScanContent.vue"; import viewContent from "./tabContentComponents/viewContent.vue"; @@ -158,10 +158,10 @@ export default { requiredThings: ["smart_scan"], }, { - id: "scan-list", - title: "Scan List", + id: "gallery", + title: "Gallery", icon: "photo_library", - component: markRaw(scanListContent), + component: markRaw(galleryContent), requiredThings: ["smart_scan"], }, ], diff --git a/webapp/src/components/tabContentComponents/scanListComponents/scanCard.vue b/webapp/src/components/tabContentComponents/galleryComponents/galleryCard.vue similarity index 77% rename from webapp/src/components/tabContentComponents/scanListComponents/scanCard.vue rename to webapp/src/components/tabContentComponents/galleryComponents/galleryCard.vue index 8f448815..d9b32d11 100644 --- a/webapp/src/components/tabContentComponents/scanListComponents/scanCard.vue +++ b/webapp/src/components/tabContentComponents/galleryComponents/galleryCard.vue @@ -12,16 +12,16 @@ /> -

{{ scanData.name }}

+
-
+
-
+
    -
  • {{ scanData.number_of_images }} images
  • -
  • Created: {{ formatDate(scanData.created) }}
  • -
  • Duration: {{ formatDuration(scanData.duration) }}
  • +
  • {{ itemData.number_of_images }} images
  • +
  • Created: {{ formatDate(itemData.created) }}
  • +
  • Duration: {{ formatDuration(itemData.duration) }}
    -
  • +
  • Not enough images to stitch
  • -
  • +
  • Interactive preview not available
  • -
  • +
  • High quality stitch not available
@@ -85,21 +85,17 @@ import { useSettingsStore } from "@/stores/settings.js"; // Export main app export default { - name: "ScanCard", + name: "GalleryCard", components: { actionButton, EndpointButton, }, props: { - scanData: { + itemData: { type: Object, required: true, }, - scansUri: { - type: String, - required: true, - }, }, emits: ["viewer-requested", "update-requested"], @@ -107,10 +103,10 @@ export default { computed: { ...mapState(useSettingsStore, ["baseUri"]), downloadStitchFile() { - return `${this.baseUri}/smart_scan/get_stitch/${this.scanData.name}`; + return `${this.baseUri}/smart_scan/get_stitch/${this.itemData.name}`; }, thumbnailPath() { - return `${this.baseUri}/smart_scan/scans/stitched_thumbnail.jpg?scan_name=${this.scanData.name}&modified=${this.scanData.modified}`; + return `${this.baseUri}/smart_scan/scans/stitched_thumbnail.jpg?scan_name=${this.itemData.name}&modified=${this.itemData.modified}`; }, }, @@ -142,14 +138,14 @@ export default { }, requestViewer() { // Notify parent that thumbnail was clicked - this.$emit("viewer-requested", this.scanData); + this.$emit("viewer-requested", this.itemData); }, async deleteScan() { try { - await this.modalConfirm(`Are you sure you want to delete ${this.scanData.name}?`); - await axios.delete(`${this.scansUri}/${this.scanData.name}`); + await this.modalConfirm(`Are you sure you want to delete ${this.itemData.name}?`); + await axios.delete(`${this.baseUri}/smart_scan/scans/${this.itemData.name}`); this.$emit("update-requested"); - this.modalNotify(`Deleted ${this.scanData.name}`); + this.modalNotify(`Deleted ${this.itemData.name}`); } catch (e) { // if the confirmation was cancelled, it's rejected with null error if (e) this.modalError(e); @@ -189,11 +185,11 @@ ul { font-weight: bold; } -.scan-card-buttons { +.gallery-card-buttons { width: 100%; } -.scan-card-title { +.gallery-card-title { text-align: center; } diff --git a/webapp/src/components/tabContentComponents/scanListComponents/openSeadragonViewer.vue b/webapp/src/components/tabContentComponents/galleryComponents/openSeadragonViewer.vue similarity index 100% rename from webapp/src/components/tabContentComponents/scanListComponents/openSeadragonViewer.vue rename to webapp/src/components/tabContentComponents/galleryComponents/openSeadragonViewer.vue diff --git a/webapp/src/components/tabContentComponents/scanListComponents/scanViewer.vue b/webapp/src/components/tabContentComponents/galleryComponents/scanViewer.vue similarity index 100% rename from webapp/src/components/tabContentComponents/scanListComponents/scanViewer.vue rename to webapp/src/components/tabContentComponents/galleryComponents/scanViewer.vue diff --git a/webapp/src/components/tabContentComponents/scanListContent.vue b/webapp/src/components/tabContentComponents/galleryContent.vue similarity index 74% rename from webapp/src/components/tabContentComponents/scanListContent.vue rename to webapp/src/components/tabContentComponents/galleryContent.vue index b8b85319..48ab275e 100644 --- a/webapp/src/components/tabContentComponents/scanListContent.vue +++ b/webapp/src/components/tabContentComponents/galleryContent.vue @@ -5,7 +5,7 @@
-
+ @@ -47,16 +47,15 @@
@@ -109,8 +109,8 @@ export default { // The actual property does not exist. So allowUndefined=true return this.thingPropertyUrl("smart_scan", "scans", true); }, - noItmesy() { - return this.all_items.length == 0; + noItems() { + return !this.all_items || this.all_items?.length === 0; }, selectedScanDZI() { if (this.selectedScan && this.selectedScan.dzi != "") { @@ -119,19 +119,20 @@ export default { return null; } }, - totalPages() { - return Math.ceil(this.all_items.length / this.itemsPerPage); + // name changed as paginateLinks has the prop totalPages + numberOfPages() { + return Math.ceil((this.all_items?.length || 0) / this.itemsPerPage); }, paginatedItems() { const start = (this.currentPage - 1) * this.itemsPerPage; - return this.all_items.slice(start, start + this.itemsPerPage); + return (this.all_items || []).slice(start, start + this.itemsPerPage); }, }, async mounted() { const store = useSettingsStore(); const { ready } = storeToRefs(store); - watch(ready, (isReady) => { + this.unwatchStoreFunction = watch(ready, (isReady) => { if (isReady) { this.refreshGallery(); } else { @@ -150,13 +151,9 @@ export default { // Update on mount (does nothing if not connected) await this.refreshGallery(); // A global signal listener to perform a gallery refresh - eventBus.on("globalRefreshGallery", () => { - this.refreshGallery(); - }); - eventBus.on("modalClosed", () => { - // Handle the modal closed event here - this.refreshGallery(); - }); + eventBus.on("globalRefreshGallery", this.refreshGallery); + // Handle the modal closed event here + eventBus.on("modalClosed", this.refreshGallery); }, beforeUnmount() { @@ -179,8 +176,12 @@ export default { async refreshGallery() { try { let all_items = await this.readThingProperty("gallery", "list_data"); - if (!all_items | (all_items.length == 0)) { - this.all_items = all_items; + // if all_items is not or the number of items in all_items is zero then + // all_items is equal to empty list. + // stop refresh gallery as all_items is empty list. + if (!all_items || all_items?.length === 0) { + this.all_items = []; + return; } all_items.forEach((item) => { item.can_stitch = !item.stitch_available && item.number_of_images > 3; @@ -189,6 +190,7 @@ export default { return b.created - a.created; }); this.all_items = all_items; + console.debug("Gallery size: %s", this.all_items.length); } catch (err) { console.error("Failed to refresh gallery items."); console.error(err); From 2e051f6d30bc78389293f38c89f57621d666575d Mon Sep 17 00:00:00 2001 From: Joe Knapper Date: Mon, 11 May 2026 16:33:29 +0000 Subject: [PATCH 3/3] Apply suggestions from code review of branch gallery-ui-transition Co-authored-by: Julian Stirling --- webapp/src/components/tabContentComponents/galleryContent.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/src/components/tabContentComponents/galleryContent.vue b/webapp/src/components/tabContentComponents/galleryContent.vue index d7ac1012..0b3313cf 100644 --- a/webapp/src/components/tabContentComponents/galleryContent.vue +++ b/webapp/src/components/tabContentComponents/galleryContent.vue @@ -176,7 +176,7 @@ export default { async refreshGallery() { try { let all_items = await this.readThingProperty("gallery", "list_data"); - // if all_items is not or the number of items in all_items is zero then + // if all_items is "falsey" or the number of items in all_items is zero then // all_items is equal to empty list. // stop refresh gallery as all_items is empty list. if (!all_items || all_items?.length === 0) {