diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/cache/daos/FavoriteMovieDao.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/cache/daos/FavoriteMovieDao.kt index 1c249dc6..c2d3edfa 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/cache/daos/FavoriteMovieDao.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/cache/daos/FavoriteMovieDao.kt @@ -13,10 +13,10 @@ interface FavoriteMovieDao { suspend fun saveFavoriteMovie(movie: MovieDetailsEntity) @Query("SELECT * FROM `Favorite Movies`") - fun getAllFavoriteMovies(): Flow> + fun getAllFavoriteMovies(): Flow?> @Query("SELECT * FROM `Favorite Movies` WHERE id = :id") - suspend fun getFavoriteMovie(id: Int): MovieDetailsEntity + fun getFavoriteMovie(id: Int): Flow @Query("DELETE FROM `Favorite Movies` WHERE id = :id") suspend fun deleteFavoriteMovie(id: Int) @@ -25,5 +25,5 @@ interface FavoriteMovieDao { suspend fun deleteAllFavoriteMovies() @Query("SELECT COUNT() FROM `Favorite Movies` WHERE id = :id") - fun isMovieFavorite(id: Int): Flow + fun isMovieFavorite(id: Int): Flow } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/FavoritesRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/FavoritesRepositoryImpl.kt index d5c174ea..cc899bda 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/FavoritesRepositoryImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/FavoritesRepositoryImpl.kt @@ -11,9 +11,11 @@ class FavoritesRepositoryImpl( private val appDatabase: AppDatabase ) : FavoritesRepository { - override suspend fun getFavouriteMovies(): Flow> { - return appDatabase.favoriteMovieDao().getAllFavoriteMovies().map { - it.map { movieDetail -> movieDetail.toDomain() } + override suspend fun getFavouriteMovies(): Result?>> { + return runCatching { + appDatabase.favoriteMovieDao().getAllFavoriteMovies().map { + it?.map { movieDetail -> movieDetail.toDomain() } + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MovieDetailsRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MovieDetailsRepositoryImpl.kt index e1b69534..25287f11 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MovieDetailsRepositoryImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MovieDetailsRepositoryImpl.kt @@ -11,7 +11,6 @@ import com.vickbt.composeApp.domain.models.Cast import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails import com.vickbt.composeApp.domain.repositories.MovieDetailsRepository -import com.vickbt.composeApp.utils.ResultState import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get @@ -19,72 +18,60 @@ import io.ktor.client.request.parameter import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map class MovieDetailsRepositoryImpl( private val httpClient: HttpClient, private val appDatabase: AppDatabase ) : MovieDetailsRepository { - override suspend fun fetchMovieDetails(movieId: Int): Flow> { - val isMovieCached = isMovieFavorite(movieId = movieId).firstOrNull() + override suspend fun fetchMovieDetails(movieId: Int): Result> { + val isMovieCached = isMovieFavorite(movieId = movieId).getOrDefault(flowOf(false)) + .firstOrNull() return if (isMovieCached == true) { - try { - val cachedFavoriteMovie = getFavoriteMovie(movieId = movieId) - flowOf(ResultState.Success(data = cachedFavoriteMovie)) - } catch (e: Exception) { - flowOf(ResultState.Failure(exception = e)) - } + getFavoriteMovie(movieId = movieId) } else { - flowOf( - safeApiCall { - val response = - httpClient.get(urlString = "movie/$movieId").body() - response.toDomain() - } - ) + safeApiCall { + httpClient.get(urlString = "movie/$movieId").body().toDomain() + } } } - override suspend fun fetchMovieCast(movieId: Int): Flow> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "movie/$movieId/credits").body() - - response.toDomain() - } - ) + override suspend fun fetchMovieCast(movieId: Int): Result> { + return safeApiCall { + httpClient.get(urlString = "movie/$movieId/credits").body().toDomain() + } } override suspend fun fetchSimilarMovies( movieId: Int, page: Int - ): Flow?>> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "movie/$movieId/similar") { - parameter("page", page) - }.body() + ): Result?>> { + return safeApiCall { + val response = httpClient.get(urlString = "movie/$movieId/similar") { + parameter("page", page) + }.body() - response.movies?.map { it.toDomain() } - } - ) + response.movies?.map { it.toDomain() } + } } override suspend fun saveFavoriteMovie(movie: MovieDetails) { - appDatabase.favoriteMovieDao().saveFavoriteMovie(movie = movie.toEntity()) + runCatching { appDatabase.favoriteMovieDao().saveFavoriteMovie(movie = movie.toEntity()) } } - override suspend fun getFavoriteMovie(movieId: Int): MovieDetails { - val favMovieDao = appDatabase.favoriteMovieDao() - return favMovieDao.getFavoriteMovie(id = movieId).toDomain() + override suspend fun getFavoriteMovie(movieId: Int): Result> { + return runCatching { + appDatabase.favoriteMovieDao().getFavoriteMovie(id = movieId).map { it?.toDomain() } + } } override suspend fun deleteFavoriteMovie(movieId: Int) { - appDatabase.favoriteMovieDao().deleteFavoriteMovie(id = movieId) + runCatching { appDatabase.favoriteMovieDao().deleteFavoriteMovie(id = movieId) } } - override suspend fun isMovieFavorite(movieId: Int): Flow { - return appDatabase.favoriteMovieDao().isMovieFavorite(id = movieId) + override suspend fun isMovieFavorite(movieId: Int): Result> { + return runCatching { appDatabase.favoriteMovieDao().isMovieFavorite(id = movieId) } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MoviesRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MoviesRepositoryImpl.kt index 9e3075ad..09c38c90 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MoviesRepositoryImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/MoviesRepositoryImpl.kt @@ -5,67 +5,57 @@ import com.vickbt.composeApp.data.network.models.MovieResultsDto import com.vickbt.composeApp.data.network.utils.safeApiCall import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.repositories.MoviesRepository -import com.vickbt.composeApp.utils.ResultState import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get import io.ktor.client.request.parameter import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf -class MoviesRepositoryImpl constructor( +class MoviesRepositoryImpl( private val httpClient: HttpClient ) : MoviesRepository { - override suspend fun fetchNowPlayingMovies(page: Int): Flow?>> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "movie/now_playing") { - parameter("page", page) - }.body() + override suspend fun fetchNowPlayingMovies(page: Int): Result?>> { + return safeApiCall { + val response = httpClient.get(urlString = "movie/now_playing") { + parameter("page", page) + }.body() - response.movies?.map { it.toDomain() } - } - ) + response.movies?.map { it.toDomain() } + } } override suspend fun fetchTrendingMovies( mediaType: String, timeWindow: String, page: Int - ): Flow?>> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "trending/$mediaType/$timeWindow") { - parameter("page", page) - }.body() - - response.movies?.map { it.toDomain() } - } - ) + ): Result?>> { + return safeApiCall { + val response = httpClient.get(urlString = "trending/$mediaType/$timeWindow") { + parameter("page", page) + }.body() + + response.movies?.map { it.toDomain() } + } } - override suspend fun fetchPopularMovies(page: Int): Flow?>> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "movie/popular") { - parameter("page", page) - }.body() + override suspend fun fetchPopularMovies(page: Int): Result?>> { + return safeApiCall { + val response = httpClient.get(urlString = "movie/popular") { + parameter("page", page) + }.body() - response.movies?.map { it.toDomain() } - } - ) + response.movies?.map { it.toDomain() } + } } - override suspend fun fetchUpcomingMovies(page: Int): Flow?>> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "movie/upcoming") { - parameter("page", page) - }.body() + override suspend fun fetchUpcomingMovies(page: Int): Result?>> { + return safeApiCall { + val response = httpClient.get(urlString = "movie/upcoming") { + parameter("page", page) + }.body() - response.movies?.map { it.toDomain() } - } - ) + response.movies?.map { it.toDomain() } + } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SearchRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SearchRepositoryImpl.kt index cdb06e96..2590b0e1 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SearchRepositoryImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SearchRepositoryImpl.kt @@ -5,13 +5,11 @@ import com.vickbt.composeApp.data.network.models.MovieResultsDto import com.vickbt.composeApp.data.network.utils.safeApiCall import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.repositories.SearchRepository -import com.vickbt.composeApp.utils.ResultState import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get import io.ktor.client.request.parameter import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf class SearchRepositoryImpl( private val httpClient: HttpClient @@ -20,16 +18,14 @@ class SearchRepositoryImpl( override suspend fun searchMovie( movieName: String, page: Int - ): Flow?>> { - return flowOf( - safeApiCall { - val response = httpClient.get(urlString = "search/movie") { - parameter("query", movieName) - parameter("page", page) - }.body() + ): Result?>> { + return safeApiCall { + val response = httpClient.get(urlString = "search/movie") { + parameter("query", movieName) + parameter("page", page) + }.body() - response.movies?.map { it.toDomain() } - } - ) + response.movies?.map { it.toDomain() } + } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SettingsRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SettingsRepositoryImpl.kt index d339142e..7648237b 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SettingsRepositoryImpl.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/datasources/SettingsRepositoryImpl.kt @@ -20,15 +20,19 @@ class SettingsRepositoryImpl( } } - override suspend fun getThemePreference(): Flow { - return dataStore.data.map { preferences -> - preferences[intPreferencesKey(KEY_THEME)] ?: 2 + override suspend fun getThemePreference(): Result> { + return runCatching { + dataStore.data.map { preferences -> + preferences[intPreferencesKey(KEY_THEME)] ?: 2 + } } } - override suspend fun getImageQualityPreference(): Flow { - return dataStore.data.map { preferences -> - preferences[intPreferencesKey(KEY_IMAGE_QUALITY)] ?: 1 + override suspend fun getImageQualityPreference(): Result> { + return runCatching { + dataStore.data.map { preferences -> + preferences[intPreferencesKey(KEY_IMAGE_QUALITY)] ?: 1 + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/utils/SafeApiCall.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/utils/SafeApiCall.kt index 3971640a..79681003 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/utils/SafeApiCall.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/utils/SafeApiCall.kt @@ -3,34 +3,14 @@ package com.vickbt.composeApp.data.network.utils import com.vickbt.composeApp.data.mappers.toDomain import com.vickbt.composeApp.data.network.models.ErrorResponseDto import com.vickbt.composeApp.domain.models.ErrorResponse -import com.vickbt.composeApp.utils.ResultState import io.ktor.client.call.body -import io.ktor.client.plugins.ClientRequestException -import io.ktor.client.plugins.RedirectResponseException -import io.ktor.client.plugins.ServerResponseException import io.ktor.client.statement.HttpResponse -import io.ktor.util.network.UnresolvedAddressException +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf -suspend fun safeApiCall(apiCall: suspend () -> T): ResultState { - return try { - ResultState.Loading - - ResultState.Success(apiCall.invoke()) - } catch (e: RedirectResponseException) { - val error = parseNetworkError(e.response.body()) - ResultState.Failure(exception = error) - } catch (e: ClientRequestException) { - val error = parseNetworkError(e.response.body()) - ResultState.Failure(exception = error) - } catch (e: ServerResponseException) { - val error = parseNetworkError(e.response.body()) - ResultState.Failure(exception = error) - } catch (e: UnresolvedAddressException) { - val error = parseNetworkError(exception = e) - ResultState.Failure(exception = error) - } catch (e: Exception) { - val error = parseNetworkError(exception = e) - ResultState.Failure(exception = error) +suspend fun safeApiCall(apiCall: suspend () -> T): Result> { + return runCatching { + flowOf(apiCall.invoke()) } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/FavoritesRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/FavoritesRepository.kt index 3f8cec02..8b1eea45 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/FavoritesRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/FavoritesRepository.kt @@ -6,5 +6,5 @@ import kotlinx.coroutines.flow.Flow interface FavoritesRepository { /**Returns a list of movies that are favourite from the database*/ - suspend fun getFavouriteMovies(): Flow> + suspend fun getFavouriteMovies(): Result?>> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MovieDetailsRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MovieDetailsRepository.kt index 08939adc..c78a3243 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MovieDetailsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MovieDetailsRepository.kt @@ -4,32 +4,31 @@ import com.vickbt.composeApp.domain.models.Cast import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX -import com.vickbt.composeApp.utils.ResultState import kotlinx.coroutines.flow.Flow interface MovieDetailsRepository { /**Fetch movie details from network source*/ - suspend fun fetchMovieDetails(movieId: Int): Flow> + suspend fun fetchMovieDetails(movieId: Int): Result> /**Fetch movie cast from network source*/ - suspend fun fetchMovieCast(movieId: Int): Flow> + suspend fun fetchMovieCast(movieId: Int): Result> /** Fetches similar movies from network source*/ suspend fun fetchSimilarMovies( movieId: Int, page: Int = STARTING_PAGE_INDEX - ): Flow?>> + ): Result?>> /**Save movie details to local cache*/ suspend fun saveFavoriteMovie(movie: MovieDetails) /**Retrieve cached movie details from local cache based on its ID*/ - suspend fun getFavoriteMovie(movieId: Int): MovieDetails? + suspend fun getFavoriteMovie(movieId: Int): Result> /**Delete previously saved movie details from local cache*/ suspend fun deleteFavoriteMovie(movieId: Int) /**Check if movie details record is available in the local cache*/ - suspend fun isMovieFavorite(movieId: Int): Flow + suspend fun isMovieFavorite(movieId: Int): Result> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MoviesRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MoviesRepository.kt index 8081f071..1194b532 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MoviesRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/MoviesRepository.kt @@ -2,24 +2,23 @@ package com.vickbt.composeApp.domain.repositories import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX -import com.vickbt.composeApp.utils.ResultState import kotlinx.coroutines.flow.Flow interface MoviesRepository { /** Fetch Now Playing movies from data source*/ - suspend fun fetchNowPlayingMovies(page: Int = STARTING_PAGE_INDEX): Flow?>> + suspend fun fetchNowPlayingMovies(page: Int = STARTING_PAGE_INDEX): Result?>> /** Fetch Trending movies from data source*/ suspend fun fetchTrendingMovies( mediaType: String = "movie", timeWindow: String = "week", page: Int = STARTING_PAGE_INDEX - ): Flow?>> + ): Result?>> /** Fetch Popular movies from data source*/ - suspend fun fetchPopularMovies(page: Int = STARTING_PAGE_INDEX): Flow?>> + suspend fun fetchPopularMovies(page: Int = STARTING_PAGE_INDEX): Result?>> /** Fetch Upcoming movies from data source*/ - suspend fun fetchUpcomingMovies(page: Int = STARTING_PAGE_INDEX): Flow?>> + suspend fun fetchUpcomingMovies(page: Int = STARTING_PAGE_INDEX): Result?>> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SearchRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SearchRepository.kt index ae0d7b62..35a407de 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SearchRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SearchRepository.kt @@ -2,7 +2,6 @@ package com.vickbt.composeApp.domain.repositories import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX -import com.vickbt.composeApp.utils.ResultState import kotlinx.coroutines.flow.Flow interface SearchRepository { @@ -11,5 +10,5 @@ interface SearchRepository { suspend fun searchMovie( movieName: String, page: Int = STARTING_PAGE_INDEX - ): Flow?>> + ): Result?>> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SettingsRepository.kt index 3233cd6d..6f5146dc 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/repositories/SettingsRepository.kt @@ -6,7 +6,7 @@ interface SettingsRepository { suspend fun savePreferenceSelection(key: String, selection: Int) - suspend fun getThemePreference(): Flow + suspend fun getThemePreference(): Result> - suspend fun getImageQualityPreference(): Flow + suspend fun getImageQualityPreference(): Result> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsViewModel.kt index 67a604e9..7e3cc326 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsViewModel.kt @@ -5,10 +5,6 @@ import androidx.lifecycle.viewModelScope import com.vickbt.composeApp.domain.models.MovieDetails import com.vickbt.composeApp.domain.repositories.MovieDetailsRepository import com.vickbt.composeApp.utils.DetailsUiState -import com.vickbt.composeApp.utils.isLoading -import com.vickbt.composeApp.utils.onFailure -import com.vickbt.composeApp.utils.onSuccess -import io.github.aakira.napier.Napier import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -28,66 +24,52 @@ class DetailsViewModel( } fun getMovieDetails(movieId: Int) = viewModelScope.launch(coroutineExceptionHandler) { - movieDetailsRepository.fetchMovieDetails(movieId = movieId).collect { movieDetailsResult -> - movieDetailsResult.isLoading { isLoading -> - _movieDetailsState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movieDetails -> - _movieDetailsState.update { it.copy(movieDetails = movieDetails) } - }.onFailure { error -> - _movieDetailsState.update { it.copy(error = error.message) } + movieDetailsRepository.fetchMovieDetails(movieId = movieId).onSuccess { data -> + data.collectLatest { movieDetails -> + _movieDetailsState.update { + it.copy(movieDetails = movieDetails, isLoading = false) + } } + }.onFailure { error -> + _movieDetailsState.update { it.copy(error = error.message, isLoading = false) } } } fun getMovieCast(movieId: Int) = viewModelScope.launch(coroutineExceptionHandler) { - movieDetailsRepository.fetchMovieCast(movieId = movieId).collect { movieCastsResult -> - movieCastsResult.isLoading { isLoading -> - _movieDetailsState.update { it.copy(isLoading = isLoading) } - }.onSuccess { cast -> - _movieDetailsState.update { it.copy(movieCast = cast.actor) } - }.onFailure { error -> - _movieDetailsState.update { it.copy(error = error.message) } + movieDetailsRepository.fetchMovieCast(movieId = movieId).onSuccess { data -> + data.collectLatest { cast -> + _movieDetailsState.update { it.copy(movieCast = cast.actor, isLoading = false) } } + }.onFailure { error -> + _movieDetailsState.update { it.copy(error = error.message, isLoading = false) } } } fun fetchSimilarMovies(movieId: Int) = viewModelScope.launch(coroutineExceptionHandler) { _movieDetailsState.update { it.copy(isLoading = true) } - movieDetailsRepository.fetchSimilarMovies(movieId).collect { similarMovies -> - similarMovies.isLoading { isLoading -> - _movieDetailsState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movies -> - _movieDetailsState.update { it.copy(similarMovies = movies) } - }.onFailure { error -> - _movieDetailsState.update { it.copy(error = error.message) } + movieDetailsRepository.fetchSimilarMovies(movieId).onSuccess { data -> + data.collectLatest { movies -> + _movieDetailsState.update { it.copy(similarMovies = movies, isLoading = false) } } + }.onFailure { error -> + _movieDetailsState.update { it.copy(error = error.message, isLoading = false) } } } fun saveFavoriteMovie(movieDetails: MovieDetails) = viewModelScope.launch(coroutineExceptionHandler) { - try { - movieDetailsRepository.saveFavoriteMovie(movie = movieDetails) - } catch (e: Exception) { - Napier.e("Error saving movie: ${e.message}") - } + movieDetailsRepository.saveFavoriteMovie(movie = movieDetails) } fun deleteFavoriteMovie(movieId: Int) = viewModelScope.launch(coroutineExceptionHandler) { - try { - movieDetailsRepository.deleteFavoriteMovie(movieId = movieId) - } catch (e: Exception) { - Napier.e("Error removing movie: ${e.message}") - } + movieDetailsRepository.deleteFavoriteMovie(movieId = movieId) } fun isMovieFavorite(movieId: Int) = viewModelScope.launch(coroutineExceptionHandler) { - try { - movieDetailsRepository.isMovieFavorite(movieId = movieId).collectLatest { isFavorite -> + movieDetailsRepository.isMovieFavorite(movieId = movieId).onSuccess { data -> + data.collectLatest { isFavorite -> _movieDetailsState.update { it.copy(isFavorite = isFavorite) } } - } catch (e: Exception) { - Napier.e("Error removing movie: ${e.message}") } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesViewModel.kt index 1b64a726..a89c9239 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/favorites/FavoritesViewModel.kt @@ -7,6 +7,7 @@ import com.vickbt.composeApp.utils.FavouritesUiState import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -26,8 +27,14 @@ class FavoritesViewModel( } fun getFavoriteMovie() = viewModelScope.launch(coroutineExceptionHandler) { - favoritesRepository.getFavouriteMovies().collect { favoriteMoviesResult -> - _favoriteMoviesState.update { it.copy(favoriteMovies = favoriteMoviesResult) } + favoritesRepository.getFavouriteMovies().onSuccess { data -> + data.collectLatest { favoriteMoviesResult -> + _favoriteMoviesState.update { + it.copy(favoriteMovies = favoriteMoviesResult, isLoading = false) + } + } + }.onFailure { error -> + _favoriteMoviesState.update { it.copy(error = error.message, isLoading = false) } } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt index 446d88ad..168d0dbc 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeViewModel.kt @@ -4,9 +4,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vickbt.composeApp.domain.repositories.MoviesRepository import com.vickbt.composeApp.utils.HomeUiState -import com.vickbt.composeApp.utils.isLoading -import com.vickbt.composeApp.utils.onFailure -import com.vickbt.composeApp.utils.onSuccess import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -31,50 +28,47 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( } fun fetchNowPlayingMovies() = viewModelScope.launch(coroutineExceptionHandler) { - moviesRepository.fetchNowPlayingMovies().collectLatest { moviesResult -> - moviesResult.isLoading { isLoading -> - _homeUiState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movies -> - _homeUiState.update { it.copy(nowPlayingMovies = movies?.take(5)) } - }.onFailure { error -> - _homeUiState.update { it.copy(error = error.message) } + moviesRepository.fetchNowPlayingMovies().onSuccess { data -> + data.collectLatest { movies -> + _homeUiState.update { + it.copy( + nowPlayingMovies = movies?.take(5), + isLoading = false + ) + } } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } } } fun fetchTrendingMovies() = viewModelScope.launch(coroutineExceptionHandler) { - moviesRepository.fetchTrendingMovies().collectLatest { moviesResult -> - moviesResult.isLoading { isLoading -> - _homeUiState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movies -> - _homeUiState.update { it.copy(trendingMovies = movies) } - }.onFailure { error -> - _homeUiState.update { it.copy(error = error.message) } + moviesRepository.fetchTrendingMovies().onSuccess { data -> + data.collectLatest { movies -> + _homeUiState.update { it.copy(trendingMovies = movies, isLoading = false) } } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } } } fun fetchPopularMovies() = viewModelScope.launch(coroutineExceptionHandler) { - moviesRepository.fetchPopularMovies().collectLatest { moviesResult -> - moviesResult.isLoading { isLoading -> - _homeUiState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movies -> - _homeUiState.update { it.copy(popularMovies = movies) } - }.onFailure { error -> - _homeUiState.update { it.copy(error = error.message) } + moviesRepository.fetchPopularMovies().onSuccess { data -> + data.collectLatest { movies -> + _homeUiState.update { it.copy(popularMovies = movies, isLoading = false) } } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } } } fun fetchUpcomingMovies() = viewModelScope.launch(coroutineExceptionHandler) { - moviesRepository.fetchUpcomingMovies().collectLatest { moviesResult -> - moviesResult.isLoading { isLoading -> - _homeUiState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movies -> - _homeUiState.update { it.copy(upcomingMovies = movies) } - }.onFailure { error -> - _homeUiState.update { it.copy(error = error.message) } + moviesRepository.fetchUpcomingMovies().onSuccess { data -> + data.collectLatest { movies -> + _homeUiState.update { it.copy(upcomingMovies = movies, isLoading = false) } } + }.onFailure { error -> + _homeUiState.update { it.copy(error = error.message, isLoading = false) } } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainViewModel.kt index 06b54a7e..3be54f4f 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/main/MainViewModel.kt @@ -24,12 +24,12 @@ class MainViewModel(private val settingsRepository: SettingsRepository) : ViewMo } private fun getAppTheme() = viewModelScope.launch(coroutineExceptionHandler) { - try { - settingsRepository.getThemePreference().collectLatest { theme -> + settingsRepository.getThemePreference().onSuccess { data -> + data.collectLatest { theme -> _mainUiState.update { it.copy(selectedTheme = theme) } } - } catch (e: Exception) { - Napier.e("ERROR getting theme: ${e.message}") + }.onFailure { + Napier.e("Error getting theme: ${it.message}") } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchViewModel.kt index c298a98f..a348415e 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchViewModel.kt @@ -4,9 +4,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vickbt.composeApp.domain.repositories.SearchRepository import com.vickbt.composeApp.utils.SearchUiState -import com.vickbt.composeApp.utils.isLoading -import com.vickbt.composeApp.utils.onFailure -import com.vickbt.composeApp.utils.onSuccess import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -26,26 +23,15 @@ class SearchViewModel(private val searchRepository: SearchRepository) : ViewMode _searchUiState.update { it.copy(isLoading = false, error = exception.message) } } - init { - getGenres() - } - fun searchMovie(movieName: String) = viewModelScope.launch(coroutineExceptionHandler) { - searchRepository.searchMovie(movieName = movieName).collectLatest { moviesResult -> - moviesResult.isLoading { isLoading -> - _searchUiState.update { it.copy(isLoading = isLoading) } - }.onSuccess { movies -> - _searchUiState.update { it.copy(movieResults = movies) } - }.onFailure { error -> - _searchUiState.update { it.copy(error = error.message) } + _searchUiState.update { it.copy(isLoading = true) } + + searchRepository.searchMovie(movieName = movieName).onSuccess { data -> + data.collectLatest { movies -> + _searchUiState.update { it.copy(movieResults = movies, isLoading = false) } } + }.onFailure { error -> + _searchUiState.update { it.copy(error = error.message, isLoading = false) } } } - - fun updateSearchQuery(searchQuery: String) { - _searchQuery.value = searchQuery - } - - private fun getGenres() = viewModelScope.launch { - } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsViewModel.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsViewModel.kt index 597f318a..7dd8aa5a 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/settings/SettingsViewModel.kt @@ -7,6 +7,7 @@ import com.vickbt.composeApp.utils.SettingsUiState import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch @@ -31,18 +32,19 @@ class SettingsViewModel(private val settingsRepository: SettingsRepository) : } fun getThemePreference() = viewModelScope.launch(coroutineExceptionHandler) { - settingsRepository.getThemePreference().collect { theme -> - _settingsUiState.update { it.copy(selectedTheme = theme, isLoading = false) } + settingsRepository.getThemePreference().onSuccess { data -> + data.collectLatest { theme -> + _settingsUiState.update { it.copy(selectedTheme = theme, isLoading = false) } + } } } fun getImageQualityPreference() = viewModelScope.launch(coroutineExceptionHandler) { - settingsRepository.getImageQualityPreference().collect { imageQuality -> - _settingsUiState.update { - it.copy( - selectedImageQuality = imageQuality, - isLoading = false - ) + settingsRepository.getImageQualityPreference().onSuccess { data -> + data.collectLatest { imageQuality -> + _settingsUiState.update { + it.copy(selectedImageQuality = imageQuality, isLoading = false) + } } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/ResultState.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/ResultState.kt deleted file mode 100644 index 28ddf419..00000000 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/ResultState.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.vickbt.composeApp.utils - -sealed class ResultState { - data class Success(val data: T) : ResultState() - data class Failure(val exception: Exception) : ResultState() - object Loading : ResultState() -} - -inline fun ResultState.isLoading(crossinline action: (isLoading: Boolean) -> Unit): ResultState { - if (this is ResultState.Loading) action(true) else action(false) - return this -} - -inline fun ResultState.onSuccess(crossinline action: (T) -> Unit): ResultState { - if (this is ResultState.Success) action(this.data) - return this -} - -inline fun ResultState.onFailure(crossinline action: (exception: Exception) -> Unit): ResultState { - if (this is ResultState.Failure) action(this.exception) - return this -}