diff --git a/app/src/main/java/com/london/app/NovixApplication.kt b/app/src/main/java/com/london/app/NovixApplication.kt index a0f792579..758814a92 100644 --- a/app/src/main/java/com/london/app/NovixApplication.kt +++ b/app/src/main/java/com/london/app/NovixApplication.kt @@ -5,7 +5,6 @@ import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import androidx.work.WorkManager import com.london.data.BuildConfig -import com.london.data.worker.MovieListSyncWorker import dagger.hilt.android.HiltAndroidApp import timber.log.Timber import javax.inject.Inject diff --git a/app/src/main/java/com/london/app/navigation/appDestination.kt b/app/src/main/java/com/london/app/navigation/appDestination.kt index 3ccc63746..9dbaa9af4 100644 --- a/app/src/main/java/com/london/app/navigation/appDestination.kt +++ b/app/src/main/java/com/london/app/navigation/appDestination.kt @@ -33,7 +33,7 @@ fun NavController.navigateToEpisodeDetails(tvShowId: Int, seasonNumber: Int, epi navigate(EpisodeDetails(tvShowId, episodeNumber, seasonNumber)) fun NavController.navigateToMovieDetails(movieId: Int) = - navigate(MovieDetails(movieId)) + navigate(MovieDetails(movieId = movieId)) fun NavController.navigateToTvShowDetails(tvShowId: Int) = navigate(TvShowDetails(tvShowId)) @@ -59,9 +59,9 @@ fun NavController.navigateToMovieCategory(genre: MovieGenreUi) = fun NavController.navigateToReviews(movieId: Int, mediaType: MediaType) = navigate(Reviews(movieId, mediaType)) -fun NavController.navigateToLoginWithPopUp() = navigateTo(Login) +fun NavController.navigateToLoginWithPopUp(source: Screen? = null) = navigateTo(Login.withSource(source)) -fun NavController.navigateToLogin() = navigate(Login) +fun NavController.navigateToLogin(source: Screen? = null) = navigate(Login.withSource(source)) fun NavController.navigateToWatchingHistory() = navigate(WatchingHistory) diff --git a/app/src/main/java/com/london/app/navigation/graph/authNavGraph.kt b/app/src/main/java/com/london/app/navigation/graph/authNavGraph.kt index 688da9c07..8ea1be80c 100644 --- a/app/src/main/java/com/london/app/navigation/graph/authNavGraph.kt +++ b/app/src/main/java/com/london/app/navigation/graph/authNavGraph.kt @@ -3,8 +3,10 @@ package com.london.app.navigation.graph import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.navigation +import androidx.navigation.toRoute import com.london.app.navigation.AppNavGraph import com.london.app.navigation.appComposable +import com.london.app.navigation.navigateTo import com.london.app.navigation.navigateToLoginWithPopUp import com.london.app.navigation.navigateToMainGraph import com.london.app.navigation.navigateToRegister @@ -18,8 +20,12 @@ fun NavGraphBuilder.authNavGraph( navController: NavHostController ) = navigation(startDestination = Login) { appComposable { + val currentRoute = it.toRoute() LoginScreen( - onNavigateToHome = navController::navigateToMainGraph, + onNavigateToHome = { + currentRoute.source?.let(navController::navigateTo) + ?: navController.navigateToMainGraph() + }, onNavigateBack = navController::navigateToWelcome, onNavigateToRegister = navController::navigateToRegister ) diff --git a/app/src/main/java/com/london/app/navigation/graph/mainNavGraph.kt b/app/src/main/java/com/london/app/navigation/graph/mainNavGraph.kt index 83c4f22fc..ce5d1a5c0 100644 --- a/app/src/main/java/com/london/app/navigation/graph/mainNavGraph.kt +++ b/app/src/main/java/com/london/app/navigation/graph/mainNavGraph.kt @@ -104,7 +104,7 @@ fun NavGraphBuilder.mainNavGraph( AccountScreen( onNavigateToWatchingHistory = ::navigateToWatchingHistory, onNavigateToMyRating = ::navigateToMyRating, - onNavigateToLogin = ::navigateToLoginWithPopUp + onNavigateToLogin = { navigateToLoginWithPopUp(source = Account) } ) } @@ -172,7 +172,7 @@ private fun NavGraphBuilder.onListNavGraph(navController: NavHostController) = appComposable { ListScreen( onNavigateToListDetails = ::navigateToListDetails, - onNavigateToLogin = ::navigateToLogin, + onNavigateToLogin = { navigateToLogin(source = Lists()) }, ) } appComposable { @@ -183,26 +183,27 @@ private fun NavGraphBuilder.onListNavGraph(navController: NavHostController) = } } -private fun NavGraphBuilder.movieDetailsNavGraph(navController: NavHostController) = - with(navController) { - appComposable { - MovieDetailsScreen( - onNavigateBack = ::navigateUp, - onNavigateToMovieCategory = ::navigateToMovieCategory, - onNavigateToMovie = ::navigateToMovieDetails, - onNavigateToActor = ::navigateToActorDetails, - onNavigateToReviews = ::navigateToReviews, - onNavigateToLogin = ::navigateToLogin, - ) - } +private fun NavGraphBuilder.movieDetailsNavGraph(navController: NavHostController) = with(navController) { + appComposable { + MovieDetailsScreen( + onNavigateBack = ::navigateUp, + onNavigateToMovieCategory = ::navigateToMovieCategory, + onNavigateToMovie = ::navigateToMovieDetails, + onNavigateToActor = ::navigateToActorDetails, + onNavigateToReviews = ::navigateToReviews, + onNavigateToLogin = { movieId -> + navigateToLogin(source = MovieDetails(movieId = movieId)) + }, + ) + } - appComposable { - MoviesByCategoryScreen( - onNavigateToMovieDetails = ::navigateToMovieDetails, - onNavigateBack = ::navigateUp, - ) - } + appComposable { + MoviesByCategoryScreen( + onNavigateToMovieDetails = ::navigateToMovieDetails, + onNavigateBack = ::navigateUp, + ) } +} private fun NavGraphBuilder.tvShowDetailsNavGraph(navController: NavHostController) = with(navController) { @@ -213,7 +214,9 @@ private fun NavGraphBuilder.tvShowDetailsNavGraph(navController: NavHostControll onNavigateToActorDetails = ::navigateToActorDetails, onNavigateBack = ::navigateUp, onNavigateToTvShowCategory = ::navigateToTvShowsByCategory, - onNavigateToLogin = ::navigateToLogin + onNavigateToLogin = { tvShowId -> + navigateToLogin(source = TvShowDetails(tvShowId = tvShowId)) + } ) } @@ -221,7 +224,15 @@ private fun NavGraphBuilder.tvShowDetailsNavGraph(navController: NavHostControll EpisodeDetailsScreen( onNavigateBack = ::navigateUp, onNavigateToActorDetails = ::navigateToActorDetails, - onNavigateToLogin = ::navigateToLogin + onNavigateToLogin = { tvShowId, seasonNumber, episodeNumber -> + navigateToLogin( + source = EpisodeDetails( + tvShowId = tvShowId, + seasonNumber = seasonNumber, + episodeNumber = episodeNumber + ) + ) + } ) } @@ -284,5 +295,4 @@ private fun NavGraphBuilder.trendingNavGraph(navController: NavHostController) = onNavigateBack = ::navigateUp ) } - } diff --git a/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingScreen.kt b/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingScreen.kt index 1720c0735..889b94ed3 100644 --- a/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingScreen.kt @@ -22,6 +22,7 @@ import com.london.designsystem.component.NovixChip import com.london.designsystem.component.TopBar import com.london.designsystem.theme.NovixTheme import com.london.designsystem.theme.ThemePreviews +import com.london.designsystem.utils.string import com.london.domain.entity.shared.MediaType import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient @@ -145,11 +146,11 @@ private fun RatingSnackBar(state: MyRatingUiState) { if (!state.isSnackBarVisible) return if (state.errorState is ErrorState.RequestFailed) { - SnackBarAnimation(state.errorState.message) + SnackBarAnimation(message = R.string.rating_delete_fail.string) } else { SnackBarAnimation( - stringResource(R.string.delete_list_successfully), - dsR.drawable.ic_success + message = R.string.rating_delete_success.string, + icon = dsR.drawable.ic_success ) } } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsContract.kt b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsContract.kt index 4a8dbba5f..9cd7e4419 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsContract.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsContract.kt @@ -5,7 +5,7 @@ import com.london.presentation.shared.genre.MovieGenreUi interface MovieDetailsContract { fun onBackClick() - fun onLoginClick() + fun onLoginClick(movieId: Int) fun onRetryClick() fun onExpandClick() fun onRateBottomSheetClick() diff --git a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsEffect.kt b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsEffect.kt index 0faaf318b..54f67011f 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsEffect.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsEffect.kt @@ -5,7 +5,7 @@ import com.london.presentation.shared.genre.MovieGenreUi sealed interface MovieDetailsEffect { data object BackNavigation : MovieDetailsEffect - data object LoginNavigation : MovieDetailsEffect + data class LoginNavigation(val movieId: Int) : MovieDetailsEffect data class MovieNavigation(val movieId: Int) : MovieDetailsEffect data class ActorNavigation(val actorId: Int) : MovieDetailsEffect data class GenreNavigation(val genre: MovieGenreUi) : MovieDetailsEffect diff --git a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsScreen.kt b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsScreen.kt index c567135cb..c9fdadce0 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsScreen.kt @@ -87,11 +87,11 @@ import com.london.presentation.utils.toLocalizedNumbers @Composable fun MovieDetailsScreen( onNavigateBack: () -> Unit, - onNavigateToLogin: () -> Unit, - onNavigateToMovie: (Int) -> Unit, - onNavigateToActor: (Int) -> Unit, - onNavigateToReviews: (Int, MediaType) -> Unit, - onNavigateToMovieCategory: (MovieGenreUi) -> Unit, + onNavigateToLogin: (movieId: Int) -> Unit, + onNavigateToMovie: (movieId: Int) -> Unit, + onNavigateToActor: (movieId: Int) -> Unit, + onNavigateToReviews: (movieId: Int, mediaType: MediaType) -> Unit, + onNavigateToMovieCategory: (genre: MovieGenreUi) -> Unit, viewModel: MovieDetailsViewModel = hiltViewModel() ) { val state by viewModel.state.collectAsStateWithLifecycle() @@ -108,7 +108,7 @@ fun MovieDetailsScreen( currentEffect.mediaType ) - is MovieDetailsEffect.LoginNavigation -> onNavigateToLogin() + is MovieDetailsEffect.LoginNavigation -> onNavigateToLogin(currentEffect.movieId) } } @@ -381,7 +381,7 @@ private fun BottomSheetsHandler( ) else if (uiState.isGuestUserBottomSheetVisible) GuestUserLoginBottomSheet( onDismissClick = movieDetailsContract::onRateBottomSheetClick, - onLoginClick = movieDetailsContract::onLoginClick, + onLoginClick = { movieDetailsContract.onLoginClick(uiState.movieId) }, ) } @@ -399,8 +399,7 @@ private fun RatingAndMetaRow( TextWithIcon( text = rate.toLocalizedNumbers(), icon = painterResource(drawable.star), - tint = NovixTheme.colors.yellowAccent, - hasInitialDot = false + tint = NovixTheme.colors.yellowAccent ) } @@ -415,6 +414,7 @@ private fun RatingAndMetaRow( TextWithIcon( icon = painterResource(drawable.time_04), text = text, + hasInitialDot = false ) if (!date.isNullOrBlank()) { diff --git a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsViewModel.kt index cac231b3b..b6c84d009 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/movie/MovieDetailsViewModel.kt @@ -35,6 +35,7 @@ class MovieDetailsViewModel @Inject constructor( init { loadMovieDetails() + args?.let { updateState { copy(isBookmarkSheetVisible = it.isBookmarkSheetVisible) } } } private fun loadMovieDetails() { @@ -75,9 +76,6 @@ class MovieDetailsViewModel @Inject constructor( emitEffect(MovieDetailsEffect.ActorNavigation(actorId)) } - override fun onLoginClick() = emitEffect(MovieDetailsEffect.LoginNavigation) - - override fun onReviewsClick(movieId: Int, mediaType: MediaType) { clearRatedState() emitEffect(MovieDetailsEffect.ReviewsNavigation(movieId, mediaType)) @@ -93,6 +91,9 @@ class MovieDetailsViewModel @Inject constructor( loadMovieDetails() } + override fun onLoginClick(movieId: Int) { + emitEffect(MovieDetailsEffect.LoginNavigation(movieId)) + } override fun onRateBottomSheetClick() = checkUserAuthenticationForRating() override fun onSelectRatingClick(rating: Int) = submitMovieRating(rating) diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailScreen.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailScreen.kt index e2694b525..6c85942db 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailScreen.kt @@ -65,7 +65,7 @@ import com.london.designsystem.R as Res @Composable fun EpisodeDetailsScreen( onNavigateBack: () -> Unit, - onNavigateToLogin: () -> Unit, + onNavigateToLogin: (tvShowId: Int, seasonNumber: Int, episodeNumber: Int) -> Unit, onNavigateToActorDetails: (Int) -> Unit, viewModel: EpisodeDetailsViewModel = hiltViewModel() ) { @@ -76,7 +76,11 @@ fun EpisodeDetailsScreen( when (currentEffect) { EpisodeDetailsEffect.BackNavigation -> onNavigateBack() is EpisodeDetailsEffect.CastNavigation -> onNavigateToActorDetails(currentEffect.episodeId) - is EpisodeDetailsEffect.LoginNavigation -> onNavigateToLogin() + is EpisodeDetailsEffect.LoginNavigation -> onNavigateToLogin( + currentEffect.tvShowId, + currentEffect.seasonNumber, + currentEffect.episodeNumber + ) } } @@ -192,7 +196,15 @@ private fun Content( ) if (uiState.isGuestUserBottomSheetVisible) GuestUserLoginBottomSheet( onDismissClick = contract::onRateEpisodeClick, - onLoginClick = contract::onLoginClick, + onLoginClick = { + uiState.episode?.let { + contract.onLoginClick( + tvShowId = it.tvShowId, + episodeNumber = it.id, + seasonNumber = it.seasonNumber + ) + } + }, ) } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsContract.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsContract.kt index 117679e94..89658a0a1 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsContract.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsContract.kt @@ -2,7 +2,7 @@ package com.london.presentation.feature.details.tvshow.episode interface EpisodeDetailsContract { fun onBackClick() - fun onLoginClick() + fun onLoginClick(tvShowId: Int, seasonNumber: Int, episodeNumber: Int) fun onRateEpisodeClick() fun onSelectRatingClick(rating: Int) diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsEffect.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsEffect.kt index c60c765f4..6dd441df3 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsEffect.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsEffect.kt @@ -2,6 +2,12 @@ package com.london.presentation.feature.details.tvshow.episode sealed interface EpisodeDetailsEffect { data object BackNavigation : EpisodeDetailsEffect - data object LoginNavigation : EpisodeDetailsEffect + data class LoginNavigation( + val tvShowId: Int, + val seasonNumber: Int, + val episodeNumber: Int + ) : + EpisodeDetailsEffect + data class CastNavigation(val episodeId: Int) : EpisodeDetailsEffect } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsViewModel.kt index 7412494f6..1b445c661 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/episode/EpisodeDetailsViewModel.kt @@ -40,7 +40,13 @@ class EpisodeDetailsViewModel @Inject constructor( override fun onBackClick() = emitEffect(EpisodeDetailsEffect.BackNavigation) - override fun onLoginClick() = emitEffect(EpisodeDetailsEffect.LoginNavigation) + override fun onLoginClick(tvShowId: Int, seasonNumber: Int, episodeNumber: Int) = emitEffect( + EpisodeDetailsEffect.LoginNavigation( + tvShowId = tvShowId, + seasonNumber = seasonNumber, + episodeNumber = episodeNumber + ) + ) override fun onRateEpisodeClick() { tryToExecute( diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailScreen.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailScreen.kt index 22154392e..95657dd34 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailScreen.kt @@ -84,9 +84,9 @@ import com.london.presentation.utils.toLocalizedNumbers @Composable fun TvShowsDetailsScreen( - onNavigateToLogin: () -> Unit, - onNavigateToActorDetails: (Int) -> Unit, - onNavigateToTvShowCategory: (TvShowGenreUi) -> Unit, + onNavigateToLogin: (tvShowId: Int) -> Unit, + onNavigateToActorDetails: (tvShowId: Int) -> Unit, + onNavigateToTvShowCategory: (genre: TvShowGenreUi) -> Unit, onNavigateToReviews: (tvShowId: Int, mediaType: MediaType) -> Unit, onNavigateBack: () -> Unit = {}, onNavigateToEpisodeDetails: (tvShowId: Int, episodeNumber: Int, seasonNumber: Int) -> Unit, @@ -116,7 +116,7 @@ fun TvShowsDetailsScreen( currentEffect.category ) - is TvShowDetailsEffect.OnLoginNavigation -> onNavigateToLogin() + is TvShowDetailsEffect.OnLoginNavigation -> onNavigateToLogin(currentEffect.tvShowId) } } @@ -318,7 +318,7 @@ private fun Content( ) else if (uiState.isGuestUserBottomSheetVisible) GuestUserLoginBottomSheet( onDismissClick = tvShowDetailsContract::onRateBottomSheetClick, - onLoginClick = tvShowDetailsContract::onLoginClick, + onLoginClick = { tvShowDetailsContract.onLoginClick(uiState.id) }, ) } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsContract.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsContract.kt index fa1883044..5e1e3ae1f 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsContract.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsContract.kt @@ -4,7 +4,7 @@ import com.london.domain.entity.shared.MediaType import com.london.presentation.shared.genre.TvShowGenreUi interface TvShowDetailsContract { - fun onLoginClick() + fun onLoginClick(tvShowId: Int) fun onBackClicked() fun onRateBottomSheetClick() fun onCastClicked(tvShowId: Int) diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsEffect.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsEffect.kt index 3218c5b94..61b7aeae1 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsEffect.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsEffect.kt @@ -11,7 +11,7 @@ sealed interface TvShowDetailsEffect { ) : TvShowDetailsEffect data object NavigateBack : TvShowDetailsEffect - data object OnLoginNavigation : TvShowDetailsEffect + data class OnLoginNavigation(val tvShowId: Int) : TvShowDetailsEffect data class NavigateToCast(val tvShowId: Int) : TvShowDetailsEffect data class NavigateToTvShowsByCategoryId(val category: TvShowGenreUi) : TvShowDetailsEffect data class NavigateToReviews(val tvShowId: Int, val mediaType: MediaType) : TvShowDetailsEffect diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModel.kt index c4e79d1cd..7a40fc1e3 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModel.kt @@ -140,7 +140,7 @@ class TvShowDetailsViewModel @Inject constructor( ) } - override fun onLoginClick() = emitEffect(TvShowDetailsEffect.OnLoginNavigation) + override fun onLoginClick(tvShowId: Int) = emitEffect(TvShowDetailsEffect.OnLoginNavigation(tvShowId)) override fun onBackClicked() = emitEffect(TvShowDetailsEffect.NavigateBack) diff --git a/presentation/src/main/java/com/london/presentation/feature/home/HomeScreen.kt b/presentation/src/main/java/com/london/presentation/feature/home/HomeScreen.kt index b8bff9f00..ac5f1b0df 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/HomeScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/HomeScreen.kt @@ -89,14 +89,14 @@ fun HomeScreen( HomeScreenContent( uiState = uiState, - homeScreenContract = viewModel + contract = viewModel ) } @Composable private fun HomeScreenContent( uiState: HomeScreenUiState, - homeScreenContract: HomeScreenContract, + contract: HomeScreenContract, ) { val screenDimensions = rememberScreenDimensions() val lazyGridState = rememberLazyGridState() @@ -117,7 +117,7 @@ private fun HomeScreenContent( recentWatchedMedia = recentWatchedMedia, upcomingMoviesLazyList = upcomingMoviesLazyList, scrollState = scrollState, - homeScreenContract = homeScreenContract + homeScreenContract = contract ) } @@ -217,9 +217,9 @@ private fun HomeScreenLayout( } BookmarkBottomSheet( - onSheetDismiss = homeScreenContract::onBookmarkSheetDismiss, + bookmarkedMovieId = uiState.bookmarkedMovieId, isSheetVisible = uiState.isBookmarkSheetVisible, - bookmarkedMovieId = uiState.bookmarkedMovieId + onSheetDismiss = homeScreenContract::onBookmarkSheetDismiss, ) } @@ -290,7 +290,7 @@ private fun HomeContentGrid( continueWatchingSection( screenWidth = screenWidth, recentWatchedMedia = recentWatchedMedia, - isLoading = uiState.isLoading, + isLoading = uiState.isRecentWatchLoading, homeScreenContract = homeScreenContract ) } @@ -313,27 +313,28 @@ private fun LazyGridScope.popularSection( homeScreenContract: HomeScreenContract ) { item(span = { GridItemSpan(maxLineSpan) }) { - if (uiState.popularMediaList.isNotEmpty()) { - PopularSection( - modifier = Modifier.requiredWidth(screenWidth), - pagerState = pagerState, - uiMediaList = uiState.popularMediaList, - onManageBookmarkClicked = { movieId -> - homeScreenContract.onManageBookmarkClicked(movieId) - }, - onCardClick = { id, mediaType -> - when (mediaType) { - MediaType.TvShow -> homeScreenContract.onTvShowClick(id) - MediaType.Movie -> homeScreenContract.onMovieClick(id) - } - } - ) - } else { + if (uiState.isPopularLoading || uiState.popularMediaList.isEmpty()) { ShimmerPopularSection( modifier = Modifier.requiredWidth(screenWidth), pagerState = pagerState, ) + return@item } + + PopularSection( + modifier = Modifier.requiredWidth(screenWidth), + pagerState = pagerState, + uiMediaList = uiState.popularMediaList, + onManageBookmarkClicked = { movieId -> + homeScreenContract.onManageBookmarkClicked(movieId) + }, + onCardClick = { id, mediaType -> + when (mediaType) { + MediaType.TvShow -> homeScreenContract.onTvShowClick(id) + MediaType.Movie -> homeScreenContract.onMovieClick(id) + } + } + ) } } diff --git a/presentation/src/main/java/com/london/presentation/feature/home/HomeScreenUiState.kt b/presentation/src/main/java/com/london/presentation/feature/home/HomeScreenUiState.kt index 29a6a5428..e306ebb8b 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/HomeScreenUiState.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/HomeScreenUiState.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.emptyFlow data class HomeScreenUiState( val error: ErrorState? = null, - val isLoading: Boolean = false, val bookmarkedMovieId: Int = 0, val isPopularLoading: Boolean = false, val isTopRatedLoading: Boolean = false, + val isRecentWatchLoading: Boolean = false, val isBookmarkSheetVisible: Boolean = false, val topRatedMediaList: List = emptyList(), val selectedMovieGenre: MovieGenreUi = MovieGenreUi.All, @@ -23,4 +23,7 @@ data class HomeScreenUiState( val upcomingMovies: Flow> = emptyFlow(), val recentWatchedMediaFlow: Flow> = emptyFlow(), val selectedCategoryFlow: MutableStateFlow = MutableStateFlow(null) -) +) { + val isLoading: Boolean + get() = isPopularLoading || isRecentWatchLoading || isTopRatedLoading +} diff --git a/presentation/src/main/java/com/london/presentation/feature/home/HomeViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/home/HomeViewModel.kt index e06d96759..f2478c017 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/HomeViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/HomeViewModel.kt @@ -1,5 +1,6 @@ package com.london.presentation.feature.home +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.cachedIn @@ -35,25 +36,71 @@ class HomeViewModel @Inject constructor( ) : BaseViewModel(HomeScreenUiState()), HomeScreenContract { init { - getHomeInfo() + initializeData() } - private fun getHomeInfo() { - initializePopularMedia() + override fun loadUpcomingMoviesClick(genre: MovieGenreUi) { + updateState { + copy(selectedCategoryFlow = selectedCategoryFlow.apply { value = genre }) + } + } + + override fun onMovieClick(id: Int) = + emitEffect(HomeScreenEffect.MovieDetailsNavigation(id)) + + override fun onTvShowClick(id: Int) = + emitEffect(HomeScreenEffect.TvShowDetailsNavigation(id)) + + override fun onMovieGenreSelect(genre: MovieGenreUi) { + if (genre == state.value.selectedMovieGenre) return + updateState { copy(selectedMovieGenre = genre) } + loadUpcomingMoviesClick(genre) + } + + override fun onTopRatedClick() = emitEffect(HomeScreenEffect.TopRatedNavigation) + + override fun onContinueWatchingClick() = emitEffect(HomeScreenEffect.ContinueWatchingNavigation) + + override fun onTrendingMoviesCardClick() = emitEffect(HomeScreenEffect.TrendingMovieNavigation) + + override fun onTrendingTvShowsCardClick() = emitEffect(HomeScreenEffect.TrendingTvShowsNavigation) + + override fun onTrendingActorsCardClick() = emitEffect(HomeScreenEffect.TrendingActorNavigation) + + override fun onRetryClick() { + updateState { copy(error = null) } initializeTopRatedMedia() handleRecentWatchedMedia() - initializeUpcomingMoviesFlow() + initializePopularMedia() } - private fun initializeUpcomingMoviesFlow() = - updateState { copy(upcomingMovies = createUpComingFlow()) } + override fun onManageBookmarkClicked(movieId: Int) { + updateState { + copy( + isBookmarkSheetVisible = true, + bookmarkedMovieId = movieId + ) + } + } - override fun loadUpcomingMoviesClick(genre: MovieGenreUi) { + override fun onBookmarkSheetDismiss() { updateState { - copy(selectedCategoryFlow = selectedCategoryFlow.apply { value = genre }) + copy( + isBookmarkSheetVisible = false, + bookmarkedMovieId = 0 + ) } } + private fun initializeData() { + initializePopularMedia() + initializeTopRatedMedia() + handleRecentWatchedMedia() + initializeUpcomingMoviesFlow() + } + + private fun initializeUpcomingMoviesFlow() = updateState { copy(upcomingMovies = createUpComingFlow()) } + private fun createUpComingFlow(): Flow> { val upcomingMoviesFlow: Flow> = state.value.selectedCategoryFlow @@ -72,15 +119,13 @@ class HomeViewModel @Inject constructor( } } - private fun initializeTopRatedMedia() { - tryToExecute( - block = { fetchTopRatedMedia() }, - onStart = { updateLoadingState(true) }, - onSuccess = { topRatedMedia -> handleTopRatedSuccess(topRatedMedia) }, - onError = { errorState -> updateErrorState(errorState) }, - onCompleted = { updateLoadingState(false) }, - ) - } + private fun initializeTopRatedMedia() = tryToExecute( + block = { fetchTopRatedMedia() }, + onStart = { updateState { copy(isTopRatedLoading = true) } }, + onSuccess = { topRatedMedia -> handleTopRatedSuccess(topRatedMedia) }, + onError = { errorState -> updateErrorState(errorState) }, + onCompleted = { updateState { copy(isTopRatedLoading = false) } }, + ) private fun handleTopRatedSuccess(topRatedMediaList: List) = updateState { copy(topRatedMediaList = topRatedMediaList.toUiMedia().shuffled()) } @@ -95,24 +140,20 @@ class HomeViewModel @Inject constructor( private fun handleRecentWatchedMedia() { tryToCollect( block = { fetchRecentWatchedMedia() }, - onStart = { updateLoadingState(true) }, onNewValue = { recentWatchedMedia -> handleRecentWatchedMediaSuccess(recentWatchedMedia) }, onError = { errorState -> updateErrorState(errorState) }, - onCompleted = { updateLoadingState(false) }, ) } - private fun initializePopularMedia() { - tryToExecute( - block = { fetchPopularMediaList() }, - onStart = { updateLoadingState(true) }, - onSuccess = { popularMedia -> handlePopularMediaSuccess(popularMedia) }, - onError = { errorState -> updateErrorState(errorState) }, - onCompleted = { updateLoadingState(false) }, - ) - } + private fun initializePopularMedia() = tryToExecute( + block = { fetchPopularMediaList() }, + onStart = { updateState { copy(isPopularLoading = true) } }, + onSuccess = { popularMedia -> handlePopularMediaSuccess(popularMedia) }, + onError = { errorState -> updateErrorState(errorState) }, + onCompleted = { updateState { copy(isPopularLoading = false) } }, + ) private fun handleRecentWatchedMediaSuccess(recentWatchedMedia: List) = updateState { copy(recentWatchedMediaFlow = flowOf(recentWatchedMedia)) } @@ -126,20 +167,14 @@ class HomeViewModel @Inject constructor( private fun combineRecentMedia( movies: Flow>, shows: Flow> - ): Flow> { - return combine(movies, shows) { movieList, showList -> - movieList.toUiMedia() + showList.toUiMedia() - } + ): Flow> = combine(movies, shows) { movieList, showList -> + movieList.toUiMedia() + showList.toUiMedia() } private fun handlePopularMediaSuccess(popularMedia: List) = updateState { copy(popularMediaList = popularMedia) } - private fun updateErrorState(errorState: ErrorState) = - updateState { copy(error = errorState) } - - private fun updateLoadingState(isLoading: Boolean) = - updateState { copy(isPopularLoading = isLoading) } + private fun updateErrorState(errorState: ErrorState) = updateState { copy(error = errorState) } private suspend fun fetchPopularMediaList(): List { val movies = getMovieUseCase.getPopularMovies() @@ -147,57 +182,4 @@ class HomeViewModel @Inject constructor( return movies.toPopularUiMedia() + tvShows.toPopularUiMedia() } - - override fun onRetryClick() { - updateState { copy(error = null) } - initializeTopRatedMedia() - handleRecentWatchedMedia() - initializePopularMedia() - } - - override fun onManageBookmarkClicked(movieId: Int) { - updateState { - copy( - isBookmarkSheetVisible = true, - bookmarkedMovieId = movieId - ) - } - } - - override fun onBookmarkSheetDismiss() { - updateState { - copy( - isBookmarkSheetVisible = false, - bookmarkedMovieId = 0 - ) - } - } - - override fun onMovieClick(id: Int) = - emitEffect(HomeScreenEffect.MovieDetailsNavigation(id)) - - override fun onTvShowClick(id: Int) = - emitEffect(HomeScreenEffect.TvShowDetailsNavigation(id)) - - override fun onMovieGenreSelect(genre: MovieGenreUi) { - if (genre == state.value.selectedMovieGenre) return - updateState { copy(selectedMovieGenre = genre) } - loadUpcomingMoviesClick(genre) - } - - override fun onTopRatedClick() = - emitEffect(HomeScreenEffect.TopRatedNavigation) - - override fun onContinueWatchingClick() = - emitEffect(HomeScreenEffect.ContinueWatchingNavigation) - - override fun onTrendingMoviesCardClick() = - emitEffect(HomeScreenEffect.TrendingMovieNavigation) - - override fun onTrendingTvShowsCardClick() = - emitEffect(HomeScreenEffect.TrendingTvShowsNavigation) - - override fun onTrendingActorsCardClick() = - emitEffect(HomeScreenEffect.TrendingActorNavigation) - } diff --git a/presentation/src/main/java/com/london/presentation/navigation/Screen.kt b/presentation/src/main/java/com/london/presentation/navigation/Screen.kt index 81bbdecf4..b32db9b74 100644 --- a/presentation/src/main/java/com/london/presentation/navigation/Screen.kt +++ b/presentation/src/main/java/com/london/presentation/navigation/Screen.kt @@ -18,7 +18,13 @@ sealed interface Screen { } @Serializable - data object Login : Screen + data object Login : Screen { + private var _source: Screen? = null + val source: Screen? + get() = _source?.also { _source = null } + + fun withSource(value: Screen?): Login = this.also { _source = value } + } @Serializable data object Home : Screen @@ -51,7 +57,7 @@ sealed interface Screen { data class TvShowDetails(val tvShowId: Int) : Screen @Serializable - data class MovieDetails(val movieId: Int) : Screen + data class MovieDetails(val movieId: Int, val isBookmarkSheetVisible: Boolean = false) : Screen @Serializable data class ActorDetails(val actorId: Int) : Screen diff --git a/presentation/src/main/java/com/london/presentation/shared/bookmarkSheet/BookmarkSheet.kt b/presentation/src/main/java/com/london/presentation/shared/bookmarkSheet/BookmarkSheet.kt index e0335dab9..63d743a94 100644 --- a/presentation/src/main/java/com/london/presentation/shared/bookmarkSheet/BookmarkSheet.kt +++ b/presentation/src/main/java/com/london/presentation/shared/bookmarkSheet/BookmarkSheet.kt @@ -56,10 +56,10 @@ import kotlinx.coroutines.launch fun BookmarkBottomSheet( isSheetVisible: Boolean, bookmarkedMovieId: Int, - onSheetDismiss: () -> Unit, modifier: Modifier = Modifier, viewModel: BookmarkSheetViewModel = hiltViewModel(), sheetState: SheetState = rememberModalBottomSheetState(), + onSheetDismiss: () -> Unit, ) { val coroutineScope = rememberCoroutineScope() val navController = LocalNavController.current @@ -93,7 +93,14 @@ fun BookmarkBottomSheet( BookmarkSheetEffect.LoginNavigation -> { hideSheet() - navController.navigate(Screen.Login) + navController.navigate( + Screen.Login.withSource( + value = Screen.MovieDetails( + movieId = bookmarkedMovieId, + isBookmarkSheetVisible = true + ) + ) + ) } } } diff --git a/presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt b/presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt index a85b30ef9..16500ea49 100644 --- a/presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt +++ b/presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt @@ -20,6 +20,7 @@ import com.london.designsystem.component.Icon import com.london.designsystem.component.Text import com.london.designsystem.theme.NovixTheme import com.london.presentation.R +import com.london.presentation.utils.isNotZeroRate @Composable fun TextWithIcon( @@ -29,6 +30,8 @@ fun TextWithIcon( hasInitialDot: Boolean = true ) { if (text.isEmpty()) return + val shouldShowDot = hasInitialDot && text.isNotZeroRate() + Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) @@ -36,7 +39,7 @@ fun TextWithIcon( val scale = LocalDensity.current.fontScale val baseIconSize = 12.dp - if (hasInitialDot) + if (shouldShowDot) { Box( modifier = Modifier .padding(4.dp) @@ -46,12 +49,13 @@ fun TextWithIcon( .align(alignment = Alignment.CenterVertically) ) - Icon( - painter = icon, - contentDescription = stringResource(R.string.image_dot), - tint = tint, - modifier = Modifier.size(baseIconSize * scale) - ) + Icon( + painter = icon, + contentDescription = stringResource(R.string.image_dot), + tint = tint, + modifier = Modifier.size(baseIconSize * scale) + ) + } Text( text = text, style = NovixTheme.typography.label.small, diff --git a/presentation/src/main/res/values-ar/strings.xml b/presentation/src/main/res/values-ar/strings.xml index fc7131cd4..0f632e4f5 100644 --- a/presentation/src/main/res/values-ar/strings.xml +++ b/presentation/src/main/res/values-ar/strings.xml @@ -1,7 +1,7 @@ بحث - ابحث... + ابحث… شوهد مؤخرًا عمليات البحث الأخيرة مسح الكل @@ -150,8 +150,9 @@ الفيلم غير موجود تم اضافه القائمه بنجاح تم حذف القائمة بنجاح - حدث خطا ما لا يمكن اضافه القائمه - تم حذف القائمة بنجاح + حدث خطأ ما لا يمكن اضافه القائمه + حدث خطأ ما، فشل حذف التقييم!"> + تم حذف القائمة بنجاح لا توجد عناصر هنا! حذف القائمة حذف القائمة المحفوظة سيحذف جميع عناصرها. هل أنت متأكد أنك تريد المتابعة؟ @@ -161,7 +162,7 @@ غلق مجموعاتي تم اضافه القائمه بنجاح - حدث خطا ما لا يمكن اضافه القائمه + حدث خطأ ما، فشل الإضافه للقائمه يرجى تسجيل الدخول للتمكن من الإضافة إالي قائمة لا يوجد قوائم متاحة لهذا الفيلم!\nقم بإنشاء واحدة جديدة الآن. تمت الإضافة إلى القائمة بنجاح diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index 41ae0c138..420df8e36 100644 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -1,7 +1,7 @@ Search - Search... + Search… Recent Viewed Recent Search Clear all @@ -153,7 +153,8 @@ list added successfully list removed successfully can not delete list - Delete rating successfully"> + Rating deleted successfully"> + Something went wrong, could not delete rating"> There is no items here! Delete list Deleting the saved list will remove all items it contains. Are you sure you want to proceed? diff --git a/presentation/src/test/java/com/london/presentation/feature/details/movie/info/MovieDetailsViewModelTest.kt b/presentation/src/test/java/com/london/presentation/feature/details/movie/info/MovieDetailsViewModelTest.kt index f3982c4b1..a10998b5a 100644 --- a/presentation/src/test/java/com/london/presentation/feature/details/movie/info/MovieDetailsViewModelTest.kt +++ b/presentation/src/test/java/com/london/presentation/feature/details/movie/info/MovieDetailsViewModelTest.kt @@ -177,8 +177,8 @@ class MovieDetailsViewModelTest { // When & Then viewModel?.effect?.test { - viewModel?.onLoginClick() - assertThat(awaitItem()).isEqualTo(MovieDetailsEffect.LoginNavigation) + viewModel?.onLoginClick(0) + assertThat(awaitItem()).isEqualTo(MovieDetailsEffect.LoginNavigation(0)) } } diff --git a/presentation/src/test/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModelTest.kt b/presentation/src/test/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModelTest.kt index 5ba316f6e..e8710c317 100644 --- a/presentation/src/test/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModelTest.kt +++ b/presentation/src/test/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsViewModelTest.kt @@ -249,8 +249,8 @@ class TvShowDetailsViewModelTest { fun `onLoginClick should emit OnLoginNavigation effect when clicked`() = runTest { // When & Then viewModel?.effect?.test { - viewModel?.onLoginClick() - assertThat(awaitItem()).isEqualTo(TvShowDetailsEffect.OnLoginNavigation) + viewModel?.onLoginClick(0) + assertThat(awaitItem()).isEqualTo(TvShowDetailsEffect.OnLoginNavigation(0)) cancelAndIgnoreRemainingEvents() } }