mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-09-21 11:15:18 -04:00
feat(connections): enable wasmJs, unblocking all v0 feature modules
The v0 web slice (feature:connections/messaging/node/settings, per
this effort's own architecture decision) all share
KmpFeatureConventionPlugin. Its apply() wired core:testing directly
into every consumer's commonTest, unconditionally, at plugin-apply
time -- before the consuming module's own build.gradle.kts kotlin {}
block (and any nonWebTest source set it creates) has even run.
core:testing has no wasmJs target, so every v0 feature module would
have hit the identical compileTestKotlinWasmJs failure the moment it
opted in, no matter what its own build.gradle.kts did to try to route
around it -- a shared-build-logic problem, not a per-module one.
Fixed by deferring the wiring to target.afterEvaluate, which checks
whether the consuming module ended up with a wasmJs target and a
nonWebTest source set and routes core:testing there instead when both
exist, with a fail-fast check() if a module has one but not the other.
Every non-wasmJs feature module keeps resolving core:testing via
commonTest exactly as before -- verified with a real compile+test run
across all nine other consumers (messaging, node, settings,
map-maplibre, intro, discovery, docs, firmware, wifi-provision), zero
regression.
core:domain (a feature:connections dependency, zero expect/actual,
zero java.*/android.* imports, every dependency already wasmJs-clean)
gets a bare wasmJs() -- mechanical.
feature:connections surfaced a sharper version of the screening test
this session has used for every prior module: "no expect/actual, no
java.*/android.* imports" is necessary but not sufficient.
ScannerViewModel.kt/CommonGetDiscoveredDevicesUseCase.kt directly
referenced core:datastore's RecentAddressesDataSource/
FirmwareRecoveryDataSource -- concrete classes that live in that
module's own nonWebMain (Preferences-backed, no wasmJs variant),
reached transitively rather than through any local expect/actual. Two
new feature-local interfaces (RecentAddressesSource,
PendingFirmwareRecoverySource) seam this off: a nonWebMain adapter
delegates to the real DataStore-backed sources unchanged, and wasmJs
gets an honest no-op (no recent-address history, no firmware-recovery
banner on web this pass) -- same shape as core:service's
TakServerIntegration seam. A real localStorage-backed implementation
is deferred until a webApp module exists to wire one in.
Also fixes an unrelated, pre-existing detekt violation
(NoUnusedImports on ProjectExtensions.kt) surfaced while re-running
build-logic/convention's own lint as part of this pass's verification
-- unrelated to this change's own logic, folded in since it was
already in front of us.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
49bd33c53e
commit
688c2fbc99
25 files changed
+246
-27
No files matched your search
@@ -69,8 +69,31 @@ class KmpFeatureConventionPlugin : Plugin<Project> {
|
||||
|
||||
implementation(libs.library("compose-multiplatform-ui"))
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets.getByName("commonTest").dependencies { implementation(project(":core:testing")) }
|
||||
// core:testing has no wasmJs target (same gap every core/* module hit while gaining one this
|
||||
// session). Wiring it into commonTest directly — as this plugin used to, unconditionally —
|
||||
// breaks compileTestKotlinWasmJs for every feature module that opts into wasmJs, since the
|
||||
// dependency is added by this shared plugin's apply(), which runs *before* the consuming
|
||||
// module's own build.gradle.kts `kotlin {}` block (and any nonWebTest source set it creates)
|
||||
// has executed. Deferring to afterEvaluate — which fires only after the whole build script has
|
||||
// run — lets us check what the consuming module actually set up and route accordingly:
|
||||
// - a module with a `nonWebTest` source set (wasmJs opted in, core:testing hoisted out of
|
||||
// commonMain the same way every core/* module did) gets core:testing wired there instead.
|
||||
// - every other feature module (no wasmJs, no nonWebTest) is wired into commonTest exactly as
|
||||
// before — fully backward-compatible, verified against every other v0/non-wasmJs consumer.
|
||||
target.afterEvaluate {
|
||||
extensions.configure<KotlinMultiplatformExtension> {
|
||||
val hasWasmJsTarget = targets.findByName("wasmJs") != null
|
||||
val nonWebTest = sourceSets.findByName("nonWebTest")
|
||||
check(!hasWasmJsTarget || nonWebTest != null) {
|
||||
"${target.path} registers wasmJs() but has no `nonWebTest` source set — " +
|
||||
"core:testing has no wasmJs target, so it must be routed away from commonTest. " +
|
||||
"See feature/connections/build.gradle.kts for the pattern."
|
||||
}
|
||||
val testSourceSet = nonWebTest ?: sourceSets.getByName("commonTest")
|
||||
testSourceSet.dependencies { implementation(project(":core:testing")) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.testing.AbstractTestTask
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.plugin.use.PluginDependency
|
||||
|
||||
@@ -24,6 +24,14 @@ plugins {
|
||||
kotlin {
|
||||
android { withHostTest { isIncludeAndroidResources = true } }
|
||||
|
||||
// Library module: bare wasmJs(), no browser(). No custom hierarchy group is needed for MAIN — zero
|
||||
// expect/actual declarations and zero java.*/android.* imports in commonMain (confirmed via grep),
|
||||
// and every commonMain dependency (core:repository/model/common/database/datastore/resources,
|
||||
// protobufs, kermit/okio/kotlinx-datetime/kotlinx-serialization-json(-okio)) already publishes a
|
||||
// wasmJs variant — same shape as core:repository/core:service, unlike core:ble/core:database.
|
||||
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
|
||||
wasmJs()
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(projects.core.repository)
|
||||
@@ -40,6 +48,17 @@ kotlin {
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
implementation(libs.kotlinx.serialization.json.okio)
|
||||
}
|
||||
commonTest.dependencies { implementation(projects.core.testing) }
|
||||
|
||||
// TEST only: core:testing has no wasmJs target (same gap every other module this session hit).
|
||||
// 7 of 13 commonTest files depend on it (confirmed via grep for the import, not assumed) — moved
|
||||
// to a nonWebTest source set; the other 6 stay in commonTest and compile for wasmJs.
|
||||
val nonWebTest by creating {
|
||||
dependsOn(commonTest.get())
|
||||
dependencies { implementation(projects.core.testing) }
|
||||
}
|
||||
getByName("jvmTest") { dependsOn(nonWebTest) }
|
||||
getByName("androidHostTest") { dependsOn(nonWebTest) }
|
||||
matching { it.name == "iosArm64Test" || it.name == "iosSimulatorArm64Test" }
|
||||
.configureEach { dependsOn(nonWebTest) }
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -15,11 +15,33 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyTemplate
|
||||
|
||||
plugins { alias(libs.plugins.meshtastic.kmp.feature) }
|
||||
|
||||
kotlin {
|
||||
android { withHostTest { isIncludeAndroidResources = true } }
|
||||
|
||||
// Feature module: bare wasmJs(), no browser() (that's for the eventual webApp executable).
|
||||
@OptIn(ExperimentalWasmDsl::class)
|
||||
wasmJs()
|
||||
|
||||
// nonWebMain: ScannerViewModel/CommonGetDiscoveredDevicesUseCase depend on RecentAddressesSource/
|
||||
// PendingFirmwareRecoverySource (feature-local interfaces, commonMain) — but the real, Preferences-backed
|
||||
// adapters (DataSourceAdapters.kt) delegate to core:datastore's RecentAddressesDataSource/
|
||||
// FirmwareRecoveryDataSource, which have no wasmJs target (androidx.datastore.preferences publishes none
|
||||
// — see core:datastore's own wasmJs milestone). Predicate, not withAndroidTarget()/withApple() — those
|
||||
// silently drop androidMain under com.android.kotlin.multiplatform.library (KT-80409), same as core:ble.
|
||||
@OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||
applyHierarchyTemplate(KotlinHierarchyTemplate.default) {
|
||||
common { group("nonWeb") { withCompilations { it.target.targetName != "wasmJs" } } }
|
||||
}
|
||||
|
||||
// The predicate above misses iosMain itself (only reaches the two leaf iOS compilations), same gap core:ble hit.
|
||||
sourceSets.getByName("iosMain") { dependsOn(sourceSets.getByName("nonWebMain")) }
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
implementation(projects.core.common)
|
||||
@@ -47,5 +69,14 @@ kotlin {
|
||||
implementation(libs.compose.multiplatform.ui.test)
|
||||
implementation(compose.desktop.currentOs)
|
||||
}
|
||||
|
||||
// TEST only: 4 of 6 commonTest files depend on core:testing (no wasmJs target — same gap every
|
||||
// other module this session hit), confirmed via grep for the import, not assumed:
|
||||
// ScannerViewModelHarness.kt/ScannerViewModelTest.kt/TcpDiscoveryHelpersTest.kt/
|
||||
// CommonGetDiscoveredDevicesUseCaseTest.kt moved to the nonWebTest source set the hierarchy
|
||||
// template above already creates (android/jvm/iOS only); the other 2 stay in commonTest and
|
||||
// compile for wasmJs. core:testing itself is wired into nonWebTest by KmpFeatureConventionPlugin
|
||||
// (afterEvaluate, routes to nonWebTest when present) — not added here.
|
||||
getByName("nonWebTest") { dependsOn(commonTest.get()) }
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -26,8 +26,6 @@ import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import org.koin.core.annotation.KoinViewModel
|
||||
import org.meshtastic.core.ble.BluetoothRepository
|
||||
import org.meshtastic.core.datastore.FirmwareRecoveryDataSource
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.model.util.anonymize
|
||||
import org.meshtastic.core.network.repository.NetworkRepository
|
||||
import org.meshtastic.core.network.repository.UsbRepository
|
||||
@@ -39,6 +37,8 @@ import org.meshtastic.core.repository.UiPrefs
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.bonding_failed_retry
|
||||
import org.meshtastic.core.resources.usb_permission_denied
|
||||
import org.meshtastic.feature.connections.data.PendingFirmwareRecoverySource
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.AndroidUsbDeviceData
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
@@ -50,14 +50,14 @@ class AndroidScannerViewModel(
|
||||
radioController: RadioController,
|
||||
radioInterfaceService: RadioInterfaceService,
|
||||
radioPrefs: RadioPrefs,
|
||||
recentAddressesDataSource: RecentAddressesDataSource,
|
||||
recentAddressesDataSource: RecentAddressesSource,
|
||||
getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase,
|
||||
networkRepository: NetworkRepository,
|
||||
dispatchers: org.meshtastic.core.di.CoroutineDispatchers,
|
||||
private val bluetoothRepository: BluetoothRepository,
|
||||
private val usbRepository: UsbRepository,
|
||||
uiPrefs: UiPrefs,
|
||||
firmwareRecoveryDataSource: FirmwareRecoveryDataSource,
|
||||
firmwareRecoveryDataSource: PendingFirmwareRecoverySource,
|
||||
bleScanner: org.meshtastic.core.ble.BleScanner? = null,
|
||||
) : ScannerViewModel(
|
||||
serviceRepository,
|
||||
|
||||
+2
-2
@@ -24,7 +24,6 @@ import org.jetbrains.compose.resources.getString
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.ble.BluetoothRepository
|
||||
import org.meshtastic.core.common.database.DatabaseManager
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
import org.meshtastic.core.model.Node
|
||||
import org.meshtastic.core.network.repository.DiscoveredService
|
||||
@@ -33,6 +32,7 @@ import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.meshtastic
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.AndroidUsbDeviceData
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
||||
@@ -44,7 +44,7 @@ import java.util.Locale
|
||||
@Single(binds = [GetDiscoveredDevicesUseCase::class])
|
||||
class AndroidGetDiscoveredDevicesUseCase(
|
||||
private val bluetoothRepository: BluetoothRepository,
|
||||
private val recentAddressesDataSource: RecentAddressesDataSource,
|
||||
private val recentAddressesDataSource: RecentAddressesSource,
|
||||
private val nodeRepository: NodeRepository,
|
||||
private val databaseManager: DatabaseManager,
|
||||
private val usbRepository: UsbRepository,
|
||||
|
||||
+4
-4
@@ -44,8 +44,6 @@ import org.meshtastic.core.ble.BleScanStartFailureReason
|
||||
import org.meshtastic.core.ble.BleScanner
|
||||
import org.meshtastic.core.ble.MeshtasticBleConstants
|
||||
import org.meshtastic.core.common.util.safeCatchingAll
|
||||
import org.meshtastic.core.datastore.FirmwareRecoveryDataSource
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.datastore.model.PendingFirmwareRecovery
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
@@ -65,6 +63,8 @@ import org.meshtastic.core.resources.getPluralStringSuspend
|
||||
import org.meshtastic.core.resources.getStringSuspend
|
||||
import org.meshtastic.core.ui.viewmodel.safeLaunch
|
||||
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
|
||||
import org.meshtastic.feature.connections.data.PendingFirmwareRecoverySource
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
@@ -143,12 +143,12 @@ open class ScannerViewModel(
|
||||
private val radioController: RadioController,
|
||||
private val radioInterfaceService: RadioInterfaceService,
|
||||
private val radioPrefs: RadioPrefs,
|
||||
private val recentAddressesDataSource: RecentAddressesDataSource,
|
||||
private val recentAddressesDataSource: RecentAddressesSource,
|
||||
private val getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase,
|
||||
private val networkRepository: NetworkRepository,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val uiPrefs: UiPrefs,
|
||||
private val firmwareRecoveryDataSource: FirmwareRecoveryDataSource,
|
||||
private val firmwareRecoveryDataSource: PendingFirmwareRecoverySource,
|
||||
private val bleScanner: BleScanner? = null,
|
||||
) : ViewModel() {
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.data
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.meshtastic.core.datastore.model.PendingFirmwareRecovery
|
||||
|
||||
/** Feature-local seam over `core:datastore`'s `FirmwareRecoveryDataSource` — see [RecentAddressesSource]. */
|
||||
interface PendingFirmwareRecoverySource {
|
||||
val pending: Flow<PendingFirmwareRecovery?>
|
||||
|
||||
suspend fun clear()
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.data
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
|
||||
/**
|
||||
* Feature-local seam over `core:datastore`'s `RecentAddressesDataSource`, which wraps `androidx.datastore.preferences`
|
||||
* and so has no wasmJs target (that library publishes zero js/wasmJs variants — see core:datastore's own wasmJs
|
||||
* milestone). Keeps [org.meshtastic.feature.connections.ScannerViewModel]/`CommonGetDiscoveredDevicesUseCase` in
|
||||
* commonMain: only the adapter delegating to the real DataSource is hoisted to `nonWebMain`, and wasmJs gets a no-op.
|
||||
*/
|
||||
interface RecentAddressesSource {
|
||||
val recentAddresses: Flow<List<RecentAddress>>
|
||||
|
||||
suspend fun add(address: RecentAddress)
|
||||
|
||||
suspend fun remove(address: String)
|
||||
}
|
||||
+2
-2
@@ -21,12 +21,12 @@ import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.meshtastic.core.common.database.DatabaseManager
|
||||
import org.meshtastic.core.common.util.safeCatchingAll
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.network.repository.DiscoveredService
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.core.resources.Res
|
||||
import org.meshtastic.core.resources.getStringSuspend
|
||||
import org.meshtastic.core.resources.meshtastic
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
* target registers its own `@Single` wrapper (see `JvmGetDiscoveredDevicesUseCase`).
|
||||
*/
|
||||
open class CommonGetDiscoveredDevicesUseCase(
|
||||
private val recentAddressesDataSource: RecentAddressesDataSource,
|
||||
private val recentAddressesDataSource: RecentAddressesSource,
|
||||
private val nodeRepository: NodeRepository,
|
||||
private val databaseManager: DatabaseManager,
|
||||
private val usbScanner: UsbScanner? = null,
|
||||
|
||||
+4
-4
@@ -17,14 +17,14 @@
|
||||
package org.meshtastic.feature.connections
|
||||
|
||||
import org.koin.core.annotation.KoinViewModel
|
||||
import org.meshtastic.core.datastore.FirmwareRecoveryDataSource
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.network.repository.NetworkRepository
|
||||
import org.meshtastic.core.repository.RadioController
|
||||
import org.meshtastic.core.repository.RadioInterfaceService
|
||||
import org.meshtastic.core.repository.RadioPrefs
|
||||
import org.meshtastic.core.repository.ServiceRepository
|
||||
import org.meshtastic.core.repository.UiPrefs
|
||||
import org.meshtastic.feature.connections.data.PendingFirmwareRecoverySource
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
|
||||
/**
|
||||
@@ -40,12 +40,12 @@ class JvmScannerViewModel(
|
||||
radioController: RadioController,
|
||||
radioInterfaceService: RadioInterfaceService,
|
||||
radioPrefs: RadioPrefs,
|
||||
recentAddressesDataSource: RecentAddressesDataSource,
|
||||
recentAddressesDataSource: RecentAddressesSource,
|
||||
getDiscoveredDevicesUseCase: GetDiscoveredDevicesUseCase,
|
||||
networkRepository: NetworkRepository,
|
||||
dispatchers: org.meshtastic.core.di.CoroutineDispatchers,
|
||||
uiPrefs: UiPrefs,
|
||||
firmwareRecoveryDataSource: FirmwareRecoveryDataSource,
|
||||
firmwareRecoveryDataSource: PendingFirmwareRecoverySource,
|
||||
bleScanner: org.meshtastic.core.ble.BleScanner? = null,
|
||||
) : ScannerViewModel(
|
||||
serviceRepository,
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ package org.meshtastic.feature.connections.domain.usecase
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.common.database.DatabaseManager
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.repository.NodeRepository
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,7 @@ import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
*/
|
||||
@Single(binds = [GetDiscoveredDevicesUseCase::class])
|
||||
class JvmGetDiscoveredDevicesUseCase(
|
||||
recentAddressesDataSource: RecentAddressesDataSource,
|
||||
recentAddressesDataSource: RecentAddressesSource,
|
||||
nodeRepository: NodeRepository,
|
||||
databaseManager: DatabaseManager,
|
||||
usbScanner: UsbScanner? = null,
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.data
|
||||
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.datastore.FirmwareRecoveryDataSource
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
|
||||
/** android/jvm/iOS binding: delegates to the real, Preferences-backed `RecentAddressesDataSource`. */
|
||||
@Single
|
||||
class RecentAddressesSourceAdapter(private val delegate: RecentAddressesDataSource) : RecentAddressesSource {
|
||||
override val recentAddresses = delegate.recentAddresses
|
||||
|
||||
override suspend fun add(address: RecentAddress) = delegate.add(address)
|
||||
|
||||
override suspend fun remove(address: String) = delegate.remove(address)
|
||||
}
|
||||
|
||||
/** android/jvm/iOS binding: delegates to the real, Preferences-backed `FirmwareRecoveryDataSource`. */
|
||||
@Single
|
||||
class PendingFirmwareRecoverySourceAdapter(private val delegate: FirmwareRecoveryDataSource) :
|
||||
PendingFirmwareRecoverySource {
|
||||
override val pending = delegate.pending
|
||||
|
||||
override suspend fun clear() = delegate.clear()
|
||||
}
|
||||
+4
-4
@@ -35,8 +35,6 @@ import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import org.meshtastic.core.ble.BleDevice
|
||||
import org.meshtastic.core.ble.BleScanner
|
||||
import org.meshtastic.core.datastore.FirmwareRecoveryDataSource
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.network.repository.DiscoveredService
|
||||
import org.meshtastic.core.network.repository.NetworkRepository
|
||||
@@ -47,6 +45,8 @@ import org.meshtastic.core.testing.FakeBluetoothRepository
|
||||
import org.meshtastic.core.testing.FakeRadioController
|
||||
import org.meshtastic.core.testing.FakeServiceRepository
|
||||
import org.meshtastic.core.testing.FakeUiPrefs
|
||||
import org.meshtastic.feature.connections.data.PendingFirmwareRecoverySource
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import org.meshtastic.feature.connections.model.DiscoveredDevices
|
||||
import org.meshtastic.feature.connections.model.GetDiscoveredDevicesUseCase
|
||||
@@ -73,8 +73,8 @@ class ScannerViewModelHarness(val testDispatcher: TestDispatcher = UnconfinedTes
|
||||
|
||||
val radioInterfaceService: RadioInterfaceService = mock(MockMode.autofill)
|
||||
val radioPrefs: RadioPrefs = mock(MockMode.autofill)
|
||||
val recentAddressesDataSource: RecentAddressesDataSource = mock(MockMode.autofill)
|
||||
val firmwareRecoveryDataSource: FirmwareRecoveryDataSource = mock(MockMode.autofill)
|
||||
val recentAddressesDataSource: RecentAddressesSource = mock(MockMode.autofill)
|
||||
val firmwareRecoveryDataSource: PendingFirmwareRecoverySource = mock(MockMode.autofill)
|
||||
val networkRepository: NetworkRepository = mock(MockMode.autofill)
|
||||
val bleScanner: BleScanner = mock(MockMode.autofill)
|
||||
|
||||
File renamed without changes.
+2
-2
@@ -26,11 +26,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.meshtastic.core.common.database.DatabaseManager
|
||||
import org.meshtastic.core.datastore.RecentAddressesDataSource
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
import org.meshtastic.core.network.repository.DiscoveredService
|
||||
import org.meshtastic.core.testing.FakeNodeRepository
|
||||
import org.meshtastic.core.testing.TestDataFactory
|
||||
import org.meshtastic.feature.connections.data.RecentAddressesSource
|
||||
import org.meshtastic.feature.connections.model.DeviceListEntry
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNotNull
|
||||
@@ -41,7 +41,7 @@ import kotlin.test.assertTrue
|
||||
class CommonGetDiscoveredDevicesUseCaseTest {
|
||||
private lateinit var useCase: CommonGetDiscoveredDevicesUseCase
|
||||
private lateinit var nodeRepository: FakeNodeRepository
|
||||
private lateinit var recentAddressesDataSource: RecentAddressesDataSource
|
||||
private lateinit var recentAddressesDataSource: RecentAddressesSource
|
||||
private lateinit var databaseManager: DatabaseManager
|
||||
private val recentAddressesFlow = MutableStateFlow<List<RecentAddress>>(emptyList())
|
||||
private val resolvedServicesFlow = MutableStateFlow<List<DiscoveredService>>(emptyList())
|
||||
File renamed without changes.
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.meshtastic.feature.connections.data
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.koin.core.annotation.Single
|
||||
import org.meshtastic.core.datastore.model.PendingFirmwareRecovery
|
||||
import org.meshtastic.core.datastore.model.RecentAddress
|
||||
|
||||
/**
|
||||
* wasmJs binding: no recent-TCP-address persistence on web (the real DataSource is Preferences-backed with no wasmJs
|
||||
* target — see [RecentAddressesSource]). [DEFERRED]: a real implementation could be written against `localStorage`,
|
||||
* mirroring core:datastore's own `LocalStorageStore`, once a webApp module exists to wire it in.
|
||||
*/
|
||||
@Single
|
||||
class NoopRecentAddressesSource : RecentAddressesSource {
|
||||
override val recentAddresses: Flow<List<RecentAddress>> = flowOf(emptyList())
|
||||
|
||||
override suspend fun add(address: RecentAddress) = Unit
|
||||
|
||||
override suspend fun remove(address: String) = Unit
|
||||
}
|
||||
|
||||
/** wasmJs binding: no firmware-recovery banner on web — see [NoopRecentAddressesSource]. */
|
||||
@Single
|
||||
class NoopPendingFirmwareRecoverySource : PendingFirmwareRecoverySource {
|
||||
override val pending: Flow<PendingFirmwareRecovery?> = flowOf(null)
|
||||
|
||||
override suspend fun clear() = Unit
|
||||
}
|
||||
Reference in new issue
Block a user