ErrorMonitor implementation more like NetworkMonitor

Removed all but the message.stateIn from AppState

Added interface and implementation, dependency injection
pull/1461/head
TM 1 year ago
parent 86c32eedd3
commit 3c69b57579

@ -41,6 +41,7 @@ import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.data.util.ErrorMonitor
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
@ -69,6 +70,9 @@ class MainActivity : ComponentActivity() {
@Inject
lateinit var networkMonitor: NetworkMonitor
@Inject
lateinit var errorMonitor: ErrorMonitor
@Inject
lateinit var timeZoneMonitor: TimeZoneMonitor
@ -133,6 +137,7 @@ class MainActivity : ComponentActivity() {
val appState = rememberNiaAppState(
networkMonitor = networkMonitor,
errorMonitor = errorMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)

@ -29,6 +29,7 @@ import androidx.navigation.compose.rememberNavController
import androidx.navigation.navOptions
import androidx.tracing.trace
import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.data.util.ErrorMonitor
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import com.google.samples.apps.nowinandroid.core.ui.TrackDisposableJank
@ -44,19 +45,17 @@ import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.BOOKM
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.FOR_YOU
import com.google.samples.apps.nowinandroid.navigation.TopLevelDestination.INTERESTS
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.datetime.TimeZone
import java.util.UUID
@Composable
fun rememberNiaAppState(
networkMonitor: NetworkMonitor,
errorMonitor: ErrorMonitor,
userNewsResourceRepository: UserNewsResourceRepository,
timeZoneMonitor: TimeZoneMonitor,
coroutineScope: CoroutineScope = rememberCoroutineScope(),
@ -67,6 +66,7 @@ fun rememberNiaAppState(
navController,
coroutineScope,
networkMonitor,
errorMonitor,
userNewsResourceRepository,
timeZoneMonitor,
) {
@ -74,6 +74,7 @@ fun rememberNiaAppState(
navController = navController,
coroutineScope = coroutineScope,
networkMonitor = networkMonitor,
errorMonitor = errorMonitor,
userNewsResourceRepository = userNewsResourceRepository,
timeZoneMonitor = timeZoneMonitor,
)
@ -85,6 +86,7 @@ class NiaAppState(
val navController: NavHostController,
coroutineScope: CoroutineScope,
networkMonitor: NetworkMonitor,
errorMonitor: ErrorMonitor,
userNewsResourceRepository: UserNewsResourceRepository,
timeZoneMonitor: TimeZoneMonitor,
) {
@ -108,44 +110,12 @@ class NiaAppState(
initialValue = false,
)
/**
* List of [ErrorMessage] to be shown to the user, via Snackbar.
*/
private val errorMessages = MutableStateFlow<List<ErrorMessage>>(emptyList())
/**
* Current [ErrorMessage] or null if there isn't any.
*/
val errorMessage: StateFlow<ErrorMessage?> = errorMessages.map { it.firstOrNull() }.stateIn(
val errorMessage = errorMonitor.errorMessage.stateIn(
scope = coroutineScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = null,
)
/**
* Creates an [ErrorMessage] from String value and adds it to the list.
*
* @param error: String value of the error message.
*
* Returns the ID of the new [ErrorMessage] if success
* Returns null if [error] is Blank
*/
fun addErrorMessage(error: String): String? {
if (error.isNotBlank()) {
val newError = ErrorMessage(error)
errorMessages.update { it + newError }
return newError.id
}
return null
}
/**
* Removes the [ErrorMessage] with the specified [id] from the list.
*/
fun clearErrorMessage(id: String) {
errorMessages.update { it.filter { item -> item.id != id } }
}
/**
* Map of top level destinations to be used in the TopBar, BottomBar and NavRail. The key is the
* route.
@ -227,11 +197,3 @@ private fun NavigationTrackingSideEffect(navController: NavHostController) {
}
}
}
/**
* Models the data needed for an error message to be displayed and tracked.
*/
data class ErrorMessage(
val message: String,
val id: String = UUID.randomUUID().toString(),
)

@ -27,7 +27,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsR
import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.data.util.ConnectivityManagerNetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.ErrorMonitor
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.SnackbarErrorMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneBroadcastMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import dagger.Binds
@ -71,4 +73,9 @@ abstract class DataModule {
@Binds
internal abstract fun binds(impl: TimeZoneBroadcastMonitor): TimeZoneMonitor
@Binds
internal abstract fun bindsErrorMonitor(
errorMonitor: SnackbarErrorMonitor,
): ErrorMonitor
}

@ -0,0 +1,28 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.data.util
import kotlinx.coroutines.flow.Flow
/**
* Interface for handling error messages.
*/
interface ErrorMonitor {
fun addErrorMessage(error: String): String?
fun clearErrorMessage(id: String)
val errorMessage: Flow<ErrorMessage?>
}

@ -0,0 +1,71 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid.core.data.util
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import java.util.UUID
/**
* Interface implementation for handling general errors.
*/
class SnackbarErrorMonitor : ErrorMonitor {
/**
* List of [ErrorMessage] to be shown to the user, via Snackbar.
*/
private val errorMessages = MutableStateFlow<List<ErrorMessage>>(emptyList())
/**
* Current [ErrorMessage] or null if there are none.
*/
override val errorMessage: Flow<ErrorMessage?> = errorMessages.map { it.firstOrNull() }
/**
* Creates an [ErrorMessage] from String value and adds it to the list.
*
* @param error: String value of the error message.
*
* Returns the ID of the new [ErrorMessage] if success
* Returns null if [error] is Blank
*/
override fun addErrorMessage(error: String): String? {
if (error.isNotBlank()) {
val newError = ErrorMessage(error)
errorMessages.update { it + newError }
return newError.id
}
return null
}
/**
* Removes the [ErrorMessage] with the specified [id] from the list.
*/
override fun clearErrorMessage(id: String) {
errorMessages.update { it.filter { item -> item.id != id } }
}
}
/**
* Models the data needed for an error message to be displayed and tracked.
*/
data class ErrorMessage(
val message: String,
val id: String = UUID.randomUUID().toString(),
)
Loading…
Cancel
Save