Frontend: Add thumb model (photo viewer refactoring)

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-04-21 16:34:43 +02:00
parent 9344a52760
commit 3a257684bd
8 changed files with 184 additions and 80 deletions

View file

@ -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;

View file

@ -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];

View file

@ -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);

View file

@ -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
View 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;

View file

@ -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() {

View file

@ -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() {

View file

@ -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);
});
});