Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.sanaa.tvapp.presentation.screens.home

import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
Expand All @@ -25,9 +26,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.tv.material3.Text
Expand All @@ -38,58 +36,69 @@ import com.sanaa.tvapp.presentation.components.MediaSection
import com.sanaa.tvapp.presentation.screens.home.component.HomeScreenLoading
import com.sanaa.tvapp.presentation.screens.home.component.HomeTabs
import com.sanaa.tvapp.presentation.screens.home.component.PopularMoviesCarousel
import com.sanaa.tvapp.presentation.screens.home.tabRoutes.HomeMoviesTapRoute
import com.sanaa.tvapp.presentation.screens.home.tabRoutes.HomeTvShowsTapRoute
import com.sanaa.tvapp.presentation.screens.navigation.LocalAppNavController
import com.sanaa.tvapp.presentation.screens.navigation.ScreensRoute
import com.sanaa.tvapp.presentation.screens.navigation.ScreensRoute.MovieDetailsRoute
import com.sanaa.tvapp.presentation.screens.navigation.ScreensRoute.TvShowDetailsRoute
import com.sanaa.tvapp.presentation.screens.searchScreen.componants.FocusableMediaCard
import com.sanaa.tvapp.state.MediaItemUiState
import com.sanaa.tvapp.state.MediaTypeUiState
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import com.sanaa.tvapp.R as tvResource

@Composable
fun HomeScreen(homeScreenViewModel: HomeScreenViewModel = hiltViewModel()) {
val scrollState = rememberScrollState()
fun HomeScreen(
homeScreenViewModel: HomeScreenViewModel = hiltViewModel()
) {
val state by homeScreenViewModel.state.collectAsStateWithLifecycle()
val upcomingMovies = state.upcomingMovies.collectAsLazyPagingItems()
when {
state.isNoInternet -> {
NetworkDisconnectionContact(
modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState),
onRetryClick = {}
)
}

state.isLoading -> {
HomeScreenLoading(
modifier = Modifier
.verticalScroll(scrollState),
)
}
EffectHandler(homeScreenViewModel.effect)

HomeScreenContent(state, homeScreenViewModel)
}

@Composable
fun HomeScreenContent(
state: HomeScreenUiState,
interactionListener: HomeScreenInteractionListener
) {
AnimatedContent(
targetState = state.isLoading to state.isNoInternet,
) { (isLoading, isNoInternet)->
when {
isNoInternet -> {
NetworkDisconnectionContact(
modifier = Modifier.fillMaxSize(),
onRetryClick = interactionListener::onRetryClick
)
}

else -> {
HomeScreenContent(state, upcomingMovies)
isLoading -> {
HomeScreenLoading(modifier = Modifier.verticalScroll(rememberScrollState()))
}

else -> {
HomeReadyContent(state, interactionListener)
}
}
}
}

@Composable
fun HomeScreenContent(state: HomeScreenUiState, upcomingMovies: LazyPagingItems<MediaItemUiState>) {
private fun HomeReadyContent(state: HomeScreenUiState, interactionListener: HomeScreenInteractionListener){
val sidePaddings = 36.dp
val scrollState = rememberScrollState()
val mainTvNavController = LocalAppNavController.current
val navController = rememberNavController()
val carouselFocusRequester = remember { FocusRequester() }

val upcomingMovies = state.upcomingMovies.collectAsLazyPagingItems()

LaunchedEffect(Unit) {
carouselFocusRequester.requestFocus()
}

Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(scrollState)
.verticalScroll(rememberScrollState())
) {
Box(modifier = Modifier.fillMaxWidth()) {
Row(
Expand Down Expand Up @@ -123,37 +132,40 @@ fun HomeScreenContent(state: HomeScreenUiState, upcomingMovies: LazyPagingItems<
.focusRequester(carouselFocusRequester),

mediaItemUiStates = state.popularMedia,
onShowDetails = { it ->
if (it.mediaTypeUiState == MediaTypeUiState.MOVIE)
mainTvNavController.navigate(ScreensRoute.MovieDetailsRoute(it.id))
else
mainTvNavController.navigate(ScreensRoute.TvShowDetailsRoute(it.id))
onShowDetails = {
interactionListener.onMediaClick(
it.id,
it.mediaTypeUiState
)
}
)

HomeTabs(
modifier = Modifier.padding(top = 12.dp),
sidePaddings = sidePaddings,
navController = navController
onTabSelected = interactionListener::onTabClick
)

NavHost(navController = navController, startDestination = HomeMoviesTapRoute) {
composable(route = HomeMoviesTapRoute::class) {
HomeMovies(state, upcomingMovies) { id ->
mainTvNavController.navigate(ScreensRoute.MovieDetailsRoute(id))
AnimatedContent(
targetState = state.selectedTab
) { selectedTab ->
when (selectedTab) {
SelectedHomeTab.MOVIES -> {
HomeMovies(state, upcomingMovies) { id ->
interactionListener.onMediaClick(id, MediaTypeUiState.MOVIE)
}
}
}

composable(route = HomeTvShowsTapRoute::class) {
HomeTvShows(state, upcomingMovies) { id ->
mainTvNavController.navigate(ScreensRoute.TvShowDetailsRoute(id))
SelectedHomeTab.TV_SHOWS -> {
HomeTvShows(state) { id ->
interactionListener.onMediaClick(id, MediaTypeUiState.TV_SHOW)
}
}
}
}
}
}


@Composable
fun HomeMovies(
state: HomeScreenUiState,
Expand Down Expand Up @@ -223,7 +235,6 @@ fun HomeMovies(
@Composable
fun HomeTvShows(
state: HomeScreenUiState,
upcomingMovies: LazyPagingItems<MediaItemUiState>,
onItemClick: (Int) -> Unit,
) {
Column {
Expand Down Expand Up @@ -273,3 +284,23 @@ fun HomeTvShows(
}
}
}


@Composable
private fun EffectHandler(
effect: Flow<HomeScreenEffect>
) {
val mainTvNavController = LocalAppNavController.current
LaunchedEffect(Unit) {
effect.collectLatest { effect ->
when (effect) {
is HomeScreenEffect.NavigateToMediaDetails -> {
if (effect.mediaTypeUiState == MediaTypeUiState.MOVIE)
mainTvNavController.navigate(MovieDetailsRoute(effect.id))
else
mainTvNavController.navigate(TvShowDetailsRoute(effect.id))
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
package com.sanaa.tvapp.presentation.screens.home

sealed class HomeScreenEffect
import com.sanaa.tvapp.state.MediaTypeUiState

sealed interface HomeScreenEffect {
data class NavigateToMediaDetails(val id: Int, val mediaTypeUiState: MediaTypeUiState) :
HomeScreenEffect
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.sanaa.tvapp.presentation.screens.home

import com.sanaa.tvapp.state.MediaTypeUiState

interface HomeScreenInteractionListener {
fun onMediaClick(id: Int, mediaTypeUiState: MediaTypeUiState)
fun onTabClick(selectedTab: SelectedHomeTab)
fun onRetryClick()
fun onSnackBarDismiss()
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,28 @@
package com.sanaa.tvapp.presentation.screens.home

import androidx.paging.PagingData
import com.sanaa.tvapp.state.GenreUiState
import com.sanaa.tvapp.state.MediaItemUiState
import com.sanaa.tvapp.state.SnackData
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf

data class HomeScreenUiState(
val featuredCarousel: FeaturedCarouselState = FeaturedCarouselState(),
val popularMedia: List<MediaItemUiState> = emptyList(),
val topRatingMovies: List<MediaItemUiState> = emptyList(),
val topRatingTvShows: List<MediaItemUiState> = emptyList(),
val continueWatchingMovies: List<MediaItemUiState> = emptyList(),
val continueWatchingTvShows: List<MediaItemUiState> = emptyList(),
val upcomingMovies: Flow<PagingData<MediaItemUiState>> = flowOf(PagingData.empty()),
val movieGenres: List<GenreUiState> = emptyList(),
val movieSelectedGenreId: Int? = null,
val isLoading: Boolean = false,
val errorMessage: String? = null,
val showBottomSheet: Boolean = false,
val isLoadingUpcoming: Boolean = false,
val isNoInternet: Boolean = false,
val userIsLoggedIn: Boolean = false,
val showSaveToListBottomSheet: Boolean = false,
val selectedMediaId: Long = 0L,
val snackBarData: SnackData? = null,
val ratedMovies: List<MediaItemUiState> = emptyList(),
val ratedTvShows: List<MediaItemUiState> = emptyList(),
val selectedTab: SelectedHomeTab = SelectedHomeTab.MOVIES,
)

data class FeaturedCarouselState(
val imageUrl: String = "",
val movieTitle: String = "",
val movieDescription: String = "",
val movieInfo: String = "",
)
enum class SelectedHomeTab {
MOVIES, TV_SHOWS
}
Loading
Loading