From 646d18afe9b35c61babbec95b88c7a9b63868af7 Mon Sep 17 00:00:00 2001 From: Mohamed Ibrahim Date: Wed, 6 Oct 2021 19:20:45 +0200 Subject: [PATCH 1/2] refactore SearchScreen composeable --- .../io/github/mohamedisoliman/pixapay/MVI.kt | 5 + .../pixapay/data/entities/ImageModel.kt | 14 +-- .../pixapay/domain/SearchUsecase.kt | 3 +- .../mohamedisoliman/pixapay/ui/navigation.kt | 9 +- .../ui/search/SearchImagesViewModel.kt | 10 +- .../pixapay/ui/search/SearchScreen.kt | 93 ++++++++++++------- .../pixapay/ui/search/SearchScreenEvents.kt | 31 +++++++ 7 files changed, 116 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt create mode 100644 app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt new file mode 100644 index 0000000..84124bc --- /dev/null +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt @@ -0,0 +1,5 @@ +package io.github.mohamedisoliman.pixapay + +interface UiEvent + +interface UiState diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/data/entities/ImageModel.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/data/entities/ImageModel.kt index 31ff0d7..25e1ceb 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/data/entities/ImageModel.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/data/entities/ImageModel.kt @@ -2,11 +2,11 @@ package io.github.mohamedisoliman.pixapay.data.entities data class ImageModel( val imageId: Long = -1, - val userName: String, - val url: String, - val likes: String, - val downloads: String, - val comments: String, - val tags: List, - val largeImageURL: String?, + val userName: String = "", + val url: String = "", + val likes: String = "", + val downloads: String = "", + val comments: String = "", + val tags: List = emptyList(), + val largeImageURL: String? = "", ) \ No newline at end of file diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt index 34aa301..37e8114 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt @@ -1,5 +1,6 @@ package io.github.mohamedisoliman.pixapay.domain +import io.github.mohamedisoliman.pixapay.UiState import io.github.mohamedisoliman.pixapay.data.ImagesRepositoryContract import io.github.mohamedisoliman.pixapay.data.entities.toImageModel import io.github.mohamedisoliman.pixapay.data.entities.ImageModel @@ -20,7 +21,7 @@ class SearchUsecase @Inject constructor( } -sealed class SearchState { +sealed class SearchState : UiState { object Empty : SearchState() class Success(val images: List) : SearchState() class Error(val throwable: Throwable) : SearchState() diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/navigation.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/navigation.kt index ecf6990..b581c5e 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/navigation.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/navigation.kt @@ -14,8 +14,10 @@ import io.github.mohamedisoliman.pixapay.R import io.github.mohamedisoliman.pixapay.ui.image_details.ImageDetailsScreen import io.github.mohamedisoliman.pixapay.ui.search.SearchImagesViewModel import io.github.mohamedisoliman.pixapay.ui.search.SearchScreen +import kotlinx.coroutines.ExperimentalCoroutinesApi +@OptIn(ExperimentalCoroutinesApi::class) @Composable fun AppNavigation( modifier: Modifier = Modifier, @@ -23,6 +25,9 @@ fun AppNavigation( ) { val viewModel: SearchImagesViewModel = hiltViewModel() + viewModel.navigateToDetails = { + navController.navigate("${Screen.ImageDetails.route}/${it}") + } NavHost( navController = navController, @@ -31,9 +36,7 @@ fun AppNavigation( ) { composable(Screen.Search.route) { - SearchScreen(viewModel) { - navController.navigate("${Screen.ImageDetails.route}/${it}") - } + SearchScreen(viewModel) } composable( diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt index eb3785e..6fc2a9f 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt @@ -18,8 +18,10 @@ class SearchImagesViewModel @Inject constructor( private var _searchViewState = MutableStateFlow(Empty) val searchViewState: StateFlow = _searchViewState - private var _searchQueryState = MutableStateFlow("fruits") - val searchQueryState: StateFlow = _searchQueryState + private var _queryState = MutableStateFlow("fruits") + val queryState: StateFlow = _queryState + + lateinit var navigateToDetails: (Long) -> Unit init { @@ -34,7 +36,7 @@ class SearchImagesViewModel @Inject constructor( fun onSearchChange(searchText: String) { - _searchQueryState.value = searchText + _queryState.value = searchText } @@ -43,7 +45,7 @@ class SearchImagesViewModel @Inject constructor( fun onSearchClicked() { - search(_searchQueryState.value) + search(_queryState.value) } } diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt index 814c7d0..80cfa7c 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt @@ -29,41 +29,84 @@ import androidx.compose.ui.unit.dp import coil.annotation.ExperimentalCoilApi import coil.compose.rememberImagePainter import io.github.mohamedisoliman.pixapay.R +import io.github.mohamedisoliman.pixapay.UiState +import io.github.mohamedisoliman.pixapay.data.entities.ImageModel import io.github.mohamedisoliman.pixapay.domain.SearchState -import io.github.mohamedisoliman.pixapay.domain.SearchState.* import io.github.mohamedisoliman.pixapay.ui.common.ImageChips import io.github.mohamedisoliman.pixapay.ui.common.isPortrait import io.github.mohamedisoliman.pixapay.ui.common.toUiModel -import io.github.mohamedisoliman.pixapay.data.entities.ImageModel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview @Preview @Composable fun PreviewSearch() { - SearchScreenContent(searchState = Empty) + SearchScreenContent() } +@OptIn(FlowPreview::class) +@ExperimentalCoroutinesApi @Composable -fun SearchScreen(viewModel: SearchImagesViewModel, onNavigateClicked: (Long) -> Unit = { }) { +fun SearchScreen(viewModel: SearchImagesViewModel) { val viewState by viewModel.searchViewState.collectAsState() - val query by viewModel.searchQueryState.collectAsState() + val query by viewModel.queryState.collectAsState() + var isLoading by remember { mutableStateOf(false) } + var showDialog by remember { mutableStateOf(false) } + var imageId by remember { mutableStateOf(-1L) } SearchScreenContent( - onImageClicked = onNavigateClicked, searchText = query, - searchState = viewState, + searchMainView = { + viewState.StateToMainView(isLoading = { + isLoading = it + }, showDialog = { + showDialog = it + }, imageId = { + imageId = it + }) + + }, onSearchChange = { viewModel.onSearchChange(it) }, - onSearchClicked = { viewModel.onSearchClicked() } + onSearchClicked = { viewModel.onSearchClicked() }, + isLoading = isLoading ) + + ConfirmationDialog(showDialog = showDialog, onConfirm = { + showDialog = false + viewModel.navigateToDetails(imageId) + }) { + showDialog = false + } +} + +@Composable +private fun UiState.StateToMainView( + imageId: (Long) -> Unit, + showDialog: (Boolean) -> Unit, + isLoading: (Boolean) -> Unit, +) { + when (this) { + is SearchState.Empty -> EmptyView() + is SearchState.Error -> ErrorView(this.throwable) + is SearchState.Success -> ImageListView(this.images) { + imageId(it) + showDialog(true) + } + is SearchState.Loading -> isLoading(true) + else -> { } + }.also { + isLoading(this is SearchState.Loading) + } } @Composable fun ConfirmationDialog( - showDialog: Boolean, + showDialog: Boolean = false, onConfirm: () -> Unit, onDismiss: () -> Unit, ) { - if (showDialog) { + if (showDialog) AlertDialog( modifier = Modifier.fillMaxWidth(), title = { Text(stringResource(R.string.dialog_title_open_image_details)) }, @@ -86,55 +129,37 @@ fun ConfirmationDialog( } } ) - } } @OptIn(ExperimentalComposeUiApi::class) @Composable private fun SearchScreenContent( searchText: String = "", + isLoading: Boolean = false, onSearchChange: (String) -> Unit = {}, onSearchClicked: () -> Unit = {}, - searchState: SearchState = Empty, - onImageClicked: (Long) -> Unit = {}, + searchMainView: @Composable () -> Unit = {}, ) { - var showDialog by remember { mutableStateOf(false) } - var imageId by remember { mutableStateOf(-1L) } - Box( modifier = Modifier.padding(top = 8.dp, start = 8.dp, end = 8.dp), contentAlignment = Alignment.TopCenter ) { - when (searchState) { - is Success -> ImageListView(searchState.images) { - imageId = it - showDialog = true - } - is Empty -> EmptyView(modifier = Modifier.align(Alignment.Center)) - is Error -> ErrorView(searchState.throwable) - } + searchMainView() SearchTopbar( searchText = searchText, onSearchChange = onSearchChange, onSearchClicked = onSearchClicked, - isLoading = searchState is Loading, + isLoading = isLoading, ) } - ConfirmationDialog(showDialog = showDialog, onConfirm = { - showDialog = false - onImageClicked(imageId) - }) { - showDialog = false - } - } @Composable -private fun EmptyView(modifier: Modifier) { +fun EmptyView(modifier: Modifier = Modifier) { PlaceHolderView( modifier = modifier, icon = Icons.Outlined.Pets, @@ -188,7 +213,7 @@ fun ErrorView( @OptIn(ExperimentalFoundationApi::class) @Composable -private fun ImageListView( +fun ImageListView( images: List, onImageClicked: (Long) -> Unit, ) { diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt new file mode 100644 index 0000000..7ad47b1 --- /dev/null +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt @@ -0,0 +1,31 @@ +package io.github.mohamedisoliman.pixapay.ui.search + +import androidx.compose.runtime.Composable +import io.github.mohamedisoliman.pixapay.UiEvent +import io.github.mohamedisoliman.pixapay.UiState +import io.github.mohamedisoliman.pixapay.domain.SearchState +import io.github.mohamedisoliman.pixapay.domain.SearchState.* + + +@Composable +fun UiState.toComposable( + onItemImageClicked: (Long) -> Unit = {}, + loading: @Composable (Boolean) -> Unit = {}, +) = when (this) { + is Empty -> EmptyView() + is Error -> ErrorView(this.throwable) + is Success -> ImageListView(this.images, onImageClicked = onItemImageClicked) + is Loading -> loading(true) + else -> NotImplementedError() +}.also { + loading(this is Loading) +} + + +sealed class SearchScreenEvent : UiEvent { + data class SearchQueryChange(val query: String) : SearchScreenEvent() + object SearchClicked : SearchScreenEvent() +} + +data class SearchTextChange(val text: String) : UiState + From 40a5a20bfb41d6029158539fa80516a7f1d1379b Mon Sep 17 00:00:00 2001 From: Mohamed Ibrahim Date: Thu, 7 Oct 2021 13:56:49 +0200 Subject: [PATCH 2/2] MVI implementation proposal version 0 --- .../io/github/mohamedisoliman/pixapay/MVI.kt | 36 ++++++++++++++ .../pixapay/domain/SearchUsecase.kt | 37 +++++++++++--- .../ui/search/SearchImagesViewModel.kt | 49 ++++++++----------- .../pixapay/ui/search/SearchMvi.kt | 9 ++++ .../pixapay/ui/search/SearchScreen.kt | 33 ++++++------- .../pixapay/ui/search/SearchScreenEvents.kt | 31 ------------ .../pixapay/domain/SearchUsecaseTest.kt | 8 +-- 7 files changed, 114 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchMvi.kt delete mode 100644 app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt index 84124bc..e27b274 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/MVI.kt @@ -1,5 +1,41 @@ package io.github.mohamedisoliman.pixapay +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch + interface UiEvent interface UiState + + +@OptIn(FlowPreview::class, kotlinx.coroutines.ExperimentalCoroutinesApi::class) +abstract class BaseViewModel(initState: S) : ViewModel() { + + + private val events = MutableSharedFlow() + private val _states = MutableStateFlow(initState) + val states = _states.asStateFlow() + + init { + events.flatMapMerge { it.eventToUsecase() } + .onEach { receiveState(it) } + .launchIn(viewModelScope) + } + + + abstract fun E.eventToUsecase(): Flow + + + fun emitEvent(event: E) { + viewModelScope.launch { events.emit(event) } + } + + open fun receiveState(state: S) { + _states.value = state + } + +} + diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt index 37e8114..cad7394 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecase.kt @@ -14,16 +14,37 @@ class SearchUsecase @Inject constructor( operator fun invoke(query: String): Flow { return imagesRepository.search(query) .map { list -> list.map { it.toImageModel() } } - .map { if (it.isEmpty()) SearchState.Empty else SearchState.Success(it) } - .onStart { emit(SearchState.Loading) } - .catch { emit(SearchState.Error(it)) } + .map { + if (it.isEmpty()) + SearchState.EmptyResult + else + SearchState.Success(searchText = query, result = it) + }.onStart { emit(SearchState.Loading) } + .catch { emit(SearchState.Error(searchText = query, it)) } } } -sealed class SearchState : UiState { - object Empty : SearchState() - class Success(val images: List) : SearchState() - class Error(val throwable: Throwable) : SearchState() - object Loading : SearchState() +sealed class SearchState( + val isLoading: Boolean = false, + val result: List? = null, + val searchText: String? = null, + val throwable: Throwable? = null, +) : UiState { + + class IDLE(searchText: String?) : SearchState(searchText = searchText) + + object EmptyResult : SearchState() + + class Success( + result: List?, + searchText: String?, + ) : SearchState(result = result, searchText = searchText) + + class Error( + searchText: String? = null, + throwable: Throwable? = null, + ) : SearchState(searchText = searchText, throwable = throwable) + + object Loading : SearchState(isLoading = true) } \ No newline at end of file diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt index 6fc2a9f..24cc637 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchImagesViewModel.kt @@ -1,51 +1,44 @@ package io.github.mohamedisoliman.pixapay.ui.search -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import io.github.mohamedisoliman.pixapay.BaseViewModel +import io.github.mohamedisoliman.pixapay.data.entities.ImageModel import io.github.mohamedisoliman.pixapay.domain.SearchState -import io.github.mohamedisoliman.pixapay.domain.SearchState.* +import io.github.mohamedisoliman.pixapay.domain.SearchState.EmptyResult +import io.github.mohamedisoliman.pixapay.domain.SearchState.Loading import io.github.mohamedisoliman.pixapay.domain.SearchUsecase -import io.github.mohamedisoliman.pixapay.data.entities.ImageModel -import kotlinx.coroutines.flow.* +import io.github.mohamedisoliman.pixapay.ui.search.SearchScreenEvent.SearchClicked +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.onStart import javax.inject.Inject @HiltViewModel class SearchImagesViewModel @Inject constructor( - val searchUsecase: SearchUsecase, -) : ViewModel() { + private val searchUsecase: SearchUsecase, +) : BaseViewModel(EmptyResult) { - private var _searchViewState = MutableStateFlow(Empty) - val searchViewState: StateFlow = _searchViewState - - private var _queryState = MutableStateFlow("fruits") - val queryState: StateFlow = _queryState + private val _queryState = MutableStateFlow("fruits") + val query = _queryState.asStateFlow() lateinit var navigateToDetails: (Long) -> Unit - init { - onSearchClicked() - } - - fun search(query: String) { - searchUsecase(query = query) - .onEach { _searchViewState.value = it } - .launchIn(viewModelScope) + emitEvent(SearchClicked(query.value)) } - fun onSearchChange(searchText: String) { - _queryState.value = searchText + fun onSearchQueryChange(query: String) { + _queryState.value = query } + fun findImage(id: Long): ImageModel? = states.value.result?.find { it.imageId == id } - fun findImage(id: Long): ImageModel? = - (_searchViewState.value as? Success)?.images?.find { it.imageId == id } - - fun onSearchClicked() { - search(_queryState.value) + override fun SearchScreenEvent.eventToUsecase(): Flow { + return when (this) { + is SearchClicked -> searchUsecase(this.query) + } } - } diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchMvi.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchMvi.kt new file mode 100644 index 0000000..343ff71 --- /dev/null +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchMvi.kt @@ -0,0 +1,9 @@ +package io.github.mohamedisoliman.pixapay.ui.search + +import io.github.mohamedisoliman.pixapay.UiEvent +import io.github.mohamedisoliman.pixapay.UiState + + +sealed class SearchScreenEvent : UiEvent { + data class SearchClicked(val query: String) : SearchScreenEvent() +} diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt index 80cfa7c..b92f1fa 100644 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt +++ b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreen.kt @@ -35,6 +35,7 @@ import io.github.mohamedisoliman.pixapay.domain.SearchState import io.github.mohamedisoliman.pixapay.ui.common.ImageChips import io.github.mohamedisoliman.pixapay.ui.common.isPortrait import io.github.mohamedisoliman.pixapay.ui.common.toUiModel +import io.github.mohamedisoliman.pixapay.ui.search.SearchScreenEvent.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview @@ -49,27 +50,25 @@ fun PreviewSearch() { @ExperimentalCoroutinesApi @Composable fun SearchScreen(viewModel: SearchImagesViewModel) { - val viewState by viewModel.searchViewState.collectAsState() - val query by viewModel.queryState.collectAsState() - var isLoading by remember { mutableStateOf(false) } + val viewState by viewModel.states.collectAsState() + val query by viewModel.query.collectAsState() var showDialog by remember { mutableStateOf(false) } var imageId by remember { mutableStateOf(-1L) } + SearchScreenContent( searchText = query, searchMainView = { - viewState.StateToMainView(isLoading = { - isLoading = it - }, showDialog = { + viewState.StateToMainView(showDialog = { showDialog = it }, imageId = { imageId = it }) }, - onSearchChange = { viewModel.onSearchChange(it) }, - onSearchClicked = { viewModel.onSearchClicked() }, - isLoading = isLoading + onSearchChange = { viewModel.onSearchQueryChange(it) }, + onSearchClicked = { viewModel.emitEvent(SearchClicked(query)) }, + isLoading = viewState.isLoading ) ConfirmationDialog(showDialog = showDialog, onConfirm = { @@ -84,19 +83,17 @@ fun SearchScreen(viewModel: SearchImagesViewModel) { private fun UiState.StateToMainView( imageId: (Long) -> Unit, showDialog: (Boolean) -> Unit, - isLoading: (Boolean) -> Unit, ) { when (this) { - is SearchState.Empty -> EmptyView() - is SearchState.Error -> ErrorView(this.throwable) - is SearchState.Success -> ImageListView(this.images) { - imageId(it) - showDialog(true) + is SearchState.EmptyResult -> EmptyView() + is SearchState.Error -> this.throwable?.let { ErrorView(it) } + is SearchState.Success -> this.result?.let { list -> + ImageListView(list) { + imageId(it) + showDialog(true) + } } - is SearchState.Loading -> isLoading(true) else -> { } - }.also { - isLoading(this is SearchState.Loading) } } diff --git a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt b/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt deleted file mode 100644 index 7ad47b1..0000000 --- a/app/src/main/java/io/github/mohamedisoliman/pixapay/ui/search/SearchScreenEvents.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.github.mohamedisoliman.pixapay.ui.search - -import androidx.compose.runtime.Composable -import io.github.mohamedisoliman.pixapay.UiEvent -import io.github.mohamedisoliman.pixapay.UiState -import io.github.mohamedisoliman.pixapay.domain.SearchState -import io.github.mohamedisoliman.pixapay.domain.SearchState.* - - -@Composable -fun UiState.toComposable( - onItemImageClicked: (Long) -> Unit = {}, - loading: @Composable (Boolean) -> Unit = {}, -) = when (this) { - is Empty -> EmptyView() - is Error -> ErrorView(this.throwable) - is Success -> ImageListView(this.images, onImageClicked = onItemImageClicked) - is Loading -> loading(true) - else -> NotImplementedError() -}.also { - loading(this is Loading) -} - - -sealed class SearchScreenEvent : UiEvent { - data class SearchQueryChange(val query: String) : SearchScreenEvent() - object SearchClicked : SearchScreenEvent() -} - -data class SearchTextChange(val text: String) : UiState - diff --git a/app/src/test/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecaseTest.kt b/app/src/test/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecaseTest.kt index b877e69..b3c4a6f 100644 --- a/app/src/test/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecaseTest.kt +++ b/app/src/test/java/io/github/mohamedisoliman/pixapay/domain/SearchUsecaseTest.kt @@ -12,13 +12,13 @@ class SearchUsecaseTest { @Test - fun `search() Then start Loading`() = runBlocking { + fun `search() THEN start with Loading state`() = runBlocking { val hit = mockk() val repository = mockRepository(flowOf(listOf(hit, hit, hit))) val result = SearchUsecase(repository).invoke("flowers").first() - assert((result is SearchState.Loading)) + assert(result is SearchState.Loading) } @Test @@ -39,7 +39,7 @@ class SearchUsecaseTest { val result = SearchUsecase(repository).invoke("flowers").last() - assert((result is SearchState.Empty)) + assert((result is SearchState.EmptyResult)) } @Test @@ -51,7 +51,7 @@ class SearchUsecaseTest { val result = SearchUsecase(repository).invoke("flowers").last() - assert((result is SearchState.Success) && result.images.size == 3) + assert((result is SearchState.Success) && result.result?.size == 3) }