Skip to content

perf(node): add stable keys and contentType to telemetry chart lists#5869

Merged
jamesarich merged 1 commit into
mainfrom
claude/objective-gates-3d545a
Jun 19, 2026
Merged

perf(node): add stable keys and contentType to telemetry chart lists#5869
jamesarich merged 1 commit into
mainfrom
claude/objective-gates-3d545a

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

Why

The telemetry/log lists in the node metrics screens rendered with bare itemsIndexed(data) { ... } — no key and no contentType. Without a stable key, Compose uses positional identity, so when a row is inserted or reordered (new telemetry arrives at the top of these time-sorted lists) the whole list is re-laid-out and animateItem can't run. This adds stable identities and content types so only the affected rows recompose.

🛠️ Changes

  • Stable item keys on every previously-unkeyed itemsIndexed in the metrics screens:
    • DeviceMetrics (both lists), AirQualityMetrics, PowerMetrics, EnvironmentMetrics → key on telemetry.time.
    • PositionLogScreens → key on position.time.
    • PaxMetrics → key on the MeshLog.uuid primary key (not received_date, which isn't guaranteed unique).
    • ScannedQrCodeDialog → key on the channel slot index (the identity already used for selection state).
  • Constant contentType added to each list so Compose can pool/reuse compositions per type. This matters most in the heterogeneous LazyColumns (ScannedQrCodeDialog interleaves description text, channel rows, LoRa-change blocks, and buttons; SignalMetrics mixes two card types).
  • SignalMetrics heterogeneous list: its LazyColumn holds both LocalStatsEntry and PacketEntry, where a bare timestamp could collide across the two subtypes. Added type-prefixed key/contentType members to the SignalLogEntry sealed interface ("local_stats_<time>" vs "packet_<meshPacket.id>") so keys stay collision-free and the two card layouts get distinct content types.

TracerouteLog and HostMetricsLog already had keys and were left untouched.

Note for reviewers

The timestamp-based keys assume time is unique within each list. This matches the screens' existing selection logic, which already treats time as each card's identity (telemetry.time.toDouble() == selectedX) — so duplicate timestamps would already have broken selection highlighting, independent of this change.

Testing Performed

  • ./gradlew spotlessApply spotlessCheck detekt assembleDebug — all pass.
  • ./gradlew :feature:node:allTests — all pass (JVM unit tests; iOS simulator compilation succeeds).

No test changes were required — this is a list-rendering/identity change with no behavioral surface in the existing unit tests.

The metrics chart screens rendered their log lists with bare
`itemsIndexed(data) { ... }` — no `key` and no `contentType`. Without a
stable key, Compose falls back to positional identity, so inserting or
reordering a row (new telemetry arrives at the top) forces a full
re-layout of the list and defeats `animateItem`.

Give each list a stable item key and a constant `contentType`:
- DeviceMetrics (both lists), AirQualityMetrics, PowerMetrics,
  EnvironmentMetrics: key on `telemetry.time`.
- PositionLogScreens: key on `position.time`.
- PaxMetrics: key on the `MeshLog.uuid` primary key rather than
  `received_date`, which isn't guaranteed unique.
- SignalMetrics: the list is heterogeneous (LocalStatsEntry vs
  PacketEntry), where a bare timestamp could collide across subtypes.
  Added type-prefixed `key`/`contentType` members to the SignalLogEntry
  sealed interface ("local_stats_<time>" vs "packet_<id>").
- ScannedQrCodeDialog: keyed on the channel slot index (the identity
  already used for selection state); the bigger win is `contentType`
  since that LazyColumn interleaves several item types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added the enhancement New feature or request label Jun 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📄 Docs staleness check — advisory

This PR modifies user-facing UI source files but does not update any page under docs/en/user/ or docs/en/developer/.

⚠️ Doc changes propagate to 3 consumers: in-app docs browser, Jekyll site (GitHub Pages), and meshtastic.org (Docusaurus sync). Updating a page in docs/en/ automatically flows to all three.

Changed source files:

core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt

What to check:

Changed area Likely doc page
feature/messaging/ docs/en/user/messages-and-channels.md
feature/node/ docs/en/user/nodes.md or docs/en/user/node-metrics.md
feature/map/ docs/en/user/map-and-waypoints.md
feature/connections/ docs/en/user/connections.md
feature/settings/ docs/en/user/settings-radio-user.md or docs/en/user/settings-module-admin.md
feature/firmware/ docs/en/user/firmware.md
feature/intro/ docs/en/user/onboarding.md
feature/discovery/ docs/en/user/discovery.md
feature/docs/ Internal docs infrastructure
core/ui/ docs/en/developer/codebase.md or component-specific user pages

New page checklist (if adding a new doc page):

  1. Create the .md file in docs/en/user/ or docs/en/developer/ with last_updated frontmatter
  2. Register in DocBundleLoader.kt with string resources (in-app browser)
  3. Jekyll and Docusaurus sync pick up new pages automatically — no config change needed

If this PR does not require a doc update (e.g., internal refactor, bug fix, test change), add the skip-docs-check label to dismiss this check.

Cross-platform note: This check is advisory while doc coverage matures. Both Android and Apple repos use the same skip-docs-check label and advisory severity. See meshtastic/design standards for shared conventions.

@jamesarich jamesarich added this pull request to the merge queue Jun 19, 2026
Merged via the queue into main with commit 3fe1deb Jun 19, 2026
19 checks passed
@jamesarich jamesarich deleted the claude/objective-gates-3d545a branch June 19, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant