From 56fea0ea6236d51ab3af8042a1273a15025bec5d Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Mon, 23 Sep 2024 18:14:44 +0300 Subject: [PATCH 01/10] Added paging library --- composeApp/build.gradle.kts | 2 ++ gradle/libs.versions.toml | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 119e1391..509dce90 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -72,6 +72,8 @@ kotlin { implementation(libs.room.runtime) implementation(libs.sqlite.bundled) + + implementation(libs.bundles.paging) } commonTest.dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4a734f32..bb87bead 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,6 +29,7 @@ datastore = "1.1.1" ksp = "2.0.20-1.0.24" sqlite = "2.5.0-SNAPSHOT" room = "2.7.0-alpha08" +paging = "3.3.0-alpha02-0.4.0" #Android Versions androidxActivity = "1.8.2" @@ -85,9 +86,11 @@ coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coil" coil-compose-core = { module = "io.coil-kt.coil3:coil-compose-core", version.ref = "coil" } coil-ktor = { module = "io.coil-kt.coil3:coil-network-ktor", version.ref = "coil" } coil-multiplatform = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } -room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } -room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } +room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" } +paging = { module = "app.cash.paging:paging-common", version.ref = "paging" } +paging-compose = { module = "app.cash.paging:paging-compose-common", version.ref = "paging" } #Android Lib Dependencies androidX-core = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } @@ -111,4 +114,5 @@ turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } [bundles] ktor = ["ktor-core", "ktor-contentNegotiation", "ktor-json", "ktor-logging"] -coil = ["coil-compose-core", "coil-compose", "coil-ktor", "coil-multiplatform"] \ No newline at end of file +coil = ["coil-compose-core", "coil-compose", "coil-ktor", "coil-multiplatform"] +paging = ["paging", "paging-compose"] \ No newline at end of file From eff0555845645f3b7ca5d79534ab0aa371c6c93c Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Mon, 23 Sep 2024 19:14:54 +0300 Subject: [PATCH 02/10] Set up paging in viewmodel --- .../data/datasources/MoviesRepositoryImpl.kt | 28 +++++++++++----- .../data/paging/BasePagingSource.kt | 32 +++++++++++++++++++ .../domain/repositories/MoviesRepository.kt | 8 ++--- .../com/vickbt/composeApp/utils/UiStates.kt | 3 +- 4 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt 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 09c38c90..b2634302 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 @@ -1,8 +1,12 @@ package com.vickbt.composeApp.data.datasources +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import com.vickbt.composeApp.data.mappers.toDomain import com.vickbt.composeApp.data.network.models.MovieResultsDto import com.vickbt.composeApp.data.network.utils.safeApiCall +import com.vickbt.composeApp.data.paging.BasePagingSource import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.repositories.MoviesRepository import io.ktor.client.HttpClient @@ -15,10 +19,10 @@ class MoviesRepositoryImpl( private val httpClient: HttpClient ) : MoviesRepository { - override suspend fun fetchNowPlayingMovies(page: Int): Result?>> { + override suspend fun fetchNowPlayingMovies(): Result?>> { return safeApiCall { val response = httpClient.get(urlString = "movie/now_playing") { - parameter("page", page) + parameter("page", 1) }.body() response.movies?.map { it.toDomain() } @@ -27,15 +31,23 @@ class MoviesRepositoryImpl( override suspend fun fetchTrendingMovies( mediaType: String, - timeWindow: String, - page: Int - ): Result?>> { - return safeApiCall { + timeWindow: String + ): Result>> { + val pagingConfig = PagingConfig(pageSize = 5, enablePlaceholders = false) + + val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "trending/$mediaType/$timeWindow") { parameter("page", page) - }.body() + }.body().movies - response.movies?.map { it.toDomain() } + response?.map { it.toDomain() } + } + + return runCatching { + Pager( + config = pagingConfig, + pagingSourceFactory = { pagingSource } + ).flow } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt new file mode 100644 index 00000000..329300ed --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt @@ -0,0 +1,32 @@ +package com.vickbt.composeApp.data.paging + +import androidx.paging.PagingState +import app.cash.paging.PagingSource +import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX + +class BasePagingSource(val fetchData: suspend (page: Int) -> List?) : + PagingSource() { + + override suspend fun load(params: LoadParams): LoadResult { + val page = params.key ?: STARTING_PAGE_INDEX + + val data = fetchData(page)?: emptyList() + + return try { + LoadResult.Page( + data = data, + prevKey = if (page == 1) null else page - 1, + nextKey = if (data.isEmpty()) null else page + 1 + ) + } catch (e: Exception) { + LoadResult.Error(e) + } + } + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) + ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) + } + } +} 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 1194b532..b6d459a7 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 @@ -1,5 +1,6 @@ package com.vickbt.composeApp.domain.repositories +import androidx.paging.PagingData import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX import kotlinx.coroutines.flow.Flow @@ -7,14 +8,13 @@ import kotlinx.coroutines.flow.Flow interface MoviesRepository { /** Fetch Now Playing movies from data source*/ - suspend fun fetchNowPlayingMovies(page: Int = STARTING_PAGE_INDEX): Result?>> + suspend fun fetchNowPlayingMovies(): Result?>> /** Fetch Trending movies from data source*/ suspend fun fetchTrendingMovies( mediaType: String = "movie", - timeWindow: String = "week", - page: Int = STARTING_PAGE_INDEX - ): Result?>> + timeWindow: String = "week" + ): Result>> /** Fetch Popular movies from data source*/ suspend fun fetchPopularMovies(page: Int = STARTING_PAGE_INDEX): Result?>> diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index a45b4059..41f906a2 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -1,5 +1,6 @@ package com.vickbt.composeApp.utils +import app.cash.paging.PagingData import com.vickbt.composeApp.domain.models.Actor import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails @@ -12,7 +13,7 @@ data class HomeUiState( val isLoading: Boolean = true, val error: String? = null, val nowPlayingMovies: List? = emptyList(), - val trendingMovies: List? = emptyList(), + val trendingMovies: PagingData? = null, val popularMovies: List? = emptyList(), val upcomingMovies: List? = emptyList(), ) From 9794810a06700fd7c09d861cd2759734e5946836 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 24 Sep 2024 12:44:22 +0300 Subject: [PATCH 03/10] Set up paging --- .../data/datasources/MoviesRepositoryImpl.kt | 8 +++--- .../data/paging/BasePagingSource.kt | 2 +- .../domain/repositories/MoviesRepository.kt | 2 +- .../ui/components/MovieCardPortraitCompact.kt | 11 +++++--- .../composeApp/ui/screens/home/HomeScreen.kt | 25 +++++++++++-------- .../ui/screens/home/HomeViewModel.kt | 5 ++-- .../com/vickbt/composeApp/utils/UiStates.kt | 5 ++-- gradle/libs.versions.toml | 8 +++--- 8 files changed, 37 insertions(+), 29 deletions(-) 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 b2634302..cac1f06c 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 @@ -1,8 +1,8 @@ package com.vickbt.composeApp.data.datasources -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.PagingData +import app.cash.paging.Pager +import app.cash.paging.PagingConfig +import app.cash.paging.PagingData import com.vickbt.composeApp.data.mappers.toDomain import com.vickbt.composeApp.data.network.models.MovieResultsDto import com.vickbt.composeApp.data.network.utils.safeApiCall @@ -33,7 +33,7 @@ class MoviesRepositoryImpl( mediaType: String, timeWindow: String ): Result>> { - val pagingConfig = PagingConfig(pageSize = 5, enablePlaceholders = false) + val pagingConfig = PagingConfig(pageSize = 20, enablePlaceholders = false) val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "trending/$mediaType/$timeWindow") { diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt index 329300ed..602bfed5 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt @@ -1,7 +1,7 @@ package com.vickbt.composeApp.data.paging -import androidx.paging.PagingState import app.cash.paging.PagingSource +import app.cash.paging.PagingState import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX class BasePagingSource(val fetchData: suspend (page: Int) -> List?) : 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 b6d459a7..d2b30cf4 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 @@ -1,6 +1,6 @@ package com.vickbt.composeApp.domain.repositories -import androidx.paging.PagingData +import app.cash.paging.PagingData import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX import kotlinx.coroutines.flow.Flow diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt index a838289e..dd8d94d4 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt @@ -24,12 +24,15 @@ import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.utils.loadImage +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.unknown_movie +import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardPortraitCompact( modifier: Modifier = Modifier, - movie: Movie, - onItemClick: (Movie) -> Unit + movie: Movie?, + onItemClick: (Movie?) -> Unit ) { Column( modifier = modifier, @@ -48,7 +51,7 @@ fun MovieCardPortraitCompact( .fillMaxWidth() .height(220.dp) .sizeIn(minHeight = 30.dp), - model = movie.posterPath?.loadImage(), + model = movie?.posterPath?.loadImage(), contentDescription = "Trending movie poster", contentScale = ContentScale.Crop, alignment = Alignment.Center, @@ -57,7 +60,7 @@ fun MovieCardPortraitCompact( Text( modifier = Modifier.width(145.dp), - text = movie.title, + text = movie?.title?: stringResource(Res.string.unknown_movie), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, fontSize = 14.sp, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index c3ff85fe..9b2e94c9 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -29,10 +28,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import com.vickbt.shared.resources.Res -import com.vickbt.shared.resources.popular_movies -import com.vickbt.shared.resources.trending_movies -import com.vickbt.shared.resources.upcoming_movies +import app.cash.paging.compose.collectAsLazyPagingItems import com.vickbt.composeApp.ui.components.MovieCardLandscape import com.vickbt.composeApp.ui.components.MovieCardPager import com.vickbt.composeApp.ui.components.MovieCardPagerIndicator @@ -41,10 +37,15 @@ import com.vickbt.composeApp.ui.components.SectionSeparator import com.vickbt.composeApp.ui.components.appbars.AppBar import com.vickbt.composeApp.ui.theme.DarkPrimaryColor import com.vickbt.composeApp.utils.WindowSize +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.popular_movies +import com.vickbt.shared.resources.trending_movies +import com.vickbt.shared.resources.upcoming_movies import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) + +@OptIn(ExperimentalFoundationApi::class) @Composable fun HomeScreen( navigator: NavHostController, @@ -56,6 +57,8 @@ fun HomeScreen( val homeUiState = viewModel.homeUiState.collectAsState().value + val movies = homeUiState.trendingMovies?.collectAsLazyPagingItems() + Scaffold( modifier = Modifier .fillMaxSize() @@ -116,6 +119,8 @@ fun HomeScreen( //region Trending Movies homeUiState.trendingMovies?.let { + val trendingMovies = it.collectAsLazyPagingItems() + SectionSeparator( modifier = Modifier .fillMaxWidth() @@ -130,11 +135,11 @@ fun HomeScreen( contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(10.dp) ) { - items(items = it) { item -> + items(trendingMovies.itemCount) { index -> MovieCardPortraitCompact( - movie = item, + movie = trendingMovies[index], onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") + navigator.navigate("/details/${movie?.id}") } ) } @@ -190,7 +195,7 @@ fun HomeScreen( MovieCardPortraitCompact( movie = item, onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") + navigator.navigate("/details/${movie?.id}") } ) } 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 168d0dbc..eeecf6c9 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 @@ -2,6 +2,7 @@ package com.vickbt.composeApp.ui.screens.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn import com.vickbt.composeApp.domain.repositories.MoviesRepository import com.vickbt.composeApp.utils.HomeUiState import kotlinx.coroutines.CoroutineExceptionHandler @@ -44,9 +45,7 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( fun fetchTrendingMovies() = viewModelScope.launch(coroutineExceptionHandler) { moviesRepository.fetchTrendingMovies().onSuccess { data -> - data.collectLatest { movies -> - _homeUiState.update { it.copy(trendingMovies = movies, isLoading = false) } - } + _homeUiState.update { it.copy(trendingMovies = data.cachedIn(viewModelScope), isLoading = false) } }.onFailure { error -> _homeUiState.update { it.copy(error = error.message, isLoading = false) } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index 41f906a2..1cd32903 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -1,9 +1,10 @@ package com.vickbt.composeApp.utils -import app.cash.paging.PagingData +import androidx.paging.PagingData import com.vickbt.composeApp.domain.models.Actor import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails +import kotlinx.coroutines.flow.Flow data class MainUiState( val selectedTheme: Int? = 0 @@ -13,7 +14,7 @@ data class HomeUiState( val isLoading: Boolean = true, val error: String? = null, val nowPlayingMovies: List? = emptyList(), - val trendingMovies: PagingData? = null, + val trendingMovies: Flow>? = null, val popularMovies: List? = emptyList(), val upcomingMovies: List? = emptyList(), ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bb87bead..da510e1e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,6 @@ kotlinxSerializationJson = "1.7.1" kotlinxDateTime = "0.6.0" napier = "2.7.1" ktor = "2.3.12" -sqlDelight = "2.0.2" buildKonfig = "0.13.3" materialWindowSizeClass = "0.3.0" navigation = "2.7.0-alpha07" @@ -29,7 +28,7 @@ datastore = "1.1.1" ksp = "2.0.20-1.0.24" sqlite = "2.5.0-SNAPSHOT" room = "2.7.0-alpha08" -paging = "3.3.0-alpha02-0.4.0" +paging = "3.3.0-alpha02-0.5.1" #Android Versions androidxActivity = "1.8.2" @@ -89,7 +88,7 @@ coil-multiplatform = { module = "io.coil-kt.coil3:coil", version.ref = "coil" } room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version.ref = "sqlite" } -paging = { module = "app.cash.paging:paging-common", version.ref = "paging" } +paging-common = { module = "app.cash.paging:paging-common", version.ref = "paging" } paging-compose = { module = "app.cash.paging:paging-compose-common", version.ref = "paging" } #Android Lib Dependencies @@ -115,4 +114,5 @@ turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } [bundles] ktor = ["ktor-core", "ktor-contentNegotiation", "ktor-json", "ktor-logging"] coil = ["coil-compose-core", "coil-compose", "coil-ktor", "coil-multiplatform"] -paging = ["paging", "paging-compose"] \ No newline at end of file +paging = ["paging-common", "paging-compose"] +#paging = ["paging-compose"] \ No newline at end of file From 6bee97cc860c27b135d6706395ae083c865504cf Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 24 Sep 2024 12:54:14 +0300 Subject: [PATCH 04/10] Set pagination on trending movies list --- .../ui/components/MovieCardPortraitCompact.kt | 16 ++++----------- .../composeApp/ui/screens/home/HomeScreen.kt | 20 ++++++++++--------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt index dd8d94d4..90cbde13 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt @@ -24,15 +24,12 @@ import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.utils.loadImage -import com.vickbt.shared.resources.Res -import com.vickbt.shared.resources.unknown_movie -import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardPortraitCompact( modifier: Modifier = Modifier, - movie: Movie?, - onItemClick: (Movie?) -> Unit + movie: Movie, + onItemClick: (Movie) -> Unit ) { Column( modifier = modifier, @@ -51,7 +48,7 @@ fun MovieCardPortraitCompact( .fillMaxWidth() .height(220.dp) .sizeIn(minHeight = 30.dp), - model = movie?.posterPath?.loadImage(), + model = movie.posterPath.loadImage(), contentDescription = "Trending movie poster", contentScale = ContentScale.Crop, alignment = Alignment.Center, @@ -60,7 +57,7 @@ fun MovieCardPortraitCompact( Text( modifier = Modifier.width(145.dp), - text = movie?.title?: stringResource(Res.string.unknown_movie), + text = movie.title, style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, fontSize = 14.sp, @@ -70,8 +67,3 @@ fun MovieCardPortraitCompact( ) } } - -@Composable -private fun Preview() { - // MovieCardPortraitCompact(movie = Movie(title = "Cocaine Bear"), onItemClick = {}) -} diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index 9b2e94c9..fd0ae7ba 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -118,8 +118,8 @@ fun HomeScreen( //endregion //region Trending Movies - homeUiState.trendingMovies?.let { - val trendingMovies = it.collectAsLazyPagingItems() + homeUiState.trendingMovies?.let { movies -> + val trendingMovies = movies.collectAsLazyPagingItems() SectionSeparator( modifier = Modifier @@ -136,12 +136,14 @@ fun HomeScreen( horizontalArrangement = Arrangement.spacedBy(10.dp) ) { items(trendingMovies.itemCount) { index -> - MovieCardPortraitCompact( - movie = trendingMovies[index], - onItemClick = { movie -> - navigator.navigate("/details/${movie?.id}") - } - ) + trendingMovies[index]?.let { + MovieCardPortraitCompact( + movie = it, + onItemClick = { movie -> + navigator.navigate("/details/${movie.id}") + } + ) + } } } } @@ -195,7 +197,7 @@ fun HomeScreen( MovieCardPortraitCompact( movie = item, onItemClick = { movie -> - navigator.navigate("/details/${movie?.id}") + navigator.navigate("/details/${movie.id}") } ) } From 513f6af8f8aa3824d24636e228a6aa679efe627c Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 24 Sep 2024 14:34:57 +0300 Subject: [PATCH 05/10] Update popular movies to use pagination --- .../data/datasources/MoviesRepositoryImpl.kt | 19 ++++++++++++------ .../vickbt/composeApp/domain/models/Movie.kt | 2 +- .../domain/repositories/MoviesRepository.kt | 2 +- .../composeApp/ui/screens/home/HomeScreen.kt | 20 +++++++++++-------- .../ui/screens/home/HomeViewModel.kt | 14 ++++++++++--- .../com/vickbt/composeApp/utils/UiStates.kt | 2 +- 6 files changed, 39 insertions(+), 20 deletions(-) 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 cac1f06c..58f918f4 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 @@ -19,6 +19,8 @@ class MoviesRepositoryImpl( private val httpClient: HttpClient ) : MoviesRepository { + private val pagingConfig = PagingConfig(pageSize = 20, enablePlaceholders = false) + override suspend fun fetchNowPlayingMovies(): Result?>> { return safeApiCall { val response = httpClient.get(urlString = "movie/now_playing") { @@ -33,8 +35,6 @@ class MoviesRepositoryImpl( mediaType: String, timeWindow: String ): Result>> { - val pagingConfig = PagingConfig(pageSize = 20, enablePlaceholders = false) - val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "trending/$mediaType/$timeWindow") { parameter("page", page) @@ -51,13 +51,20 @@ class MoviesRepositoryImpl( } } - override suspend fun fetchPopularMovies(page: Int): Result?>> { - return safeApiCall { + override suspend fun fetchPopularMovies(): Result>> { + val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "movie/popular") { parameter("page", page) - }.body() + }.body().movies - response.movies?.map { it.toDomain() } + response?.map { it.toDomain() } + } + + return runCatching { + Pager( + config = pagingConfig, + pagingSourceFactory = { pagingSource } + ).flow } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt index 85b4f1aa..b6cbd137 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt @@ -3,7 +3,7 @@ package com.vickbt.composeApp.domain.models data class Movie( val adult: Boolean? = null, - val backdropPath: String, + val backdropPath: String? = null, val id: Int, 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 d2b30cf4..09d27518 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 @@ -17,7 +17,7 @@ interface MoviesRepository { ): Result>> /** Fetch Popular movies from data source*/ - suspend fun fetchPopularMovies(page: Int = STARTING_PAGE_INDEX): Result?>> + suspend fun fetchPopularMovies(): Result>> /** Fetch Upcoming movies from data source*/ suspend fun fetchUpcomingMovies(page: Int = STARTING_PAGE_INDEX): Result?>> diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index fd0ae7ba..3d319424 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -179,7 +179,9 @@ fun HomeScreen( //endregion //region Popular Movies - homeUiState.popularMovies?.let { + homeUiState.popularMovies?.let { movies -> + val popularMovies = movies.collectAsLazyPagingItems() + Column(modifier = Modifier.padding(bottom = 90.dp)) { SectionSeparator( modifier = Modifier @@ -193,13 +195,15 @@ fun HomeScreen( contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(10.dp), ) { - items(items = it) { item -> - MovieCardPortraitCompact( - movie = item, - onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") - } - ) + items(popularMovies.itemCount) { index -> + popularMovies[index]?.let { + MovieCardPortraitCompact( + movie = it, + onItemClick = { movie -> + navigator.navigate("/details/${movie.id}") + } + ) + } } } } 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 eeecf6c9..101d7e9b 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 @@ -45,7 +45,12 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( fun fetchTrendingMovies() = viewModelScope.launch(coroutineExceptionHandler) { moviesRepository.fetchTrendingMovies().onSuccess { data -> - _homeUiState.update { it.copy(trendingMovies = data.cachedIn(viewModelScope), isLoading = false) } + _homeUiState.update { + it.copy( + trendingMovies = data.cachedIn(viewModelScope), + isLoading = false + ) + } }.onFailure { error -> _homeUiState.update { it.copy(error = error.message, isLoading = false) } } @@ -53,8 +58,11 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( fun fetchPopularMovies() = viewModelScope.launch(coroutineExceptionHandler) { moviesRepository.fetchPopularMovies().onSuccess { data -> - data.collectLatest { movies -> - _homeUiState.update { it.copy(popularMovies = movies, isLoading = false) } + _homeUiState.update { + it.copy( + popularMovies = data.cachedIn(viewModelScope), + isLoading = false + ) } }.onFailure { error -> _homeUiState.update { it.copy(error = error.message, isLoading = false) } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index 1cd32903..841d37b0 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -15,7 +15,7 @@ data class HomeUiState( val error: String? = null, val nowPlayingMovies: List? = emptyList(), val trendingMovies: Flow>? = null, - val popularMovies: List? = emptyList(), + val popularMovies: Flow>? = null, val upcomingMovies: List? = emptyList(), ) From 3a53a44c44916792c68bd9d08d4903af3d7357fc Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Tue, 24 Sep 2024 14:49:53 +0300 Subject: [PATCH 06/10] Update upcoming movies to use pagination --- .../data/datasources/MoviesRepositoryImpl.kt | 15 ++++++++--- .../data/network/models/MovieDto.kt | 10 +++---- .../vickbt/composeApp/domain/models/Movie.kt | 8 +++--- .../domain/repositories/MoviesRepository.kt | 5 ++-- .../ui/components/MovieCardLandscape.kt | 5 +++- .../ui/components/MovieCardPager.kt | 5 +++- .../ui/components/MovieCardPortraitCompact.kt | 7 +++-- .../composeApp/ui/screens/home/HomeScreen.kt | 27 ++++++++++--------- .../ui/screens/home/HomeViewModel.kt | 7 +++-- .../com/vickbt/composeApp/utils/UiStates.kt | 2 +- 10 files changed, 56 insertions(+), 35 deletions(-) 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 58f918f4..5ccc5bf0 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 @@ -68,13 +68,20 @@ class MoviesRepositoryImpl( } } - override suspend fun fetchUpcomingMovies(page: Int): Result?>> { - return safeApiCall { + override suspend fun fetchUpcomingMovies(): Result>> { + val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "movie/upcoming") { parameter("page", page) - }.body() + }.body().movies - response.movies?.map { it.toDomain() } + response?.map { it.toDomain() } + } + + return runCatching { + Pager( + config = pagingConfig, + pagingSourceFactory = { pagingSource } + ).flow } } } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDto.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDto.kt index 5bc90ae2..8724047d 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDto.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDto.kt @@ -9,7 +9,7 @@ data class MovieDto( val adult: Boolean? = null, @SerialName("backdrop_path") - val backdropPath: String, + val backdropPath: String? = null, @SerialName("genre_ids") val genreIds: List? = null, @@ -21,22 +21,22 @@ data class MovieDto( val originalLanguage: String? = null, @SerialName("original_title") - val originalTitle: String, + val originalTitle: String? = null, @SerialName("overview") - val overview: String, + val overview: String? = null, @SerialName("popularity") val popularity: Double? = null, @SerialName("poster_path") - val posterPath: String, + val posterPath: String? = null, @SerialName("release_date") val releaseDate: String? = null, @SerialName("title") - val title: String, + val title: String? = null, @SerialName("video") val video: Boolean? = null, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt index b6cbd137..aebf4b35 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/Movie.kt @@ -9,17 +9,17 @@ data class Movie( val originalLanguage: String? = null, - val originalTitle: String, + val originalTitle: String? = null, - val overview: String, + val overview: String? = null, val popularity: Double? = null, - val posterPath: String, + val posterPath: String? = null, val releaseDate: String? = null, - val title: String, + val title: String? = null, val video: Boolean? = null, 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 09d27518..ab427421 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,7 +2,6 @@ package com.vickbt.composeApp.domain.repositories import app.cash.paging.PagingData import com.vickbt.composeApp.domain.models.Movie -import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX import kotlinx.coroutines.flow.Flow interface MoviesRepository { @@ -14,11 +13,11 @@ interface MoviesRepository { suspend fun fetchTrendingMovies( mediaType: String = "movie", timeWindow: String = "week" - ): Result>> + ): Result>> /** Fetch Popular movies from data source*/ suspend fun fetchPopularMovies(): Result>> /** Fetch Upcoming movies from data source*/ - suspend fun fetchUpcomingMovies(page: Int = STARTING_PAGE_INDEX): Result?>> + suspend fun fetchUpcomingMovies(): Result>> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt index c8960738..b9aaa0a7 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardLandscape.kt @@ -41,6 +41,9 @@ import com.vickbt.composeApp.utils.capitalizeEachWord import com.vickbt.composeApp.utils.getRating import com.vickbt.composeApp.utils.getReleaseDate import com.vickbt.composeApp.utils.loadImage +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.unknown_movie +import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardLandscape( @@ -94,7 +97,7 @@ fun MovieCardLandscape( //region Movie Title Text( modifier = Modifier, - text = movie.title, + text = movie.title ?: stringResource(Res.string.unknown_movie), fontSize = 18.sp, maxLines = 2, style = MaterialTheme.typography.titleMedium, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPager.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPager.kt index 45a1c586..8b3509b6 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPager.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPager.kt @@ -39,6 +39,9 @@ import com.vickbt.composeApp.ui.components.ratingbar.RatingBarStyle import com.vickbt.composeApp.ui.components.ratingbar.StepSize import com.vickbt.composeApp.utils.getRating import com.vickbt.composeApp.utils.loadImage +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.unknown_movie +import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardPager( @@ -86,7 +89,7 @@ fun MovieCardPager( ) { Text( modifier = Modifier, - text = movie.title, + text = movie.title ?: stringResource(Res.string.unknown_movie), fontSize = 28.sp, maxLines = 2, style = MaterialTheme.typography.titleMedium, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt index 90cbde13..7ad79dff 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt @@ -24,6 +24,9 @@ import androidx.compose.ui.unit.sp import coil3.compose.AsyncImage import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.utils.loadImage +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.unknown_movie +import org.jetbrains.compose.resources.stringResource @Composable fun MovieCardPortraitCompact( @@ -48,7 +51,7 @@ fun MovieCardPortraitCompact( .fillMaxWidth() .height(220.dp) .sizeIn(minHeight = 30.dp), - model = movie.posterPath.loadImage(), + model = movie.posterPath?.loadImage(), contentDescription = "Trending movie poster", contentScale = ContentScale.Crop, alignment = Alignment.Center, @@ -57,7 +60,7 @@ fun MovieCardPortraitCompact( Text( modifier = Modifier.width(145.dp), - text = movie.title, + text = movie.title?: stringResource(Res.string.unknown_movie), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, fontSize = 14.sp, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index 3d319424..d7383862 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState @@ -150,7 +149,9 @@ fun HomeScreen( //endregion //region Upcoming Movies - homeUiState.upcomingMovies?.let { + homeUiState.upcomingMovies?.let { movies -> + val upcomingMovies = movies.collectAsLazyPagingItems() + SectionSeparator( modifier = Modifier .fillMaxWidth() @@ -163,16 +164,18 @@ fun HomeScreen( horizontalArrangement = Arrangement.spacedBy(14.dp), modifier = Modifier.wrapContentHeight() ) { - items(items = it) { item -> - MovieCardLandscape( - modifier = Modifier - .width(300.dp) - .height(245.dp), - movie = item, - onClickItem = { movie -> - navigator.navigate("/details/${movie.id}") - } - ) + items(upcomingMovies.itemCount) { index -> + upcomingMovies[index]?.let { + MovieCardLandscape( + modifier = Modifier + .width(300.dp) + .height(245.dp), + movie = it, + onClickItem = { movie -> + navigator.navigate("/details/${movie.id}") + } + ) + } } } } 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 101d7e9b..4fcd87c7 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 @@ -71,8 +71,11 @@ class HomeViewModel(private val moviesRepository: MoviesRepository) : ViewModel( fun fetchUpcomingMovies() = viewModelScope.launch(coroutineExceptionHandler) { moviesRepository.fetchUpcomingMovies().onSuccess { data -> - data.collectLatest { movies -> - _homeUiState.update { it.copy(upcomingMovies = movies, isLoading = false) } + _homeUiState.update { + it.copy( + upcomingMovies = data.cachedIn(viewModelScope), + isLoading = false + ) } }.onFailure { error -> _homeUiState.update { it.copy(error = error.message, isLoading = false) } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index 841d37b0..479b9f2c 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -16,7 +16,7 @@ data class HomeUiState( val nowPlayingMovies: List? = emptyList(), val trendingMovies: Flow>? = null, val popularMovies: Flow>? = null, - val upcomingMovies: List? = emptyList(), + val upcomingMovies: Flow>? = null, ) data class DetailsUiState( From 4c8b673d1650338f53ac0f71a0abf857e068a7a9 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Wed, 25 Sep 2024 10:23:26 +0300 Subject: [PATCH 07/10] Set up pagination for search screen --- .../data/datasources/SearchRepositoryImpl.kt | 25 +++++++++++------ .../domain/repositories/SearchRepository.kt | 6 ++--- .../ui/screens/search/SearchScreen.kt | 27 +++++++++++-------- .../ui/screens/search/SearchViewModel.kt | 12 ++++----- .../com/vickbt/composeApp/utils/UiStates.kt | 2 +- 5 files changed, 42 insertions(+), 30 deletions(-) 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 2590b0e1..fb966062 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 @@ -1,8 +1,11 @@ package com.vickbt.composeApp.data.datasources +import app.cash.paging.Pager +import app.cash.paging.PagingConfig +import app.cash.paging.PagingData import com.vickbt.composeApp.data.mappers.toDomain import com.vickbt.composeApp.data.network.models.MovieResultsDto -import com.vickbt.composeApp.data.network.utils.safeApiCall +import com.vickbt.composeApp.data.paging.BasePagingSource import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.repositories.SearchRepository import io.ktor.client.HttpClient @@ -15,17 +18,23 @@ class SearchRepositoryImpl( private val httpClient: HttpClient ) : SearchRepository { - override suspend fun searchMovie( - movieName: String, - page: Int - ): Result?>> { - return safeApiCall { + private val pagingConfig = PagingConfig(pageSize = 20, enablePlaceholders = false) + + override suspend fun searchMovie(movieName: String): Result>> { + val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "search/movie") { parameter("query", movieName) parameter("page", page) - }.body() + }.body().movies + + response?.map { it.toDomain() } + } - response.movies?.map { it.toDomain() } + return runCatching { + Pager( + config = pagingConfig, + pagingSourceFactory = { pagingSource } + ).flow } } } 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 35a407de..70ad67df 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 @@ -1,5 +1,6 @@ package com.vickbt.composeApp.domain.repositories +import app.cash.paging.PagingData import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX import kotlinx.coroutines.flow.Flow @@ -7,8 +8,5 @@ import kotlinx.coroutines.flow.Flow interface SearchRepository { // Search movie from network source - suspend fun searchMovie( - movieName: String, - page: Int = STARTING_PAGE_INDEX - ): Result?>> + suspend fun searchMovie(movieName: String, ): Result>> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt index b4df5572..11f6a338 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.rounded.Close @@ -38,10 +37,11 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController -import com.vickbt.shared.resources.Res -import com.vickbt.shared.resources.title_search +import app.cash.paging.compose.collectAsLazyPagingItems import com.vickbt.composeApp.ui.components.MovieCardPortrait import com.vickbt.composeApp.utils.WindowSize +import com.vickbt.shared.resources.Res +import com.vickbt.shared.resources.title_search import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import org.koin.core.annotation.KoinExperimentalAPI @@ -120,6 +120,8 @@ fun SearchScreen( ) } else { searchUiState.movieResults?.let { movieResults -> + val searchResults = movieResults.collectAsLazyPagingItems() + LazyVerticalGrid( modifier = Modifier.fillMaxSize(), columns = if (windowSize == WindowSize.COMPACT) { @@ -131,14 +133,17 @@ fun SearchScreen( verticalArrangement = Arrangement.spacedBy(8.dp), contentPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp) ) { - items(items = movieResults) { item -> - MovieCardPortrait( - modifier = Modifier, - movie = item, - onItemClick = { movie -> - navigator.navigate("/details/${movie.id}") - } - ) + + items(searchResults.itemCount) { index -> + searchResults[index]?.let { movies -> + MovieCardPortrait( + modifier = Modifier, + movie = movies, + onItemClick = { movie -> + navigator.navigate("/details/${movie.id}") + } + ) + } } } } 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 a348415e..956b11d8 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 @@ -2,12 +2,12 @@ package com.vickbt.composeApp.ui.screens.search import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn import com.vickbt.composeApp.domain.repositories.SearchRepository import com.vickbt.composeApp.utils.SearchUiState 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 @@ -16,9 +16,6 @@ class SearchViewModel(private val searchRepository: SearchRepository) : ViewMode private val _searchUiState = MutableStateFlow(SearchUiState(isLoading = false)) val searchUiState = _searchUiState.asStateFlow() - private val _searchQuery = MutableStateFlow("") - val searchQuery = _searchQuery.asStateFlow() - private val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception -> _searchUiState.update { it.copy(isLoading = false, error = exception.message) } } @@ -27,8 +24,11 @@ class SearchViewModel(private val searchRepository: SearchRepository) : ViewMode _searchUiState.update { it.copy(isLoading = true) } searchRepository.searchMovie(movieName = movieName).onSuccess { data -> - data.collectLatest { movies -> - _searchUiState.update { it.copy(movieResults = movies, isLoading = false) } + _searchUiState.update { + it.copy( + movieResults = data.cachedIn(viewModelScope), + isLoading = false + ) } }.onFailure { error -> _searchUiState.update { it.copy(error = error.message, isLoading = false) } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index 479b9f2c..590ce109 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -31,7 +31,7 @@ data class DetailsUiState( data class SearchUiState( val isLoading: Boolean = true, val error: String? = null, - val movieResults: List? = emptyList() + val movieResults: Flow>? = null ) data class FavouritesUiState( From 27bdde6e54dc0191a786569d7bdc01364a472e68 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Wed, 25 Sep 2024 10:35:25 +0300 Subject: [PATCH 08/10] Added pagination to similar movies section --- .../datasources/MovieDetailsRepositoryImpl.kt | 24 +++++++++++++------ .../repositories/MovieDetailsRepository.kt | 7 ++---- .../ui/screens/details/DetailsScreen.kt | 10 +++++--- .../ui/screens/details/DetailsViewModel.kt | 8 +++++-- .../com/vickbt/composeApp/utils/UiStates.kt | 2 +- 5 files changed, 33 insertions(+), 18 deletions(-) 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 25287f11..d1cf7541 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 @@ -1,5 +1,8 @@ package com.vickbt.composeApp.data.datasources +import androidx.paging.PagingData +import app.cash.paging.Pager +import app.cash.paging.PagingConfig import com.vickbt.composeApp.data.cache.AppDatabase import com.vickbt.composeApp.data.mappers.toDomain import com.vickbt.composeApp.data.mappers.toEntity @@ -7,6 +10,7 @@ import com.vickbt.composeApp.data.network.models.CastDto import com.vickbt.composeApp.data.network.models.MovieDetailsDto import com.vickbt.composeApp.data.network.models.MovieResultsDto import com.vickbt.composeApp.data.network.utils.safeApiCall +import com.vickbt.composeApp.data.paging.BasePagingSource import com.vickbt.composeApp.domain.models.Cast import com.vickbt.composeApp.domain.models.Movie import com.vickbt.composeApp.domain.models.MovieDetails @@ -25,6 +29,8 @@ class MovieDetailsRepositoryImpl( private val appDatabase: AppDatabase ) : MovieDetailsRepository { + private val pagingConfig = PagingConfig(pageSize = 20, enablePlaceholders = false) + override suspend fun fetchMovieDetails(movieId: Int): Result> { val isMovieCached = isMovieFavorite(movieId = movieId).getOrDefault(flowOf(false)) .firstOrNull() @@ -33,8 +39,8 @@ class MovieDetailsRepositoryImpl( getFavoriteMovie(movieId = movieId) } else { safeApiCall { - httpClient.get(urlString = "movie/$movieId").body().toDomain() - } + httpClient.get(urlString = "movie/$movieId").body().toDomain() + } } } @@ -44,17 +50,21 @@ class MovieDetailsRepositoryImpl( } } - override suspend fun fetchSimilarMovies( - movieId: Int, - page: Int - ): Result?>> { - return safeApiCall { + override suspend fun fetchSimilarMovies(movieId: Int): Result>> { + val pagingSource = BasePagingSource { page -> val response = httpClient.get(urlString = "movie/$movieId/similar") { parameter("page", page) }.body() response.movies?.map { it.toDomain() } } + + return runCatching { + Pager( + config = pagingConfig, + pagingSourceFactory = { pagingSource } + ).flow + } } override suspend fun saveFavoriteMovie(movie: MovieDetails) { 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 c78a3243..e6a65156 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 @@ -1,9 +1,9 @@ package com.vickbt.composeApp.domain.repositories +import androidx.paging.PagingData 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 kotlinx.coroutines.flow.Flow interface MovieDetailsRepository { @@ -15,10 +15,7 @@ interface MovieDetailsRepository { suspend fun fetchMovieCast(movieId: Int): Result> /** Fetches similar movies from network source*/ - suspend fun fetchSimilarMovies( - movieId: Int, - page: Int = STARTING_PAGE_INDEX - ): Result?>> + suspend fun fetchSimilarMovies(movieId: Int): Result>> /**Save movie details to local cache*/ suspend fun saveFavoriteMovie(movie: MovieDetails) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt index 71165fbe..72bd0ecf 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/details/DetailsScreen.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController +import app.cash.paging.compose.collectAsLazyPagingItems import com.vickbt.composeApp.ui.components.ItemMovieCast import com.vickbt.composeApp.ui.components.MovieCardPortrait import com.vickbt.composeApp.ui.components.MovieRatingSection @@ -152,7 +153,8 @@ fun DetailsScreen( //endregion //region Similar Movies - if (!movieDetailsState.similarMovies.isNullOrEmpty()) { + movieDetailsState.similarMovies?.let { + val similarMovies = it.collectAsLazyPagingItems() Text( modifier = Modifier.padding(horizontal = 16.dp), text = stringResource(Res.string.similar_movies), @@ -165,8 +167,10 @@ fun DetailsScreen( contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(10.dp) ) { - items(items = movieDetailsState.similarMovies) { movie -> - MovieCardPortrait(movie = movie, onItemClick = {}) + items(similarMovies.itemCount) { index -> + similarMovies[index]?.let { movie -> + MovieCardPortrait(movie = movie, onItemClick = {}) + } } } } 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 7e3cc326..7765b070 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 @@ -2,6 +2,7 @@ package com.vickbt.composeApp.ui.screens.details import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.paging.cachedIn import com.vickbt.composeApp.domain.models.MovieDetails import com.vickbt.composeApp.domain.repositories.MovieDetailsRepository import com.vickbt.composeApp.utils.DetailsUiState @@ -48,8 +49,11 @@ class DetailsViewModel( fun fetchSimilarMovies(movieId: Int) = viewModelScope.launch(coroutineExceptionHandler) { _movieDetailsState.update { it.copy(isLoading = true) } movieDetailsRepository.fetchSimilarMovies(movieId).onSuccess { data -> - data.collectLatest { movies -> - _movieDetailsState.update { it.copy(similarMovies = movies, isLoading = false) } + _movieDetailsState.update { + it.copy( + similarMovies = data.cachedIn(viewModelScope), + isLoading = false + ) } }.onFailure { error -> _movieDetailsState.update { it.copy(error = error.message, isLoading = false) } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt index 590ce109..8a14d4db 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/utils/UiStates.kt @@ -24,7 +24,7 @@ data class DetailsUiState( val error: String? = null, val movieDetails: MovieDetails? = null, val movieCast: List? = emptyList(), - val similarMovies: List? = emptyList(), + val similarMovies: Flow>? = null, val isFavorite: Boolean? = false ) From 408b5de3109764771b323566fd792886cae67231 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Wed, 25 Sep 2024 10:38:46 +0300 Subject: [PATCH 09/10] Set movie backdrop as nullable --- .../vickbt/composeApp/data/network/models/MovieDetailsDto.kt | 4 ++-- .../com/vickbt/composeApp/domain/models/MovieDetails.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDetailsDto.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDetailsDto.kt index 012f9075..67a598da 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDetailsDto.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/network/models/MovieDetailsDto.kt @@ -10,7 +10,7 @@ data class MovieDetailsDto( val adult: Boolean? = null, @SerialName("backdrop_path") - val backdropPath: String, + val backdropPath: String? = null, @SerialName("genres") val genres: List? = null, @@ -37,7 +37,7 @@ data class MovieDetailsDto( val popularity: Double? = null, @SerialName("poster_path") - val posterPath: String, + val posterPath: String? = null, @SerialName("release_date") val releaseDate: String? = null, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieDetails.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieDetails.kt index 416f2562..e4f1ada9 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieDetails.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/domain/models/MovieDetails.kt @@ -4,7 +4,7 @@ data class MovieDetails( val adult: Boolean? = null, - val backdropPath: String, + val backdropPath: String? = null, val homepage: String? = null, @@ -20,7 +20,7 @@ data class MovieDetails( val popularity: Double? = null, - val posterPath: String, + val posterPath: String? = null, val releaseDate: String? = null, From 7c715e2ccb2e08cf777589a6dd7b831be18733b7 Mon Sep 17 00:00:00 2001 From: Victor Kabata Date: Wed, 25 Sep 2024 10:40:33 +0300 Subject: [PATCH 10/10] Linting --- .../com/vickbt/composeApp/data/paging/BasePagingSource.kt | 2 +- .../vickbt/composeApp/domain/repositories/SearchRepository.kt | 3 +-- .../composeApp/ui/components/MovieCardPortraitCompact.kt | 2 +- .../kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt | 1 - .../com/vickbt/composeApp/ui/screens/search/SearchScreen.kt | 1 - 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt index 602bfed5..04fe16b2 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/data/paging/BasePagingSource.kt @@ -10,7 +10,7 @@ class BasePagingSource(val fetchData: suspend (page: Int) -> List?) override suspend fun load(params: LoadParams): LoadResult { val page = params.key ?: STARTING_PAGE_INDEX - val data = fetchData(page)?: emptyList() + val data = fetchData(page) ?: emptyList() return try { LoadResult.Page( 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 70ad67df..8ac29653 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,11 +2,10 @@ package com.vickbt.composeApp.domain.repositories import app.cash.paging.PagingData import com.vickbt.composeApp.domain.models.Movie -import com.vickbt.composeApp.domain.utils.Constants.STARTING_PAGE_INDEX import kotlinx.coroutines.flow.Flow interface SearchRepository { // Search movie from network source - suspend fun searchMovie(movieName: String, ): Result>> + suspend fun searchMovie(movieName: String): Result>> } diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt index 7ad79dff..fa6cf36d 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/components/MovieCardPortraitCompact.kt @@ -60,7 +60,7 @@ fun MovieCardPortraitCompact( Text( modifier = Modifier.width(145.dp), - text = movie.title?: stringResource(Res.string.unknown_movie), + text = movie.title ?: stringResource(Res.string.unknown_movie), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSurface, fontSize = 14.sp, diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt index d7383862..191dcb90 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/home/HomeScreen.kt @@ -43,7 +43,6 @@ import com.vickbt.shared.resources.upcoming_movies import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel - @OptIn(ExperimentalFoundationApi::class) @Composable fun HomeScreen( diff --git a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt index 11f6a338..54f58d2d 100644 --- a/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/vickbt/composeApp/ui/screens/search/SearchScreen.kt @@ -133,7 +133,6 @@ fun SearchScreen( verticalArrangement = Arrangement.spacedBy(8.dp), contentPadding = PaddingValues(vertical = 8.dp, horizontal = 16.dp) ) { - items(searchResults.itemCount) { index -> searchResults[index]?.let { movies -> MovieCardPortrait(