From bfc535915219618c40d45bdaad3d089428758688 Mon Sep 17 00:00:00 2001 From: shanecodezzz Date: Sun, 15 Feb 2026 10:29:16 -0800 Subject: [PATCH] fix(SearchViewModel.kt): all five viewmodelscope.launch blocks and one... Added try-catch with logging to all coroutine launches and a catch operator to the recentSearchQueriesUiState Flow. --- .../feature/search/impl/SearchViewModel.kt | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt index 13628de70..957626d55 100644 --- a/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt +++ b/feature/search/impl/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/impl/SearchViewModel.kt @@ -29,6 +29,7 @@ import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUs import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult import dagger.hilt.android.lifecycle.HiltViewModel +import android.util.Log import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch @@ -38,6 +39,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.coroutines.cancellation.CancellationException @HiltViewModel class SearchViewModel @Inject constructor( @@ -84,6 +86,10 @@ class SearchViewModel @Inject constructor( val recentSearchQueriesUiState: StateFlow = recentSearchQueriesUseCase() .map(RecentSearchQueriesUiState::Success) + .catch { + Log.e(TAG, "Failed to load recent search queries", it) + emit(RecentSearchQueriesUiState.Loading) + } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), @@ -104,32 +110,62 @@ class SearchViewModel @Inject constructor( fun onSearchTriggered(query: String) { if (query.isBlank()) return viewModelScope.launch { - recentSearchRepository.insertOrReplaceRecentSearch(searchQuery = query) + try { + recentSearchRepository.insertOrReplaceRecentSearch(searchQuery = query) + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + Log.e(TAG, "Failed to save recent search query", exception) + } } analyticsHelper.logEventSearchTriggered(query = query) } fun clearRecentSearches() { viewModelScope.launch { - recentSearchRepository.clearRecentSearches() + try { + recentSearchRepository.clearRecentSearches() + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + Log.e(TAG, "Failed to clear recent searches", exception) + } } } fun setNewsResourceBookmarked(newsResourceId: String, isChecked: Boolean) { viewModelScope.launch { - userDataRepository.setNewsResourceBookmarked(newsResourceId, isChecked) + try { + userDataRepository.setNewsResourceBookmarked(newsResourceId, isChecked) + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + Log.e(TAG, "Failed to bookmark news resource", exception) + } } } fun followTopic(followedTopicId: String, followed: Boolean) { viewModelScope.launch { - userDataRepository.setTopicIdFollowed(followedTopicId, followed) + try { + userDataRepository.setTopicIdFollowed(followedTopicId, followed) + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + Log.e(TAG, "Failed to follow topic", exception) + } } } fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) { viewModelScope.launch { - userDataRepository.setNewsResourceViewed(newsResourceId, viewed) + try { + userDataRepository.setNewsResourceViewed(newsResourceId, viewed) + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + Log.e(TAG, "Failed to mark news resource as viewed", exception) + } } } } @@ -148,3 +184,4 @@ private const val SEARCH_QUERY_MIN_LENGTH = 2 /** Minimum number of the fts table's entity count where it's considered as search is not ready */ private const val SEARCH_MIN_FTS_ENTITY_COUNT = 1 private const val SEARCH_QUERY = "searchQuery" +private const val TAG = "SearchViewModel"