mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
refactor(ui): Replace waitForState and shareLatest with lifecycle-aware state collection
Add collectAsStateWithLifecycle for all Compose state collection (except Glance widgets). Add asLiveState() extension to ViewModel2 as a simpler replacement for shareLatest(scope = vmScope). Remove custom waitForState helper and shareLatest utility.
This commit is contained in:
@@ -24,8 +24,6 @@ PR titles appear in auto-generated changelogs and are read by users. Use **ELI5,
|
||||
| **General** | Dashboard, settings, notifications, themes, onboarding, support, app-wide UI |
|
||||
| **Fix** | Bug fixes spanning multiple areas |
|
||||
|
||||
For non-user-facing PRs (refactors, tests, CI, dependency bumps, translations), apply the `changelog-ignore` GitHub label. These don't need user-facing titles.
|
||||
|
||||
### Title Examples
|
||||
|
||||
- `Widget: Add color themes and transparency slider`
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package eu.darken.capod.common.compose
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.produceState
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Composable
|
||||
fun <T> waitForState(flow: Flow<T>): State<T?> {
|
||||
return produceState(initialValue = null) {
|
||||
flow.collect { value = it }
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,12 @@ import androidx.activity.viewModels
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.view.WindowCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.theming.CapodTheme
|
||||
import eu.darken.capod.common.uix.Activity2
|
||||
@@ -41,7 +40,7 @@ class RecorderActivity : Activity2() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
val themeState by generalSettings.themeState.collectAsState(initial = generalSettings.currentThemeState)
|
||||
val themeState by generalSettings.themeState.collectAsStateWithLifecycle(initialValue = generalSettings.currentThemeState)
|
||||
CapodTheme(state = themeState) {
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
val useDarkIcons = backgroundColor.luminance() > 0.5f
|
||||
@@ -61,7 +60,7 @@ class RecorderActivity : Activity2() {
|
||||
}
|
||||
}
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
RecorderScreen(
|
||||
state = it,
|
||||
|
||||
@@ -14,41 +14,17 @@ import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.scan
|
||||
import kotlinx.coroutines.flow.shareIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
import kotlinx.coroutines.flow.transform
|
||||
import kotlinx.coroutines.flow.transformWhile
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* Create a stateful flow, with the initial value of null, but never emits a null value.
|
||||
* Helper method to create a new flow without suspending and without initial value
|
||||
* The flow collector will just wait for the first value
|
||||
*/
|
||||
fun <T : Any> Flow<T>.shareLatest(
|
||||
tag: String? = null,
|
||||
scope: CoroutineScope,
|
||||
started: SharingStarted = SharingStarted.WhileSubscribed(replayExpirationMillis = 0)
|
||||
) = this
|
||||
.onStart { if (tag != null) log(tag) { "shareLatest(...) start" } }
|
||||
.onEach { if (tag != null) log(tag) { "shareLatest(...) emission: $it" } }
|
||||
.onCompletion { if (tag != null) log(tag) { "shareLatest(...) completed." } }
|
||||
.catch {
|
||||
if (tag != null) log(tag) { "shareLatest(...) catch(): ${it.asLog()}" }
|
||||
throw it
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = started,
|
||||
initialValue = null
|
||||
)
|
||||
.filterNotNull()
|
||||
|
||||
fun <T : Any?> Flow<T>.replayingShare(scope: CoroutineScope) = this.shareIn(
|
||||
scope = scope,
|
||||
|
||||
@@ -13,7 +13,11 @@ import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@@ -64,4 +68,12 @@ abstract class ViewModel2(
|
||||
|
||||
open fun <T> Flow<T>.launchInViewModel() = this.launchIn(vmScope)
|
||||
|
||||
fun <T : Any> Flow<T>.asLiveState(
|
||||
started: SharingStarted = SharingStarted.WhileSubscribed(5_000),
|
||||
): Flow<T> = this.stateIn(
|
||||
scope = vmScope,
|
||||
started = started,
|
||||
initialValue = null,
|
||||
).filterNotNull()
|
||||
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
@@ -49,7 +49,7 @@ class MainActivity : Activity2() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
val themeState by generalSettings.themeState.collectAsState(initial = generalSettings.currentThemeState)
|
||||
val themeState by generalSettings.themeState.collectAsStateWithLifecycle(initialValue = generalSettings.currentThemeState)
|
||||
val backStack = rememberNavBackStack(startDestination)
|
||||
navCtrl.setup(backStack)
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.preview.MockPodDataProvider
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
@@ -129,11 +129,11 @@ fun OverviewScreenHost(vm: OverviewViewModel = hiltViewModel()) {
|
||||
}
|
||||
}
|
||||
|
||||
val stateHolder = waitForState(vm.state)
|
||||
val state = stateHolder.value ?: return
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
val currentState = state ?: return
|
||||
|
||||
OverviewScreen(
|
||||
state = state,
|
||||
state = currentState,
|
||||
onRequestPermission = { vm.requestPermission(it) },
|
||||
onManageDevices = { vm.goToDeviceManager() },
|
||||
onSettings = { vm.goToSettings() },
|
||||
|
||||
@@ -9,7 +9,7 @@ import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.SingleEventFlow
|
||||
import eu.darken.capod.common.flow.combine
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
|
||||
import eu.darken.capod.common.flow.throttleLatest
|
||||
import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
@@ -74,7 +74,7 @@ class OverviewViewModel @Inject constructor(
|
||||
monitorControl.startMonitor()
|
||||
}
|
||||
}
|
||||
.shareLatest(scope = vmScope)
|
||||
.asLiveState()
|
||||
|
||||
private val updateTicker = channelFlow<Unit> {
|
||||
while (isActive) {
|
||||
@@ -113,7 +113,7 @@ class OverviewViewModel @Inject constructor(
|
||||
upgradeInfo = upgradeInfo,
|
||||
showUnmatchedDevices = showUnmatched,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
data class State(
|
||||
val now: Instant,
|
||||
|
||||
@@ -29,7 +29,7 @@ import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.BuildConfigWrap
|
||||
import eu.darken.capod.common.PrivacyPolicy
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
@@ -41,7 +41,7 @@ fun SettingsScreenHost(vm: SettingsViewModel = hiltViewModel()) {
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
SettingsScreen(
|
||||
state = it,
|
||||
|
||||
@@ -4,7 +4,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.WebpageTool
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -23,7 +23,7 @@ class SettingsViewModel @Inject constructor(
|
||||
|
||||
val state = upgradeRepo.upgradeInfo
|
||||
.map { State(sponsorUrl = upgradeRepo.getSponsorUrl()) }
|
||||
.shareLatest(scope = vmScope)
|
||||
.asLiveState()
|
||||
|
||||
fun openUrl(url: String) {
|
||||
webpageTool.open(url)
|
||||
|
||||
@@ -51,7 +51,7 @@ import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.bluetooth.ScannerMode
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||
@@ -74,7 +74,7 @@ fun GeneralSettingsScreenHost(vm: GeneralSettingsViewModel = hiltViewModel()) {
|
||||
vm.launchUpgradeFlow.collect { action -> activity?.let { action(it) } }
|
||||
}
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
GeneralSettingsScreen(
|
||||
state = it,
|
||||
|
||||
+2
-3
@@ -6,7 +6,6 @@ import eu.darken.capod.common.bluetooth.ScannerMode
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.SingleEventFlow
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.theming.ThemeColor
|
||||
import eu.darken.capod.common.theming.ThemeMode
|
||||
@@ -41,7 +40,7 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
val themeState: ThemeState,
|
||||
)
|
||||
|
||||
private val isPro = upgradeRepo.upgradeInfo.map { it.isPro }.shareLatest(scope = vmScope)
|
||||
private val isPro = upgradeRepo.upgradeInfo.map { it.isPro }.asLiveState()
|
||||
|
||||
val launchUpgradeFlow = SingleEventFlow<(Activity) -> Unit>()
|
||||
|
||||
@@ -77,7 +76,7 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
useIndirectScanResultCallback = compat[2] as Boolean,
|
||||
themeState = themeState,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
fun setMonitorMode(mode: MonitorMode) {
|
||||
generalSettings.monitorMode.value = mode
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||
@@ -33,7 +33,7 @@ fun DebugSettingsScreenHost(vm: DebugSettingsViewModel = hiltViewModel()) {
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
DebugSettingsScreen(
|
||||
state = it,
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import javax.inject.Inject
|
||||
@@ -31,7 +31,7 @@ class DebugSettingsViewModel @Inject constructor(
|
||||
showFakeData = fakeData,
|
||||
showUnfiltered = unfiltered,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
fun setDebugModeEnabled(enabled: Boolean) {
|
||||
debugSettings.isDebugModeEnabled.value = enabled
|
||||
|
||||
@@ -33,7 +33,7 @@ import eu.darken.capod.common.PrivacyPolicy
|
||||
import eu.darken.capod.common.WebpageTool
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.debug.recording.ui.RecorderConsentDialog
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
@@ -50,7 +50,7 @@ fun SupportScreenHost(vm: SupportViewModel = hiltViewModel()) {
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
|
||||
var showShortRecordingWarning by remember { mutableStateOf(false) }
|
||||
|
||||
|
||||
+2
-2
@@ -57,7 +57,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.WebpageTool
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.debug.recording.ui.RecorderConsentDialog
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
@@ -120,7 +120,7 @@ fun ContactFormScreenHost(vm: ContactFormViewModel = hiltViewModel()) {
|
||||
}
|
||||
}
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
ContactFormScreen(
|
||||
state = it,
|
||||
|
||||
@@ -9,8 +9,8 @@ import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.view.WindowCompat
|
||||
@@ -18,7 +18,6 @@ import androidx.glance.appwidget.GlanceAppWidgetManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.theming.CapodTheme
|
||||
@@ -61,7 +60,7 @@ class WidgetConfigurationActivity : Activity2() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
val themeState by generalSettings.themeState.collectAsState(initial = generalSettings.currentThemeState)
|
||||
val themeState by generalSettings.themeState.collectAsStateWithLifecycle(initialValue = generalSettings.currentThemeState)
|
||||
CapodTheme(state = themeState) {
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
val useDarkIcons = backgroundColor.luminance() > 0.5f
|
||||
@@ -72,7 +71,7 @@ class WidgetConfigurationActivity : Activity2() {
|
||||
insetsController.isAppearanceLightNavigationBars = useDarkIcons
|
||||
}
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let { currentState ->
|
||||
WidgetConfigurationScreen(
|
||||
state = currentState,
|
||||
|
||||
@@ -9,7 +9,6 @@ import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.combine
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
import eu.darken.capod.common.uix.ViewModel2
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import eu.darken.capod.profiles.core.DeviceProfile
|
||||
@@ -70,7 +69,7 @@ class WidgetConfigurationViewModel @Inject constructor(
|
||||
activePreset = activePreset,
|
||||
isCustomMode = activePreset == null,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
data class State(
|
||||
val profiles: List<DeviceProfile> = emptyList(),
|
||||
|
||||
@@ -39,7 +39,7 @@ import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.preview.MockPodDataProvider
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
@@ -50,7 +50,7 @@ fun DeviceManagerScreenHost(vm: DeviceManagerViewModel = hiltViewModel()) {
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
DeviceManagerScreen(
|
||||
state = it,
|
||||
|
||||
@@ -4,7 +4,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
|
||||
import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.profiles.core.DeviceProfile
|
||||
@@ -20,7 +20,7 @@ class DeviceManagerViewModel @Inject constructor(
|
||||
|
||||
val state = deviceProfilesRepo.profiles.map { profiles ->
|
||||
State(profiles = profiles)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
data class State(
|
||||
val profiles: List<DeviceProfile>,
|
||||
|
||||
+2
-2
@@ -61,7 +61,7 @@ import eu.darken.capod.R
|
||||
import eu.darken.capod.common.bluetooth.BluetoothDevice2
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.toHex
|
||||
@@ -77,7 +77,7 @@ fun DeviceProfileCreationScreenHost(
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
|
||||
var showUnsavedChangesDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
+1
-2
@@ -11,7 +11,6 @@ import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.SingleEventFlow
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
import eu.darken.capod.common.fromHex
|
||||
import eu.darken.capod.common.toHex
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
@@ -118,7 +117,7 @@ class DeviceProfileCreationViewModel @Inject constructor(
|
||||
minimumSignalQuality = editorState.minimumSignalQuality,
|
||||
canSave = formValid && hasChanges,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
data class State(
|
||||
val isEditMode: Boolean,
|
||||
|
||||
@@ -43,7 +43,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||
@@ -55,7 +55,7 @@ fun ReactionSettingsScreenHost(vm: ReactionSettingsViewModel = hiltViewModel())
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
val activity = LocalContext.current as Activity
|
||||
|
||||
state?.let {
|
||||
|
||||
@@ -4,7 +4,7 @@ import android.app.Activity
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
@@ -35,7 +35,7 @@ class ReactionSettingsViewModel @Inject constructor(
|
||||
val showPopUpOnConnection: Boolean,
|
||||
)
|
||||
|
||||
private val isPro = upgradeRepo.upgradeInfo.map { it.isPro }.shareLatest(scope = vmScope)
|
||||
private val isPro = upgradeRepo.upgradeInfo.map { it.isPro }.asLiveState()
|
||||
|
||||
val state = combine(
|
||||
isPro,
|
||||
@@ -57,7 +57,7 @@ class ReactionSettingsViewModel @Inject constructor(
|
||||
showPopUpOnCaseOpen = values[6] as Boolean,
|
||||
showPopUpOnConnection = values[7] as Boolean,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
}.asLiveState()
|
||||
|
||||
fun setOnePodMode(enabled: Boolean) {
|
||||
reactionSettings.onePodMode.value = enabled
|
||||
|
||||
@@ -32,7 +32,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.compose.waitForState
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
|
||||
@@ -41,7 +41,7 @@ fun TroubleShooterScreenHost(vm: TroubleShooterViewModel = hiltViewModel()) {
|
||||
ErrorEventHandler(vm)
|
||||
NavigationEventHandler(vm)
|
||||
|
||||
val state by waitForState(vm.state)
|
||||
val state by vm.state.collectAsStateWithLifecycle(initialValue = null)
|
||||
state?.let {
|
||||
TroubleShooterScreen(
|
||||
state = it,
|
||||
|
||||
@@ -10,7 +10,7 @@ import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.INFO
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.monitor.core.PodMonitor
|
||||
@@ -45,7 +45,7 @@ class TroubleShooterViewModel @Inject constructor(
|
||||
|
||||
data class State(val bleState: BleState)
|
||||
|
||||
val state = _bleState.map { State(it) }.shareLatest(scope = vmScope)
|
||||
val state = _bleState.map { State(it) }.asLiveState()
|
||||
|
||||
init {
|
||||
_bleState
|
||||
|
||||
@@ -112,6 +112,7 @@ fun DependencyHandlerScope.addCompose() {
|
||||
|
||||
implementation("androidx.activity:activity-compose:1.12.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.5")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.5")
|
||||
|
||||
implementation("androidx.hilt:hilt-navigation-compose:1.3.0-alpha01")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user