Frontend: Add thumb model (photo viewer refactoring)
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
9344a52760
commit
3a257684bd
8 changed files with 184 additions and 80 deletions
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
123
frontend/src/model/thumb.js
Normal file
123
frontend/src/model/thumb.js
Normal file
|
@ -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;
|
|
@ -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() {
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
<script>
|
||||
import Photo from "model/photo";
|
||||
import Thumb from "model/thumb";
|
||||
import Event from "pubsub-js";
|
||||
|
||||
export default {
|
||||
|
@ -151,9 +152,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() {
|
||||
|
|
|
@ -6,7 +6,6 @@ let assert = chai.assert;
|
|||
describe("common/viewer", () => {
|
||||
it("should construct viewer", () => {
|
||||
const viewer = new Viewer();
|
||||
assert.equal(viewer.photos, "");
|
||||
assert.equal(viewer.el, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue