|
@@ -23,6 +23,8 @@ Additional information can be found in our Developer Guide:
|
|
|
|
|
|
*/
|
|
|
|
|
|
+import memoizeOne from 'memoize-one';
|
|
|
+
|
|
|
import RestModel from "model/rest";
|
|
|
import File from "model/file";
|
|
|
import Marker from "model/marker";
|
|
@@ -179,17 +181,21 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
classes() {
|
|
|
+ return this.generateClasses(this.isPlayable(), Clipboard.has(this), this.Portrait, this.Favorite, this.Private, this.Files.length > 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ generateClasses = memoizeOne((isPlayable, isInClipboard, portrait, favorite, isPrivate, hasMultipleFiles) => {
|
|
|
let classes = ["is-photo", "uid-" + this.UID, "type-" + this.Type];
|
|
|
|
|
|
- if (this.isPlayable()) classes.push("is-playable");
|
|
|
- if (Clipboard.has(this)) classes.push("is-selected");
|
|
|
- if (this.Portrait) classes.push("is-portrait");
|
|
|
- if (this.Favorite) classes.push("is-favorite");
|
|
|
- if (this.Private) classes.push("is-private");
|
|
|
- if (this.Files.length > 1) classes.push("is-stack");
|
|
|
+ if (isPlayable) classes.push("is-playable");
|
|
|
+ if (isInClipboard) classes.push("is-selected");
|
|
|
+ if (portrait) classes.push("is-portrait");
|
|
|
+ if (favorite) classes.push("is-favorite");
|
|
|
+ if (isPrivate) classes.push("is-private");
|
|
|
+ if (hasMultipleFiles) classes.push("is-stack");
|
|
|
|
|
|
return classes;
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
localDayString() {
|
|
|
if (!this.TakenAtLocal) {
|
|
@@ -295,7 +301,7 @@ export class Photo extends RestModel {
|
|
|
let iso = this.localDateString(time);
|
|
|
let zone = this.getTimeZone();
|
|
|
|
|
|
- if (this.getTimeZone() === "") {
|
|
|
+ if (zone === "") {
|
|
|
zone = "UTC";
|
|
|
}
|
|
|
|
|
@@ -303,9 +309,13 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
utcDate() {
|
|
|
- return DateTime.fromISO(this.TakenAt).toUTC();
|
|
|
+ return this.generateUtcDate(this.TakenAt);
|
|
|
}
|
|
|
|
|
|
+ generateUtcDate = memoizeOne((takenAt) => {
|
|
|
+ return DateTime.fromISO(takenAt).toUTC();
|
|
|
+ })
|
|
|
+
|
|
|
baseName(truncate) {
|
|
|
let result = this.fileBase(this.FileName ? this.FileName : this.mainFile().Name);
|
|
|
|
|
@@ -352,14 +362,18 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
isPlayable() {
|
|
|
- if (this.Type === MediaAnimated) {
|
|
|
+ return this.generateIsPlayable(this.Type, this.Files);
|
|
|
+ }
|
|
|
+
|
|
|
+ generateIsPlayable = memoizeOne((type, files) => {
|
|
|
+ if (type === MediaAnimated) {
|
|
|
return true;
|
|
|
- } else if (!this.Files) {
|
|
|
+ } else if (!files) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- return this.Files.findIndex((f) => f.Video) !== -1;
|
|
|
- }
|
|
|
+ return files.some((f) => f.Video);
|
|
|
+ })
|
|
|
|
|
|
videoParams() {
|
|
|
const uri = this.videoUrl();
|
|
@@ -416,18 +430,22 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
videoFile() {
|
|
|
- if (!this.Files) {
|
|
|
+ return this.getVideoFileFromFiles(this.Files);
|
|
|
+ }
|
|
|
+
|
|
|
+ getVideoFileFromFiles = memoizeOne((files) => {
|
|
|
+ if (!files) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- let file = this.Files.find((f) => f.Codec === CodecAvc1);
|
|
|
+ let file = files.find((f) => f.Codec === CodecAvc1);
|
|
|
|
|
|
if (!file) {
|
|
|
- file = this.Files.find((f) => f.FileType === FormatMp4);
|
|
|
+ file = files.find((f) => f.FileType === FormatMp4);
|
|
|
}
|
|
|
|
|
|
if (!file) {
|
|
|
- file = this.Files.find((f) => !!f.Video);
|
|
|
+ file = files.find((f) => !!f.Video);
|
|
|
}
|
|
|
|
|
|
if (!file) {
|
|
@@ -435,7 +453,7 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
return file;
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
gifFile() {
|
|
|
if (!this.Files) {
|
|
@@ -456,18 +474,22 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
mainFile() {
|
|
|
- if (!this.Files) {
|
|
|
+ return this.getMainFileFromFiles(this.Files)
|
|
|
+ }
|
|
|
+
|
|
|
+ getMainFileFromFiles = memoizeOne((files) => {
|
|
|
+ if (!files) {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- let file = this.Files.find((f) => !!f.Primary);
|
|
|
+ let file = files.find((f) => !!f.Primary);
|
|
|
|
|
|
if (file) {
|
|
|
return file;
|
|
|
}
|
|
|
|
|
|
- return this.Files.find((f) => f.FileType === FormatJpeg);
|
|
|
- }
|
|
|
+ return files.find((f) => f.FileType === FormatJpeg);
|
|
|
+ })
|
|
|
|
|
|
jpegFiles() {
|
|
|
if (!this.Files) {
|
|
@@ -478,18 +500,20 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
mainFileHash() {
|
|
|
- if (this.Files) {
|
|
|
- let file = this.mainFile();
|
|
|
+ return this.generateMainFileHash(this.mainFile(), this.Hash);
|
|
|
+ }
|
|
|
|
|
|
- if (file && file.Hash) {
|
|
|
- return file.Hash;
|
|
|
+ generateMainFileHash = memoizeOne((mainFile, hash) => {
|
|
|
+ if (this.Files) {
|
|
|
+ if (mainFile && mainFile.Hash) {
|
|
|
+ return mainFile.Hash;
|
|
|
}
|
|
|
- } else if (this.Hash) {
|
|
|
- return this.Hash;
|
|
|
+ } else if (hash) {
|
|
|
+ return hash;
|
|
|
}
|
|
|
|
|
|
return "";
|
|
|
- }
|
|
|
+ });
|
|
|
|
|
|
fileModels() {
|
|
|
let result = [];
|
|
@@ -516,20 +540,22 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
thumbnailUrl(size) {
|
|
|
- let hash = this.mainFileHash();
|
|
|
+ return this.generateThumbnailUrl(this.mainFileHash(), this.videoFile(), config.contentUri, config.previewToken(), size);
|
|
|
+ }
|
|
|
|
|
|
- if (!hash) {
|
|
|
- let video = this.videoFile();
|
|
|
+ generateThumbnailUrl = memoizeOne((mainFileHash, videoFile, contentUri, previewToken, size) => {
|
|
|
+ let hash = mainFileHash;
|
|
|
|
|
|
- if (video && video.Hash) {
|
|
|
- return `${config.contentUri}/t/${video.Hash}/${config.previewToken()}/${size}`;
|
|
|
+ if (!hash) {
|
|
|
+ if (videoFile && videoFile.Hash) {
|
|
|
+ return `${contentUri}/t/${videoFile.Hash}/${previewToken}/${size}`;
|
|
|
}
|
|
|
|
|
|
- return `${config.contentUri}/svg/photo`;
|
|
|
+ return `${contentUri}/svg/photo`;
|
|
|
}
|
|
|
|
|
|
- return `${config.contentUri}/t/${hash}/${config.previewToken()}/${size}`;
|
|
|
- }
|
|
|
+ return `${contentUri}/t/${hash}/${previewToken}/${size}`;
|
|
|
+ })
|
|
|
|
|
|
getDownloadUrl() {
|
|
|
return `${config.apiUri}/dl/${this.mainFileHash()}?t=${config.downloadToken()}`;
|
|
@@ -616,33 +642,41 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
getDateString(showTimeZone) {
|
|
|
- if (!this.TakenAt || this.Year === YearUnknown) {
|
|
|
+ return this.generateDateString(showTimeZone, this.TakenAt, this.Year, this.Month, this.Day, this.TimeZone);
|
|
|
+ }
|
|
|
+
|
|
|
+ generateDateString = memoizeOne((showTimeZone, takenAt, year, month, day, timeZone) => {
|
|
|
+ if (!takenAt || year === YearUnknown) {
|
|
|
return $gettext("Unknown");
|
|
|
- } else if (this.Month === MonthUnknown) {
|
|
|
+ } else if (month === MonthUnknown) {
|
|
|
return this.localYearString();
|
|
|
- } else if (this.Day === DayUnknown) {
|
|
|
+ } else if (day === DayUnknown) {
|
|
|
return this.localDate().toLocaleString({
|
|
|
month: long,
|
|
|
year: num,
|
|
|
});
|
|
|
- } else if (this.TimeZone) {
|
|
|
+ } else if (timeZone) {
|
|
|
return this.localDate().toLocaleString(showTimeZone ? DATE_FULL_TZ : DATE_FULL);
|
|
|
}
|
|
|
|
|
|
return this.localDate().toLocaleString(DateTime.DATE_HUGE);
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
- shortDateString() {
|
|
|
- if (!this.TakenAt || this.Year === YearUnknown) {
|
|
|
+ shortDateString = () => {
|
|
|
+ return this.generateShortDateString(this.TakenAt, this.Year, this.Month, this.Day)
|
|
|
+ }
|
|
|
+
|
|
|
+ generateShortDateString = memoizeOne((takenAt, year, month, day) => {
|
|
|
+ if (!takenAt || year === YearUnknown) {
|
|
|
return $gettext("Unknown");
|
|
|
- } else if (this.Month === MonthUnknown) {
|
|
|
+ } else if (month === MonthUnknown) {
|
|
|
return this.localYearString();
|
|
|
- } else if (this.Day === DayUnknown) {
|
|
|
+ } else if (day === DayUnknown) {
|
|
|
return this.localDate().toLocaleString({ month: "long", year: "numeric" });
|
|
|
}
|
|
|
|
|
|
return this.localDate().toLocaleString(DateTime.DATE_MED);
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
hasLocation() {
|
|
|
return this.Lat !== 0 || this.Lng !== 0;
|
|
@@ -660,19 +694,23 @@ export class Photo extends RestModel {
|
|
|
return $gettext("Unknown");
|
|
|
}
|
|
|
|
|
|
- locationInfo() {
|
|
|
- if (this.PlaceID === "zz" && this.Country !== "zz") {
|
|
|
- const country = countries.find((c) => c.Code === this.Country);
|
|
|
+ locationInfo = () => {
|
|
|
+ return this.generateLocationInfo(this.PlaceID, this.Country, this.Place, this.PlaceLabel)
|
|
|
+ }
|
|
|
+
|
|
|
+ generateLocationInfo = memoizeOne((placeId, countryCode, place, placeLabel) => {
|
|
|
+ if (placeId === "zz" && countryCode !== "zz") {
|
|
|
+ const country = countries.find((c) => c.Code === countryCode);
|
|
|
|
|
|
if (country) {
|
|
|
return country.Name;
|
|
|
}
|
|
|
- } else if (this.Place && this.Place.Label) {
|
|
|
- return this.Place.Label;
|
|
|
+ } else if (place && place.Label) {
|
|
|
+ return place.Label;
|
|
|
}
|
|
|
|
|
|
- return this.PlaceLabel ? this.PlaceLabel : $gettext("Unknown");
|
|
|
- }
|
|
|
+ return placeLabel ? placeLabel : $gettext("Unknown");
|
|
|
+ })
|
|
|
|
|
|
addSizeInfo(file, info) {
|
|
|
if (!file) {
|
|
@@ -699,18 +737,19 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- getVideoInfo() {
|
|
|
- let info = [];
|
|
|
- let file = this.videoFile();
|
|
|
-
|
|
|
- if (!file) {
|
|
|
- file = this.mainFile();
|
|
|
- }
|
|
|
+ getVideoInfo = () => {
|
|
|
+ let file = this.videoFile() || this.mainFile();
|
|
|
+ return this.generateVideoInfo(file)
|
|
|
+ }
|
|
|
|
|
|
+ generateVideoInfo = memoizeOne((file) => {
|
|
|
if (!file) {
|
|
|
return $gettext("Video");
|
|
|
}
|
|
|
|
|
|
+ const info = [];
|
|
|
+
|
|
|
+
|
|
|
if (file.Duration > 0) {
|
|
|
info.push(Util.duration(file.Duration));
|
|
|
}
|
|
@@ -726,30 +765,35 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
return info.join(", ");
|
|
|
+ })
|
|
|
+
|
|
|
+ getPhotoInfo = () => {
|
|
|
+ let file = this.videoFile();
|
|
|
+ if (!file || !file.Width) {
|
|
|
+ file = this.mainFile();
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.generatePhotoInfo(this.Camera, this.CameraModel, this.CameraMake, file);
|
|
|
}
|
|
|
|
|
|
- getPhotoInfo() {
|
|
|
+ generatePhotoInfo = memoizeOne((camera, cameraModel, cameraMake, file) => {
|
|
|
let info = [];
|
|
|
|
|
|
- if (this.Camera) {
|
|
|
- if (this.Camera.Model.length > 7) {
|
|
|
- info.push(this.Camera.Model);
|
|
|
+ if (camera) {
|
|
|
+ if (camera.Model.length > 7) {
|
|
|
+ info.push(camera.Model);
|
|
|
} else {
|
|
|
- info.push(this.Camera.Make + " " + this.Camera.Model);
|
|
|
+ info.push(camera.Make + " " + camera.Model);
|
|
|
}
|
|
|
- } else if (this.CameraModel && this.CameraMake) {
|
|
|
- if (this.CameraModel.length > 7) {
|
|
|
- info.push(this.CameraModel);
|
|
|
+ } else if (cameraModel && cameraMake) {
|
|
|
+ if (cameraModel.length > 7) {
|
|
|
+ info.push(cameraModel);
|
|
|
} else {
|
|
|
- info.push(this.CameraMake + " " + this.CameraModel);
|
|
|
+ info.push(cameraMake + " " + cameraModel);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- let file = this.videoFile();
|
|
|
-
|
|
|
- if (!file || !file.Width) {
|
|
|
- file = this.mainFile();
|
|
|
- } else if (file.Codec) {
|
|
|
+ if (file && file.Width && file.Codec) {
|
|
|
info.push(file.Codec.toUpperCase());
|
|
|
}
|
|
|
|
|
@@ -760,7 +804,7 @@ export class Photo extends RestModel {
|
|
|
}
|
|
|
|
|
|
return info.join(", ");
|
|
|
- }
|
|
|
+ })
|
|
|
|
|
|
getCamera() {
|
|
|
if (this.Camera) {
|