Add home screen

This commit is contained in:
Alexandru Gabor 2022-02-28 16:57:29 +02:00 committed by Alex Gabor
parent a9dc65d0b6
commit b6e4d282b7
7 changed files with 173 additions and 21 deletions

View file

@ -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")

View file

@ -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")
})
}
}
}

View file

@ -15,7 +15,7 @@ import org.fnives.test.showcase.R
@Composable
fun AuthScreen(
authScreenState: AuthScreenState = rememberAuthScreen()
authScreenState: AuthScreenState = rememberAuthScreenState()
) {
Column(
Modifier

View file

@ -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()}")

View file

@ -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
)
}

View file

@ -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<List<FavouriteContent>>(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<FavouriteContent>()
}
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)
}
}
}
}

View file

@ -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"