The email and callsign changesets add "did not change" errors when
created with empty attrs (on mount). Guard all field error displays
on changeset.action being set so errors only appear after user
interaction.
Cover to_float, to_decimal, normalize_data_type, weather_fields,
sanitize_packet_strings, to_float_safe, has_weather_data, create_point,
extract_coordinates, lat/lon helpers, PHG extraction, and altitude
extraction with 79 new tests across 4 test files.
Cover weather data with spaces/dots for missing values, negative
temperatures, negative altitudes, combined PHG+altitude+RNG
stripping, nil comments, comments that become empty after cleaning,
and false-match protection for comments starting with P or h. Also
fix strip_weather_data to require at least one weather field after
the preamble to avoid false-matching course/speed in non-weather
comments.
Weather packets had raw data strings (e.g. 007/000g000t054...)
stored as the comment instead of the trailing human-readable text.
Now iteratively strips fixed-width APRS weather fields in any order,
handling both position-included (XXX/XXX) and positionless (cXXXsXXX)
formats. Also strips RNG data and fixes altitude regex to handle
negative values. Empty comments after cleaning return nil.
Drop 10 dead parser-compat columns (srccallsign, dstcallsign,
origpacket, body, header, alive, posambiguity, symboltable,
symbolcode, messaging) and move 14 display-only columns into a
single `data` JSONB column (PHG, telemetry, radiorange,
information_field, format, posresolution, position_ambiguity,
luminosity, rain_midnight).
Reduces row width from ~70 to ~48 columns. Table is ephemeral
so migration uses DROP/recreate. Also fixes snow_24h -> snow bug
in weather check query and adds null byte stripping for JSONB
string sanitization.
Convert app.js, info_map.js, error_boundary.js, and time_ago_hook.js
to TypeScript with proper typing for Phoenix hooks, Leaflet, DOM
interactions, and timer management. Update esbuild hex package to
~> 0.10 and binary to 0.25.4. Entry point changed to app.ts.
Convert the packets table to a time-partitioned table with daily
partitions (PARTITION BY RANGE on received_at). This replaces the
expensive CTE-based batch DELETE cleanup with instant DROP TABLE
partition drops — no dead tuples, no WAL pressure, no VACUUM needed.
- Add PartitionManager GenServer to create/drop daily partitions
- Simplify PacketCleanupWorker to delegate to PartitionManager
- Replace gridsquare/geocalc deps with local Maidenhead module
- Remove gettext_pseudolocalize dependency
- Fix Decimal comparison in packet consumer test
- Extract prepare_markers_for_push/2 to reduce nesting depth in
handle_info
- Split detect_item_or_object into smaller predicate and application
functions to reduce cyclomatic complexity
- Coerce Decimal/nil values to native numbers in telemetry metrics
(PostgreSQL sum() returns numeric type mapped to Decimal by Postgrex)
- Align locate button horizontally with zoom controls
- Remove CSS `bottom: 50px !important` on popups that pushed them too
high above markers
- Hide trails when markers are clustered (zoom < 11) so trails don't
point to invisible markers inside cluster bubbles
- Fix locate button overlap with zoom controls and keep it always
light mode
- Remove duplicate slideover/toggle CSS from layout files (single
source of truth in component)
- Remove dead CSS classes and standardize breakpoints to 1024px
- Add isDestroyed guards on async callbacks to prevent race conditions
- Escape callsigns in marker HTML to prevent XSS
- Replace Leaflet _container private API with getContainer()
- Clean up about page placeholder text
Replace trail color palette with bold, high-saturation colors that avoid
yellows, oranges, and grays (common road colors on map tiles). Interleave
warm/cool tones so consecutive color assignments are maximally distinct.
Change color assignment to prefer globally unique colors first, falling
back to proximity-based deconfliction only when all 15 colors are in use.
- Replace N+1 get_latest_packet_for_callsign calls in RF path loading
with single get_latest_packets_for_callsigns batch query (DISTINCT ON)
- Change batch packet processing to collect marker removal IDs and push
one remove_markers_batch event instead of individual remove_marker calls
- Add callsignIndex reverse map (callsign_group → Set<markerID>) for O(1)
lookup in new_packet/new_packets handlers instead of scanning all markers
- Server tracks callsign replacements in prepare_packet_state, sends
convert_to_historical list so JS skips full markerStates scan
- Historical packets get red dot HTML server-side in build_minimal_packet_data
instead of JS createMarkerIcon generating it
- Add callsign_group field to all packet data output; server pre-sorts
by group with chronological order so JS iterates directly
- Normalize heat map intensity in Clustering.cluster_packets (min/50, cap 1.0)
instead of JS doing it per-point
- Remove buildPopupContent JS fallback and unused escapeHtml import;
server already provides popup in all data paths
- Batch real-time packet push_events: collect marker data across the
packet batch and send a single "new_packets" WebSocket message instead
of one per packet. JS handler processes all live→historical conversions
before adding new markers to avoid intra-batch conflicts.
- Batch RF path station queries: replace N+1 get_latest_packet_for_callsign
calls with a single DISTINCT ON query selecting only callsign + lat/lng.
- Add read_concurrency: true to streaming_packets_subscribers and :aprsme
ETS tables (read-heavy, rarely written).
- Remove dead GC code in PacketConsumer (threshold > 1000 unreachable with
max batch_size of 100).
Delete 5 unused modules: Presence (untracked), LiveView ShutdownHandler
(unused mixin), RateLimiterWrapper (pointless delegation), Passcode
(only used by own test), MapLive.Utils (pure delegates).
Fix ShutdownHandler to not call deleted Presence. Inline ParamUtils
at call sites. Remove cachex dep and 27 stale entries from mix.lock.
Clean commented-out Exq/Redis config from application.ex.
System A (PacketManager + PacketStore ETS) had a fatal ID mismatch:
extract_packet_ids returned Ecto UUIDs but PacketStore stored under
MD5 hashes — cleanup could never find stored packets. Unified all
packet tracking on visible_packets map in LiveView assigns.
Deletes packet_manager.ex and packet_store.ex, removes from
supervision tree and PacketDistributor.
Historical map loading previously fetched all 73 columns (SELECT *) from
the packets table (~2068 bytes/row). Added get_recent_packets_for_map/1
with select_map_fields/1 that fetches only the 22 columns needed for map
rendering (~200 bytes/row), yielding a ~3x query speedup.
Also removes unused all_packets tracking from PacketProcessor and
simplifies visible_packets cleanup in Index.