feat: node list density switching with compact layout and field toggles#5444
Conversation
ffa1bfb to
b5d6e4d
Compare
|
ca65619 to
7bbcc05
Compare
📄 Docs staleness check — advisoryThis PR modifies user-facing UI source files but does not update any page under
Changed source files: What to check:
New page checklist (if adding a new doc page):
If this PR does not require a doc update (e.g., internal refactor, bug fix, test change), add the
|
9d736a7 to
15502a6
Compare
…toggles Implement the Node List Layout feature (Phases 1-6): - Add NodeListDensity enum (COMPLETE/COMPACT) in core:model - Add 10 DataStore preferences for density and field toggles - Create NodeItemCompact with two-column layout, adaptive chip sizing, and toggle-driven fields (power, last heard, location, hops, signal, channel, role, telemetry) - Add accessibility semantics (mergeDescendants, contentDescription, Role.Button) to both NodeItem and NodeItemCompact - Create NodeLayoutSettings with SegmentedButton density picker and 9 SwitchPreference toggles for compact mode - Integrate settings into Android and Desktop settings screens - Wire NodeListScreen to delegate between layouts based on density - Create NodeListHelp ModalBottomSheet with signal quality legend - Add help IconButton to NodeListScreen app bar - Add ~23 new string resources for layout settings and help text - Update FakeUiPrefs for test compatibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…emetry, tests - Remove @file:Suppress("detekt:ALL") from NodeListScreen.kt - Fix 3 detekt violations: remove unused onNavigateToChannels param, add modifier param to NodeListScreen, remove blank line - Plumb lastHeardIsRelative through LastHeardInfo → NodeItemCompact → NodeListScreen for relative/absolute time toggle - Add hasPowerMetrics icon to CompactTelemetryIcons (3 of 3 model-supported) - Update buildNodeDescription() a11y helper for relative time flag - Add BuildNodeDescriptionTest (14 tests) covering signal visibility, battery range, hops, distance, favorite, and online/offline - Add NodeListDensityTest (6 tests) covering density string fallback Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rsing - H1: Wrap buildNodeDescription in remember(thatNode) in NodeItem and remember(thatNode, lastHeardIsRelative) in NodeItemCompact to avoid runBlocking formatAgo calls on every recomposition during scroll - H2/M2: Extract NodeListDensity.fromName() companion method, replace 3 duplicated entries.firstOrNull fallback sites (ViewModel + 2 Settings) - H3: Remove NODE_LIST_DENSITY from boolean enum, use companion const DENSITY_KEY + DEFAULT_DENSITY for string pref type-safety - L2: Update NodeListDensityTest to test fromName() directly instead of duplicating the parsing logic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- M7: Extract buildNodeDescription() from NodeItem.kt into its own BuildNodeDescription.kt file with internal visibility, eliminating orphaned public API in a component package - M4: Replace inline .collectAsStateWithLifecycle().value with by delegation in both SettingsScreen and DesktopSettingsScreen for consistent state observation pattern - Extract magic numbers (SNR_UNSET_THRESHOLD, MAX_BATTERY_PERCENT) to named constants to satisfy detekt MagicNumber rule Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- FR-013: Add online/offline icon (green checkmark / orange moon) before timestamp in Row 2 - FR-012: Row 1 now shows only PKC icon, name, and favorite star per spec (removed NodeStatusIcons from compact name row) - FR-019: Device Role section now renders conditional unmessageable and MQTT icons alongside the role icon - Remove unused connectionState/deviceType params from NodeItemCompact Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add onClickLabel/onLongClickLabel to combinedClickable in both NodeItem and NodeItemCompact for proper TalkBack action announcements - Add semantics heading() to section titles in NodeListHelp bottom sheet for screen reader navigation between sections - Add string resources: node_list_click_label, node_list_long_click_label Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
M4: Extract NodeDescriptionStrings data class with rememberNodeDescriptionStrings()
composable resolver. buildNodeDescription now takes localized strings param
instead of hardcoded English. Added 9 a11y_node_* string resources.
M1: Replace mutableListOf<@composable> with keyed Pair<String, @composable>
list in CompactCombinedRow. Each item gets a stable key() wrapper for
correct Compose identity across recompositions.
M2: Bump compact icon size from 14dp to 16dp (M3 minimum for dense UI).
Extract COMPACT_ICON_SIZE_DP constant for consistency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add @PreviewLightDark composables for: - NodeItem (complete density): remote node + active/this-node - NodeItemCompact: all fields, minimal, active/this-node - NodeLayoutSettings: compact toggles + complete description Add corresponding @previewTest entries in NodeScreenshotTests and SettingsScreenshotTests with reference screenshots (14 images). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace adaptive chip height formula (36-70dp based on row count) with a fixed 48dp square. The adaptive sizing caused the avatar to tower over the content when multiple rows were visible. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rows All compact rows (health, metrics, footer) now use Arrangement.SpaceEvenly to distribute items across the full card width instead of middot-separated clusters. Removes FlowRow dependency since wrapping is no longer needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Items start flush left and end flush right with even gaps between. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Computes bearing between local and remote node, converts to 8-point compass direction (N, NE, E, SE, S, SW, W, NW), and appends to distance text (e.g. '2.3 km NW'). Shown in both compact and complete views. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace text compass direction (NW, SE, etc.) with a rotated MapCompass icon. Bearing is now a separate visual field — the compass arrow points in the direction of the remote node. Shown in both compact and complete. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add FR-029 to FR-034: neutral card background, node-color border, packet-received glow animation with spring physics, M3 color roles for text hierarchy, SpaceBetween layout, bearing as rotated compass - Add NFR-005: glow animation performance constraint - Add Phase 8 tasks (NL-T048 to NL-T055) with dependency graph - Update architecture section with M3 Expressive card treatment docs - Add risks for glow perf and dark node color visibility - Aligns with meshtastic/design standards v1.4 §1 (Circle Standard) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Resolve FR-014 vs FR-033 conflict: FR-014 now defers to FR-033 (FlowRow + SpaceBetween, no VerticalDivider) - Fix NL-T020 task description to match FR-033 - Clarify NL-T052 as regression fix (chip-inline was iterative drift) - Replace duplicate NL-T053 (adaptive sizing) with FR-034 (bearing icon) - Fix critical path in tasks.md to include Phase 8 - Add Constitution V cross-platform spec exemption justification - Add NodeCardGlow to Key Components table Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove node-color background tinting, use neutral surface (FR-029) - Add node-color BorderStroke modulated by online state (FR-030) - Implement packet-received glow animation with spring physics (FR-031) - New NodeCardGlow.kt: Animatable + fastSpatialSpec bloom + slowSpatialSpec decay - Zero overhead when not animating (shadow only applied when alpha > 0) - Replace alpha-based text emphasis with M3 color roles (FR-032) - contentColor.copy(alpha=0.7f) → MaterialTheme.colorScheme.outline - Restore two-column layout in compact mode (FR-009, FR-052) - Column 1: NodeChip + battery, Column 2: content rows - Add HorizontalDivider before Complete footer (FR-054) - Compliant with Design Standards v1.4 §1 (neutral card background) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The LaunchedEffect(lastHeard) was triggering on first composition when lastHeard > 0 (which is always true for nodes heard in the past). This caused every card to glow when scrolling into view in a LazyColumn. Fix: track previous lastHeard value to distinguish initial composition from actual changes. Only animate when the value genuinely changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Battery is already displayed below the node chip in Column 1. Having it
again in the health row was redundant and caused signal quality text to
ellipsize ('G...' instead of 'Good') due to overcrowding.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eardInfo Remove separate Success/DeviceSleep icons from compact health row and complete header. Instead, tint the LastHeardInfo antenna icon directly with tertiary (online) or outline (offline) color based on 2-hour lastHeard threshold. Cleaner visual — one icon conveys both 'last heard time' and 'online status' through color alone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Battery fill now grows from flat end (right) toward terminal (left), matching standard UI convention. Previously filled in reverse. Also switch online status tint from tertiary (blue) to StatusGreen, matching the established connected/good color pattern used throughout the app (ConnectionsNavIcon, LoraSignalIndicator, SecurityIcon). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the generic ic_memory chip icon in the node list footer with the actual device SVG from flasher.meshtastic.org. The NodeListViewModel resolves device images via DeviceHardwareRepository (DB-cached) and passes the URL to NodeItemCompact and NodeItem. - Add Coil dependency to core:ui for AsyncImage support - Add deviceImageUrl parameter to NodeItemCompact, NodeItem, HardwareInfo - NodeListViewModel resolves unique hw_model values → image URLs lazily - Falls back to ic_memory vector icon when image unavailable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Merge the DeviceDetailsSection and NodeDetailsSection into one unified card. The device hardware image is displayed as a centered "hero" at the top with the model name and support badge, followed by a horizontal divider, then all node identity info. Key changes: - NodeDetailsSection now accepts optional DeviceHardware and reportedTarget params to render the hero section - DeviceHeroSection composable with avatar, name, and support badge - DeviceHardwareImage extracted as internal for reuse - Removed standalone DeviceDetailsSection from NodeDetailList - Uses animateContentSize() for smooth expansion when hardware data loads asynchronously - Added preview for the combined card with device hero Shared element transitions researched but deferred: the API is experimental (@ExperimentalSharedTransitionApi), has limited CMP support on iOS/Desktop, and the adaptive ListDetailSceneStrategy already handles the list→detail transition well. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch from centered vertical Column to a horizontal Row with 64dp avatar on the left and device name + support badge on the right. Saves ~50% vertical space in the hero section. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove explicit surface color override from node list cards. The M3 default cardColors() uses surfaceContainerLow which is the standard filled card treatment for list items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Update layout structure from 3-row to 4-row compact (health, metrics, footer) - Document device hardware images from CDN (DeviceHardwareRepository) - Fix glow animation values (0→1f bloom, not 0→0.6f) - Update card colors to M3 default (surfaceContainerLow) - Replace adaptive chip sizing with actual defaultMinSize behavior - Update toggle DataStore keys to actual kebab-case values - Add NFRs for image dedup and Coil caching - Add FR-033 through FR-035 for CDN images, node status, transport icon - Exclude node detail combined card changes (separate scope) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…odes - Move showTelemetry toggle above mode-specific section (shared) - Add 'Compact Fields' section header for compact-only toggles - Rename 'Device Role' to 'Device & Role' for clarity - Complete mode now shows shared toggle + description text - Compact mode shows shared toggle + compact-specific toggles Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
15502a6 to
63821cf
Compare
…aram order - Add missing @PreviewTest/@PreviewLightDark/@composable annotations lost during rebase conflict resolution in NodeScreenshotTests.kt - Move modifier param before optional params in NodeListScreen (ComposableParamOrder) - Remove unused onNavigateToChannels param from NodeListScreen (UnusedParameter) - Remove corresponding call site in AdaptiveNodeListScreen Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Blend 5% of the node's identity color into the card container color using lerp(). Transparent nodeColor (nodes with no assigned color) keeps the default M3 surfaceContainerLow background unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Card tint: 0.05 → 0.08 (lerp fraction) - Active border: 0.5 → 0.65 alpha - Inactive border: 0.2 → 0.3 alpha Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Document shared Environment Metrics toggle behavior - Update preview placement (below density picker) - Correct DataStore density key to node-list-density - Align card tint/border requirements with implemented values - Update toggle labels and requirement wording for Device & Role Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add HorizontalDivider separators between the preview section, shared toggle, and compact-specific field toggles to reduce visual density and improve section readability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The formatDateTime path requires Android Application context via ContextServices.app which is unavailable in the layoutlib screenshot renderer. Switch to lastHeardIsRelative=true to use the context-free DateUtils.getRelativeTimeSpanString path instead. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Node List Density Switching with Compact Layout and Field Toggles
Summary
Complete redesign of the node list UI with density switching (Compact/Complete), configurable field visibility, device hardware images from CDN, M3 Expressive card theming with node color tint, and a combined device+node details card on the detail screen.
Key Features
Screenshots
Complete Mode
Complete Mode — Active (Online)
Compact Mode — All Fields
Compact Mode — Active
Compact Mode — Minimal Fields
Settings — Complete Layout
Settings — Compact Layout
Node Detail Content
Technical Details
core/uiandfeature/nodeDataStorepreferences incore/datalerp()for card tint andmotionSchemefor glow animationanimateContentSize()for smooth settings preview transitions