diff --git a/frontend/src/common/viewer.js b/frontend/src/common/viewer.js index dba52436f..45c5933b3 100644 --- a/frontend/src/common/viewer.js +++ b/frontend/src/common/viewer.js @@ -1,41 +1,14 @@ import PhotoSwipe from "photoswipe"; import PhotoSwipeUI_Default from "photoswipe/dist/photoswipe-ui-default.js"; +const thumbs = window.clientConfig.thumbnails; + class Viewer { constructor() { - this.photos = []; this.el = null; this.gallery = null; } - photosWithSizes() { - return this.photos.map(this.createPhotoSizes); - } - - createPhotoSizes(photo) { - const result = { - title: photo.PhotoTitle, - download_url: photo.getDownloadUrl(), - original_w: photo.FileWidth, - original_h: photo.FileHeight, - uuid: photo.PhotoUUID, - }; - - const thumbs = window.clientConfig.thumbnails; - - for (let i = 0; i < thumbs.length; i++) { - let size = photo.calculateSize(thumbs[i].Width, thumbs[i].Height); - - result[thumbs[i].Name] = { - src: photo.getThumbnailUrl(thumbs[i].Name), - w: size.width, - h: size.height, - }; - } - - return result; - } - getEl() { if (!this.el) { this.el = document.getElementById("p-photo-viewer"); @@ -50,15 +23,12 @@ class Viewer { return this.el; } - show(photos, index = 0) { - if (!Array.isArray(photos) || photos.length === 0 || index >= photos.length) { - console.log("Array passed to gallery was empty:", photos); + show(items, index = 0) { + if (!Array.isArray(items) || items.length === 0 || index >= items.length) { + console.log("Array passed to gallery was empty:", items); return; } - this.photos = photos; - - const shareButtons = [ {id: "fit_720", template: "Tiny (size)", label: "Tiny", url: "{{raw_image_url}}", download: true}, {id: "fit_1280", template: "Small (size)", label: "Small", url: "{{raw_image_url}}", download: true}, @@ -83,21 +53,19 @@ class Viewer { arrowEl: true, preloaderEl: true, getImageURLForShare: function (button) { - const photo = gallery.currItem; + const item = gallery.currItem; if(button.id === "original") { - button.label = button.template.replace("size", photo.original_w + " × " + photo.original_h); - return photo.download_url; + button.label = button.template.replace("size", item.original_w + " × " + item.original_h); + return item.download_url; } else { - button.label = button.template.replace("size", photo[button.id].w + " × " + photo[button.id].h); - return photo[button.id].src + "?download=1"; + button.label = button.template.replace("size", item[button.id].w + " × " + item[button.id].h); + return item[button.id].src + "?download=1"; } }, }; - let photosWithSizes = this.photosWithSizes(); - - let gallery = new PhotoSwipe(this.getEl(), PhotoSwipeUI_Default, photosWithSizes, options); + let gallery = new PhotoSwipe(this.getEl(), PhotoSwipeUI_Default, items, options); let realViewportWidth; let realViewportHeight; let previousSize; @@ -143,8 +111,6 @@ class Viewer { } static mapViewportToImageSize(viewportWidth, viewportHeight) { - const thumbs = window.clientConfig.thumbnails; - for (let i = 0; i < thumbs.length; i++) { if (thumbs[i].Width >= viewportWidth || thumbs[i].Height >= viewportHeight) { return thumbs[i].Name; diff --git a/frontend/src/model/model.js b/frontend/src/model/model.js index b201cc62b..febf54430 100644 --- a/frontend/src/model/model.js +++ b/frontend/src/model/model.js @@ -9,16 +9,17 @@ class Model { } } - setValues(values) { + setValues(values, scalarOnly) { if (!values) return; for (let key in values) { if (values.hasOwnProperty(key) && key !== "__originalValues") { this[key] = values[key]; - if (typeof values[key] === "object") { - this.__originalValues[key] = JSON.parse(JSON.stringify(values[key])); - } else { + + if (typeof values[key] !== "object") { this.__originalValues[key] = values[key]; + } else if (!scalarOnly) { + this.__originalValues[key] = JSON.parse(JSON.stringify(values[key])); } } @@ -36,15 +37,15 @@ class Model { let val; if (defaults.hasOwnProperty(key)) { switch (typeof defaults[key]) { - case "bigint": - case "number": - val = parseFloat(this[key]); - break; - case "boolean": - val = !!this[key]; - break; - default: - val = this[key]; + case "bigint": + case "number": + val = parseFloat(this[key]); + break; + case "boolean": + val = !!this[key]; + break; + default: + val = this[key]; } } else { val = this[key]; diff --git a/frontend/src/model/photo.js b/frontend/src/model/photo.js index d159b4b96..a88dec1c3 100644 --- a/frontend/src/model/photo.js +++ b/frontend/src/model/photo.js @@ -104,7 +104,7 @@ class Photo extends RestModel { return; } - const primary = this.Files.find(f => f.FilePrimary === true); + const primary = this.Files.find(f => !!f.FilePrimary); if (!primary) { return; @@ -115,19 +115,32 @@ class Photo extends RestModel { this.FileHeight = primary.FileHeight; } - getThumbnailUrl(type) { - if (this.Files && this.Files.length) { + primaryFileHash() { + if (this.Files) { const primary = this.Files.find(f => !!f.FilePrimary); - return "/api/v1/thumbnails/" + primary.FileHash + "/" + type; + + if (primary && primary.FileHash) { + return primary.FileHash; + } } else if (this.FileHash) { - return "/api/v1/thumbnails/" + this.FileHash + "/" + type; + return this.FileHash; } - return "/api/v1/svg/photo"; + return "" + } + + getThumbnailUrl(type) { + let hash = this.primaryFileHash(); + + if (!hash) { + return "/api/v1/svg/photo"; + } + + return "/api/v1/thumbnails/" + hash + "/" + type; } getDownloadUrl() { - return "/api/v1/download/" + this.FileHash; + return "/api/v1/download/" + this.primaryFileHash(); } getThumbnailSrcset() { @@ -283,9 +296,9 @@ class Photo extends RestModel { let files = this.Files; for (let i = 0; i < files.length; i++) { - let photo = new this.constructor(this.getValues()) - photo.setValues(files[i]) - photos.push(photo) + let photo = new this.constructor(this.getValues()); + photo.setValues(files[i], true); + photos.push(photo); } console.log("PHOTOS", photos); diff --git a/frontend/src/model/rest.js b/frontend/src/model/rest.js index 8a14c99b2..26b0d5b69 100644 --- a/frontend/src/model/rest.js +++ b/frontend/src/model/rest.js @@ -90,16 +90,16 @@ class Rest extends Model { let offset = 0; if (response.headers) { - if (response.headers['x-count']) { - count = response.headers['x-count']; + if (response.headers["x-count"]) { + count = response.headers["x-count"]; } - if (response.headers['x-limit']) { - limit = response.headers['x-limit']; + if (response.headers["x-limit"]) { + limit = response.headers["x-limit"]; } - if (response.headers['x-offset']) { - offset = response.headers['x-offset']; + if (response.headers["x-offset"]) { + offset = response.headers["x-offset"]; } } diff --git a/frontend/src/model/thumb.js b/frontend/src/model/thumb.js new file mode 100644 index 000000000..d4bc9705e --- /dev/null +++ b/frontend/src/model/thumb.js @@ -0,0 +1,123 @@ +import Model from "./model"; + +const thumbs = window.clientConfig.thumbnails; + +class Thumb extends Model { + getDefaults() { + return { + uuid: "", + title: "", + original_w: "", + original_h: "", + download_url: "", + }; + } + + static fromPhotos(photos) { + let result = []; + + photos.forEach((p) => { + result.push(this.fromPhoto(p)); + }); + + return result; + } + + static fromPhoto(photo) { + if (photo.Files) { + return this.fromFile(photo, photo.Files.find(f => !!f.FilePrimary)); + } + + const result = { + uuid: photo.PhotoUUID, + title: photo.PhotoTitle, + download_url: "/api/v1/download/" + photo.FileHash, + original_w: photo.FileWidth, + original_h: photo.FileHeight, + }; + + for (let i = 0; i < thumbs.length; i++) { + let size = photo.calculateSize(thumbs[i].Width, thumbs[i].Height); + + result[thumbs[i].Name] = { + src: photo.getThumbnailUrl(thumbs[i].Name), + w: size.width, + h: size.height, + }; + } + + return new this(result); + } + + static fromFile(photo, file) { + const result = { + uuid: photo.PhotoUUID, + title: photo.PhotoTitle, + download_url: "/api/v1/download/" + file.FileHash, + original_w: file.FileWidth, + original_h: file.FileHeight, + }; + + thumbs.forEach((t) => { + let size = this.calculateSize(file, t.Width, t.Height); + + result[t.Name] = { + src: this.thumbnailUrl(file, t.Name), + w: size.width, + h: size.height, + }; + }); + + return new this(result); + } + + static fromFiles(photos) { + let result = []; + + photos.forEach((p) => { + if (!p.Files) return; + + p.Files.forEach((f) => { + if (f.FileType === 'jpg') { + result.push(this.fromFile(p, f)); + } + } + ); + }); + + return result; + } + + static calculateSize(file, width, height) { + if (width >= file.FileWidth && height >= file.FileHeight) { // Smaller + return {width: file.FileWidth, height: file.FileHeight}; + } + + const srcAspectRatio = file.FileWidth / file.FileHeight; + const maxAspectRatio = width / height; + + let newW, newH; + + if (srcAspectRatio > maxAspectRatio) { + newW = width; + newH = Math.round(newW / srcAspectRatio); + + } else { + newH = height; + newW = Math.round(newH * srcAspectRatio); + } + + return {width: newW, height: newH}; + } + + static thumbnailUrl(file, type) { + if (!file.FileHash) { + return "/api/v1/svg/photo"; + + } + + return "/api/v1/thumbnails/" + file.FileHash + "/" + type; + } +} + +export default Thumb; diff --git a/frontend/src/pages/album/photos.vue b/frontend/src/pages/album/photos.vue index 6aa24d7b6..b6f7501e7 100644 --- a/frontend/src/pages/album/photos.vue +++ b/frontend/src/pages/album/photos.vue @@ -47,6 +47,7 @@ import Photo from "model/photo"; import Album from "model/album"; import Event from "pubsub-js"; + import Thumb from "../../model/thumb"; export default { name: 'p-page-album-photos', @@ -143,9 +144,9 @@ }, openPhoto(index, showMerged) { if (showMerged) { - this.$viewer.show(this.results[index].expand(), 0) + this.$viewer.show(Thumb.fromFiles([this.results[index]]), 0) } else { - this.$viewer.show(this.results, index); + this.$viewer.show(Thumb.fromPhotos(this.results), index); } }, loadMore() { diff --git a/frontend/src/pages/photos.vue b/frontend/src/pages/photos.vue index da0e49b48..41fac4cc1 100644 --- a/frontend/src/pages/photos.vue +++ b/frontend/src/pages/photos.vue @@ -36,6 +36,7 @@