Skip to content

Release v7.1.0 - #416

Merged
dkastl merged 31 commits into
mainfrom
next
Jul 27, 2026
Merged

Release v7.1.0#416
dkastl merged 31 commits into
mainfrom
next

Conversation

@dkastl

@dkastl dkastl commented Jul 27, 2026

Copy link
Copy Markdown
Member

Promotes next to main for the v7.1.0 release.

Feature release on the v7 line: nearby-issue auto-watch (#14, filed 2021), configurable distance units (#10, filed 2021), the Map settings admin entry (#309), documented spatial API filters (#58, filed 2021), plus view/observer cleanups and CI token hardening. The 7.0 → 7.1 upgrade is drop-in — no new database migrations. Full release notes will be published with the v7.1.0 GitHub release after this merges.

All 29 commits were developed, reviewed and CI-verified in individual PRs on next (#405#415), each green on the Redmine 6.0/6.1/7.0 matrix.

Issues resolved on next that close with this merge:

Closes #10
Closes #14
Closes #58
Closes #278
Closes #309
Closes #323

dkastl and others added 30 commits July 27, 2026 18:21
First step for #14 (local notifications / watch areas): a user can opt
in on the My account page to automatically watch new issues within a
chosen distance of their stored location.

- UserPreferencePatch stores gtt_watch_nearby / gtt_watch_radius in the
  serialized UserPreference#others hash (no schema change), with a
  validated gtt_watch_radius_km reader
- New fieldset rendered right below the User Map on My account (the
  map is where the location is set, so the dependency is visible in
  place); controls are disabled with a hint until a location exists
- en/ja/de locale strings

The watcher assignment on issue creation follows in a separate PR.
- omit the hidden '0' fallback while the controls are disabled, so
  saving other account settings with no stored location no longer
  silently turns the preference off
- cap gtt_watch_radius_km server-side at NEARBY_WATCH_MAX_RADIUS_KM
  (the form max mirrors the constant); client-side limits are easy to
  bypass and an unbounded radius would make the watcher query expensive
Completes #14: users who enabled the nearby-watch preference become
watchers of issues created (or receiving geometry) within their chosen
distance of their stored location.

- NearbyWatchers narrows candidates in SQL (active users with a
  location, distance on the geography type, hard cap from the
  preference constant, issue geometry bound as EWKB hex) and checks the
  per-user radius and issue visibility in Ruby
- wired as an after_save inside the issue's transaction, so core's
  after_create_commit notification already sees the new watchers and
  they receive the creation mail; failures are logged, never rolled
  into the issue save
- add_watcher is idempotent, so geometry moves simply re-run the check
Core's after-create bookkeeping bumps lock_version on the stored row,
so updating the original in-memory instance raised StaleObjectError in
CI. Update the reloaded record, as a real request would.
feat: subscribe opted-in nearby users as watchers of new issues
The 'global page or gtt module enabled' condition was copy-pasted
across six views; it now reads gtt_module_active?(@project). The two
views with stricter semantics (issues index map and the issue form
hook require a project) use safe navigation instead and keep their
behavior.

Resolves #278 (closed via the next release PR)
The edit/comment icon click handler guessed with a 500 ms setTimeout
when the issue edit form (and the map inside it) would become visible.
The map element already has a ResizeObserver for updateSize; it now
also detects the zero-size -> visible transition and zooms to the
features, reacting to the actual size change however the form was
opened. handleEditIcon is removed.

Resolves #323 (closed via the next release PR)
'GTT Settings' assumes the reader knows the plugin's internal name,
which instances deploying under their own branding do not surface.
'Map settings' says what the page configures and pairs with the
existing 'Map layers' admin entry. New locale key en/ja/de; the shared
label_gtt_settings_headline stays for the project settings tab.
Tabler's map-cog added to the plugin sprite, so the entry is visually
distinct from core Settings and reads as a sibling of Map layers (the
plain Tabler map right below it).
feat: admin menu shortcut to the plugin settings
…lper

refactor: extract the gtt module view check into a helper
Review follow-up: wasHidden is initialized from the target's current
size instead of assuming the observer's initial fire, and the last
entry access is guarded.
…server

refactor: zoom on hidden-to-visible via the resize observer
The bbox and distance API filters and distance sorting have been
implemented and covered by integration tests for a long time
(test/integration/issue_filter_api_test.rb), but doc/api.md never
mentioned them. Documents both parameter formats, all operators, the
response distance attribute, and the project-scoped limitation.

Also refreshes the stale tracker-icon example in the settings endpoint
response (icons are {id, svg} JSON since the v7 SVG icon rework).

Resolves #58 (closed via the next release PR)
docs: document the spatial issue filters and distance sorting
First part of #10: a distance_unit plugin setting (m, km, ft, mi, nm)
that drives what users see. Meters remain the internal unit everywhere
(storage, SQL, filter wire format, REST API).

- RedmineGtt::DistanceUnit: unit table, current/api_unit readers with
  fallback, to/from-meters conversion
- distance column: header carries the unit (caption proc), list and
  CSV cells render converted via a QueriesHelper prepend
- api_distance_unit setting reserved with meters as the only option,
  so the API contract can widen later without a settings migration
- settings UI selects + en/ja/de locales

The distance filter input and the nearby-watch radius field follow in
separate PRs.
Second part of #10. The distance filter row displays and accepts its
bounds in the configured unit: values coming from the server (meters)
convert for display, and a submit hook on #query_form converts back to
integer meters, so the wire format, saved queries and the API stay
metric. The unit label renders next to the inputs.

The hook binds via jQuery when present because core submits the form
with $('#query_form').submit(), which bypasses native listeners.
Final part of #10, and the reason to land units before #14 ships: the
watch radius is now stored in meters (the internal unit) and entered
and displayed in the configured display unit on My account.

- gtt_watch_radius stores meters; the account form goes through the
  gtt_watch_radius_in_unit virtual attribute (unit -> meters on write,
  meters -> unit on read); the raw key is no longer mass-assignable
- NEARBY_WATCH_MAX_RADIUS_KM becomes NEARBY_WATCH_MAX_RADIUS_M
  (1,000,000 m); NearbyWatchers compares meters directly
- the field's unit label and max derive from the setting; step allows
  fractions (0.5 km = 500 m)

Since #14 is unreleased, no stored-preference migration is needed.
- unknown units fall back to meters instead of raising KeyError (the
  setting can be hand-edited, callers may pass user-supplied strings)
- the CSV separator test derives its expectation from the locale
  instead of hardcoding '.'
feat: configurable distance display unit
feat: distance filter input in the configured display unit
- gtt_watch_radius_in_unit keeps 6 decimals for fractions (enough for
  1 m in any supported unit), so a read-save round trip cannot drift
  the stored meters; regression test with a non-round mile value
- the form's min is 1 meter expressed in the display unit instead of
  0, which the server would treat as unset
feat: nearby-watch radius in the configured display unit
Resolves the two open code-scanning alerts
(actions/missing-workflow-permissions): the test workflow only checks
out code and runs tests, so the default token permissions are wider
than needed. release.yml already declares its own permissions.
ci: restrict the workflow GITHUB_TOKEN to contents: read
@dkastl
dkastl merged commit 2f5ce02 into main Jul 27, 2026
20 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

1 participant