feat: node list density switching with compact layout and field toggles#5444
Draft
jamesarich wants to merge 50 commits into
Draft
feat: node list density switching with compact layout and field toggles#5444jamesarich wants to merge 50 commits into
jamesarich wants to merge 50 commits into
Conversation
…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>
Remove custom chip sizing — let NodeChip use its own defaultMinSize (minWidth=64dp, minHeight=28dp) for consistent dimensions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use StatusYellow for favorite star in Compact layout to match Complete layout's NodeStatusIcons behavior. Remove unused contentColor param from CompactNameRow. Regenerate affected screenshots. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add OnlineStatusIcon (green checkmark/gray sleep) next to LastHeardInfo in the Complete layout header, matching Compact's behavior. Icon only shows for remote nodes (not thisNode, which already has ConnectionsNavIcon). Regenerate affected Complete screenshots. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TransportIcon was hardcoded to Color.White, causing inconsistent tinting in the node list. Default to LocalContentColor.current so the icon inherits the correct content color from its container. Preserve explicit Color.White for MessageItem where the chat bubble requires it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds 20 unit tests verifying: - nodeListDensity defaults to COMPLETE and round-trips COMPACT - All 9 boolean toggles default to true (except lastHeardIsRelative = false) - All setters persist their values correctly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ffa1bfb to
b5d6e4d
Compare
These composables are pure UI components with no feature-level logic. Moving them to core:ui makes them reusable across feature modules (e.g., feature:settings for live preview) without cross-feature deps. Also moves BuildNodeDescription and NodeStatusIcons (helpers used by NodeItem) and fixes detekt parameter ordering in NodeItem. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Renders a comprehensive sample node directly in the settings screen so users can see how toggling density and field visibility affects the node list appearance in real-time. Fulfills FR-008. The preview shows a realistic node with all fields populated: - Battery, signal, position, hops, channel, role - Environment metrics (temperature, humidity, pressure) - PKC key, favorite status Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add NodeLayoutSettingsCompactMinimalPreview showing toggles-off state - Add ScreenshotNodeLayoutSettingsCompactMinimal screenshot test - Move BuildNodeDescriptionTest to core:ui (follows the code it tests) - Update screenshot references to reflect live preview in settings - Update import in SettingsScreenshotTests for new preview Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Status: Not Started → Implemented - FR-008: Updated to reflect hardcoded sample node (not Room query) - Component table: NodeItem/NodeItemCompact now in core:ui - Assumptions: Updated live preview description Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Add 6 isolated screenshot test variants for the preview sample node: - Complete density (Celsius/metric) - Complete density (Fahrenheit/imperial) - Compact with all fields enabled - Compact with signal + last heard only - Compact with name only (no optional fields) Each variant captured in both light and dark themes (12 new images). Makes previewSampleNode() internal for cross-module preview access. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split the single CompactCombinedRow into two logical rows that mirror the Complete density layout: Row 3 (Position + Signal): distance | elevation | hops | signal | channel | sats Row 4 (Device + Telemetry): hardware | role+unmessageable+mqtt | node ID | telemetry icons Previously, all fields were jammed into one row making it hard to visually correlate field positions between Complete and Compact. Added fields previously missing from Compact: - Elevation (controlled by showLocation toggle) - Satellite count (controlled by showLocation toggle) - Hardware model (controlled by showRole toggle) - Node ID (controlled by showRole toggle) No new toggles needed — fields are grouped logically under existing toggles (showLocation = GPS data, showRole = device identity). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Distance was never visible in screenshots because thisNode was null, making thisNode?.distance(thatNode) return null. Added previewLocalNode() ~1.6km away from the sample node so distance renders in both Complete and Compact previews when showLocation is on. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When all fields are enabled, the single Row overflows on narrower screens. Switch to FlowRow so items wrap to the next line instead of being clipped off the right edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace FlowRow-based compact layout with structured 3-row design: - Row 1 (Identity): Name + PKC icon + favorite star - Row 2 (Health): Online dot + last heard + battery + distance + signal - Row 3 (Footer): Hardware · role · hops · channel + telemetry icons Use middot separators instead of VerticalDivider for cleaner wrapping. Battery shows icon+percentage, signal shows qualitative label. Footer uses labelSmall typography with reduced emphasis. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2664e21 to
c29c711
Compare
Replace SpaceBetween with spacedBy(12.dp) + weight/Spacer for more consistent visual spacing across battery, signal, and footer rows. MetricsGrid uses spacedBy(12.dp) instead of SpaceBetween. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
c29c711 to
873ecfb
Compare
Remove unmessageable/mqtt text symbols (✗/⌁) from compact footer — they were rendering as unrecognizable glyphs. Remove fillMaxWidth from footer row so telemetry icons sit adjacent to text instead of being pushed to far right. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ca65619 to
7bbcc05
Compare
Adjusted drawBehind insets to align with ic_battery_horiz_000 viewport: - insetLeft: 0.11 → 0.25 (inner body starts at x=240/960) - insetRight: 0.22 → 0.167 (inner body ends at x=800/960) - insetVertical: 0.28 → 0.375 (inner body y=360→600 in 960h) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace Row with FlowRow in CompactHealthRow so long absolute dates wrap instead of pushing signal off-screen - Increase footer spacedBy from 4dp to 6dp for even spacing between text segments and status icons Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add hopsAway parameter to previewSampleNode() for direct-heard variant - Use relative time in signal-only preview (absolute date formatter crashes in headless screenshot test JVM) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Matches Complete view placement — unmessageable icon now appears in the top row next to the favorite star instead of buried in the footer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
In compact mode, distance now uses IconInfo directly without the 'Distance' label text — just the icon and the value (e.g. '2.2 km'). This matches the compact convention of icon+value for all fields. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The pin-drop, temperature, and power icons in compact mode were confusing — they indicated data availability but without context. Complete view already shows actual telemetry values directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- showTelemetry now toggles the environment sensors grid (temp, humidity, baro, soil, voltage, current, IAQ) in Complete view - Renamed toggle label from 'Log Icons' to 'Environment Metrics' - Toggle was previously a no-op after removing telemetry presence icons from compact mode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Shows temp, humidity, and barometric pressure as icon + value only (no labels). Controlled by the 'Environment Metrics' toggle. Uses FlowRow for wrapping on narrow screens. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace ROUTER/CLIENT/etc text with the role-specific icon (mountain flag for router, person for client, etc). Keeps the footer more compact and visually scannable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rewrite CompactFooterRow with SegmentedRow helper (middot-separated) - Hardware: icon + model name (no label) - Role: icon only (icon IS the information) - Hops: icon + count number - Channel: numbered counter icon only - MQTT: icon only when via MQTT - Extract channelIcon() helper (Counter0–Counter8 mapping) - Remove stale @Suppress("UnusedParameter") now that showTelemetry is used Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Matrix screenshots show node items in various toggle combinations: - Compact: all fields, health only, no metrics, no footer, metrics+footer, minimal - Complete: with/without metrics toggle Each row labeled with active state for quick visual reference. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the avatar chip from a separate leading column into the name row. This allows health, metrics, and footer rows to span the full card width instead of being constrained by the chip's horizontal space. 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Node List Item Layout Redesign
Redesigned compact and complete density node items with M3 Expressive patterns.
Changes
Screenshots
Compact — All Fields (Light / Dark)
Compact — Active Node (Light / Dark)
Complete — Full (Light / Dark)
Complete — Active Node (Light / Dark)
Toggle Matrix — Compact (Light / Dark)
Toggle Matrix — Complete (Light / Dark)
Settings Panel