Skip to content

Self-gate Set Time/Date on a location fix; drop timezone name from note (follow-up to #508)#512

Merged
brickbots merged 2 commits into
mainfrom
gate-time-entry-on-location
Jul 1, 2026
Merged

Self-gate Set Time/Date on a location fix; drop timezone name from note (follow-up to #508)#512
brickbots merged 2 commits into
mainfrom
gate-time-entry-on-location

Conversation

@brickbots

@brickbots brickbots commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Follow-up to #508. Two changes to the manual Set Time/Date screen.

Approach changed (v2): the location gate now lives inside the screen (self-gating) instead of a menu-layer callback that hard-blocked entry. See ADR 0019.

1. Self-gate entry on a location fix

Manual time is interpreted in the observer's local timezone, which is only derived from a location fix (set_time / set_datetime read location().timezone); without one the entry would localise against a bogus UTC zone (the #508 bug class, see ADR-0018).

Rather than blocking navigation at the menu, the module gates itself: UITimeEntry opens normally, and when there is no location().lock it renders a "Set location first" notice (with a Cancel hint) in place of the entry boxes, keeps its key handlers and its set_time exit-callback inert, and the user backs out with LEFT/Cancel. The check is live, so the boxes appear the instant a fix locks while the screen is open.

UIDateEntry (chained from the time screen, and equally dependent on the zone) carries the same guard. It is only reachable through the gated time screen today, but self-gating keeps it correct if it is ever surfaced directly (e.g. relocated in the menu tree).

Why not hard-block? In text_menu.key_right, a menu item's "callback" is dispatched before its "class" and its return value cancels navigation, whereas a "class" item's pre_callback return value is discarded — so a plain callback is the only way to refuse to open a screen. ADR 0019 records the decision not to: hard-blocking hides the feature and splits the precondition across the menu and the module. Self-gating keeps the feature discoverable and the precondition in one owner. A reusable UIModule.draw_gate_message() renders the notice.

2. Revert the local-time note to a fixed label (unchanged from v1)

#508 built the note as "Enter " + <IANA tz> + " Time", which overruns the 128px panel for long zone names (e.g. America/Argentina/Buenos_Aires). draw_local_time_note now draws _("Enter Local Time") — no timezone name — mirroring UIDateEntry's _("Enter Local Date").

Docs

  • ADR 0019UI modules self-gate their preconditions (enter-then-back-out), not a menu hard-block.
  • docs/ax/ui/CONTEXT.md — defines the Self-gating module term.

Tests

  • test_time_date_gate.py (replaces test_callbacks_time_gate.py): drives UITimeEntry/UIDateEntry directly — locked accepts entry and fires the callback; unlocked is inert (digits ignored, no chaining, callback suppressed); the gate message renders.
  • test_ui_modules.py: UITimeEntry returns to menu-node discovery (dropped the dynamic-launch entry + spec builder), keeping test_all_ui_modules_covered satisfied. Cold/warm crash-smoke now exercises both the gated and normal render paths.
  • test_web_remote_tools.py: refreshed docstrings; added a regression test that the screen opens even with no location seeded (guards against reverting to a hard-block).

Verification

  • ruff check + ruff format --check: clean
  • mypy PiFinder: no issues (138 files)
  • test_ui_modules.py: 222 passed / 2 skipped (hip_main.dat-dependent chart tests)
  • pytest -m "smoke or unit": 706 passed
  • Visual (headless): confirmed the locked entry screen (boxes + fixed "Enter Local Time" note) and the unlocked gate screen ("Set location / first" + Cancel hint) both render correctly on the 128px panel.

i18n .pot left untouched; the gate reuses the existing Set location\nfirst and  Cancel msgids, so no new strings.

🤖 Generated with Claude Code

brickbots and others added 2 commits June 30, 2026 17:24
…rom note

Follow-up to PR #508. Two changes to the manual Set Time/Date screen:

1. Gate entry on a known location. Manual time is interpreted in the
   observer's local timezone, which is only derived from a location fix
   (see set_time / set_datetime). Route the menu item through a new
   callbacks.enter_time_entry gate that pushes UITimeEntry only once
   shared_state has a locked location, otherwise bouncing the user with a
   "Set location first" hint. Previously the screen fell back to UTC
   silently when no location was set.

2. Revert the local-time note to a fixed "Enter Local Time" (PR #508 had
   built "Enter <IANA tz> Time", which overruns the panel for long zone
   names like America/Argentina/Buenos_Aires). The zone is no longer
   needed for display now that a location is guaranteed, and this mirrors
   UIDateEntry's "Enter Local Date".

Tests:
- test_callbacks_time_gate.py: unit-level gate behaviour (blocked without
  lock, opens + wires set_time with a lock).
- test_ui_modules.py: UITimeEntry moves from menu-node discovery to the
  dynamic-launch list (it's now pushed by a callback), keeping the
  all-modules-covered guard satisfied.
- test_web_remote_tools.py: seed a location via POST /gps/update before
  navigating to Set Time/Date.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e menu

Rework of the location gate from PR #512. Instead of a menu-layer callback
(callbacks.enter_time_entry) that refused to open the screen without a
location fix, the Set Time/Date screen now opens normally and gates itself:
with no location lock it shows a "Set location first" notice with inert
entry boxes, and the user backs out with LEFT/Cancel.

- menu_structure: Set Time/Date goes back to a direct "class": UITimeEntry
  push (re-add the import); drop callbacks.enter_time_entry.
- timeentry/dateentry: self-gate on a live location().lock check -- update()
  draws the notice, key handlers no-op, and inactive() skips the
  set_time/set_datetime callback so it never runs against a bogus UTC zone.
  UIDateEntry carries the same guard for correctness if ever surfaced
  directly, though it is only reachable via the (gated) time screen today.
- base: add reusable UIModule.draw_gate_message() for the notice.
- keep the fixed "Enter Local Time" note (no timezone name) from #508.

Docs:
- ADR 0019: "UI modules self-gate their preconditions (enter-then-back-out)".
- ui/CONTEXT.md: define "Self-gating module".

Tests:
- replace test_callbacks_time_gate.py with test_time_date_gate.py (drives
  the modules directly: locked accepts entry + fires callback; unlocked is
  inert; gate message renders).
- test_ui_modules: UITimeEntry returns to menu-node discovery (drop the
  dynamic-launch entry + spec builder).
- test_web_remote_tools: screen opens regardless of location; add a
  no-location regression test; refresh docstrings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@brickbots brickbots changed the title Gate Set Time/Date on a location fix; drop timezone name from note (follow-up to #508) Self-gate Set Time/Date on a location fix; drop timezone name from note (follow-up to #508) Jul 1, 2026
@brickbots brickbots merged commit b57263d into main Jul 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant