Frontend: Tighten Photo numeric caps and reveal inline errors #5584

ISO, F Number, and Focal Length on the Edit Dialog and Camera popover
previously capped at 1048576, much wider than the backend's SetExposure
clamps (ISO and FocalLength <= 128000, FNumber <= 256). The looser caps
let the rule pass values the server then quietly rejects. Tightens each
to match the backend, adds Exposure: 64 to PhotoMaxLength, and routes
the Exposure rule through PhotoMaxLength.Exposure instead of a hardcoded
literal.

Drops the bare hide-details attribute from Country, Altitude, ISO,
Exposure, F Number, and Focal Length on both surfaces so the rule's
inline error renders below the input on overflow. Date/time inputs
(Day/Month/Year/Time autocompletes + the standalone DateTime dialog)
stay as-is: autocompletes can only emit valid items, the Time field
combines rules.time() with the :error="invalidDate" visual cue and the
form-validate save gate.
This commit is contained in:
Michael Mayer 2026-05-16 13:26:39 +00:00
parent 04f5dd922f
commit 499ac5d009
6 changed files with 14 additions and 18 deletions

View file

@ -58,7 +58,6 @@
<v-col cols="6">
<v-text-field
v-model="iso"
hide-details
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@ -66,14 +65,13 @@
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.number(false, 0, 1048576)"
:rules="rules.number(false, 0, 128000)"
class="input-iso"
></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field
v-model="exposure"
hide-details
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@ -81,14 +79,13 @@
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.text(false, 0, 64)"
:rules="rules.text(false, 0, PhotoMaxLength.Exposure, $gettext('Exposure'))"
class="input-exposure"
></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field
v-model="fNumber"
hide-details
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@ -96,20 +93,19 @@
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.number(false, 0, 1048576)"
:rules="rules.number(false, 0, 256)"
class="input-fnumber"
></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field
v-model="focalLength"
hide-details
autocomplete="off"
:label="$gettext('Focal Length')"
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.number(false, 0, 1048576)"
:rules="rules.number(false, 0, 128000)"
class="input-focal-length"
></v-text-field>
</v-col>
@ -128,6 +124,7 @@
</template>
<script>
import { MaxLength as PhotoMaxLength } from "model/photo";
import { rules } from "common/form";
export default {
@ -146,6 +143,7 @@ export default {
data() {
return {
rules,
PhotoMaxLength,
cameraID: 0,
lensID: 0,
iso: "",

View file

@ -157,7 +157,6 @@
:disabled="disabled"
:readonly="!!(view.model.Lat || view.model.Lng)"
:label="$gettext('Country')"
hide-details
hide-no-data
autocomplete="off"
item-value="Code"
@ -174,7 +173,6 @@
<v-text-field
v-model="view.model.Altitude"
:disabled="disabled"
hide-details
flat
autocomplete="off"
autocorrect="off"
@ -211,7 +209,6 @@
<v-text-field
v-model="view.model.Iso"
:disabled="disabled"
hide-details
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@ -219,7 +216,7 @@
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.number(false, 0, 1048576)"
:rules="rules.number(false, 0, 128000)"
class="input-iso"
></v-text-field>
</v-col>
@ -227,7 +224,6 @@
<v-text-field
v-model="view.model.Exposure"
:disabled="disabled"
hide-details
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@ -235,7 +231,7 @@
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.text(false, 0, 64)"
:rules="rules.text(false, 0, PhotoMaxLength.Exposure, $gettext('Exposure'))"
class="input-exposure"
></v-text-field>
</v-col>
@ -261,7 +257,6 @@
<v-text-field
v-model="view.model.FNumber"
:disabled="disabled"
hide-details
autocomplete="off"
autocorrect="off"
autocapitalize="none"
@ -269,7 +264,7 @@
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.number(false, 0, 1048576)"
:rules="rules.number(false, 0, 256)"
class="input-fnumber"
></v-text-field>
</v-col>
@ -277,13 +272,12 @@
<v-text-field
v-model="view.model.FocalLength"
:disabled="disabled"
hide-details
autocomplete="off"
:label="$gettext('Focal Length')"
placeholder=""
density="comfortable"
validate-on="input"
:rules="rules.number(false, 0, 1048576)"
:rules="rules.number(false, 0, 128000)"
class="input-focal-length"
></v-text-field>
</v-col>

View file

@ -35,6 +35,7 @@ export const MaxLength = Object.freeze({
License: 1024,
Keywords: 2048,
Notes: 2048,
Exposure: 64,
});
// Photo models core metadata for images and videos shown in the UI.

View file

@ -778,6 +778,7 @@ describe("component/photo/batch-edit", () => {
License: 1024,
Keywords: 2048,
Notes: 2048,
Exposure: 64,
});
const cases = [

View file

@ -506,6 +506,7 @@ describe("component/photo/edit/details", () => {
License: 1024,
Keywords: 2048,
Notes: 2048,
Exposure: 64,
});
const cases = [

View file

@ -22,6 +22,7 @@ describe("model/photo", () => {
License: 1024,
Keywords: 2048,
Notes: 2048,
Exposure: 64,
});
// Frozen so consumers can't accidentally mutate per-field caps.
expect(Object.isFrozen(MaxLength)).toBe(true);