diff --git a/app/src/main/java/com/london/app/MainActivity.kt b/app/src/main/java/com/london/app/MainActivity.kt index d1287532b..f7148bfd2 100644 --- a/app/src/main/java/com/london/app/MainActivity.kt +++ b/app/src/main/java/com/london/app/MainActivity.kt @@ -12,9 +12,9 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalView @@ -25,9 +25,9 @@ import com.london.data.worker.MovieListSyncWorker import com.london.designsystem.theme.NovixTheme import com.london.domain.service.AppPreferencesService import com.london.presentation.localization.LocalizationManager -import com.london.presentation.localization.wrapWithLocale -import com.london.presentation.shared.ContentRestrictionProvider -import com.london.presentation.shared.LocalContentRestrictionLevel +import com.london.presentation.shared.contentRestriction.ContentRestrictionProvider +import com.london.presentation.shared.contentRestriction.LocalContentRestrictionLevel +import com.london.presentation.utils.wrapWithLocale import dagger.hilt.android.AndroidEntryPoint import java.util.Locale import javax.inject.Inject diff --git a/data/src/test/java/com/london/data/repository/AuthenticationRepositoryImplTest.kt b/data/src/test/java/com/london/data/repository/AuthenticationRepositoryImplTest.kt index bc283d4ae..73756287e 100644 --- a/data/src/test/java/com/london/data/repository/AuthenticationRepositoryImplTest.kt +++ b/data/src/test/java/com/london/data/repository/AuthenticationRepositoryImplTest.kt @@ -10,6 +10,7 @@ import com.london.data.remote.model.authentication.SessionResponse import com.london.data.remote.source.account.AccountRemoteDataSource import com.london.data.remote.source.authentication.AuthenticationRemoteDataSource import com.london.data.repository.authentication.AuthenticationRepositoryImpl +import com.london.domain.service.AppPreferencesService import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every @@ -30,6 +31,7 @@ class AuthenticationRepositoryImplTest { private val authenticationPreferences: AuthenticationPreferences = mockk(relaxed = true) private val customMovieListLocalDataSource: CustomMovieListLocalDataSource = mockk(relaxed = true) + private val appPreferencesService: AppPreferencesService = mockk(relaxed = true) @Before fun setUp() { @@ -37,7 +39,8 @@ class AuthenticationRepositoryImplTest { authenticationRemoteDataSource = authRemoteDataSource, accountRemoteDataSource = accountRemoteDataSource, authenticationPreferences = authenticationPreferences, - customMovieListLocalDataSource = customMovieListLocalDataSource + customMovieListLocalDataSource = customMovieListLocalDataSource, + appPreferencesService = appPreferencesService, ) } diff --git a/presentation/src/main/java/com/london/presentation/feature/account/AccountUiState.kt b/presentation/src/main/java/com/london/presentation/feature/account/AccountUiState.kt index ac5b81fe8..4ee22cbd7 100644 --- a/presentation/src/main/java/com/london/presentation/feature/account/AccountUiState.kt +++ b/presentation/src/main/java/com/london/presentation/feature/account/AccountUiState.kt @@ -10,8 +10,6 @@ data class AccountUiState( val error: ErrorState? = null, val userAvatar: String? = null, val isLoading: Boolean = false, - val isDarkMode: Boolean = false, - val isLightMode: Boolean = false, val currentLanguage: String = "", val showUserMenu: Boolean = false, val currentAppearance: String = "", diff --git a/presentation/src/main/java/com/london/presentation/feature/account/AccountViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/account/AccountViewModel.kt index b273b4cb6..c9abc4266 100644 --- a/presentation/src/main/java/com/london/presentation/feature/account/AccountViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/account/AccountViewModel.kt @@ -38,21 +38,12 @@ class AccountViewModel @Inject constructor( override fun onLogoutConfirmed() { tryToExecute( block = { authenticationUseCase.logout() }, - onStart = { - updateState { copy(isLogoutLoading = true) } - }, + onStart = { updateState { copy(isLogoutLoading = true) } }, onSuccess = { isSuccess: Boolean -> - if (isSuccess) { - updateState { copy(isLogoutLoading = false) } - } else { - updateState { - copy(error = ErrorState.RequestFailed("Logout failed")) - } - } + if (isSuccess) updateState { copy(isLogoutLoading = false) } + else updateState { copy(error = ErrorState.RequestFailed("Logout failed")) } }, - onCompleted = { - emitEffect(AccountEffect.LoginNavigation) - } + onCompleted = { emitEffect(AccountEffect.LoginNavigation) } ) } @@ -64,18 +55,13 @@ class AccountViewModel @Inject constructor( ) } - override fun onWatchingHistoryClick() = - emitEffect(AccountEffect.WatchingHistoryNavigation) - - - override fun onMyRatingClick() = - emitEffect(AccountEffect.MyRatingNavigation) + override fun onWatchingHistoryClick() = emitEffect(AccountEffect.WatchingHistoryNavigation) + override fun onMyRatingClick() = emitEffect(AccountEffect.MyRatingNavigation) override fun onContentRestrictionClick() = updateState { copy(activeBottomSheet = ActiveBottomSheet.ContentRestriction) } - override fun onContentRestrictionSave(level: ContentRestrictionLevel) { appPreferencesService.setContentRestrictionLevel(level) updateState { @@ -86,48 +72,28 @@ class AccountViewModel @Inject constructor( } } - override fun onChangePasswordClick() = - emitEffect(AccountEffect.ChangePasswordNavigation()) - - override fun onAppearanceClick() = - updateState { - copy(activeBottomSheet = ActiveBottomSheet.Appearance) - } + override fun onChangePasswordClick() = emitEffect(AccountEffect.ChangePasswordNavigation()) - override fun onDarkModeSelected() = - updateState { - copy(appTheme = AppTheme.DARK) - } + override fun onAppearanceClick() = setActiveBottomSheet(ActiveBottomSheet.Appearance) + override fun onDarkModeSelected() = setAppTheme(AppTheme.DARK) - override fun onLightModeSelected() = - updateState { - copy(appTheme = AppTheme.LIGHT) - } + override fun onLightModeSelected() = setAppTheme(AppTheme.LIGHT) override fun onAppearanceModeSave() { appPreferencesService.setAppTheme(state.value.appTheme) onBottomSheetDismiss() } - override fun showAppearanceBottomSheet() = - updateState { - copy(activeBottomSheet = ActiveBottomSheet.Appearance) - } + override fun showAppearanceBottomSheet() = setActiveBottomSheet(ActiveBottomSheet.Appearance) - override fun onLanguageClick() = - updateState { - copy(activeBottomSheet = ActiveBottomSheet.Language) - } + override fun onLanguageClick() = setActiveBottomSheet(ActiveBottomSheet.Language) - override fun onEnglishSelected() = - updateState { - copy(appLanguage = AppLanguage.ENGLISH) - } - override fun onArabicSelected() = - updateState { - copy(appLanguage = AppLanguage.ARABIC) - } + override fun onEnglishSelected() = updateState { + copy(appLanguage = AppLanguage.ENGLISH) + } + + override fun onArabicSelected() = updateState { copy(appLanguage = AppLanguage.ARABIC) } override fun onLanguageSettingsSave() { appPreferencesService.setAppLanguage(state.value.appLanguage) @@ -150,45 +116,32 @@ class AccountViewModel @Inject constructor( ) } - override fun onLoginClick() = - emitEffect(AccountEffect.LoginNavigation) + override fun onLoginClick() = emitEffect(AccountEffect.LoginNavigation) private fun initializeAppTheme() { val isAppDarkMode = appPreferencesService.isAppDarkMode.value - updateState { - copy(appTheme = if (isAppDarkMode) AppTheme.DARK else AppTheme.LIGHT) - } + setAppTheme(if (isAppDarkMode) AppTheme.DARK else AppTheme.LIGHT) } - private fun initializeAppLanguage() { - updateState { - copy(appLanguage = appPreferencesService.appLanguage.value) - } - } + private fun initializeAppLanguage() = + updateState { copy(appLanguage = appPreferencesService.appLanguage.value) } private fun observeContentRestrictionLevel() { appPreferencesService.contentRestrictionLevel - .onEach { level -> - updateState { copy(currentContentRestriction = level) } - } + .onEach { level -> setCurrentContentRestrictionLevel(level) } .launchIn(viewModelScope) } + private fun setCurrentContentRestrictionLevel(level: ContentRestrictionLevel) = + updateState { copy(currentContentRestriction = level) } + private fun checkUserLoginStatus() { tryToExecute( block = { authenticationUseCase.isLoggedIn() }, - onStart = { - updateState { copy(isLoading = true) } - }, - onSuccess = { isLoggedIn: Boolean -> - updateState { copy(isUserLoggedIn = isLoggedIn) } - }, - onError = { - updateState { copy(isUserLoggedIn = false) } - }, - onCompleted = { - updateState { copy(isLoading = false) } - } + onStart = { updateState { copy(isLoading = true) } }, + onSuccess = { isLoggedIn: Boolean -> updateState { copy(isUserLoggedIn = isLoggedIn) } }, + onError = { updateState { copy(isUserLoggedIn = false) } }, + onCompleted = { updateState { copy(isLoading = false) } } ) } @@ -206,4 +159,8 @@ class AccountViewModel @Inject constructor( ) } + private fun setActiveBottomSheet(activeBottomSheet: ActiveBottomSheet) = + updateState { copy(activeBottomSheet = activeBottomSheet) } + + private fun setAppTheme(appTheme: AppTheme) = updateState { copy(appTheme = appTheme) } } diff --git a/presentation/src/main/java/com/london/presentation/feature/account/components/UserProfileSection.kt b/presentation/src/main/java/com/london/presentation/feature/account/components/UserProfileSection.kt index 19c337497..eee99623e 100644 --- a/presentation/src/main/java/com/london/presentation/feature/account/components/UserProfileSection.kt +++ b/presentation/src/main/java/com/london/presentation/feature/account/components/UserProfileSection.kt @@ -30,7 +30,7 @@ import com.london.designsystem.component.Text import com.london.designsystem.theme.NovixTheme import com.london.domain.entity.contentrestriction.ContentRestrictionLevel import com.london.presentation.R -import com.london.presentation.shared.ImageView +import com.london.presentation.shared.item.ImageView import com.london.designsystem.R as dsR @Composable diff --git a/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingContract.kt b/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingContract.kt index 3b2df0ba0..174b7c989 100644 --- a/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingContract.kt +++ b/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingContract.kt @@ -3,7 +3,6 @@ package com.london.presentation.feature.account.rating interface MyRatingContract { fun onBackClick() fun onRetryClick() - fun onItemClick(id: Int) fun onMovieClick(id: Int) fun onTvShowClick(id: Int) fun onDeleteMovieClick(id: Int) 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 8faeb8833..1720c0735 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 @@ -25,11 +25,11 @@ import com.london.designsystem.theme.ThemePreviews import com.london.domain.entity.shared.MediaType import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.EmptyGenreLayout import com.london.presentation.shared.SnackBarAnimation import com.london.presentation.shared.base.ErrorState import com.london.presentation.shared.buildscreen.BuildScreen import com.london.presentation.shared.container.MediaLazyVerticalGrid +import com.london.presentation.shared.layout.EmptyGenreLayout import com.london.presentation.utils.Listen import com.london.presentation.utils.detailsTopBar import com.london.presentation.utils.toLocalizedNumbers diff --git a/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingViewModel.kt index 3d4be0f8c..83c99669b 100644 --- a/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/account/rating/MyRatingViewModel.kt @@ -47,14 +47,6 @@ class MyRatingViewModel @Inject constructor( ) } - override fun onItemClick(id: Int) { - tryToExecute( - block = { manageRatingUseCase.getRatedMediaById(id) }, - onSuccess = { ratedMedia -> handleRatedMediaNavigation(ratedMedia, id) }, - onError = { errorState -> updateState { copy(errorState = errorState) } } - ) - } - override fun onRatingCategorySelected(category: RatingCategory) = updateState { copy(selectedRatingCategory = category) } @@ -85,15 +77,4 @@ class MyRatingViewModel @Inject constructor( ) } } - - private fun handleRatedMediaNavigation(ratedMedia: RatedMedia?, id: Int) { - ratedMedia?.let { rated -> - val effect = when (rated.mediaType) { - MediaType.Movie -> MyRatingEffect.MovieDetailsNavigation(id) - MediaType.TvShow -> MyRatingEffect.TvShowDetailsNavigation(id) - } - emitEffect(effect) - } - } - } diff --git a/presentation/src/main/java/com/london/presentation/feature/authentication/register/RegistrationViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/authentication/register/RegistrationViewModel.kt index 64be24e89..bd009732a 100644 --- a/presentation/src/main/java/com/london/presentation/feature/authentication/register/RegistrationViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/authentication/register/RegistrationViewModel.kt @@ -16,15 +16,13 @@ class RegistrationViewModel @Inject constructor() : override fun onPageLoaded(url: String?) { url?.let { currentUrl -> updateState { copy(currentUrl = currentUrl, isLoading = false) } - if (isRegistrationCompleteUrl(currentUrl)) { + if (isRegistrationCompleteUrl(currentUrl)) emitEffect(RegistrationEffect.RegistrationComplete) - } } } - override fun onUrlChanged(url: String) { - updateState { copy(currentUrl = url) } - } + override fun onUrlChanged(url: String) = updateState { copy(currentUrl = url) } + override fun shouldInterceptUrl(url: String): Boolean { if (!isUrlAllowed(url)) { diff --git a/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesScreen.kt b/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesScreen.kt index 8ccd45409..c6260235f 100644 --- a/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesScreen.kt @@ -33,10 +33,10 @@ import com.london.designsystem.theme.ThemePreviews import com.london.designsystem.utils.string import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.CategoriesItem import com.london.presentation.shared.MediaCategory import com.london.presentation.shared.genre.MovieGenreUi import com.london.presentation.shared.genre.TvShowGenreUi +import com.london.presentation.shared.item.CategoriesItem import com.london.presentation.utils.Listen import com.london.presentation.utils.gridColumns import com.london.presentation.utils.navBarBottomPadding diff --git a/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesUiState.kt b/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesUiState.kt index 09885ad6f..1b93ab155 100644 --- a/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesUiState.kt +++ b/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesUiState.kt @@ -5,7 +5,6 @@ import com.london.presentation.shared.genre.MovieGenreUi import com.london.presentation.shared.genre.TvShowGenreUi data class CategoriesUiState( - val selectedCategory: MediaCategory = MediaCategory.Movies, val movieGenres: List = MovieGenreUi.getListWithoutAll(), val tvShowGenres: List = TvShowGenreUi.getListWithoutAll(), diff --git a/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesViewModel.kt index 419667630..e2b580877 100644 --- a/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/category/main/CategoriesViewModel.kt @@ -18,5 +18,4 @@ class CategoriesViewModel : BaseViewModel(C if (category == state.value.selectedCategory) return updateState { copy(selectedCategory = category) } } - } diff --git a/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryContract.kt b/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryContract.kt index b9b2ae301..ecb0607a8 100644 --- a/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryContract.kt +++ b/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryContract.kt @@ -2,6 +2,5 @@ package com.london.presentation.feature.category.tvshow interface TvShowCategoryContract { fun onBackClick() - fun onSavedClick(tvShowId: Int) fun onTvShowClick(tvShowId: Int) } diff --git a/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryScreen.kt b/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryScreen.kt index 7738eb358..7e699bf19 100644 --- a/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryScreen.kt @@ -102,7 +102,6 @@ private fun Preview() { Content( state = TvShowCategoryUiState(), contract = object : TvShowCategoryContract { - override fun onSavedClick(tvShowId: Int) {} override fun onTvShowClick(tvShowId: Int) {} override fun onBackClick() {} } diff --git a/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryViewModel.kt index a2c9700e5..36c556ec3 100644 --- a/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/category/tvshow/TvShowCategoryViewModel.kt @@ -36,8 +36,6 @@ class TvShowCategoryViewModel @Inject constructor( override fun onBackClick() = emitEffect(TvShowCategoryEffect.BackNavigation) - override fun onSavedClick(tvShowId: Int) = Unit //TODO("Save Tv Show Not yet implemented") - private fun initializeTvShows(genreUi: TvShowGenreUi) { tryToExecute( onStart = { onInitializeTvShowsStarted(genreUi = genreUi) }, diff --git a/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsScreen.kt b/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsScreen.kt index 949869c06..6dd35adb6 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsScreen.kt @@ -53,13 +53,13 @@ import com.london.designsystem.theme.NovixTheme import com.london.domain.entity.actor.ActorMediaItems import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.ConditionalText import com.london.presentation.shared.CustomBackDropImagePager import com.london.presentation.shared.HomeCard -import com.london.presentation.shared.ImageView -import com.london.presentation.shared.TextWithIcon import com.london.presentation.shared.bookmarkSheet.BookmarkBottomSheet import com.london.presentation.shared.buildscreen.BuildScreen +import com.london.presentation.shared.item.ImageView +import com.london.presentation.shared.text.ConditionalText +import com.london.presentation.shared.text.TextWithIcon import com.london.presentation.utils.Listen import com.london.presentation.utils.detailsTopBar import com.london.presentation.utils.offsetLayout diff --git a/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsViewModel.kt index 00fd4fd9d..8e15ea0d6 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/actor/ActorDetailsViewModel.kt @@ -5,6 +5,7 @@ import com.london.domain.usecase.details.actor.GetActorUseCase import com.london.presentation.navigation.Screen import com.london.presentation.navigation.getArgs import com.london.presentation.shared.base.BaseViewModel +import com.london.presentation.shared.base.ErrorState import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -27,29 +28,26 @@ class ActorDetailsViewModel @Inject constructor( getActorInformation() } - override fun onBackClick() { - emitEffect(ActorEffect.BackNavigation) - } + override fun onBackClick() = emitEffect(ActorEffect.BackNavigation) + + override fun onActorGalleryClick(actorId: Int) = emitEffect(ActorEffect.GalleryNavigation(actorId)) - override fun onActorGalleryClick(actorId: Int) { - emitEffect(ActorEffect.GalleryNavigation(actorId)) - } - override fun onTopMoviePicksClick(actorId: Int) { + override fun onTopMoviePicksClick(actorId: Int) = emitEffect(ActorEffect.TopMoviePicksNavigation(actorId)) - } - override fun onMovieScreenClick(movieId: Int) { + + override fun onMovieScreenClick(movieId: Int) = emitEffect(ActorEffect.MovieScreenNavigation(movieId)) - } - override fun onTopTvShowPicksClick(actorId: Int) { + + override fun onTopTvShowPicksClick(actorId: Int) = emitEffect(ActorEffect.TopTvShowPicksNavigation(actorId)) - } - override fun onTvShowScreenClick(tvShowId: Int) { + + override fun onTvShowScreenClick(tvShowId: Int) = emitEffect(ActorEffect.TvShowScreenNavigation(tvShowId)) - } + override fun onManageBookmarkClicked(movieId: Int) { updateState { @@ -78,50 +76,44 @@ class ActorDetailsViewModel @Inject constructor( private fun getActorImage() { tryToExecute( - block = { - getActorUseCase.getActorImagesById(actorId ?: 0) - }, - onStart = { updateState { copy(isLoading = true) } }, + block = { getActorUseCase.getActorImagesById(actorId ?: 0) }, + onStart = { setLoadingState(true) }, onSuccess = { images -> updateState { copy(actorImageDetails = images) } }, - onError = { errorState -> updateState { copy(error = errorState) } }, - onCompleted = { updateState { copy(isLoading = false) } }, + onError = ::setErrorState, + onCompleted = { setLoadingState(false) }, ) } private fun getActorDetails() { tryToExecute( - block = { - getActorUseCase.getActorDetailsById(actorId ?: 0) - }, - onStart = { updateState { copy(isLoading = true) } }, + block = { getActorUseCase.getActorDetailsById(actorId ?: 0) }, + onStart = { setLoadingState(true) }, onSuccess = { actorDetails -> updateState { copy(actorDetails = actorDetails) } }, - onError = { errorState -> updateState { copy(error = errorState) } }, - onCompleted = { updateState { copy(isLoading = false) } }, + onError = ::setErrorState, + onCompleted = { setLoadingState(false) }, ) } private fun getActorMovieDetails() { tryToExecute( - block = { - getActorUseCase.getActorMoviePicksById(actorId ?: 0) - }, - onStart = { updateState { copy(isLoading = true) } }, + block = { getActorUseCase.getActorMoviePicksById(actorId ?: 0) }, + onStart = { setLoadingState(true) }, onSuccess = { movieDetails -> updateState { copy(actorMovieDetails = movieDetails) } }, - onError = { errorState -> updateState { copy(error = errorState) } }, - onCompleted = { updateState { copy(isLoading = false) } }, + onError = ::setErrorState, + onCompleted = { setLoadingState(false) }, ) } private fun getActorTvShowDetails() { tryToExecute( - block = { - getActorUseCase.getActorTvShowPicksById(actorId ?: 0) - }, - onStart = { updateState { copy(isLoading = true) } }, + block = { getActorUseCase.getActorTvShowPicksById(actorId ?: 0) }, + onStart = { setLoadingState(true) }, onSuccess = { tvShows -> updateState { copy(actorTvShowDetails = tvShows) } }, - onError = { errorState -> updateState { copy(error = errorState) } }, - onCompleted = { updateState { copy(isLoading = false) } }, + onError = ::setErrorState, + onCompleted = { setLoadingState(false) }, ) } + private fun setLoadingState(isLoading: Boolean) = updateState { copy(isLoading = isLoading) } + private fun setErrorState(errorState: ErrorState) = updateState { copy(error = errorState) } } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryScreen.kt b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryScreen.kt index f4fc8579a..0c44fdd91 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryScreen.kt @@ -31,8 +31,8 @@ import com.london.designsystem.component.button.ErrorImage import com.london.designsystem.theme.NovixTheme import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.ImageView import com.london.presentation.shared.buildscreen.BuildScreen +import com.london.presentation.shared.item.ImageView import com.london.presentation.utils.Listen @Composable diff --git a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryViewModel.kt index a617dd607..174bbe600 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/gallery/ActorsGalleryViewModel.kt @@ -39,8 +39,5 @@ class ActorsGalleryViewModel @Inject constructor( loadImages(actorId) } - override fun onBackClick() { - emitEffect(ActorsGalleryEffect.BackNavigation) - } - + override fun onBackClick() = emitEffect(ActorsGalleryEffect.BackNavigation) } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/topmoviespicks/TopMoviesPicksViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/topmoviespicks/TopMoviesPicksViewModel.kt index 9b6663276..9234a043f 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/topmoviespicks/TopMoviesPicksViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/topmoviespicks/TopMoviesPicksViewModel.kt @@ -32,9 +32,8 @@ class TopMoviesPicksViewModel @Inject constructor( emitEffect(TopMoviesPicksEffect.MovieDetailsNavigation(movieId)) } - override fun onBackClick() { - emitEffect(TopMoviesPicksEffect.BackNavigation) - } + override fun onBackClick() = emitEffect(TopMoviesPicksEffect.BackNavigation) + override fun onManageBookmarkClicked(movieId: Int) { updateState { @@ -63,5 +62,4 @@ class TopMoviesPicksViewModel @Inject constructor( onCompleted = { updateState { copy(isLoading = false) } }, ) } - } diff --git a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/toptvshowspicks/TopTvShowsPicksViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/toptvshowspicks/TopTvShowsPicksViewModel.kt index 9930bfba9..11ed1628c 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/actor/info/toptvshowspicks/TopTvShowsPicksViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/actor/info/toptvshowspicks/TopTvShowsPicksViewModel.kt @@ -28,9 +28,8 @@ class TopTvShowsPicksViewModel @Inject constructor( getActorTvShowsPicksData() } - override fun onBackClick() { - emitEffect(TopTvShowsPicksEffect.BackNavigation) - } + override fun onBackClick() = emitEffect(TopTvShowsPicksEffect.BackNavigation) + override fun onTvShowClick(tvShowId: Int) { emitEffect(TopTvShowsPicksEffect.TvShowDetailsNavigation(tvShowId)) 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 de2cf1818..c567135cb 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 @@ -63,17 +63,17 @@ import com.london.presentation.R.drawable import com.london.presentation.R.string.more_like_this import com.london.presentation.R.string.overview import com.london.presentation.R.string.view_reviews -import com.london.presentation.shared.ActorItem import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.ConditionalText import com.london.presentation.shared.CustomBackDropImagePager -import com.london.presentation.shared.FooterSection import com.london.presentation.shared.HomeCard import com.london.presentation.shared.SnackBarAnimation -import com.london.presentation.shared.TextWithIcon import com.london.presentation.shared.bookmarkSheet.BookmarkBottomSheet import com.london.presentation.shared.buildscreen.BuildScreen import com.london.presentation.shared.genre.MovieGenreUi +import com.london.presentation.shared.item.ActorItem +import com.london.presentation.shared.section.FooterSection +import com.london.presentation.shared.text.ConditionalText +import com.london.presentation.shared.text.TextWithIcon import com.london.presentation.utils.Listen import com.london.presentation.utils.detailsTopBar import com.london.presentation.utils.getLocalizedTimeUnit 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 41397477e..cac231b3b 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 @@ -42,98 +42,62 @@ class MovieDetailsViewModel @Inject constructor( loadAdditionalMovieData() } - private fun loadMainMovieData() { - tryToExecute( - block = { fetchMainMovieData() }, - onStart = { setLoadingState(true) }, - onSuccess = { movieData -> handleMainMovieDataSuccess(movieData) }, - onError = { errorState -> updateState { copy(error = errorState) } }, - onCompleted = { setLoadingState(false) } - ) - } + override fun onBackClick() = emitEffect(MovieDetailsEffect.BackNavigation) - private fun loadAdditionalMovieData() { - tryToExecute( - block = { fetchAdditionalMovieData() }, - onSuccess = { additionalData -> handleAdditionalMovieDataSuccess(additionalData) }, - onError = { errorState -> updateState { copy(error = errorState) } }, - onCompleted = { setLoadingState(false) } - ) + override fun onManageBookmarkClicked(movieId: Int) { + updateState { + copy( + isBookmarkSheetVisible = true, + bookmarkedMovieId = movieId + ) + } } - private suspend fun fetchMainMovieData(): MainMovieData { - val cast = movieDetails.getMovieCast(movieId) - val images = movieDetails.getMovieImages(movieId) - val details = movieDetails.getMovieDetails(movieId) - return MainMovieData(details, images, cast) + override fun onBookmarkSheetDismiss() { + updateState { + copy( + isBookmarkSheetVisible = false, + bookmarkedMovieId = 0 + ) + } } - private suspend fun fetchAdditionalMovieData(): AdditionalMovieDetailsData { - val similarMovies = movieDetails.getSimilarMovies(movieId) - val movieVideos = movieDetails.getMovieVideo(movieId) - val movieRating = getUserMovieRating() + override fun onExpandClick() = updateState { copy(expanded = !expanded) } - return AdditionalMovieDetailsData( - similarMovies = similarMovies, - movieVideoUrl = movieVideos, - movieRating = movieRating - ) + + override fun onMovieClick(movieId: Int) { + clearRatedState() + emitEffect(MovieDetailsEffect.MovieNavigation(movieId)) } - private suspend fun getUserMovieRating(): Int { - return if (authenticationUseCase.isLoggedIn()) - ratingUseCase.getRateAccountMovieStatesById(movieId) else 0 + override fun onActorClick(actorId: Int) { + clearRatedState() + emitEffect(MovieDetailsEffect.ActorNavigation(actorId)) } - private suspend fun handleMainMovieDataSuccess(movieData: MainMovieData) { - val (details, images, cast) = movieData - updateMovieDetailsState(details, images, cast) - addMovieToRecentHistory(details) + override fun onLoginClick() = emitEffect(MovieDetailsEffect.LoginNavigation) + + + override fun onReviewsClick(movieId: Int, mediaType: MediaType) { + clearRatedState() + emitEffect(MovieDetailsEffect.ReviewsNavigation(movieId, mediaType)) } - private fun handleAdditionalMovieDataSuccess(additionalData: AdditionalMovieDetailsData) { - updateState { - copy( - similarMovies = additionalData.similarMovies, - movieVideo = additionalData.movieVideoUrl.firstOrNull().orEmpty(), - isRated = additionalData.movieRating != 0 && !state.value.isGuestUser - ) - } + override fun onGenreClick(genre: MovieGenreUi) { + clearRatedState() + emitEffect(MovieDetailsEffect.GenreNavigation(genre)) } - private fun updateMovieDetailsState( - details: MovieDetails, - images: List, - cast: List - ) { - updateState { - copy( - movieId = details.id, - movieName = details.title, - movieGenres = details.genres.map { it.toUi() }, - movieRating = details.voteAverage, - movieDuration = details.runtime.toString(), - releaseDate = details.releaseDate, - movieOverview = details.overview, - movieImages = images.ifEmpty { listOf(details.posterUrl) }, - actors = cast - ) - } + override fun onRetryClick() { + resetErrorState() + loadMovieDetails() } - private fun resetErrorState() = - updateState { copy(error = null) } + override fun onRateBottomSheetClick() = checkUserAuthenticationForRating() - private fun setLoadingState(isLoading: Boolean) = - updateState { copy(isLoading = isLoading) } + override fun onSelectRatingClick(rating: Int) = submitMovieRating(rating) - private fun checkUserAuthenticationForRating() { - tryToExecute( - block = { authenticationUseCase.isLoggedIn() }, - onSuccess = { isLoggedIn -> handleRatingAuthenticationResult(isLoggedIn) }, - onError = { errorState -> updateState { copy(error = errorState) } } - ) - } + private fun clearRatedState() = updateState { copy(isSuccessfullyRated = null) } private fun handleRatingAuthenticationResult(isLoggedIn: Boolean) = if (isLoggedIn) showRatingBottomSheet() else showGuestUserBottomSheet() @@ -199,69 +163,94 @@ class MovieDetailsViewModel @Inject constructor( manageRecentMovieWatchedUseCase.addMovieToRecentWatched(movie) } - override fun onBackClick() { - emitEffect(MovieDetailsEffect.BackNavigation) + private fun loadMainMovieData() { + tryToExecute( + block = { fetchMainMovieData() }, + onStart = { setLoadingState(true) }, + onSuccess = { movieData -> handleMainMovieDataSuccess(movieData) }, + onError = { errorState -> updateState { copy(error = errorState) } }, + onCompleted = { setLoadingState(false) } + ) } - override fun onManageBookmarkClicked(movieId: Int) { - updateState { - copy( - isBookmarkSheetVisible = true, - bookmarkedMovieId = movieId - ) - } + private fun loadAdditionalMovieData() { + tryToExecute( + block = { fetchAdditionalMovieData() }, + onSuccess = { additionalData -> handleAdditionalMovieDataSuccess(additionalData) }, + onError = { errorState -> updateState { copy(error = errorState) } }, + onCompleted = { setLoadingState(false) } + ) } - override fun onBookmarkSheetDismiss() { - updateState { - copy( - isBookmarkSheetVisible = false, - bookmarkedMovieId = 0 - ) - } + private suspend fun fetchMainMovieData(): MainMovieData { + val cast = movieDetails.getMovieCast(movieId) + val images = movieDetails.getMovieImages(movieId) + val details = movieDetails.getMovieDetails(movieId) + return MainMovieData(details, images, cast) } - override fun onExpandClick() { - updateState { copy(expanded = !expanded) } - } + private suspend fun fetchAdditionalMovieData(): AdditionalMovieDetailsData { + val similarMovies = movieDetails.getSimilarMovies(movieId) + val movieVideos = movieDetails.getMovieVideo(movieId) + val movieRating = getUserMovieRating() - override fun onMovieClick(movieId: Int) { - clearRatedState() - emitEffect(MovieDetailsEffect.MovieNavigation(movieId)) + return AdditionalMovieDetailsData( + similarMovies = similarMovies, + movieVideoUrl = movieVideos, + movieRating = movieRating + ) } - override fun onActorClick(actorId: Int) { - clearRatedState() - emitEffect(MovieDetailsEffect.ActorNavigation(actorId)) + private suspend fun getUserMovieRating(): Int { + return if (authenticationUseCase.isLoggedIn()) + ratingUseCase.getRateAccountMovieStatesById(movieId) else 0 } - override fun onLoginClick() { - emitEffect(MovieDetailsEffect.LoginNavigation) + private suspend fun handleMainMovieDataSuccess(movieData: MainMovieData) { + val (details, images, cast) = movieData + updateMovieDetailsState(details, images, cast) + addMovieToRecentHistory(details) } - override fun onReviewsClick(movieId: Int, mediaType: MediaType) { - clearRatedState() - emitEffect(MovieDetailsEffect.ReviewsNavigation(movieId, mediaType)) + private fun handleAdditionalMovieDataSuccess(additionalData: AdditionalMovieDetailsData) { + updateState { + copy( + similarMovies = additionalData.similarMovies, + movieVideo = additionalData.movieVideoUrl.firstOrNull().orEmpty(), + isRated = additionalData.movieRating != 0 && !state.value.isGuestUser + ) + } } - override fun onGenreClick(genre: MovieGenreUi) { - clearRatedState() - emitEffect(MovieDetailsEffect.GenreNavigation(genre)) + private fun updateMovieDetailsState( + details: MovieDetails, + images: List, + cast: List + ) { + updateState { + copy( + movieId = details.id, + movieName = details.title, + movieGenres = details.genres.map { it.toUi() }, + movieRating = details.voteAverage, + movieDuration = details.runtime.toString(), + releaseDate = details.releaseDate, + movieOverview = details.overview, + movieImages = images.ifEmpty { listOf(details.posterUrl) }, + actors = cast + ) + } } - override fun onRetryClick() { - resetErrorState() - loadMovieDetails() - } + private fun resetErrorState() = updateState { copy(error = null) } - override fun onRateBottomSheetClick() { - checkUserAuthenticationForRating() - } + private fun setLoadingState(isLoading: Boolean) = updateState { copy(isLoading = isLoading) } - override fun onSelectRatingClick(rating: Int) { - submitMovieRating(rating) + private fun checkUserAuthenticationForRating() { + tryToExecute( + block = { authenticationUseCase.isLoggedIn() }, + onSuccess = { isLoggedIn -> handleRatingAuthenticationResult(isLoggedIn) }, + onError = { errorState -> updateState { copy(error = errorState) } } + ) } - - private fun clearRatedState() = updateState { copy(isSuccessfullyRated = null) } - } 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 5e134939f..e2694b525 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 @@ -44,16 +44,16 @@ import com.london.designsystem.component.Text import com.london.designsystem.component.TopBar import com.london.designsystem.theme.NovixTheme import com.london.presentation.R -import com.london.presentation.shared.ActorItem import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.ConditionalText import com.london.presentation.shared.CustomBackDropImagePager -import com.london.presentation.shared.FooterSection -import com.london.presentation.shared.RatingItem import com.london.presentation.shared.SnackBarAnimation -import com.london.presentation.shared.TextWithIcon import com.london.presentation.shared.base.ErrorState import com.london.presentation.shared.buildscreen.BuildScreen +import com.london.presentation.shared.item.ActorItem +import com.london.presentation.shared.item.RatingItem +import com.london.presentation.shared.section.FooterSection +import com.london.presentation.shared.text.ConditionalText +import com.london.presentation.shared.text.TextWithIcon import com.london.presentation.utils.Listen import com.london.presentation.utils.detailsTopBar import com.london.presentation.utils.headerDetailsCard @@ -84,7 +84,7 @@ fun EpisodeDetailsScreen( onBack = viewModel::onBackClick, isLoading = uiState.isLoading, isError = uiState.error == ErrorState.NoInternet, - onRetry = viewModel::onRetry + onRetry = viewModel::onRetryClick ) { Content( uiState = uiState, 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 bc349c021..117679e94 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 @@ -5,4 +5,6 @@ interface EpisodeDetailsContract { fun onLoginClick() fun onRateEpisodeClick() fun onSelectRatingClick(rating: Int) + + fun onRetryClick() } 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 e54f37ad8..7412494f6 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 @@ -87,9 +87,8 @@ class EpisodeDetailsViewModel @Inject constructor( ) } - private fun setLoadingState() { - updateState { copy(isLoading = true) } - } + private fun setLoadingState() = updateState { copy(isLoading = true) } + private fun handleEpisodeDataLoaded(data: EpisodeDetailsData) { updateState { @@ -101,14 +100,16 @@ class EpisodeDetailsViewModel @Inject constructor( } } - private fun handleError(errorState: ErrorState) { - updateState { copy(error = errorState) } - } + private fun handleError(errorState: ErrorState) = updateState { copy(error = errorState) } - private fun clearLoadingState() { - updateState { copy(isLoading = false) } - } + private fun clearLoadingState() = updateState { copy(isLoading = false) } + + override fun onRetryClick() { + updateState { copy(error = null) } + loadEpisodeDetails() + loadVideoProvider() + } data class EpisodeDetailsData( val episode: EpisodeDetails, val images: List, @@ -173,11 +174,8 @@ class EpisodeDetailsViewModel @Inject constructor( ).first() } - private fun loadEpisodeRatingSuccess(rating: Int) { - updateState { - copy(isRated = rating != DEFAULT_RATING && !isGuestUser) - } - } + private fun loadEpisodeRatingSuccess(rating: Int) = + updateState { copy(isRated = rating != DEFAULT_RATING && !isGuestUser) } private fun loadEpisodeRating() { tryToExecute( @@ -198,12 +196,6 @@ class EpisodeDetailsViewModel @Inject constructor( } } - fun onRetry() { - updateState { copy(error = null) } - loadEpisodeDetails() - loadVideoProvider() - } - private companion object { const val DEFAULT_RATING = 0 } 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 314ad5005..22154392e 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 @@ -62,17 +62,17 @@ import com.london.designsystem.theme.noRippleClickable import com.london.domain.entity.shared.MediaType import com.london.domain.entity.tvshow.cast.TvShowCastMember import com.london.domain.entity.tvshow.episode.Episodes -import com.london.presentation.shared.ActorItem import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.ConditionalText import com.london.presentation.shared.CustomBackDropImagePager -import com.london.presentation.shared.FooterSection -import com.london.presentation.shared.ImageView -import com.london.presentation.shared.RatingItem import com.london.presentation.shared.SnackBarAnimation import com.london.presentation.shared.base.ErrorState import com.london.presentation.shared.buildscreen.BuildScreen import com.london.presentation.shared.genre.TvShowGenreUi +import com.london.presentation.shared.item.ActorItem +import com.london.presentation.shared.item.ImageView +import com.london.presentation.shared.item.RatingItem +import com.london.presentation.shared.section.FooterSection +import com.london.presentation.shared.text.ConditionalText import com.london.presentation.utils.Listen import com.london.presentation.utils.convertDate import com.london.presentation.utils.detailsTopBar diff --git a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsUiState.kt b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsUiState.kt index e5af8e5e7..c4dff4cf7 100644 --- a/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsUiState.kt +++ b/presentation/src/main/java/com/london/presentation/feature/details/tvshow/info/TvShowDetailsUiState.kt @@ -11,7 +11,6 @@ data class TvShowDetailsUiState( val name: String = "", val overview: String = "", val selectedRating: Int = 0, - val isSaved: Boolean = false, val isError: Boolean = false, val isRated: Boolean = false, val numberOfSeasons: Int = 0, 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 3fa6ff919..c4e79d1cd 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 @@ -13,8 +13,10 @@ import com.london.domain.usecase.recent.watched.tvshow.ManageRecentTvShowWatched import com.london.presentation.navigation.Screen import com.london.presentation.navigation.getArgs import com.london.presentation.shared.base.BaseViewModel +import com.london.presentation.shared.base.ErrorState import com.london.presentation.shared.genre.TvShowGenreUi import com.london.presentation.shared.genre.toUi +import com.london.presentation.utils.orZero import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @@ -31,7 +33,7 @@ class TvShowDetailsViewModel @Inject constructor( TvShowDetailsContract { private val args = savedStateHandle.getArgs() - private val tvShowId: Int = args?.tvShowId ?: 0 + private val tvShowId: Int = args?.tvShowId.orZero() init { initializeGetTvShowDetailsData() @@ -43,8 +45,7 @@ class TvShowDetailsViewModel @Inject constructor( fun initializeEpisodesBySeasons(seasonNumber: Int = 1) { tryToExecute( block = { - val episodesBySeason = - getTvEpisodesUseCase.getTvShowSeasonEpisodes(tvShowId, seasonNumber) + val episodesBySeason = getTvEpisodesUseCase.getTvShowSeasonEpisodes(tvShowId, seasonNumber) val videoProvider = getTvShowUseCase.getTvSeasonTrailer(tvShowId, seasonNumber) Triple(episodesBySeason.episodes, episodesBySeason, videoProvider) }, @@ -64,7 +65,7 @@ class TvShowDetailsViewModel @Inject constructor( } fun onRetry() { - updateState { copy(error = null) } + setLoadingState(null) initializeGetTvShowDetailsData() initializeGetCastData() initializeGetImagesData() @@ -101,20 +102,16 @@ class TvShowDetailsViewModel @Inject constructor( tryToExecute( block = { authenticationUseCase.isLoggedIn() }, onSuccess = { isLoggedIn -> - if (isLoggedIn) - updateState { copy(isRateBottomSheetVisible = isRateBottomSheetVisible.not()) } + if (isLoggedIn) updateState { copy(isRateBottomSheetVisible = isRateBottomSheetVisible.not()) } else updateState { copy( isGuestUserBottomSheetVisible = isGuestUserBottomSheetVisible.not(), isGuestUser = true ) - } }, - onError = { errorState -> - updateState { copy(error = errorState) } - } + onError = ::setErrorState ) } @@ -126,10 +123,8 @@ class TvShowDetailsViewModel @Inject constructor( onSuccess = { updateState { copy( - selectedRating = rating, isRateBottomSheetVisible = false, isSuccessfullyRated = true, - isRated = true ) } }, @@ -141,41 +136,30 @@ class TvShowDetailsViewModel @Inject constructor( ) } }, - onCompleted = { updateState { copy(isLoading = false) } }, + onCompleted = { setLoadingState(false) }, ) } override fun onLoginClick() = emitEffect(TvShowDetailsEffect.OnLoginNavigation) - override fun onBackClicked() { - emitEffect(TvShowDetailsEffect.NavigateBack) - } + override fun onBackClicked() = emitEffect(TvShowDetailsEffect.NavigateBack) + private fun initializeGetImagesData() { tryToExecute( - block = { - getTvShowUseCase.getImagesTvShowById(tvShowId) - }, - onStart = { updateState { copy(isLoading = true) } }, - onSuccess = { images -> - updateState { - copy( - tvImages = images, - ) - } - }, - onError = { error -> updateState { copy(error = error) } }, - onCompleted = { updateState { copy(isLoading = false) } }, + block = { getTvShowUseCase.getImagesTvShowById(tvShowId) }, + onStart = { setLoadingState(true) }, + onSuccess = { images -> updateState { copy(tvImages = images) } }, + onError = ::setErrorState, + onCompleted = { setLoadingState(false) }, ) } private fun initializeGetCastData() { tryToExecute( - block = { - getTvShowUseCase.getTvShowCastById(tvShowId) - }, - onStart = { updateState { copy(isLoading = true) } }, + block = { getTvShowUseCase.getTvShowCastById(tvShowId) }, + onStart = { setLoadingState(true) }, onSuccess = { cast -> updateState { copy( @@ -184,15 +168,8 @@ class TvShowDetailsViewModel @Inject constructor( ) } }, - onError = { errorState -> - updateState { - copy( - isLoading = false, - error = errorState - ) - } - }, - onCompleted = { updateState { copy(isLoading = false) } }, + onError = { errorState -> setErrorState(errorState);setLoadingState(false) }, + onCompleted = { setLoadingState(false) }, ) } @@ -216,7 +193,7 @@ class TvShowDetailsViewModel @Inject constructor( Triple(tvShowDetails, rating, episodes) }, - onStart = { updateState { copy(isLoading = true) } }, + onStart = { setLoadingState(true) }, onSuccess = { (tvShowDetails, rating, episodes) -> updateState { copy( @@ -250,10 +227,8 @@ class TvShowDetailsViewModel @Inject constructor( ) ) }, - onError = { errorState -> - updateState { copy(error = errorState) } - }, - onCompleted = { updateState { copy(isLoading = false) } }, + onError = { errorState -> updateState { copy(error = errorState) } }, + onCompleted = { setLoadingState(false) }, ) } @@ -264,4 +239,8 @@ class TvShowDetailsViewModel @Inject constructor( manageRecentViewedUseCase.addToRecentViewed(tvShow) private fun clearRatedState() = updateState { copy(isSuccessfullyRated = null) } + + private fun setLoadingState(loading: Boolean?) = updateState { copy(isLoading = loading ?: false) } + + private fun setErrorState(errorState: ErrorState) = updateState { copy(error = errorState) } } 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 ab3f4ba21..b8bff9f00 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 @@ -54,9 +54,9 @@ import com.london.presentation.feature.home.upcoming.UpcomingMovieItem import com.london.presentation.feature.home.upcoming.UpcomingSectionTitle import com.london.presentation.feature.home.upcoming.UpcomingStickyHeader import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.CarousalShimmerEffect import com.london.presentation.shared.bookmarkSheet.BookmarkBottomSheet import com.london.presentation.shared.buildscreen.NetworkErrorScreen +import com.london.presentation.shared.shimmer.CarousalShimmerEffect import com.london.presentation.utils.Listen import com.london.presentation.utils.gridColumns import com.london.presentation.utils.navBarBottomPadding 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 2a6f7d8e4..e06d96759 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 @@ -17,8 +17,6 @@ import com.london.presentation.shared.base.ErrorState import com.london.presentation.shared.base.createPagingSourceFlow import com.london.presentation.shared.genre.MovieGenreUi import com.london.presentation.shared.genre.toDomain -import com.london.presentation.utils.toPopularUiMedia -import com.london.presentation.utils.toUiMedia import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow diff --git a/presentation/src/main/java/com/london/presentation/feature/home/continuewatching/ContinueWatchingViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/home/continuewatching/ContinueWatchingViewModel.kt index d3e639295..5c9d8d4d9 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/continuewatching/ContinueWatchingViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/continuewatching/ContinueWatchingViewModel.kt @@ -113,5 +113,4 @@ class ContinueWatchingViewModel @Inject constructor( updateState { copy(movies = movies, tvSeries = tvSeries) } private fun setLoadingState(isLoading: Boolean) = updateState { copy(isLoading = isLoading) } - } diff --git a/presentation/src/main/java/com/london/presentation/utils/homeMapper.kt b/presentation/src/main/java/com/london/presentation/feature/home/homeMapper.kt similarity index 93% rename from presentation/src/main/java/com/london/presentation/utils/homeMapper.kt rename to presentation/src/main/java/com/london/presentation/feature/home/homeMapper.kt index 0dd1cb854..a54b4dc7b 100644 --- a/presentation/src/main/java/com/london/presentation/utils/homeMapper.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/homeMapper.kt @@ -1,11 +1,10 @@ -package com.london.presentation.utils +package com.london.presentation.feature.home import com.london.domain.entity.movie.Movie import com.london.domain.entity.popular.PopularMedia import com.london.domain.entity.shared.MediaType import com.london.domain.entity.toprated.TopRatedMedia import com.london.domain.entity.tvshow.TvShow -import com.london.presentation.feature.home.HomeUiMedia import com.london.presentation.feature.home.popular.PopularUiMedia @JvmName("topRatedMovieToUiMedia") diff --git a/presentation/src/main/java/com/london/presentation/feature/home/popular/PopularSection.kt b/presentation/src/main/java/com/london/presentation/feature/home/popular/PopularSection.kt index 471deb141..f58c3f7b4 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/popular/PopularSection.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/popular/PopularSection.kt @@ -48,7 +48,7 @@ import com.london.presentation.feature.home.popular.PopularSection.SCALE_SIDE_CA import com.london.presentation.feature.home.popular.PopularSection.TRANSFORM_ORIGIN_X import com.london.presentation.feature.home.popular.PopularSection.TRANSFORM_ORIGIN_Y import com.london.presentation.shared.HomeCard -import com.london.presentation.shared.RatingItem +import com.london.presentation.shared.item.RatingItem import com.london.presentation.utils.toLocalizedNumbers import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay diff --git a/presentation/src/main/java/com/london/presentation/feature/home/trending/movie/TrendingMoviesScreen.kt b/presentation/src/main/java/com/london/presentation/feature/home/trending/movie/TrendingMoviesScreen.kt index b17a82b50..72863d173 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/trending/movie/TrendingMoviesScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/trending/movie/TrendingMoviesScreen.kt @@ -3,7 +3,6 @@ package com.london.presentation.feature.home.trending.movie import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -26,10 +25,10 @@ import com.london.designsystem.component.TopBar import com.london.designsystem.theme.NovixTheme import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.GenresSection import com.london.presentation.shared.bookmarkSheet.BookmarkBottomSheet import com.london.presentation.shared.buildscreen.BuildScreen import com.london.presentation.shared.container.MediaLazyVerticalGrid +import com.london.presentation.shared.genre.GenresSection import com.london.presentation.utils.Listen import com.london.presentation.utils.detailsTopBar diff --git a/presentation/src/main/java/com/london/presentation/feature/home/trending/tvshow/TrendingTvShowsScreen.kt b/presentation/src/main/java/com/london/presentation/feature/home/trending/tvshow/TrendingTvShowsScreen.kt index f1cb8598a..03720afe3 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/trending/tvshow/TrendingTvShowsScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/trending/tvshow/TrendingTvShowsScreen.kt @@ -25,9 +25,9 @@ import com.london.designsystem.component.TopBar import com.london.designsystem.theme.NovixTheme import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.GenresSection import com.london.presentation.shared.buildscreen.BuildScreen import com.london.presentation.shared.container.MediaLazyVerticalGrid +import com.london.presentation.shared.genre.GenresSection import com.london.presentation.utils.Listen import com.london.presentation.utils.detailsTopBar diff --git a/presentation/src/main/java/com/london/presentation/feature/home/upcoming/UpcomingSection.kt b/presentation/src/main/java/com/london/presentation/feature/home/upcoming/UpcomingSection.kt index 62c6d6a37..e2f95eacb 100644 --- a/presentation/src/main/java/com/london/presentation/feature/home/upcoming/UpcomingSection.kt +++ b/presentation/src/main/java/com/london/presentation/feature/home/upcoming/UpcomingSection.kt @@ -25,9 +25,9 @@ import com.london.domain.entity.movie.UpComingMovie import com.london.presentation.R import com.london.presentation.feature.home.HomeScreenContract import com.london.presentation.feature.home.HomeScreenUiState -import com.london.presentation.shared.GenresSection import com.london.presentation.shared.HomeCard -import com.london.presentation.shared.ShimmerMovieCard +import com.london.presentation.shared.genre.GenresSection +import com.london.presentation.shared.shimmer.ShimmerMovieCard @Composable fun UpcomingSectionTitle(isLoading: Boolean) { diff --git a/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsScreen.kt b/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsScreen.kt index 52fb49629..82c10c272 100644 --- a/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsScreen.kt @@ -44,10 +44,10 @@ import com.london.designsystem.theme.NovixTheme import com.london.domain.entity.review.Review import com.london.presentation.R import com.london.presentation.shared.BackgroundGradient -import com.london.presentation.shared.ConditionalText -import com.london.presentation.shared.ImageView -import com.london.presentation.shared.RatingItem import com.london.presentation.shared.buildscreen.BuildScreen +import com.london.presentation.shared.item.ImageView +import com.london.presentation.shared.item.RatingItem +import com.london.presentation.shared.text.ConditionalText import com.london.presentation.utils.Listen import com.london.presentation.utils.isLoading import com.london.presentation.utils.reverseDateFormat diff --git a/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsViewModel.kt index deec6cbf6..80eb20cb2 100644 --- a/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/reviews/ReviewsViewModel.kt @@ -11,6 +11,7 @@ import com.london.presentation.navigation.getArgs import com.london.presentation.shared.base.BaseViewModel import com.london.presentation.shared.base.ErrorState import com.london.presentation.shared.base.createPagingSourceFlow +import com.london.presentation.utils.orZero import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import javax.inject.Inject @@ -24,56 +25,39 @@ class ReviewsViewModel @Inject constructor( private val args = savedStateHandle.getArgs() private val mediaType: MediaType = args?.mediaType ?: MediaType.Movie - private val mediaId: Int = args?.mediaId ?: 0 + private val mediaId: Int = args?.mediaId.orZero() init { loadReviews() } override fun onRetry() { - updateState { copy(error = null) } + setErrorState(null) loadReviews() } - override fun onBackClicked() { - emitEffect(ReviewEffect.NavigateBack) - } + override fun onBackClicked() = emitEffect(ReviewEffect.NavigateBack) + private fun loadReviews() { tryToExecute( - block = { - createPagingSourceFlow { _, pageNumber -> - fetchReviewsByMediaType(pageNumber) - } - }, - onStart = { - updateState { copy(isLoading = true) } - }, + block = { createPagingSourceFlow { _, pageNumber -> fetchReviewsByMediaType(pageNumber) } }, + onStart = { setLoadingState(true) }, onSuccess = ::handleLoadReviewsSuccess, onError = ::handleLoadReviewsError, - onCompleted = { - updateState { copy(isLoading = false) } - } + onCompleted = { setLoadingState(false) } ) } private fun handleLoadReviewsSuccess(pagingFlow: Flow>) { - updateState { - copy( - reviews = pagingFlow, - isLoading = false, - error = null - ) - } + setLoadingState(false) + setErrorState(null) + updateState { copy(reviews = pagingFlow) } } private fun handleLoadReviewsError(errorState: ErrorState) { - updateState { - copy( - error = errorState, - isLoading = false - ) - } + setErrorState(errorState) + setLoadingState(false) } private suspend fun fetchReviewsByMediaType(pageNumber: Int) = when (mediaType) { @@ -81,4 +65,7 @@ class ReviewsViewModel @Inject constructor( MediaType.TvShow -> getTvShowUseCase.getTvShowReviews(mediaId, pageNumber) } + private fun setLoadingState(loading: Boolean) = updateState { copy(isLoading = loading) } + + private fun setErrorState(errorState: ErrorState?) = updateState { copy(error = errorState) } } diff --git a/presentation/src/main/java/com/london/presentation/feature/search/SearchScreen.kt b/presentation/src/main/java/com/london/presentation/feature/search/SearchScreen.kt index b3f5367eb..825f4834f 100644 --- a/presentation/src/main/java/com/london/presentation/feature/search/SearchScreen.kt +++ b/presentation/src/main/java/com/london/presentation/feature/search/SearchScreen.kt @@ -71,7 +71,6 @@ import com.london.presentation.shared.container.MediaLazyVerticalGrid import com.london.presentation.utils.Listen import com.london.presentation.utils.ResultOrEmpty import com.london.presentation.utils.navBarBottomPadding -import com.london.presentation.utils.toRecentViewed @Composable fun SearchScreen( diff --git a/presentation/src/main/java/com/london/presentation/feature/search/SearchViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/search/SearchViewModel.kt index b48d006da..dafecc121 100644 --- a/presentation/src/main/java/com/london/presentation/feature/search/SearchViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/search/SearchViewModel.kt @@ -64,9 +64,7 @@ class SearchViewModel @Inject constructor( ) } - override fun updateSearchState(updater: SearchUiState.() -> SearchUiState) { - updateState(updater) - } + override fun updateSearchState(updater: SearchUiState.() -> SearchUiState) = updateState(updater) override fun onSearchQueryChange(newValue: TextFieldValue) { updateState { copy(searchQuery = newValue) } @@ -358,5 +356,4 @@ class SearchViewModel @Inject constructor( category = state.value.selectedCategory ) } - } diff --git a/presentation/src/main/java/com/london/presentation/utils/mediaToRecentViewedMapper.kt b/presentation/src/main/java/com/london/presentation/feature/search/searchMapper.kt similarity index 83% rename from presentation/src/main/java/com/london/presentation/utils/mediaToRecentViewedMapper.kt rename to presentation/src/main/java/com/london/presentation/feature/search/searchMapper.kt index 061448b6b..fd2d8fdcc 100644 --- a/presentation/src/main/java/com/london/presentation/utils/mediaToRecentViewedMapper.kt +++ b/presentation/src/main/java/com/london/presentation/feature/search/searchMapper.kt @@ -1,12 +1,11 @@ -package com.london.presentation.utils +package com.london.presentation.feature.search import com.london.domain.entity.movie.Movie import com.london.domain.entity.recent.RecentViewed import com.london.domain.entity.shared.MediaType import com.london.domain.entity.tvshow.TvShow -fun Movie.toRecentViewed(): RecentViewed = - RecentViewed( +fun Movie.toRecentViewed(): RecentViewed = RecentViewed( id = this.id, imageUrl = this.posterUrl, type = MediaType.Movie, diff --git a/presentation/src/main/java/com/london/presentation/feature/welcome/onboarding/OnboardingViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/welcome/onboarding/OnboardingViewModel.kt index 6e949701b..2e4fd5c43 100644 --- a/presentation/src/main/java/com/london/presentation/feature/welcome/onboarding/OnboardingViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/welcome/onboarding/OnboardingViewModel.kt @@ -16,9 +16,8 @@ class OnboardingViewModel @Inject constructor( private val appPreferencesService: AppPreferencesService ) : BaseViewModel(OnboardingUiState()) { - fun onPageChanged(page: Int) { - updateState { copy(currentPage = page) } - } + fun onPageChanged(page: Int) = updateState { copy(currentPage = page) } + fun scrollToPage(pagerState: PagerState, targetPage: Int, scope: CoroutineScope) { scope.launch { @@ -40,11 +39,9 @@ class OnboardingViewModel @Inject constructor( fun scrollNext(pagerState: PagerState, scope: CoroutineScope) { val nextPage = pagerState.currentPage + 1 - if (nextPage <= pagerState.pageCount - 1) { + if (nextPage <= pagerState.pageCount - 1) scrollToPage(pagerState = pagerState, targetPage = nextPage, scope = scope) - } else { - navigateToWelcome() - } + else navigateToWelcome() } fun navigateToWelcome() { diff --git a/presentation/src/main/java/com/london/presentation/feature/welcome/splash/SplashViewModel.kt b/presentation/src/main/java/com/london/presentation/feature/welcome/splash/SplashViewModel.kt index 53e59fcb8..3bf4cf4a6 100644 --- a/presentation/src/main/java/com/london/presentation/feature/welcome/splash/SplashViewModel.kt +++ b/presentation/src/main/java/com/london/presentation/feature/welcome/splash/SplashViewModel.kt @@ -32,7 +32,6 @@ class SplashViewModel @Inject constructor( authenticationUseCase.isLoggedIn() -> SplashEffect.Home else -> SplashEffect.Welcome } - _effect.emit(destination) } } diff --git a/presentation/src/main/java/com/london/presentation/navigation/LocalNavController.kt b/presentation/src/main/java/com/london/presentation/navigation/LocalNavController.kt index 33a7412f2..3787851cd 100644 --- a/presentation/src/main/java/com/london/presentation/navigation/LocalNavController.kt +++ b/presentation/src/main/java/com/london/presentation/navigation/LocalNavController.kt @@ -3,6 +3,5 @@ package com.london.presentation.navigation import androidx.compose.runtime.staticCompositionLocalOf import androidx.navigation.NavHostController -val LocalNavController = staticCompositionLocalOf { - error("Nav controller is not provided") -} +val LocalNavController = + staticCompositionLocalOf { error("Nav controller is not provided") } diff --git a/presentation/src/main/java/com/london/presentation/shared/CustomBackDropImagePager.kt b/presentation/src/main/java/com/london/presentation/shared/CustomBackDropImagePager.kt index e670aa9c1..1a090b729 100644 --- a/presentation/src/main/java/com/london/presentation/shared/CustomBackDropImagePager.kt +++ b/presentation/src/main/java/com/london/presentation/shared/CustomBackDropImagePager.kt @@ -24,6 +24,7 @@ import com.london.designsystem.component.UnSuitableEye import com.london.designsystem.component.button.ErrorImage import com.london.designsystem.theme.NovixTheme import com.london.presentation.R +import com.london.presentation.shared.item.ImageView import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay import kotlinx.coroutines.isActive diff --git a/presentation/src/main/java/com/london/presentation/shared/HomeCard.kt b/presentation/src/main/java/com/london/presentation/shared/HomeCard.kt index 4069d73ce..c138e1a33 100644 --- a/presentation/src/main/java/com/london/presentation/shared/HomeCard.kt +++ b/presentation/src/main/java/com/london/presentation/shared/HomeCard.kt @@ -24,6 +24,8 @@ import com.london.designsystem.component.UnSuitableEye import com.london.designsystem.component.button.ErrorImage import com.london.designsystem.theme.NovixTheme import com.london.designsystem.theme.ThemePreviews +import com.london.presentation.shared.item.ImageView +import com.london.presentation.shared.shimmer.ShimmerMovieCard @Composable fun HomeCard( diff --git a/presentation/src/main/java/com/london/presentation/shared/TriangleBlurredShape.kt b/presentation/src/main/java/com/london/presentation/shared/TriangleBlurredShape.kt deleted file mode 100644 index e5a7a08d8..000000000 --- a/presentation/src/main/java/com/london/presentation/shared/TriangleBlurredShape.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.london.presentation.shared - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.blur -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.unit.dp -import com.london.designsystem.theme.NovixTheme - -@Composable -fun TriangleBlurredShape() { - val triangleBackgroundColor: Color = NovixTheme.colors.primary.copy(alpha = 0.08f) - Box( - modifier = Modifier - .fillMaxSize() - .blur(150.dp) - .drawWithContent { - val path = Path().apply { - moveTo(0f, 0f) - lineTo(size.width * 0.7f, 0f) - lineTo(0f, size.height * 0.25f) - close() - } - drawPath( - path, - color = triangleBackgroundColor, - ) - drawContent() - } - ) -} diff --git a/presentation/src/main/java/com/london/presentation/shared/container/ActorLazyVerticalColumn.kt b/presentation/src/main/java/com/london/presentation/shared/container/ActorLazyVerticalColumn.kt index 19c288be3..623a97c73 100644 --- a/presentation/src/main/java/com/london/presentation/shared/container/ActorLazyVerticalColumn.kt +++ b/presentation/src/main/java/com/london/presentation/shared/container/ActorLazyVerticalColumn.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.paging.compose.LazyPagingItems import com.london.domain.entity.actor.Actor -import com.london.presentation.shared.ActorItem +import com.london.presentation.shared.item.ActorItem @Composable fun ActorLazyVerticalColumn( diff --git a/presentation/src/main/java/com/london/presentation/shared/container/MediaLazyGridWithFilter.kt b/presentation/src/main/java/com/london/presentation/shared/container/MediaLazyGridWithFilter.kt index cb7b9e47b..b5cd9dd7c 100644 --- a/presentation/src/main/java/com/london/presentation/shared/container/MediaLazyGridWithFilter.kt +++ b/presentation/src/main/java/com/london/presentation/shared/container/MediaLazyGridWithFilter.kt @@ -10,10 +10,10 @@ import com.london.designsystem.theme.NovixTheme import com.london.designsystem.theme.ThemePreviews import com.london.domain.entity.genre.MovieGenre import com.london.domain.entity.movie.Movie -import com.london.presentation.shared.EmptyGenreLayout -import com.london.presentation.shared.MediaGenreFilters +import com.london.presentation.shared.genre.MediaGenreFilters import com.london.presentation.shared.genre.MovieGenreUi import com.london.presentation.shared.genre.TvShowGenreUi +import com.london.presentation.shared.layout.EmptyGenreLayout @Composable fun MediaLazyGridWithFilter( diff --git a/presentation/src/main/java/com/london/presentation/shared/ContentRestrictionLocal.kt b/presentation/src/main/java/com/london/presentation/shared/contentRestriction/ContentRestrictionLocal.kt similarity index 79% rename from presentation/src/main/java/com/london/presentation/shared/ContentRestrictionLocal.kt rename to presentation/src/main/java/com/london/presentation/shared/contentRestriction/ContentRestrictionLocal.kt index 053189415..f9348217f 100644 --- a/presentation/src/main/java/com/london/presentation/shared/ContentRestrictionLocal.kt +++ b/presentation/src/main/java/com/london/presentation/shared/contentRestriction/ContentRestrictionLocal.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.contentRestriction import androidx.compose.runtime.compositionLocalOf import com.london.domain.entity.contentrestriction.ContentRestrictionLevel diff --git a/presentation/src/main/java/com/london/presentation/shared/ContentRestrictionProvider.kt b/presentation/src/main/java/com/london/presentation/shared/contentRestriction/ContentRestrictionProvider.kt similarity index 90% rename from presentation/src/main/java/com/london/presentation/shared/ContentRestrictionProvider.kt rename to presentation/src/main/java/com/london/presentation/shared/contentRestriction/ContentRestrictionProvider.kt index 673087bac..98a9c0188 100644 --- a/presentation/src/main/java/com/london/presentation/shared/ContentRestrictionProvider.kt +++ b/presentation/src/main/java/com/london/presentation/shared/contentRestriction/ContentRestrictionProvider.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.contentRestriction import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState diff --git a/presentation/src/main/java/com/london/presentation/shared/GenresSection.kt b/presentation/src/main/java/com/london/presentation/shared/genre/GenresSection.kt similarity index 97% rename from presentation/src/main/java/com/london/presentation/shared/GenresSection.kt rename to presentation/src/main/java/com/london/presentation/shared/genre/GenresSection.kt index 1c89cd59f..008cda2e4 100644 --- a/presentation/src/main/java/com/london/presentation/shared/GenresSection.kt +++ b/presentation/src/main/java/com/london/presentation/shared/genre/GenresSection.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.genre import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box diff --git a/presentation/src/main/java/com/london/presentation/shared/MediaGenreFilters.kt b/presentation/src/main/java/com/london/presentation/shared/genre/MediaGenreFilters.kt similarity index 94% rename from presentation/src/main/java/com/london/presentation/shared/MediaGenreFilters.kt rename to presentation/src/main/java/com/london/presentation/shared/genre/MediaGenreFilters.kt index 6e5fb76a9..6cddd1908 100644 --- a/presentation/src/main/java/com/london/presentation/shared/MediaGenreFilters.kt +++ b/presentation/src/main/java/com/london/presentation/shared/genre/MediaGenreFilters.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.genre import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues @@ -15,8 +15,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.london.designsystem.component.NovixChip import com.london.designsystem.theme.ThemePreviews -import com.london.presentation.shared.genre.MovieGenreUi -import com.london.presentation.shared.genre.TvShowGenreUi @Composable fun MediaGenreFilters( diff --git a/presentation/src/main/java/com/london/presentation/shared/ActorItem.kt b/presentation/src/main/java/com/london/presentation/shared/item/ActorItem.kt similarity index 99% rename from presentation/src/main/java/com/london/presentation/shared/ActorItem.kt rename to presentation/src/main/java/com/london/presentation/shared/item/ActorItem.kt index b46833805..c47ecdbca 100644 --- a/presentation/src/main/java/com/london/presentation/shared/ActorItem.kt +++ b/presentation/src/main/java/com/london/presentation/shared/item/ActorItem.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.item import androidx.compose.foundation.border import androidx.compose.foundation.clickable diff --git a/presentation/src/main/java/com/london/presentation/shared/CategoryItem.kt b/presentation/src/main/java/com/london/presentation/shared/item/CategoryItem.kt similarity index 99% rename from presentation/src/main/java/com/london/presentation/shared/CategoryItem.kt rename to presentation/src/main/java/com/london/presentation/shared/item/CategoryItem.kt index c99a6a3f1..38cdba328 100644 --- a/presentation/src/main/java/com/london/presentation/shared/CategoryItem.kt +++ b/presentation/src/main/java/com/london/presentation/shared/item/CategoryItem.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.item import androidx.compose.foundation.background import androidx.compose.foundation.border diff --git a/presentation/src/main/java/com/london/presentation/shared/ImageView.kt b/presentation/src/main/java/com/london/presentation/shared/item/ImageView.kt similarity index 93% rename from presentation/src/main/java/com/london/presentation/shared/ImageView.kt rename to presentation/src/main/java/com/london/presentation/shared/item/ImageView.kt index 946523c1b..9dd7787c1 100644 --- a/presentation/src/main/java/com/london/presentation/shared/ImageView.kt +++ b/presentation/src/main/java/com/london/presentation/shared/item/ImageView.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.item import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -7,6 +7,7 @@ import androidx.compose.ui.layout.ContentScale import com.london.domain.entity.contentrestriction.ContentRestrictionLevel import com.london.imageharamblur.ui.ImageFilterConfig import com.london.imageharamblur.ui.ImageViewFilter +import com.london.presentation.shared.contentRestriction.LocalContentRestrictionLevel @Composable fun ImageView( diff --git a/presentation/src/main/java/com/london/presentation/shared/RatingItem.kt b/presentation/src/main/java/com/london/presentation/shared/item/RatingItem.kt similarity index 96% rename from presentation/src/main/java/com/london/presentation/shared/RatingItem.kt rename to presentation/src/main/java/com/london/presentation/shared/item/RatingItem.kt index cb055266d..54686badc 100644 --- a/presentation/src/main/java/com/london/presentation/shared/RatingItem.kt +++ b/presentation/src/main/java/com/london/presentation/shared/item/RatingItem.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.item import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/presentation/src/main/java/com/london/presentation/shared/EmptyGenreLayout.kt b/presentation/src/main/java/com/london/presentation/shared/layout/EmptyGenreLayout.kt similarity index 97% rename from presentation/src/main/java/com/london/presentation/shared/EmptyGenreLayout.kt rename to presentation/src/main/java/com/london/presentation/shared/layout/EmptyGenreLayout.kt index c0ff9bf5e..06a3a0c69 100644 --- a/presentation/src/main/java/com/london/presentation/shared/EmptyGenreLayout.kt +++ b/presentation/src/main/java/com/london/presentation/shared/layout/EmptyGenreLayout.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.layout import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement diff --git a/presentation/src/main/java/com/london/presentation/shared/FooterSection.kt b/presentation/src/main/java/com/london/presentation/shared/section/FooterSection.kt similarity index 98% rename from presentation/src/main/java/com/london/presentation/shared/FooterSection.kt rename to presentation/src/main/java/com/london/presentation/shared/section/FooterSection.kt index 6bf951df2..0b3b00987 100644 --- a/presentation/src/main/java/com/london/presentation/shared/FooterSection.kt +++ b/presentation/src/main/java/com/london/presentation/shared/section/FooterSection.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.section import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.animateDpAsState diff --git a/presentation/src/main/java/com/london/presentation/shared/CarousalShimmer.kt b/presentation/src/main/java/com/london/presentation/shared/shimmer/CarousalShimmer.kt similarity index 97% rename from presentation/src/main/java/com/london/presentation/shared/CarousalShimmer.kt rename to presentation/src/main/java/com/london/presentation/shared/shimmer/CarousalShimmer.kt index 68f65fa53..dc04a5099 100644 --- a/presentation/src/main/java/com/london/presentation/shared/CarousalShimmer.kt +++ b/presentation/src/main/java/com/london/presentation/shared/shimmer/CarousalShimmer.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.shimmer import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box diff --git a/presentation/src/main/java/com/london/presentation/shared/shimmerMovieCard.kt b/presentation/src/main/java/com/london/presentation/shared/shimmer/shimmerMovieCard.kt similarity index 92% rename from presentation/src/main/java/com/london/presentation/shared/shimmerMovieCard.kt rename to presentation/src/main/java/com/london/presentation/shared/shimmer/shimmerMovieCard.kt index 93f9812ee..eb2ea0de8 100644 --- a/presentation/src/main/java/com/london/presentation/shared/shimmerMovieCard.kt +++ b/presentation/src/main/java/com/london/presentation/shared/shimmer/shimmerMovieCard.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.shimmer import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height diff --git a/presentation/src/main/java/com/london/presentation/shared/ConditionalText.kt b/presentation/src/main/java/com/london/presentation/shared/text/ConditionalText.kt similarity index 98% rename from presentation/src/main/java/com/london/presentation/shared/ConditionalText.kt rename to presentation/src/main/java/com/london/presentation/shared/text/ConditionalText.kt index 93f290906..e9a444fd5 100644 --- a/presentation/src/main/java/com/london/presentation/shared/ConditionalText.kt +++ b/presentation/src/main/java/com/london/presentation/shared/text/ConditionalText.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.text import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource diff --git a/presentation/src/main/java/com/london/presentation/shared/TextWithIcon.kt b/presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt similarity index 97% rename from presentation/src/main/java/com/london/presentation/shared/TextWithIcon.kt rename to presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt index 62c625880..a85b30ef9 100644 --- a/presentation/src/main/java/com/london/presentation/shared/TextWithIcon.kt +++ b/presentation/src/main/java/com/london/presentation/shared/text/TextWithIcon.kt @@ -1,4 +1,4 @@ -package com.london.presentation.shared +package com.london.presentation.shared.text import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement diff --git a/presentation/src/main/java/com/london/presentation/utils/searchUtils.kt b/presentation/src/main/java/com/london/presentation/utils/ResultOrEmpty.kt similarity index 66% rename from presentation/src/main/java/com/london/presentation/utils/searchUtils.kt rename to presentation/src/main/java/com/london/presentation/utils/ResultOrEmpty.kt index 01030cc2c..79097005b 100644 --- a/presentation/src/main/java/com/london/presentation/utils/searchUtils.kt +++ b/presentation/src/main/java/com/london/presentation/utils/ResultOrEmpty.kt @@ -8,10 +8,4 @@ fun ResultOrEmpty( otherItems: List? = null, emptyContent: @Composable () -> Unit, content: @Composable () -> Unit -) { - if (items.isEmpty() && otherItems.isNullOrEmpty()) { - emptyContent() - } else { - content() - } -} +) = if (items.isEmpty() && otherItems.isNullOrEmpty()) emptyContent() else content() diff --git a/presentation/src/main/java/com/london/presentation/utils/extensions.kt b/presentation/src/main/java/com/london/presentation/utils/extensions.kt deleted file mode 100644 index cd2ef30fd..000000000 --- a/presentation/src/main/java/com/london/presentation/utils/extensions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.london.presentation.utils - -import androidx.compose.ui.platform.UriHandler - -fun UriHandler.openUrl(url: String) = runCatching { - openUri(url) -} diff --git a/presentation/src/main/java/com/london/presentation/localization/LocaleUtils.kt b/presentation/src/main/java/com/london/presentation/utils/locale.kt similarity index 89% rename from presentation/src/main/java/com/london/presentation/localization/LocaleUtils.kt rename to presentation/src/main/java/com/london/presentation/utils/locale.kt index 1073ffb8a..45ef2dd34 100644 --- a/presentation/src/main/java/com/london/presentation/localization/LocaleUtils.kt +++ b/presentation/src/main/java/com/london/presentation/utils/locale.kt @@ -1,4 +1,4 @@ -package com.london.presentation.localization +package com.london.presentation.utils import android.content.Context import android.content.res.Configuration diff --git a/presentation/src/main/java/com/london/presentation/utils/modifierExtensions.kt b/presentation/src/main/java/com/london/presentation/utils/modifierExtensions.kt index 3a9fe9f8b..5e5440058 100644 --- a/presentation/src/main/java/com/london/presentation/utils/modifierExtensions.kt +++ b/presentation/src/main/java/com/london/presentation/utils/modifierExtensions.kt @@ -26,17 +26,6 @@ fun Modifier.offsetLayout() = layout { measurable, constraints -> } } -fun Modifier.episodeLayout() = layout { measurable, constraints -> - val placeable = measurable.measure(constraints) - - val yOffsetPx = with(this) { 44.dp.roundToPx() } - val adjustedHeight = (placeable.height - yOffsetPx).coerceAtLeast(0) - - layout(placeable.width, adjustedHeight) { - placeable.placeRelative(0, -yOffsetPx) - } -} - @Composable fun Modifier.headerDetailsCard() = fillMaxWidth() .episodeLayout() @@ -59,3 +48,14 @@ fun Modifier.detailsTopBar(backgroundAlpha: Float) = fillMaxWidth() fun Modifier.navBarBottomPadding() = padding(bottom = 70.dp).padding( bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() ) + +private fun Modifier.episodeLayout() = layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + + val yOffsetPx = with(this) { 44.dp.roundToPx() } + val adjustedHeight = (placeable.height - yOffsetPx).coerceAtLeast(0) + + layout(placeable.width, adjustedHeight) { + placeable.placeRelative(0, -yOffsetPx) + } +} diff --git a/presentation/src/main/java/com/london/presentation/utils/utils.kt b/presentation/src/main/java/com/london/presentation/utils/utils.kt index 307d04bd6..a3f792a67 100644 --- a/presentation/src/main/java/com/london/presentation/utils/utils.kt +++ b/presentation/src/main/java/com/london/presentation/utils/utils.kt @@ -3,6 +3,7 @@ package com.london.presentation.utils import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalWindowInfo +import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems @@ -112,13 +113,9 @@ fun String?.getValueOf(key: String): String? { return this } -fun String.isNotZeroRate() = runCatching { - this != "0.0" && this != "٠٫٠" -}.getOrDefault(false) +fun String.isNotZeroRate() = runCatching { this != "0.0" && this != "٠٫٠" }.getOrDefault(false) -fun Double.isNotZeroRate() = runCatching { - this != 0.0 -}.getOrDefault(false) +fun Double.isNotZeroRate() = runCatching { this != 0.0 }.getOrDefault(false) @Composable fun gridColumns(itemWidth: Int = 158): Int = runCatching { @@ -128,9 +125,7 @@ fun gridColumns(itemWidth: Int = 158): Int = runCatching { ((screenWidth - screenPaddingPx) / itemWidthPx).toInt().coerceAtLeast(2) }.getOrDefault(1) -fun Int?.isNotNull(): Boolean { - return this != null -} +fun Int?.isNotNull(): Boolean = this != null fun shouldShowLoading( isLoading: Boolean, @@ -138,3 +133,7 @@ fun shouldShowLoading( pagingFlow: LazyPagingItems<*>? = null ): Boolean = isLoading || (handlePagingLoadingAutomatically && pagingFlow?.loadState?.refresh is LoadState.Loading) + +fun Int?.orZero(): Int = this ?: 0 + +fun UriHandler.openUrl(url: String) = runCatching { openUri(url) } diff --git a/presentation/src/test/java/com/london/presentation/feature/account/account/AccountViewModelTest.kt b/presentation/src/test/java/com/london/presentation/feature/account/account/AccountViewModelTest.kt index dd5ab4d0a..d58af694a 100644 --- a/presentation/src/test/java/com/london/presentation/feature/account/account/AccountViewModelTest.kt +++ b/presentation/src/test/java/com/london/presentation/feature/account/account/AccountViewModelTest.kt @@ -27,7 +27,7 @@ import org.junit.Before import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) -class AccountViewModelTest { +class AccountViewModelTest { private lateinit var appPreferencesService: AppPreferencesService private lateinit var authenticationUseCase: AuthenticationUseCase diff --git a/presentation/src/test/java/com/london/presentation/feature/account/rating/MyRatingViewModelTest.kt b/presentation/src/test/java/com/london/presentation/feature/account/rating/MyRatingViewModelTest.kt index ec9193169..50b9cff84 100644 --- a/presentation/src/test/java/com/london/presentation/feature/account/rating/MyRatingViewModelTest.kt +++ b/presentation/src/test/java/com/london/presentation/feature/account/rating/MyRatingViewModelTest.kt @@ -215,99 +215,6 @@ class MyRatingViewModelTest { } } - @Test - fun `onItemClick should emit movie navigation effect when movie is found`() = runTest(mainDispatcher) { - // Given - val manageRatingUseCase = mockk(relaxed = true) - coEvery { manageRatingUseCase.getRatedMediaSorted() } returns createMockRatedMedia() - val viewModel = MyRatingViewModel(manageRatingUseCase = manageRatingUseCase) - val movieId = 1 - val mockMovie = RatedMedia( - id = movieId, - title = "Test Movie", - posterPath = "/test.jpg", - rating = 8, - mediaType = MediaType.Movie - ) - coEvery { manageRatingUseCase.getRatedMediaById(movieId) } returns mockMovie - - advanceUntilIdle() - - // When & Then - viewModel.effect.test { - viewModel.onItemClick(movieId) - assertThat(awaitItem()).isEqualTo(MyRatingEffect.MovieDetailsNavigation(movieId)) - cancelAndIgnoreRemainingEvents() - } - } - - @Test - fun `onItemClick should emit tv show navigation effect when tv show is found`() = runTest(mainDispatcher) { - // Given - val manageRatingUseCase = mockk(relaxed = true) - coEvery { manageRatingUseCase.getRatedMediaSorted() } returns createMockRatedMedia() - val viewModel = MyRatingViewModel(manageRatingUseCase = manageRatingUseCase) - val tvShowId = 3 - val mockTvShow = RatedMedia( - id = tvShowId, - title = "Test TV Show", - posterPath = "/test.jpg", - rating = 9, - mediaType = MediaType.TvShow - ) - coEvery { manageRatingUseCase.getRatedMediaById(tvShowId) } returns mockTvShow - - advanceUntilIdle() - - // When & Then - viewModel.effect.test { - viewModel.onItemClick(tvShowId) - assertThat(awaitItem()).isEqualTo(MyRatingEffect.TvShowDetailsNavigation(tvShowId)) - cancelAndIgnoreRemainingEvents() - } - } - - @Test - fun `onItemClick should not emit effect when media is not found`() = runTest(mainDispatcher) { - // Given - val manageRatingUseCase = mockk(relaxed = true) - coEvery { manageRatingUseCase.getRatedMediaSorted() } returns createMockRatedMedia() - val viewModel = MyRatingViewModel(manageRatingUseCase = manageRatingUseCase) - val nonExistentId = 999 - coEvery { manageRatingUseCase.getRatedMediaById(nonExistentId) } returns null - - advanceUntilIdle() - - // When & Then - viewModel.effect.test { - viewModel.onItemClick(nonExistentId) - expectNoEvents() - } - } - - @Test - fun `onItemClick should handle error when use case throws exception`() = runTest(mainDispatcher) { - // Given - val manageRatingUseCase = mockk(relaxed = true) - coEvery { manageRatingUseCase.getRatedMediaSorted() } returns createMockRatedMedia() - val viewModel = MyRatingViewModel(manageRatingUseCase = manageRatingUseCase) - val mediaId = 1 - coEvery { manageRatingUseCase.getRatedMediaById(mediaId) } throws RuntimeException("Network error") - - advanceUntilIdle() - - // When & Then - viewModel.state.test { - viewModel.onItemClick(mediaId) - var state = awaitItem() - while (state.errorState == null) { - state = awaitItem() - } - assertThat(state.errorState).isNotNull() - cancelAndIgnoreRemainingEvents() - } - } - private fun createMockRatedMedia(): List { return listOf( RatedMedia(