feat(node): msh.to device hardware links ("I want one" section + Settings directory)#5714
Merged
Merged
Conversation
Port the Meshtastic-Apple device-links feature (spec 010-device-mshto-links): surface vendor, product-variant, and region-filtered marketplace purchase links for a node's hardware in a collapsible "I want one" section, plus a Settings "Device Links" directory. Every link resolves via https://msh.to/{shortCode}. - Bundle urls.json (from meshtastic/msh.to) + app-maintained marketplaces.json - New Room `device_link` table (DB v38->39 auto-migration) + DAO/local data source - DeviceLinkRepository(Impl) + pure DeviceLinkMatcher (exact-vendor / variant / marketplace prefix-suffix matching, rak-prefix strip, locale region filter); isVendor + orphan reconciliation rides the existing device-hardware refresh, self-seeds on first read - currentRegionCode() KMP expect/actual for marketplace region filtering - UI: DeviceLinksSection (node detail, via MetricsState.deviceLinks) and DeviceLinkDirectoryScreen + SettingsRoute.DeviceLinks; desktop Koin stub - Tests: DeviceLinkMatcherTest (11) + DeviceLinkRepositoryImplTest (3) Verified: spotlessCheck, detekt, assembleDebug, test, allTests, kmpSmokeCompile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
📸 Screenshot reference staleness — advisoryThis PR modifies preview composables but does not update screenshot reference images.
Changed preview files: How to update: ./gradlew :screenshot-tests:updateDebugScreenshotTestThen commit the updated reference PNGs. If this change is intentionally preview-only (e.g., adding a preview that doesn't need a test yet), add the |
Contributor
✅ Docs staleness check passedThis PR includes updates to |
- Unify marketplace classification into a single delimiter-aware DeviceLinkMatcher.marketplaceKeyFor used by both import-time region tagging and the matcher/UI. Fixes 36 aliexpress-prefix codes that were tagged null-region (rendered bold like vendor links yet sorted last), and stops bare names like `muziworks` inheriting `muzi` regions. - Run DeviceLinkRepository.reconcile() outside the device-hardware network timeout so a deadline can't cancel it mid-write (partial orphan prune). - Memoize bundled urls.json/marketplaces.json parsing (immutable assets) to drop repeated disk I/O + JSON parse on the node-detail hot path. - Add the Device Links entry to DesktopSettingsScreen for cross-platform parity (route/screen/VM were commonMain but unreachable on desktop). - Reuse core/ui ListItem in the directory instead of a hand-rolled row. - Drop the dead `rak-` variant branch; expose expand/collapse state to accessibility via stateDescription. - Tests: +marketplaceKeyForUsesDelimiterBounds, +aliexpress-prefix and +bare-name import tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Only add the "I want one" DeviceLinksSection as a LazyColumn item when there
are links to show. The previous unconditional `item {}` left an empty slot
that `Arrangement.spacedBy(24.dp)` still padded, shifting NodeDetailContent
layout and breaking the screenshot reference comparison. Guarding the item
restores byte-identical rendering (validateDebugScreenshotTest: 234/234) with
no reference-image changes needed.
- Document the "I want one" section and the Settings → Device Links directory in
docs/en/user/nodes.md (addresses the docs staleness advisory).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Addressed both advisories in
|
Merged
Resolved conflicts: - Room schema renumbered v39 -> v41 (FTS5=v39, air-quality=v40): version=41, AutoMigration(40->41), DeviceLinkEntity table, regenerated 41.json, kept release's 39.json - Routes / SettingsNavigation / SettingsScreen: union DeviceLinks + AppFunctionsSettings entries - proto pinned to 6b1ded4 (master HEAD) - session_context.md: take release/2.8.0
jamesarich
added a commit
that referenced
this pull request
Jun 10, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 10, 2026
…cher
Replace the bundled urls.json + marketplaces.json and the client-side DeviceLinkMatcher heuristic with a fetch of the now-live /resource/deviceLinks endpoint, which returns fully-resolved links (type, targets, regions). The client seeds from a bundled snapshot, refreshes from the network, and filters the cache - mirroring the DeviceHardware repository pattern.
- core/model: NetworkDeviceLink DTOs; DeviceLink drops originalUrl, adds targets
- core/network: ApiService.getDeviceLinks() + DeviceLinksRemoteDataSource
- core/database: device_link drops original_url, adds targets; DB v42 -> v43
- core/data: rewrite DeviceLinkRepositoryImpl (seed -> single-flight refresh); delete DeviceLinkMatcher + the MshTo datasource/model
- assets: replace urls.json + marketplaces.json with a device_links.json snapshot
Internal links are excluded from the directory per the API contract ("clients exclude 'internal' from purchase UI"). F-Droid and desktop fall back to the bundled snapshot when the API is unavailable, as they do for device hardware.
Follow-up to #5714; depends on meshtastic/api#94 and meshtastic/msh.to#3 (both merged).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 10, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 10, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 12, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 13, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 16, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 16, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jamesarich
added a commit
that referenced
this pull request
Jun 16, 2026
…ings directory) (#5714) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.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.
What
Ports the Meshtastic-Apple device msh.to links feature (Meshtastic-Apple#1898, spec
010-device-mshto-links) to Android. When viewing a node's hardware, a collapsible "I want one" section surfaces:Every link opens via the
https://msh.to/{shortCode}redirect. A Settings → Device Links directory lists all imported short codes.Why
Connects users to relevant resources (vendor pages, regional retailers) for the hardware they own or are viewing, supporting the Meshtastic backer/partner ecosystem — parity with the iOS app.
How
Mirrors the existing
DeviceHardwarepipeline (bundled-JSON seed + Room + reconcile-on-refresh):androidApp/src/main/assets/urls.json(copied as-is from the meshtastic/msh.to repo) + app-maintainedmarketplaces.json(shipping regions + prefix/suffix match patterns).device_linktable (DB v38→39 auto-migration;39.jsonschema committed),DeviceLinkEntity/DeviceLinkDao/DeviceLinkLocalDataSource; addedDeviceHardwareDao.getAllTargets().DeviceLinkRepository(Impl)+ a pure, unit-testedDeviceLinkMatcher(exact-vendor → variant → marketplace prefix/suffix matching,rak-prefix strip, locale region filter).isVendor/orphan reconciliation ridesDeviceHardwareRepositoryImpl's network refresh; self-seeds on first read.currentRegionCode()KMP expect/actual (android/jvm =Locale.country, ios noop) for marketplace region filtering.DeviceLinksSectionon the node detail screen (fed viaMetricsState.deviceLinksfromCommonGetNodeDetailsUseCase);DeviceLinkDirectoryScreen+SettingsRoute.DeviceLinks. Desktop Koin graph gets a stub binding for the Android-only JSON data source.Reviewer notes
architecture-as-enum decode pitfall flagged in the Apple spec is already safe on Android (architectureis aString, parsed withignoreUnknownKeys).DeviceLinkMatcherTest(11, covers the spec's acceptance scenarios) andDeviceLinkRepositoryImplTest(3, in-memory DB).spotlessCheck,detekt,assembleDebug,test,allTests(1213),kmpSmokeCompile..agent_memorysession-handover +strings-index.txtregeneration.🤖 Generated with Claude Code