Frontend: Enforce braced if-bodies via ESLint curly rule #4966

This commit is contained in:
Michael Mayer 2026-05-16 07:40:10 +00:00
parent e92c4ee80b
commit bdd33df25b
30 changed files with 667 additions and 224 deletions

View file

@ -84,6 +84,7 @@ export default defineConfig([
},
],
"semi": ["error", "always"],
"curly": ["warn", "all"],
"no-unused-vars": ["warn"],
"no-console": 0,
"no-case-declarations": 0,

View file

@ -229,7 +229,9 @@ export class Clipboard {
}
setIds(ids) {
if (!Array.isArray(ids)) return;
if (!Array.isArray(ids)) {
return;
}
this.selection = ids;
this.selectionMap = {};

View file

@ -402,7 +402,9 @@ export default class Config {
const perms = ["update", "search", "manage", "share", "delete"];
perms.forEach((perm) => {
if (this.deny(resource, perm)) result.push(`disable-${perm}`);
if (this.deny(resource, perm)) {
result.push(`disable-${perm}`);
}
});
return result;

View file

@ -17,7 +17,9 @@ const state = {
function evict(field) {
const slot = state[field];
if (!slot) return;
if (!slot) {
return;
}
slot.data = null;
slot.fetch = null;
}
@ -44,8 +46,12 @@ function fetchAlbums() {
function get(field, fetcher) {
const slot = state[field];
if (slot.data) return Promise.resolve(slot.data);
if (slot.fetch) return slot.fetch;
if (slot.data) {
return Promise.resolve(slot.data);
}
if (slot.fetch) {
return slot.fetch;
}
slot.fetch = fetcher()
.then((data) => {
slot.data = data;

View file

@ -100,7 +100,9 @@ export default class $util {
// spaces and trimmed. Emoji sequences (ZWJ, skin tone, regional indicators)
// are preserved so emoji-only titles round-trip.
static normalizeTitle(s) {
if (s === null || s === undefined) return "";
if (s === null || s === undefined) {
return "";
}
return (
String(s)
.toLowerCase()
@ -116,7 +118,9 @@ export default class $util {
);
}
static slugifyLabelTitle(s) {
if (s === null || s === undefined) return "";
if (s === null || s === undefined) {
return "";
}
return String(s)
.toLowerCase()
.replace(/&/g, "and")
@ -318,8 +322,9 @@ export default class $util {
I: 1,
};
let a;
if (number < 1 || number > 3999) return "";
else {
if (number < 1 || number > 3999) {
return "";
} else {
for (let key in romanNumList) {
a = Math.floor(number / romanNumList[key]);
if (a >= 0) {

View file

@ -358,7 +358,9 @@ const InteractiveTargetSelector = 'button, input, textarea, select, a[href], [ro
// isInteractiveTarget reports whether the touch target is (or sits inside) a tappable widget.
function isInteractiveTarget(target) {
if (!target || typeof target.closest !== "function") return false;
if (!target || typeof target.closest !== "function") {
return false;
}
return target.closest(InteractiveTargetSelector) !== null;
}
@ -366,15 +368,25 @@ function isInteractiveTarget(target) {
// accidental horizontal navigation while the lightbox is active. Scoped to edge bands
// only — inner-area touches and taps on interactive widgets pass through.
export function preventNavigationTouchEvent(ev) {
if (!(ev instanceof TouchEvent) || !ev.cancelable) return;
if (ev.type !== TouchStartEvent && ev.type !== TouchMoveEvent) return;
if (!(ev instanceof TouchEvent) || !ev.cancelable) {
return;
}
if (ev.type !== TouchStartEvent && ev.type !== TouchMoveEvent) {
return;
}
const touch = ev.touches[0] || (ev.changedTouches && ev.changedTouches[0]);
if (!touch) return;
if (!touch) {
return;
}
const atLeftEdge = touch.clientX <= NavGestureEdgeBand;
const atRightEdge = touch.clientX >= window.innerWidth - NavGestureEdgeBand;
const atTopEdge = touch.clientY <= NavGestureEdgeBand;
if (!atLeftEdge && !atRightEdge && !atTopEdge) return;
if (isInteractiveTarget(ev.target)) return;
if (!atLeftEdge && !atRightEdge && !atTopEdge) {
return;
}
if (isInteractiveTarget(ev.target)) {
return;
}
ev.preventDefault();
}

View file

@ -170,9 +170,15 @@ export default {
},
getChipIcon(item) {
if (item.action === "add") return "mdi-plus";
if (item.action === "remove") return "mdi-minus";
if (item.mixed) return "mdi-circle-half-full";
if (item.action === "add") {
return "mdi-plus";
}
if (item.action === "remove") {
return "mdi-minus";
}
if (item.mixed) {
return "mdi-circle-half-full";
}
return null;
},
@ -188,7 +194,9 @@ export default {
},
handleChipClick(item) {
if (this.loading || this.disabled) return;
if (this.loading || this.disabled) {
return;
}
let newAction;
@ -255,14 +263,20 @@ export default {
return;
}
if (!title) return;
if (!title) {
return;
}
let resolvedApplied = false;
if (typeof this.resolveItemFromText === "function") {
const resolved = this.resolveItemFromText(title);
if (resolved && typeof resolved === "object") {
if (resolved.title) title = resolved.title;
if (resolved.value) value = resolved.value;
if (resolved.title) {
title = resolved.title;
}
if (resolved.value) {
value = resolved.value;
}
resolvedApplied = true;
}
}

View file

@ -1564,7 +1564,9 @@ export default {
}
const ok = await this.confirmDiscardSidebar();
if (!ok) return;
if (!ok) {
return;
}
this.closing = true;
@ -1595,7 +1597,9 @@ export default {
// onChange().
wrapPswpNavGuards() {
const pswp = this.pswp();
if (!pswp || pswp.__navGuardsInstalled) return;
if (!pswp || pswp.__navGuardsInstalled) {
return;
}
const origPrev = pswp.prev ? pswp.prev.bind(pswp) : null;
const origNext = pswp.next ? pswp.next.bind(pswp) : null;
if (origPrev) {
@ -1605,7 +1609,9 @@ export default {
return origPrev();
}
const ok = await this.confirmDiscardSidebar();
if (!ok) return;
if (!ok) {
return;
}
this._suppressNavCheck = true;
return origPrev();
};
@ -1617,7 +1623,9 @@ export default {
return origNext();
}
const ok = await this.confirmDiscardSidebar();
if (!ok) return;
if (!ok) {
return;
}
this._suppressNavCheck = true;
return origNext();
};
@ -1749,7 +1757,9 @@ export default {
if (!ok) {
this._suppressNavCheck = true;
const p = this.pswp();
if (p && typeof p.goTo === "function") p.goTo(rollbackIndex);
if (p && typeof p.goTo === "function") {
p.goTo(rollbackIndex);
}
}
});
});
@ -1881,10 +1891,14 @@ export default {
// the freshly-saved row; the overlay re-renders via the `markers`
// computed.
onCreateFaceMarker(area) {
if (!this.photo.UID || !this.shouldShowEditButton() || this.faceMarkers.busy) return;
if (!this.photo.UID || !this.shouldShowEditButton() || this.faceMarkers.busy) {
return;
}
const file = Array.isArray(this.photo.Files) ? this.photo.Files.find((f) => !!f.Primary) : null;
if (!file || !file.UID) return;
if (!file || !file.UID) {
return;
}
const marker = new Marker({
FileUID: file.UID,
@ -1900,7 +1914,9 @@ export default {
marker
.save()
.then(() => {
if (!file.Markers) file.Markers = [];
if (!file.Markers) {
file.Markers = [];
}
file.Markers.push(marker.getValues());
Photo.evictCache(this.photo.UID);
// Trigger inline naming on the fresh row in the sidebar.
@ -1927,8 +1943,12 @@ export default {
// computed, which re-reads `photo.getMarkers(true)` whenever the
// underlying `file.Markers` array is mutated.
onEjectFaceMarker(marker) {
if (!this.photo.UID || !this.shouldShowEditButton() || this.faceMarkers.busy) return;
if (!marker || !marker.SubjUID || typeof marker.clearSubject !== "function") return;
if (!this.photo.UID || !this.shouldShowEditButton() || this.faceMarkers.busy) {
return;
}
if (!marker || !marker.SubjUID || typeof marker.clearSubject !== "function") {
return;
}
this.faceMarkers.setBusy(true);
marker
@ -1950,9 +1970,13 @@ export default {
// leave `file.Markers[idx]` (the raw object the overlay re-derives
// from via `photo.getMarkers(true)`) stale.
syncMarkerInFile(marker) {
if (!marker || !marker.UID || !this.photo.UID || !Array.isArray(this.photo.Files)) return;
if (!marker || !marker.UID || !this.photo.UID || !Array.isArray(this.photo.Files)) {
return;
}
const file = this.photo.Files.find((f) => !!f.Primary);
if (!file || !Array.isArray(file.Markers)) return;
if (!file || !Array.isArray(file.Markers)) {
return;
}
const idx = file.Markers.findIndex((mm) => mm.UID === marker.UID);
if (idx >= 0) {
file.Markers[idx] = typeof marker.getValues === "function" ? marker.getValues() : { ...file.Markers[idx], ...marker };
@ -1963,8 +1987,12 @@ export default {
// cache so future reads see the updated row; the overlay re-renders
// via the `markers` computed.
onReloadFaceMarkers(marker) {
if (marker) this.syncMarkerInFile(marker);
if (this.photo.UID) Photo.evictCache(this.photo.UID);
if (marker) {
this.syncMarkerInFile(marker);
}
if (this.photo.UID) {
Photo.evictCache(this.photo.UID);
}
},
// Handles the overlay's `remove` emit ( on the inline confirm pill
// that appears when the user clicks an unnamed marker in edit mode).
@ -1974,8 +2002,12 @@ export default {
// never reach this handler the overlay's hit-test skips them and
// the backend gate (`marker.SubjUID` truthy) is a defense in depth.
onRemoveFaceMarker(marker) {
if (!this.photo.UID || !this.shouldShowEditButton() || this.faceMarkers.busy) return;
if (!marker || marker.SubjUID || typeof marker.reject !== "function") return;
if (!this.photo.UID || !this.shouldShowEditButton() || this.faceMarkers.busy) {
return;
}
if (!marker || marker.SubjUID || typeof marker.reject !== "function") {
return;
}
const file = Array.isArray(this.photo.Files) ? this.photo.Files.find((f) => !!f.Primary) : null;
const uid = marker.UID;
@ -1986,7 +2018,9 @@ export default {
.then(() => {
if (file && Array.isArray(file.Markers) && uid) {
const idx = file.Markers.findIndex((mm) => mm.UID === uid);
if (idx >= 0) file.Markers.splice(idx, 1);
if (idx >= 0) {
file.Markers.splice(idx, 1);
}
}
Photo.evictCache(this.photo.UID);
})
@ -2568,7 +2602,9 @@ export default {
// typed into sidebar inputs. `preventDefault` makes `_onKeyDown`
// bail before its switch statement.
onPswpKeyDown(ev) {
if (!ev || !this.info) return;
if (!ev || !this.info) {
return;
}
const active = document.activeElement;
if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement || (active && active.isContentEditable)) {
ev.preventDefault();
@ -2579,7 +2615,9 @@ export default {
// navigation reaches sidebar inputs/chips. Vuetify + `$view` re-anchor
// any focus that escapes the modal.
onTabKey(ev) {
if (!ev) return;
if (!ev) {
return;
}
const root = this.$refs.container || this.$refs.content;
const active = document.activeElement;
if (root && active && root.contains(active)) {
@ -2989,7 +3027,9 @@ export default {
}
const ok = await this.confirmDiscardSidebar();
if (!ok) return;
if (!ok) {
return;
}
this.info = false;
// Restore the user's persisted caption preference. The sidebar

View file

@ -177,7 +177,9 @@ export default {
this.$view.leave(this);
},
loadFromPhoto() {
if (!this.photo) return;
if (!this.photo) {
return;
}
this.cameraID = this.photo.CameraID || 0;
this.lensID = this.photo.LensID || 0;

View file

@ -179,7 +179,9 @@ export default {
this.$view.leave(this);
},
loadFromPhoto() {
if (!this.photo) return;
if (!this.photo) {
return;
}
this.day = this.photo.Day;
this.month = this.photo.Month;
@ -191,17 +193,23 @@ export default {
this.invalidDate = false;
},
effectiveYear() {
if (this.year > 0) return this.year;
if (this.year > 0) {
return this.year;
}
const y = this.photo?.TakenAtLocal ? parseInt(this.photo.TakenAtLocal.substring(0, 4)) : new Date().getUTCFullYear();
return isNaN(y) ? new Date().getUTCFullYear() : y;
},
effectiveMonth() {
if (this.month > 0) return this.month;
if (this.month > 0) {
return this.month;
}
const m = this.photo?.TakenAtLocal ? parseInt(this.photo.TakenAtLocal.substring(5, 7)) : new Date().getUTCMonth() + 1;
return isNaN(m) ? new Date().getUTCMonth() + 1 : m;
},
clampDayToValidRange() {
if (this.day <= 0) return;
if (this.day <= 0) {
return;
}
const maxDay = new Date(Date.UTC(this.effectiveYear(), this.effectiveMonth(), 0)).getUTCDate();
if (this.day > maxDay) {
this.day = maxDay;
@ -260,22 +268,32 @@ export default {
this.updateLocalDate();
},
localYearString() {
if (this.year <= 0) return "";
if (this.year <= 0) {
return "";
}
return this.year.toString().padStart(4, "0");
},
localMonthString() {
if (this.month <= 0) return "01";
if (this.month <= 0) {
return "01";
}
return this.month.toString().padStart(2, "0");
},
localDayString() {
if (this.day <= 0) return "01";
if (this.day <= 0) {
return "01";
}
return this.day.toString().padStart(2, "0");
},
updateLocalDate() {
if (!this.photo) return;
if (!this.photo) {
return;
}
const yearStr = this.localYearString();
if (!yearStr) return;
if (!yearStr) {
return;
}
const date = yearStr + "-" + this.localMonthString() + "-" + this.localDayString();
const time = this.time || "12:00:00";
@ -289,7 +307,9 @@ export default {
this.$emit("close");
},
confirm() {
if (this.invalidDate) return;
if (this.invalidDate) {
return;
}
this.$emit("confirm", {
Day: this.day,

View file

@ -159,10 +159,14 @@ export default {
},
computed: {
isValidCoordinateInput() {
if (!this.coordinateInput) return false;
if (!this.coordinateInput) {
return false;
}
const parts = this.coordinateInput.split(",").map((part) => part.trim());
if (parts.length !== 2) return false;
if (parts.length !== 2) {
return false;
}
const lat = parseFloat(parts[0]);
const lng = parseFloat(parts[1]);
@ -216,7 +220,9 @@ export default {
}
},
applyCoordinates() {
if (!this.isValidCoordinateInput) return;
if (!this.isValidCoordinateInput) {
return;
}
const parts = this.coordinateInput.split(",").map((part) => part.trim());
const lat = parseFloat(parts[0]);

View file

@ -817,24 +817,34 @@ export default {
}
const t = String(inputTitle).trim();
if (!t) return null;
if (!t) {
return null;
}
const nt = $util.normalizeTitle(t);
const st = $util.slugifyLabelTitle(t);
let found = this.cachedLabelOptions.find((o) => o.title.toLowerCase() === t.toLowerCase());
if (found) return { value: found.value, title: found.title };
if (found) {
return { value: found.value, title: found.title };
}
found = this.cachedLabelOptions.find((o) => o.slug === st || o.customSlug === st);
if (found) return { value: found.value, title: found.title };
if (found) {
return { value: found.value, title: found.title };
}
found = this.cachedLabelOptions.find((o) => $util.normalizeTitle(o.title) === nt);
if (found) return { value: found.value, title: found.title };
if (found) {
return { value: found.value, title: found.title };
}
return { value: "", title: t };
},
changeValue(newValue, fieldType, fieldName) {
if (!fieldName) return;
if (!fieldName) {
return;
}
const previousValue = this.previousFormData[fieldName].value;
this.formData[fieldName].action = this.actions.update;
@ -860,7 +870,9 @@ export default {
this.getIcon(fieldType, fieldName);
},
changeSelectValue(newValue, fieldType, fieldName) {
if (!fieldName) return;
if (!fieldName) {
return;
}
const previousValue = this.previousFormData[fieldName].value;
this.formData[fieldName].action = this.actions.update;
@ -917,11 +929,15 @@ export default {
return y > 0 && m > 0 && !yearMixed && !monthMixed;
},
clampBatchDayIfResolvable() {
if (!this.isBatchDateResolvable()) return;
if (!this.isBatchDateResolvable()) {
return;
}
const y = this.batchYear();
const m = this.batchMonth();
const d = parseInt(this.formData?.Day?.value, 10);
if (isNaN(d) || d <= 0) return; // Unknown or empty: do nothing in batch UI
if (isNaN(d) || d <= 0) {
return;
} // Unknown or empty: do nothing in batch UI
const maxDay = new Date(Date.UTC(y, m, 0)).getUTCDate();
if (d > maxDay) {
this.formData.Day.value = maxDay;
@ -929,7 +945,9 @@ export default {
}
},
changeToggleValue(newValue, fieldName) {
if (!fieldName) return;
if (!fieldName) {
return;
}
const previousValue = this.previousFormData[fieldName].value;
this.formData[fieldName].action = this.actions.update;
@ -976,7 +994,9 @@ export default {
const formKey = key || name;
const fieldData = this.values[formKey];
if (!fieldData) return;
if (!fieldData) {
return;
}
const { value, placeholder } = this.getFieldData(type, name);
this.formData[formKey] = {
@ -1004,7 +1024,9 @@ export default {
// Set values for toggle fields (boolean fields)
this.toggleFieldsArray.forEach((fieldName) => {
const fieldData = this.values[fieldName];
if (!fieldData) return;
if (!fieldData) {
return;
}
const toggleValue = this.getToggleValue(fieldName);
@ -1062,7 +1084,9 @@ export default {
},
toggleOptions(fieldName) {
const fieldData = this.values[fieldName];
if (!fieldData) return [];
if (!fieldData) {
return [];
}
const result = [
{ text: this.$gettext("Yes"), value: true },
@ -1116,7 +1140,9 @@ export default {
const fieldData = this.values[fieldName];
const isDeleted = this.deletedFields?.[fieldName];
if (!fieldData) return;
if (!fieldData) {
return;
}
const previousField = this.previousFormData[fieldName];
if (this.formData[fieldName].value !== previousField?.value || isDeleted) {
@ -1133,7 +1159,9 @@ export default {
const fieldData = this.values[fieldName];
const isDeleted = this.deletedFields?.[fieldName];
if (!fieldData) return { value: "", placeholder: "", persistent: false };
if (!fieldData) {
return { value: "", placeholder: "", persistent: false };
}
// Helper function to format numeric values
const formatNumericValue = (value) => {

View file

@ -531,20 +531,26 @@ export default {
},
// Returns the effective year used for validation: explicit year or from TakenAtLocal if unknown
effectiveYear() {
if (this.view?.model?.Year && this.view.model.Year > 0) return this.view.model.Year;
if (this.view?.model?.Year && this.view.model.Year > 0) {
return this.view.model.Year;
}
const y = this.view?.model?.TakenAtLocal ? parseInt(this.view.model.TakenAtLocal.substring(0, 4)) : new Date().getUTCFullYear();
return isNaN(y) ? new Date().getUTCFullYear() : y;
},
// Returns the effective month used for validation: explicit month or from TakenAtLocal if unknown
effectiveMonth() {
if (this.view?.model?.Month && this.view.model.Month > 0) return this.view.model.Month;
if (this.view?.model?.Month && this.view.model.Month > 0) {
return this.view.model.Month;
}
const m = this.view?.model?.TakenAtLocal ? parseInt(this.view.model.TakenAtLocal.substring(5, 7)) : new Date().getUTCMonth() + 1;
return isNaN(m) ? new Date().getUTCMonth() + 1 : m;
},
// Clamp day to the maximum valid day of the current effective month/year
clampDayToValidRange() {
const day = this.view?.model?.Day || 0;
if (day <= 0) return; // Unknown day stays unknown
if (day <= 0) {
return;
} // Unknown day stays unknown
const y = this.effectiveYear();
const m = this.effectiveMonth();
// JS Date trick: day 0 of next month yields last day of current month

View file

@ -170,7 +170,9 @@ export default {
}
},
onReject(model) {
if (this.busy || !model) return;
if (this.busy || !model) {
return;
}
this.busy = true;
this.$notify.blockUI("busy");
@ -315,14 +317,18 @@ export default {
});
},
onApprove(model) {
if (this.busy || !model) return;
if (this.busy || !model) {
return;
}
this.busy = true;
model.approve().finally(() => (this.busy = false));
},
onClearSubject(model) {
if (this.busy || !model) return;
if (this.busy || !model) {
return;
}
this.busy = true;
this.$notify.blockUI("busy");

View file

@ -174,7 +174,9 @@ export default {
return this.mode === FaceMarkerEdit;
},
svgStyle() {
if (!this.bounds) return { display: "none" };
if (!this.bounds) {
return { display: "none" };
}
return {
position: "absolute",
left: `${this.bounds.left}px`,
@ -190,7 +192,9 @@ export default {
return this.hoverCursor ? { cursor: this.hoverCursor } : {};
},
confirmStyle() {
if (!this.pending || !this.bounds) return { display: "none" };
if (!this.pending || !this.bounds) {
return { display: "none" };
}
const left = this.bounds.left + this.pending.x + this.pending.w / 2;
const top = this.bounds.top + this.pending.y + this.pending.h;
return {
@ -204,7 +208,9 @@ export default {
// coordinate space. Used to anchor the remove-confirm pill and to
// highlight the target rectangle.
removingMarkerRect() {
if (!this.removingMarker || !this.bounds) return null;
if (!this.removingMarker || !this.bounds) {
return null;
}
const m = this.removingMarker;
return {
x: m.X * this.bounds.width,
@ -215,7 +221,9 @@ export default {
},
removeConfirmStyle() {
const r = this.removingMarkerRect;
if (!r || !this.bounds) return { display: "none" };
if (!r || !this.bounds) {
return { display: "none" };
}
const left = this.bounds.left + r.x + r.w / 2;
const top = this.bounds.top + r.y + r.h;
return {
@ -270,7 +278,9 @@ export default {
methods: {
imageElement() {
const el = this.pswp?.currSlide?.content?.element;
if (el instanceof HTMLImageElement) return el;
if (el instanceof HTMLImageElement) {
return el;
}
if (el && typeof el.querySelector === "function") {
return el.querySelector("img.pswp__image");
}
@ -287,7 +297,9 @@ export default {
this._loadListenedImg = null;
return;
}
if (this._loadListenedImg === img) return;
if (this._loadListenedImg === img) {
return;
}
this.detachImageLoadListener();
this._loadListenedImg = img;
this._onImgLoad = () => this.scheduleUpdate();
@ -301,7 +313,9 @@ export default {
this._onImgLoad = null;
},
attachPswpListeners() {
if (!this.pswp || typeof this.pswp.on !== "function") return;
if (!this.pswp || typeof this.pswp.on !== "function") {
return;
}
this._onZoomPan = () => this.scheduleUpdate();
this._onChange = () => {
this.attachImageLoadListener();
@ -314,16 +328,24 @@ export default {
this.pswp.on("imageClickAction", this._onChange);
},
detachPswpListeners() {
if (!this.pswp || typeof this.pswp.off !== "function") return;
if (this._onZoomPan) this.pswp.off("zoomPanUpdate", this._onZoomPan);
if (!this.pswp || typeof this.pswp.off !== "function") {
return;
}
if (this._onZoomPan) {
this.pswp.off("zoomPanUpdate", this._onZoomPan);
}
if (this._onChange) {
this.pswp.off("change", this._onChange);
this.pswp.off("imageClickAction", this._onChange);
}
if (this._onResize) this.pswp.off("resize", this._onResize);
if (this._onResize) {
this.pswp.off("resize", this._onResize);
}
},
scheduleUpdate() {
if (this.rafHandle) return;
if (this.rafHandle) {
return;
}
this.rafHandle = requestAnimationFrame(() => {
this.rafHandle = null;
this.updateBounds();
@ -332,13 +354,17 @@ export default {
updateBounds() {
const img = this.imageElement();
if (!img || !this.$refs.root) {
if (this.bounds !== null) this.bounds = null;
if (this.bounds !== null) {
this.bounds = null;
}
return;
}
const imgRect = img.getBoundingClientRect();
const parentRect = this.$refs.root.getBoundingClientRect();
if (imgRect.width <= 0 || imgRect.height <= 0) {
if (this.bounds !== null) this.bounds = null;
if (this.bounds !== null) {
this.bounds = null;
}
return;
}
// getBoundingClientRect returns the <img> box, not the letterboxed pixel
@ -378,17 +404,25 @@ export default {
this.bounds = { left, top, width, height };
},
onPointerDown(ev) {
if (!this.isEditMode) return;
if (!this.isEditMode) {
return;
}
if (!this.bounds) {
this.updateBounds();
if (!this.bounds) return;
if (!this.bounds) {
return;
}
}
if (ev.button !== undefined && ev.button !== 0) return;
if (ev.button !== undefined && ev.button !== 0) {
return;
}
const local = this.toLocal(ev.clientX, ev.clientY);
if (!this.insideBounds(local)) return;
if (!this.insideBounds(local)) {
return;
}
if (this.pending) {
const corner = this.hitTestCorner(local, this.pending);
@ -415,7 +449,9 @@ export default {
// Clicking outside a marker cancels any pending remove pill so a
// fresh draw can start from the same gesture without a prior
// click "stealing" focus.
if (this.removingMarker) this.removingMarker = null;
if (this.removingMarker) {
this.removingMarker = null;
}
this.stopEventFromPswp(ev);
this.pending = null;
@ -429,16 +465,22 @@ export default {
// Returns the first unnamed marker whose pixel rect contains the
// given local point, or null if none. Named markers are skipped.
findMarkerAt(local) {
if (!this.bounds || !Array.isArray(this.markers)) return null;
if (!this.bounds || !Array.isArray(this.markers)) {
return null;
}
for (const m of this.markers) {
if (!m || m.SubjUID) continue;
if (!m || m.SubjUID) {
continue;
}
const rect = {
x: m.X * this.bounds.width,
y: m.Y * this.bounds.height,
w: m.W * this.bounds.width,
h: m.H * this.bounds.height,
};
if (this.insidePending(local, rect)) return m;
if (this.insidePending(local, rect)) {
return m;
}
}
return null;
},
@ -447,7 +489,9 @@ export default {
// from the updated photo state.
onConfirmRemove() {
const m = this.removingMarker;
if (!m) return;
if (!m) {
return;
}
this.removingMarker = null;
this.$emit("remove", m);
},
@ -456,8 +500,12 @@ export default {
this.removingMarker = null;
},
onPointerMove(ev) {
if (!this.interaction || !this.dragStart || !this.bounds) return;
if (this.pointerId !== null && ev.pointerId !== this.pointerId) return;
if (!this.interaction || !this.dragStart || !this.bounds) {
return;
}
if (this.pointerId !== null && ev.pointerId !== this.pointerId) {
return;
}
const local = this.toLocal(ev.clientX, ev.clientY);
const cx = Math.max(0, Math.min(this.bounds.width, local.x));
@ -465,15 +513,25 @@ export default {
if (this.interaction === InteractionMove) {
const origin = this.dragStart.pending;
if (!origin) return;
if (!origin) {
return;
}
const dx = local.x - this.dragStart.local.x;
const dy = local.y - this.dragStart.local.y;
let nx = origin.x + dx;
let ny = origin.y + dy;
if (nx < 0) nx = 0;
if (ny < 0) ny = 0;
if (nx + origin.w > this.bounds.width) nx = this.bounds.width - origin.w;
if (ny + origin.h > this.bounds.height) ny = this.bounds.height - origin.h;
if (nx < 0) {
nx = 0;
}
if (ny < 0) {
ny = 0;
}
if (nx + origin.w > this.bounds.width) {
nx = this.bounds.width - origin.w;
}
if (ny + origin.h > this.bounds.height) {
ny = this.bounds.height - origin.h;
}
this.pending = { x: nx, y: ny, w: origin.w, h: origin.h };
return;
}
@ -497,8 +555,12 @@ export default {
let sw = side;
let sh = side;
if (signX < 0) sx = this.dragStart.local.x - side;
if (signY < 0) sy = this.dragStart.local.y - side;
if (signX < 0) {
sx = this.dragStart.local.x - side;
}
if (signY < 0) {
sy = this.dragStart.local.y - side;
}
if (sx < 0) {
sw += sx;
@ -521,8 +583,12 @@ export default {
sh -= over;
}
if (sw < 0) sw = 0;
if (sh < 0) sh = 0;
if (sw < 0) {
sw = 0;
}
if (sh < 0) {
sh = 0;
}
if (this.interaction === InteractionResize) {
this.pending = { x: sx, y: sy, w: sw, h: sh };
@ -531,8 +597,12 @@ export default {
}
},
onPointerUp(ev) {
if (!this.interaction) return;
if (this.pointerId !== null && ev && ev.pointerId !== this.pointerId) return;
if (!this.interaction) {
return;
}
if (this.pointerId !== null && ev && ev.pointerId !== this.pointerId) {
return;
}
this.detachWindowPointerListeners();
@ -547,7 +617,9 @@ export default {
// Move/resize have already written the up-to-date `pending`; only
// the draw path needs to promote its draft into pending.
if (wasInteraction !== InteractionDraw) return;
if (wasInteraction !== InteractionDraw) {
return;
}
if (!draft || !this.bounds || draft.w < MIN_DRAW_SIZE || draft.h < MIN_DRAW_SIZE) {
return;
@ -556,11 +628,15 @@ export default {
this.pending = draft;
},
onConfirmPending() {
if (this.busy) return;
if (this.busy) {
return;
}
const pending = this.pending;
const bounds = this.bounds;
if (!pending || !bounds) return;
if (!pending || !bounds) {
return;
}
const nx = pending.x / bounds.width;
const ny = pending.y / bounds.height;
@ -607,7 +683,9 @@ export default {
},
// handleEnter mirrors a click; no-op during draft / drag / remove-confirm.
handleEnter() {
if (this.busy || this.interaction || this.removingMarker || !this.pending) return;
if (this.busy || this.interaction || this.removingMarker || !this.pending) {
return;
}
this.onConfirmPending();
},
// handleEscape cancels in-progress draw/move/resize or clears the pending
@ -623,7 +701,9 @@ export default {
}
if (this.interaction === InteractionMove || this.interaction === InteractionResize) {
const snapshot = this.dragStart && this.dragStart.pending;
if (snapshot) this.pending = { ...snapshot };
if (snapshot) {
this.pending = { ...snapshot };
}
this.interaction = null;
this.resizeCorner = null;
this.pointerId = null;
@ -642,8 +722,12 @@ export default {
return false;
},
stopEventFromPswp(ev) {
if (typeof ev.stopPropagation === "function") ev.stopPropagation();
if (typeof ev.preventDefault === "function" && ev.cancelable !== false) ev.preventDefault();
if (typeof ev.stopPropagation === "function") {
ev.stopPropagation();
}
if (typeof ev.preventDefault === "function" && ev.cancelable !== false) {
ev.preventDefault();
}
},
attachWindowPointerListeners() {
window.addEventListener("pointermove", this.onPointerMove);
@ -665,7 +749,9 @@ export default {
};
for (const key of Object.keys(corners)) {
const c = corners[key];
if (Math.hypot(p.x - c.x, p.y - c.y) <= r) return key;
if (Math.hypot(p.x - c.x, p.y - c.y) <= r) {
return key;
}
}
return null;
},
@ -676,12 +762,19 @@ export default {
// math in onPointerMove works the same way as for the draw path.
beginResize(corner, ev) {
const p = this.pending;
if (!p) return;
if (!p) {
return;
}
let anchor;
if (corner === "tl") anchor = { x: p.x + p.w, y: p.y + p.h };
else if (corner === "tr") anchor = { x: p.x, y: p.y + p.h };
else if (corner === "bl") anchor = { x: p.x + p.w, y: p.y };
else anchor = { x: p.x, y: p.y };
if (corner === "tl") {
anchor = { x: p.x + p.w, y: p.y + p.h };
} else if (corner === "tr") {
anchor = { x: p.x, y: p.y + p.h };
} else if (corner === "bl") {
anchor = { x: p.x + p.w, y: p.y };
} else {
anchor = { x: p.x, y: p.y };
}
this.stopEventFromPswp(ev);
this.interaction = InteractionResize;
@ -696,45 +789,65 @@ export default {
this.attachWindowPointerListeners();
},
onHoverMove(ev) {
if (!this.isEditMode || this.interaction) return;
if (!this.isEditMode || this.interaction) {
return;
}
if (!this.bounds) {
if (this.hoverCursor !== null) this.hoverCursor = null;
if (this.hoverCursor !== null) {
this.hoverCursor = null;
}
return;
}
const local = this.toLocal(ev.clientX, ev.clientY);
if (!this.insideBounds(local)) {
if (this.hoverCursor !== null) this.hoverCursor = null;
if (this.hoverCursor !== null) {
this.hoverCursor = null;
}
return;
}
if (this.pending) {
const corner = this.hitTestCorner(local, this.pending);
if (corner) {
const c = corner === "tl" || corner === "br" ? "nwse-resize" : "nesw-resize";
if (this.hoverCursor !== c) this.hoverCursor = c;
if (this.hoverCursor !== c) {
this.hoverCursor = c;
}
return;
}
if (this.insidePending(local, this.pending)) {
if (this.hoverCursor !== "move") this.hoverCursor = "move";
if (this.hoverCursor !== "move") {
this.hoverCursor = "move";
}
return;
}
}
// Hovering an unnamed marker rect: signal it is clickable for
// removal. Named markers fall through to the default cursor.
if (this.findMarkerAt(local)) {
if (this.hoverCursor !== "pointer") this.hoverCursor = "pointer";
if (this.hoverCursor !== "pointer") {
this.hoverCursor = "pointer";
}
return;
}
if (this.hoverCursor !== null) this.hoverCursor = null;
if (this.hoverCursor !== null) {
this.hoverCursor = null;
}
},
onHoverLeave() {
if (this.hoverCursor !== null) this.hoverCursor = null;
if (this.hoverCursor !== null) {
this.hoverCursor = null;
}
},
// onWheel re-dispatches wheel events on PhotoSwipe's element while in edit
// mode (overlay's pointer-events: auto would otherwise swallow zoom gestures).
onWheel(ev) {
if (!this.isEditMode) return;
if (!this.isEditMode) {
return;
}
const pswpEl = this.pswp?.element;
if (!pswpEl) return;
if (!pswpEl) {
return;
}
if (typeof ev.preventDefault === "function" && ev.cancelable !== false) {
ev.preventDefault();
}
@ -757,7 +870,9 @@ export default {
},
beginMove(local, ev) {
const p = this.pending;
if (!p) return;
if (!p) {
return;
}
this.stopEventFromPswp(ev);
this.interaction = InteractionMove;
this.resizeCorner = null;
@ -771,7 +886,9 @@ export default {
this.attachWindowPointerListeners();
},
toLocal(clientX, clientY) {
if (!this.bounds || !this.$refs.root) return { x: 0, y: 0 };
if (!this.bounds || !this.$refs.root) {
return { x: 0, y: 0 };
}
const rect = this.$refs.root.getBoundingClientRect();
return {
x: clientX - rect.left - this.bounds.left,
@ -782,8 +899,12 @@ export default {
return this.bounds && p.x >= 0 && p.y >= 0 && p.x <= this.bounds.width && p.y <= this.bounds.height;
},
clamp01(v) {
if (v < 0) return 0;
if (v >= 1) return 0.999999;
if (v < 0) {
return 0;
}
if (v >= 1) {
return 0.999999;
}
return v;
},
},

View file

@ -288,7 +288,9 @@ export default {
},
watch: {
search(q) {
if (this.loading) return;
if (this.loading) {
return;
}
const exists = this.paths.findIndex((p) => p.abs === q);

View file

@ -660,15 +660,21 @@ export default {
// `photo.Caption` as the empty-string default must fall through to
// `model.Caption`.
const raw = this.photo?.Caption || this.model?.Caption;
if (!raw) return "";
if (!raw) {
return "";
}
return this.$util.sanitizeHtml(this.$util.encodeHTML(raw));
},
notesHtml() {
if (!this.photo?.Details?.Notes) return "";
if (!this.photo?.Details?.Notes) {
return "";
}
return this.$util.sanitizeHtml(this.$util.encodeHTML(this.photo.Details.Notes));
},
cameraInfo() {
if (!this.photo) return "";
if (!this.photo) {
return "";
}
// Backend returns the "Unknown" placeholder camera (CameraID=1,
// Camera={Make:"", Model:"Unknown"}) when no EXIF camera is set, and
// formatCamera() happily renders that as " Unknown". Suppress it so
@ -677,26 +683,38 @@ export default {
(this.photo.CameraID && this.photo.CameraID > 1) ||
(this.photo.CameraMake && this.photo.CameraMake.trim()) ||
(this.photo.CameraModel && this.photo.CameraModel.trim() && this.photo.CameraModel !== "Unknown");
if (!hasRealCamera) return "";
if (!hasRealCamera) {
return "";
}
// Suppress "Unknown, ISO 100"-style rows when only ISO/exposure are set.
if (!this.$util.formatCamera(this.photo.Camera, this.photo.CameraID, this.photo.CameraMake, this.photo.CameraModel, false)) return "";
if (!this.$util.formatCamera(this.photo.Camera, this.photo.CameraID, this.photo.CameraMake, this.photo.CameraModel, false)) {
return "";
}
const info = this.photo.getCameraInfo();
return info !== this.$gettext("Unknown") ? info : "";
},
lensInfo() {
if (!this.photo) return "";
if (!this.photo) {
return "";
}
const hasLens =
(this.photo.LensID && this.photo.LensID > 1) || this.photo.LensMake || this.photo.LensModel || this.photo.Lens?.Model || this.photo.Lens?.Make;
if (!hasLens) return "";
if (!hasLens) {
return "";
}
const info = this.photo.getLensInfo();
return info !== this.$gettext("Unknown") ? info : "";
},
exifInfo() {
if (!this.photo) return "";
if (!this.photo) {
return "";
}
return this.photo.getExifInfo();
},
people() {
if (!this.photo) return [];
if (!this.photo) {
return [];
}
return this.photo.getMarkers(true);
},
// Sorted, locale-aware copy of `$config.values.people` for the marker
@ -704,14 +722,18 @@ export default {
// `people.{created,updated,deleted}` events.
knownPeople() {
const values = this.$config && this.$config.values;
if (!values || !Array.isArray(values.people)) return [];
if (!values || !Array.isArray(values.people)) {
return [];
}
return values.people
.filter((p) => p && p.Name)
.slice()
.sort((a, b) => (a.Name || "").localeCompare(b.Name || "", undefined, { sensitivity: "base", numeric: true }));
},
labels() {
if (!this.photo?.Labels) return [];
if (!this.photo?.Labels) {
return [];
}
// Sort by name the backend orders by uncertainty/topicality but
// the sidebar doesn't surface those scores.
return this.photo.Labels.filter((l) => l.Label && l.Label.Name && l.Uncertainty < 100)
@ -719,7 +741,9 @@ export default {
.sort((a, b) => (a.Label.Name || "").localeCompare(b.Label.Name || "", undefined, { sensitivity: "base", numeric: true }));
},
albums() {
if (!this.photo?.Albums) return [];
if (!this.photo?.Albums) {
return [];
}
return this.photo.Albums.filter((a) => a.Title && !a.Private)
.slice()
.sort((a, b) => (a.Title || "").localeCompare(b.Title || "", undefined, { sensitivity: "base", numeric: true }));
@ -774,7 +798,9 @@ export default {
label: this.$pgettext("Photo", "Title"),
read: (p) => p?.Title,
write: (p, v) => {
if (p) p.Title = v;
if (p) {
p.Title = v;
}
},
display: "text",
maxLength: PhotoMaxLength.Title,
@ -784,7 +810,9 @@ export default {
label: this.$gettext("Caption"),
read: (p) => p?.Caption,
write: (p, v) => {
if (p) p.Caption = v;
if (p) {
p.Caption = v;
}
},
display: "html",
htmlValue: "captionHtml",
@ -796,7 +824,9 @@ export default {
icon: "mdi-flower-tulip",
read: (p) => p?.Details?.Subject,
write: (p, v) => {
if (p?.Details) p.Details.Subject = v;
if (p?.Details) {
p.Details.Subject = v;
}
},
display: "text",
maxLength: PhotoMaxLength.Subject,
@ -808,7 +838,9 @@ export default {
icon: "mdi-copyright",
read: (p) => p?.Details?.Copyright,
write: (p, v) => {
if (p?.Details) p.Details.Copyright = v;
if (p?.Details) {
p.Details.Copyright = v;
}
},
display: "text",
maxLength: PhotoMaxLength.Copyright,
@ -820,7 +852,9 @@ export default {
icon: "mdi-account-tie",
read: (p) => p?.Details?.Artist,
write: (p, v) => {
if (p?.Details) p.Details.Artist = v;
if (p?.Details) {
p.Details.Artist = v;
}
},
display: "text",
maxLength: PhotoMaxLength.Artist,
@ -832,7 +866,9 @@ export default {
icon: "mdi-scale-balance",
read: (p) => p?.Details?.License,
write: (p, v) => {
if (p?.Details) p.Details.License = v;
if (p?.Details) {
p.Details.License = v;
}
},
display: "text",
maxLength: PhotoMaxLength.License,
@ -844,7 +880,9 @@ export default {
icon: "mdi-tag-multiple-outline",
read: (p) => p?.Details?.Keywords,
write: (p, v) => {
if (p?.Details) p.Details.Keywords = v;
if (p?.Details) {
p.Details.Keywords = v;
}
},
display: "text",
maxLength: PhotoMaxLength.Keywords,
@ -857,7 +895,9 @@ export default {
icon: null,
read: (p) => p?.Details?.Notes,
write: (p, v) => {
if (p?.Details) p.Details.Notes = v;
if (p?.Details) {
p.Details.Notes = v;
}
},
display: "html",
htmlValue: "notesHtml",
@ -911,7 +951,9 @@ export default {
// combined Location row. Users without places-view ACL see only the
// lat/lng so altitude isn't leaked through the sidebar.
coordinatesLine() {
if (!this.model?.Lat || !this.model?.Lng) return "";
if (!this.model?.Lat || !this.model?.Lng) {
return "";
}
const coords = this.model.getLatLngShort();
if (this.altitude && this.canViewPlaces) {
return `${coords}\u2002${this.altitude}`;
@ -1048,7 +1090,9 @@ export default {
},
},
newMarkerUid(uid) {
if (!uid) return;
if (!uid) {
return;
}
this.$nextTick(() => this.focusMarkerInput(uid));
},
},
@ -1061,8 +1105,12 @@ export default {
// isEditable already requires canViewLibrary so we don't repeat the
// ACL check here; only preload the lists the user is allowed to see.
if (this.isEditable) {
if (this.canViewLabels) this.loadChipOptions("labels");
if (this.canViewAlbums) this.loadChipOptions("albums");
if (this.canViewLabels) {
this.loadChipOptions("labels");
}
if (this.canViewAlbums) {
this.loadChipOptions("albums");
}
}
},
methods: {
@ -1209,7 +1257,9 @@ export default {
this.$nextTick(() => {
const editor = this._inlineEditorEl;
if (editor && typeof editor.focus === "function") editor.focus();
if (editor && typeof editor.focus === "function") {
editor.focus();
}
});
},
// Eye-icon click handler (non-editable users only display mode
@ -1279,7 +1329,9 @@ export default {
}
});
Object.keys(this.markerDrafts).forEach((uid) => {
if (!seen.has(uid)) delete this.markerDrafts[uid];
if (!seen.has(uid)) {
delete this.markerDrafts[uid];
}
});
},
markerInputValue(uid) {
@ -1312,7 +1364,9 @@ export default {
this.$emit("naming-started");
this.$nextTick(() => {
const input = this.$el && this.$el.querySelector(`[data-marker-uid="${uid}"] input`);
if (input) input.focus();
if (input) {
input.focus();
}
});
},
// Match a typed name against knownPeople case-insensitively so the backend
@ -1426,7 +1480,9 @@ export default {
this.addNameDialog = { visible: false, markerUid: "", name: "" };
if (markerUid && name) {
const marker = this.findMarker(markerUid);
if (marker) this.commitMarkerName(marker, this.findKnownPerson(name), name);
if (marker) {
this.commitMarkerName(marker, this.findKnownPerson(name), name);
}
}
this.resolveAddNameNav();
},
@ -1445,7 +1501,9 @@ export default {
resolveAddNameNav() {
const resolve = this._addNameNavResolver;
this._addNameNavResolver = null;
if (resolve) resolve(true);
if (resolve) {
resolve(true);
}
},
cancelMarkerName(marker) {
if (!marker || !marker.UID) {
@ -1463,7 +1521,9 @@ export default {
// marker's input (P1-9) rather than document.activeElement so an
// unrelated focused element isn't blurred by mistake.
const input = this.$el && this.$el.querySelector(`[data-marker-uid="${marker.UID}"] input`);
if (input && typeof input.blur === "function") input.blur();
if (input && typeof input.blur === "function") {
input.blur();
}
},
resetInlineEdits() {
if (this.editingField) {
@ -1497,7 +1557,9 @@ export default {
hasPendingEdit() {
for (const uid of Object.keys(this.markerDrafts)) {
const d = this.markerDrafts[uid];
if (!d) continue;
if (!d) {
continue;
}
if (this.unwrapMarkerName(d.current).trim() !== (d.original || "").trim()) {
return true;
}
@ -1531,12 +1593,18 @@ export default {
flushDirtyMarkerDrafts() {
Object.keys(this.markerDrafts).forEach((uid) => {
const draft = this.markerDrafts[uid];
if (!draft) return;
if (!draft) {
return;
}
const name = this.unwrapMarkerName(draft.current).trim();
const original = (draft.original || "").trim();
if (!name || name === original) return;
if (!name || name === original) {
return;
}
const marker = this.findMarker(uid);
if (marker) this.confirmMarkerName(marker, "blur");
if (marker) {
this.confirmMarkerName(marker, "blur");
}
});
},
// Async guard used by the lightbox before closing / hiding / navigating.
@ -1556,7 +1624,9 @@ export default {
this._addNameNavResolver = resolve;
});
}
if (!this.hasPendingEdit()) return Promise.resolve(true);
if (!this.hasPendingEdit()) {
return Promise.resolve(true);
}
if (this.discardDialog.visible && this.discardDialog.resolver) {
// Another request is already waiting on the dialog; reuse it.
return new Promise((resolve) => {
@ -1577,13 +1647,17 @@ export default {
const r = this.discardDialog.resolver;
this.discardDialog.resolver = null;
this.resetInlineEdits();
if (r) r(true);
if (r) {
r(true);
}
},
onDiscardCancel() {
this.discardDialog.visible = false;
const r = this.discardDialog.resolver;
this.discardDialog.resolver = null;
if (r) r(false);
if (r) {
r(false);
}
},
confirmField() {
if (!this.photo || !this.canEdit) {
@ -1640,7 +1714,9 @@ export default {
if (this._editStartedAt && Date.now() - this._editStartedAt < 200) {
return;
}
if (!this.editingField) return;
if (!this.editingField) {
return;
}
this.confirmField();
},
formatTime(model) {
@ -2092,7 +2168,9 @@ export default {
photo
.update()
.then(() => {
if (!this.view?.model) return;
if (!this.view?.model) {
return;
}
this.view.model.TakenAtLocal = photo.TakenAtLocal;
this.view.model.TimeZone = photo.TimeZone;
})

View file

@ -51,9 +51,15 @@ export class Album extends Collection {
classes(selected) {
let classes = ["is-album", "uid-" + this.UID, "type-" + this.Type];
if (this.Favorite) classes.push("is-favorite");
if (this.Private) classes.push("is-private");
if (selected) classes.push("is-selected");
if (this.Favorite) {
classes.push("is-favorite");
}
if (this.Private) {
classes.push("is-private");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -47,8 +47,12 @@ export class Face extends RestModel {
classes(selected) {
let classes = ["is-face", "uid-" + this.ID];
if (this.Hidden) classes.push("is-hidden");
if (selected) classes.push("is-selected");
if (this.Hidden) {
classes.push("is-hidden");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -64,10 +64,18 @@ export class File extends RestModel {
classes(selected) {
let classes = ["is-file", "uid-" + this.UID];
if (this.Primary) classes.push("is-primary");
if (this.Sidecar) classes.push("is-sidecar");
if (this.Video) classes.push("is-video");
if (selected) classes.push("is-selected");
if (this.Primary) {
classes.push("is-primary");
}
if (this.Sidecar) {
classes.push("is-sidecar");
}
if (this.Video) {
classes.push("is-video");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -38,9 +38,15 @@ export class Folder extends RestModel {
classes(selected) {
let classes = ["is-folder", "uid-" + this.UID];
if (this.Favorite) classes.push("is-favorite");
if (this.Private) classes.push("is-private");
if (selected) classes.push("is-selected");
if (this.Favorite) {
classes.push("is-favorite");
}
if (this.Private) {
classes.push("is-private");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -35,8 +35,12 @@ export class Label extends Collection {
classes(selected) {
let classes = ["is-label", "uid-" + this.UID];
if (this.Favorite) classes.push("is-favorite");
if (selected) classes.push("is-selected");
if (this.Favorite) {
classes.push("is-favorite");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -39,9 +39,15 @@ export class Marker extends RestModel {
classes(selected) {
let classes = ["is-marker", "uid-" + this.getId()];
if (this.Invalid) classes.push("is-invalid");
if (this.Review) classes.push("is-review");
if (selected) classes.push("is-selected");
if (this.Invalid) {
classes.push("is-invalid");
}
if (this.Review) {
classes.push("is-review");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -51,7 +51,9 @@ export class Model {
// (used when a partial update should not reset object diffs). The reserved
// key "__originalValues" is always ignored. No-op for falsy `values`.
setValues(values, scalarOnly) {
if (!values) return;
if (!values) {
return;
}
for (let key in values) {
if (values.hasOwnProperty(key) && key !== "__originalValues") {

View file

@ -158,12 +158,24 @@ export class Photo extends RestModel {
generateClasses = memoizeOne((isPlayable, isInClipboard, portrait, favorite, isPrivate, isStack) => {
let classes = ["is-photo", "uid-" + this.UID, "type-" + this.Type];
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 (isStack) 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 (isStack) {
classes.push("is-stack");
}
return classes;
});
@ -762,27 +774,35 @@ export class Photo extends RestModel {
// Originals only?
if (s.download.originals && file.Root.length > 1) {
// Don't download broken files and sidecars.
if ($config.debug) console.log(`download: skipped ${file.Root} file ${file.Name}`);
if ($config.debug) {
console.log(`download: skipped ${file.Root} file ${file.Name}`);
}
return;
}
// Skip metadata sidecar files?
if (!s.download.mediaSidecar && (file.MediaType === media.Sidecar || file.Sidecar)) {
// Don't download broken files and sidecars.
if ($config.debug) console.log(`download: skipped sidecar file ${file.Name}`);
if ($config.debug) {
console.log(`download: skipped sidecar file ${file.Name}`);
}
return;
}
// Skip RAW images?
if (!s.download.mediaRaw && (file.MediaType === media.Raw || file.FileType === media.Raw)) {
if ($config.debug) console.log(`download: skipped raw file ${file.Name}`);
if ($config.debug) {
console.log(`download: skipped raw file ${file.Name}`);
}
return;
}
// If this is a video, always skip stacked images...
// see https://github.com/photoprism/photoprism/issues/1436
if (this.Type === media.Video && !(file.MediaType === media.Video || file.Video)) {
if ($config.debug) console.log(`download: skipped video sidecar ${file.Name}`);
if ($config.debug) {
console.log(`download: skipped video sidecar ${file.Name}`);
}
return;
}
@ -1102,10 +1122,18 @@ export class Photo extends RestModel {
getExifInfo() {
const parts = [];
if (this.FocalLength) parts.push(this.FocalLength + "mm");
if (this.FNumber) parts.push("\u0192/" + this.FNumber);
if (this.Iso) parts.push("ISO " + this.Iso);
if (this.Exposure) parts.push(this.Exposure);
if (this.FocalLength) {
parts.push(this.FocalLength + "mm");
}
if (this.FNumber) {
parts.push("\u0192/" + this.FNumber);
}
if (this.Iso) {
parts.push("ISO " + this.Iso);
}
if (this.Exposure) {
parts.push(this.Exposure);
}
return parts.join(" \u2022 ");
}
@ -1210,7 +1238,9 @@ export class Photo extends RestModel {
// Distinct from Thumb.addToAlbum (grid layer, Removed flag); both contracts
// are pinned in tests.
addToAlbum(albumUID) {
if (!albumUID) return Promise.resolve(this);
if (!albumUID) {
return Promise.resolve(this);
}
return $api
.post(`albums/${albumUID}/photos`, { photos: [this.UID] })
.then(() => {
@ -1222,7 +1252,9 @@ export class Photo extends RestModel {
// removeFromAlbum mirrors addToAlbum's evict + refind pattern.
removeFromAlbum(albumUID) {
if (!albumUID) return Promise.resolve(this);
if (!albumUID) {
return Promise.resolve(this);
}
return $api
.delete(`albums/${albumUID}/photos`, { data: { photos: [this.UID] } })
.then(() => {
@ -1380,7 +1412,9 @@ export class Photo extends RestModel {
const start = Math.max(0, index - before);
const end = Math.min(models.length - 1, index + after);
for (let i = start; i <= end; i++) {
if (i === index) continue;
if (i === index) {
continue;
}
const uid = models[i]?.UID;
if (uid) {
tasks.push(Photo.prefetch(uid));

View file

@ -12,7 +12,9 @@ export class Settings extends Model {
}
setValues(values, scalarOnly) {
if (!values) return;
if (!values) {
return;
}
if (values.maps?.style === "basic" || values.maps?.style === "offline") {
values.maps.style = "";

View file

@ -46,11 +46,21 @@ export class Subject extends Collection {
classes(selected) {
let classes = ["is-subject", "uid-" + this.UID];
if (this.Favorite) classes.push("is-favorite");
if (this.Hidden) classes.push("is-hidden");
if (this.Private) classes.push("is-private");
if (this.Excluded) classes.push("is-excluded");
if (selected) classes.push("is-selected");
if (this.Favorite) {
classes.push("is-favorite");
}
if (this.Hidden) {
classes.push("is-hidden");
}
if (this.Private) {
classes.push("is-private");
}
if (this.Excluded) {
classes.push("is-excluded");
}
if (selected) {
classes.push("is-selected");
}
return classes;
}

View file

@ -310,7 +310,9 @@ export default {
this.loadMore();
},
loadMore() {
if (this.scrollDisabled) return;
if (this.scrollDisabled) {
return;
}
if (this.offset === 0) {
this.loading = true;

View file

@ -569,7 +569,9 @@ export default {
});
},
onShow(model) {
if (this.busy || !model) return;
if (this.busy || !model) {
return;
}
this.busy = true;
model.show().finally(() => {
@ -578,7 +580,9 @@ export default {
});
},
onHide(model) {
if (this.busy || !model) return;
if (this.busy || !model) {
return;
}
this.busy = true;
model.hide().finally(() => {
@ -587,7 +591,9 @@ export default {
});
},
toggleHidden(model) {
if (this.busy || !model) return;
if (this.busy || !model) {
return;
}
this.busy = true;

View file

@ -790,7 +790,9 @@ export default {
this.dirty = true;
this.complete = false;
if (this.context !== contexts.Archive) break;
if (this.context !== contexts.Archive) {
break;
}
for (let i = 0; i < data.entities.length; i++) {
const uid = data.entities[i];