Skip to content

Instantly share code, notes, and snippets.

@raspberrypisig
Last active July 20, 2025 10:51
Show Gist options
  • Save raspberrypisig/97898f5f837847bc732e339a89aa3388 to your computer and use it in GitHub Desktop.
Save raspberrypisig/97898f5f837847bc732e339a89aa3388 to your computer and use it in GitHub Desktop.
class SearchViewModel @Inject constructor(
private val searchUseCase: dagger.Lazy<SearchUseCase>,
private val wordsUseCase: GetWordsUseCase,
) : ViewModel() {
data class UiState(
val isLoading: Boolean = true,
val words: List<String> = emptyList()
)
val state: StateFlow<UiState> = flow {
emit(UiState(isLoading = true))
val words = wordsUseCase.invoke()
emit(UiState(isLoading = false, words = words))
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UiState())
}
val originalStateFlow: StateFlow<Int> = MutableStateFlow(1)
val transformedStateFlow: StateFlow<String> = originalStateFlow
.map { value -> "Value: $value" }
.stateIn(
scope = CoroutineScope(Dispatchers.Default), // Or a suitable scope
started = SharingStarted.Lazily,
initialValue = "Value: ${originalStateFlow.value}" // Initial value for the new StateFlow
)
// ---------- Domain ----------
class GetArticleUseCase @Inject constructor(
private val repo: ArticleRepository
) {
operator fun invoke(id: String): Flow<Article> = repo.observe(id)
}
class PublishArticleUseCase @Inject constructor(
private val repo: ArticleRepository
) {
suspend operator fun invoke(article: Article) { repo.publish(article) }
}
// ---------- ViewModel ----------
@HiltViewModel
class ArticleViewModel @Inject constructor(
private val getArticle: GetArticleUseCase,
private val publishArticle: PublishArticleUseCase
) : ViewModel() {
val uiState: StateFlow<ArticleUiState> =
getArticle(articleId)
.map { it.toUiState() }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), ArticleUiState.Loading)
fun publish() {
viewModelScope.launch {
publishArticle(uiState.value.toArticle())
}
}
}
class SearchViewModel @Inject constructor(
private val searchUseCase: dagger.Lazy<SearchUseCase>,
) : ViewModel() {
private val searchQuery = MutableStateFlow("")
val uiState: LiveData<SearchUiState> = searchQuery
.debounce(DEBOUNCE_TIME_IN_MILLIS)
.asLiveData()
.switchMap(::createUiState)
private fun createUiState(query: @JvmSuppressWildcards String) = liveData {
Timber.d("collectLatest(), query:[%s]", query)
if (query.isEmpty()) {
emit(SearchUiState.Idle)
return@liveData
}
try {
emit(SearchUiState.Loading)
val photos = searchUseCase.get().invoke(query)
if (photos.isEmpty()) {
emit(SearchUiState.EmptyResult)
} else {
emit(SearchUiState.Success(photos))
}
} catch (e: Exception) {
emit(SearchUiState.Error(e))
}
}
fun onQueryChanged(query: String?) {
query ?: return
searchQuery.value = query
}
sealed class SearchUiState {
data object Loading : SearchUiState()
data object Idle : SearchUiState()
data class Success(val photos: List<FlickrPhoto>) : SearchUiState()
data object EmptyResult : SearchUiState()
data class Error(val exception: Throwable) : SearchUiState()
}
companion object {
private const val DEBOUNCE_TIME_IN_MILLIS = 300L
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment