From b6e4d282b718538e33c35adf9affcafd14669239 Mon Sep 17 00:00:00 2001 From: Alexandru Gabor Date: Mon, 28 Feb 2022 16:57:29 +0200 Subject: [PATCH] Add home screen --- app/build.gradle | 1 + .../ui/compose/screen/AppNavigation.kt | 24 +++++- .../ui/compose/screen/auth/AuthScreen.kt | 2 +- .../ui/compose/screen/auth/AuthScreenState.kt | 5 +- .../ui/compose/screen/home/HomeScreen.kt | 79 +++++++++++++++--- .../ui/compose/screen/home/HomeScreenState.kt | 81 ++++++++++++++++++- gradlescripts/versions.gradle | 2 +- 7 files changed, 173 insertions(+), 21 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 5e58fcd..a33ff6c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,6 +95,7 @@ dependencies { implementation "androidx.room:room-ktx:$androidx_room_version" implementation "io.coil-kt:coil:$coil_version" + implementation "io.coil-kt:coil-compose:$coil_version" implementation project(":core") diff --git a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/AppNavigation.kt b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/AppNavigation.kt index 4ab1c20..f0a71b8 100644 --- a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/AppNavigation.kt +++ b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/AppNavigation.kt @@ -2,7 +2,6 @@ package org.fnives.test.showcase.ui.compose.screen import androidx.compose.foundation.background import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier @@ -12,6 +11,9 @@ import androidx.navigation.compose.rememberNavController import kotlinx.coroutines.delay import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase import org.fnives.test.showcase.ui.compose.screen.auth.AuthScreen +import org.fnives.test.showcase.ui.compose.screen.auth.rememberAuthScreenState +import org.fnives.test.showcase.ui.compose.screen.home.HomeScreen +import org.fnives.test.showcase.ui.compose.screen.home.rememberHomeScreenState import org.fnives.test.showcase.ui.compose.screen.splash.SplashScreen import org.koin.androidx.compose.get @@ -25,9 +27,23 @@ fun AppNavigation() { navController.navigate(if (isUserLogeInUseCase.invoke()) "Home" else "Auth") } - NavHost(navController, startDestination = "Splash", modifier = Modifier.background(MaterialTheme.colors.surface)) { + NavHost( + navController, + startDestination = "Splash", + modifier = Modifier.background(MaterialTheme.colors.surface) + ) { composable("Splash") { SplashScreen() } - composable("Auth") { AuthScreen() } - composable("Home") { Text("Home") } + composable("Auth") { + val authState = rememberAuthScreenState() + AuthScreen(authState) + if (authState.navigateToHome?.consume() != null) { + navController.navigate("Home") + } + } + composable("Home") { + HomeScreen(rememberHomeScreenState { + navController.navigate("Auth") + }) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreen.kt b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreen.kt index 4b4f740..c53a2ea 100644 --- a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreen.kt +++ b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreen.kt @@ -15,7 +15,7 @@ import org.fnives.test.showcase.R @Composable fun AuthScreen( - authScreenState: AuthScreenState = rememberAuthScreen() + authScreenState: AuthScreenState = rememberAuthScreenState() ) { Column( Modifier diff --git a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreenState.kt b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreenState.kt index a7fa90b..c461da3 100644 --- a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreenState.kt +++ b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/auth/AuthScreenState.kt @@ -11,7 +11,7 @@ import org.fnives.test.showcase.ui.shared.Event import org.koin.androidx.compose.get @Composable -fun rememberAuthScreen( +fun rememberAuthScreenState( stateScope: CoroutineScope = rememberCoroutineScope(), loginUseCase: LoginUseCase = get(), ): AuthScreenState { @@ -64,8 +64,7 @@ class AuthScreenState( when (loginStatus) { LoginStatus.SUCCESS -> navigateToHome = Event(Unit) LoginStatus.INVALID_CREDENTIALS -> error = Event(ErrorType.INVALID_CREDENTIALS) - LoginStatus.INVALID_USERNAME -> error = Event(ErrorType.UNSUPPORTED_USERNAME).also { println("asdasdasd: ${it.hashCode()}") - } + LoginStatus.INVALID_USERNAME -> error = Event(ErrorType.UNSUPPORTED_USERNAME) LoginStatus.INVALID_PASSWORD -> error = Event(ErrorType.UNSUPPORTED_PASSWORD) } println("asdasdasd: ${error.hashCode()}") diff --git a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreen.kt b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreen.kt index b0cd4fa..cdbda71 100644 --- a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreen.kt +++ b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreen.kt @@ -1,39 +1,100 @@ package org.fnives.test.showcase.ui.compose.screen.home -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import coil.compose.rememberImagePainter import com.google.accompanist.swiperefresh.SwipeRefresh import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import org.fnives.test.showcase.R +import org.fnives.test.showcase.model.content.FavouriteContent @Composable fun HomeScreen( - homeScreenState = rememberHomeScreenState() + homeScreenState: HomeScreenState = rememberHomeScreenState() ) { Column(Modifier.fillMaxSize()) { - Title() - SwipeRefresh(state = rememberSwipeRefreshState(isRefreshing = false), onRefresh = { }) { + Row(verticalAlignment = Alignment.CenterVertically) { + Title(Modifier.weight(1f)) + Image( + painter = painterResource(id = R.drawable.logout_24), + contentDescription = null, + colorFilter = ColorFilter.tint(MaterialTheme.colors.primary), + modifier = Modifier + .padding(16.dp) + .clickable { homeScreenState.onLogout() } + ) + } + SwipeRefresh( + state = rememberSwipeRefreshState(isRefreshing = homeScreenState.loading), + onRefresh = { + homeScreenState.onRefresh() + }) { LazyColumn { - + items(homeScreenState.content) { item -> + Item( + Modifier.padding(horizontal = 16.dp, vertical = 8.dp), + favouriteContent = item, + onFavouriteToggle = { homeScreenState.onFavouriteToggleClicked(item.content.id) } + ) + } } } } } +@Composable +private fun Item( + modifier: Modifier = Modifier, + favouriteContent: FavouriteContent, + onFavouriteToggle: () -> Unit, +) { + Row(modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + Image( + painter = rememberImagePainter(favouriteContent.content.imageUrl.url), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .height(120.dp) + .aspectRatio(1f) + .clip(RoundedCornerShape(12.dp)) + ) + Column( + Modifier + .weight(1f) + .padding(horizontal = 16.dp) + ) { + Text(text = favouriteContent.content.title) + Text(text = favouriteContent.content.description) + } + Image( + painter = painterResource(id = if (favouriteContent.isFavourite) R.drawable.favorite_24 else R.drawable.favorite_border_24), + contentDescription = null, + Modifier.clickable { onFavouriteToggle() } + ) + } +} + @Composable -private fun Title() { +private fun Title(modifier: Modifier = Modifier) { Text( stringResource(id = R.string.login_title), - modifier = Modifier.padding(16.dp), + modifier = modifier.padding(16.dp), style = MaterialTheme.typography.h4 ) } diff --git a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreenState.kt b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreenState.kt index fe59ac1..580af5b 100644 --- a/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreenState.kt +++ b/app/src/main/java/org/fnives/test/showcase/ui/compose/screen/home/HomeScreenState.kt @@ -1,41 +1,116 @@ package org.fnives.test.showcase.ui.compose.screen.home -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.launch import org.fnives.test.showcase.core.content.AddContentToFavouriteUseCase import org.fnives.test.showcase.core.content.FetchContentUseCase import org.fnives.test.showcase.core.content.GetAllContentUseCase import org.fnives.test.showcase.core.content.RemoveContentFromFavouritesUseCase import org.fnives.test.showcase.core.login.LogoutUseCase +import org.fnives.test.showcase.model.content.ContentId +import org.fnives.test.showcase.model.content.FavouriteContent +import org.fnives.test.showcase.model.shared.Resource import org.koin.androidx.compose.get @Composable fun rememberHomeScreenState( + stateScope: CoroutineScope = rememberCoroutineScope(), getAllContentUseCase: GetAllContentUseCase = get(), logoutUseCase: LogoutUseCase = get(), fetchContentUseCase: FetchContentUseCase = get(), addContentToFavouriteUseCase: AddContentToFavouriteUseCase = get(), removeContentFromFavouritesUseCase: RemoveContentFromFavouritesUseCase = get(), + onLogout: () -> Unit = {}, ): HomeScreenState { return remember { HomeScreenState( + stateScope, getAllContentUseCase, logoutUseCase, fetchContentUseCase, addContentToFavouriteUseCase, - removeContentFromFavouritesUseCase + removeContentFromFavouritesUseCase, + onLogout, ) } } class HomeScreenState( + private val stateScope: CoroutineScope, private val getAllContentUseCase: GetAllContentUseCase, private val logoutUseCase: LogoutUseCase, private val fetchContentUseCase: FetchContentUseCase, private val addContentToFavouriteUseCase: AddContentToFavouriteUseCase, - private val removeContentFromFavouritesUseCase: RemoveContentFromFavouritesUseCase + private val removeContentFromFavouritesUseCase: RemoveContentFromFavouritesUseCase, + private val logoutEvent: () -> Unit, ) { var loading by mutableStateOf(false) private set + var isError by mutableStateOf(false) + private set + var content by mutableStateOf>(emptyList()) + private set + init { + stateScope.launch { + fetch().collect { + content = it + } + } + } + + private fun fetch() = getAllContentUseCase.get() + .mapNotNull { + when (it) { + is Resource.Error -> { + isError = true + loading = false + return@mapNotNull emptyList() + } + is Resource.Loading -> { + isError = false + loading = true + return@mapNotNull null + } + is Resource.Success -> { + isError = false + loading = false + return@mapNotNull it.data + } + } + } + + fun onLogout() { + stateScope.launch { + logoutUseCase.invoke() + logoutEvent() + } + } + + fun onRefresh() { + if (loading) return + loading = true + stateScope.launch { + fetchContentUseCase.invoke() + } + } + + fun onFavouriteToggleClicked(contentId: ContentId) { + stateScope.launch { + val item = content.firstOrNull { it.content.id == contentId } ?: return@launch + if (item.isFavourite) { + removeContentFromFavouritesUseCase.invoke(contentId) + } else { + addContentToFavouriteUseCase.invoke(contentId) + } + } + } } \ No newline at end of file diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index e1d8d3e..c22bee4 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -15,7 +15,7 @@ project.ext { coroutines_version = "1.6.0" turbine_version = "0.7.0" koin_version = "3.1.2" - coil_version = "1.1.1" + coil_version = "1.4.0" retrofit_version = "2.9.0" okhttp_version = "4.9.1" moshi_version = "1.13.0"