Skip to content

Commit

Permalink
Add pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
ayodelekehinde committed Sep 9, 2023
1 parent 738a5a2 commit f8bac5d
Show file tree
Hide file tree
Showing 20 changed files with 122 additions and 103 deletions.
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

plugins {
kotlin("jvm") version "1.7.20" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "1.7.22" apply false
kotlin("jvm") version "1.9.0" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.0" apply false
id("io.ktor.plugin") version "2.1.3" apply false
kotlin("multiplatform").version("1.7.20").apply(false)
id("org.jetbrains.compose").version("1.2.2").apply(false)
kotlin("multiplatform").version("1.9.0").apply(false)
id("org.jetbrains.compose").version("1.5.0").apply(false)
}
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
Expand Down
14 changes: 12 additions & 2 deletions desktop/src/jvmMain/kotlin/data/remote/Movie.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package data.remote

import kotlinx.serialization.Serializable
import presentation.player.Subtitle

@Serializable
data class Movie(
val name: String,
val url: String,
val image: String,
val movieType: MovieType,
val id: Int = 0,
val summary: String = "",
val genre: String = "",
val releaseDate: String = "",
Expand All @@ -18,7 +18,17 @@ data class Movie(
val subtitleUrl: String = ""
){
fun isComplete() = summary.isNotEmpty() && streamingUrl.isNotEmpty()

fun addId() = copy(id = generateNextId())
companion object {
private var currentId = 0
}
// Use a synchronized function to generate the next ID safely
private fun generateNextId(): Int {
synchronized(this) {
currentId++
return currentId
}
}
}

enum class MovieType{
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/jvmMain/kotlin/data/remote/MovieDataSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ class MovieDataSource(private val clientEngine: HttpClientEngine) {
val name = if (isSeries) it.select(".title").text() else nameAndImageElement.attr("title")
val image = nameAndImageElement.attr("src")
val movieUrl = it.select("a").attr("href")
Movie(name, movieUrl, image, getGenre(url))
Movie(name = name, url = movieUrl, image = image, getGenre(url)).addId()
}
}
}
Expand Down
43 changes: 12 additions & 31 deletions desktop/src/jvmMain/kotlin/domain/usecase/GetTitlesUseCase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@ package domain.usecase
import data.remote.Movie
import data.remote.MovieType
import domain.repo.MovieRepo
import presentation.home.SidePaneOptions

class GetTitlesUseCase(private val movieRepo: MovieRepo) {

suspend operator fun invoke(titleOptions: TitleOptions): Map<String, List<Movie>> {
suspend operator fun invoke(titleOptions: TitleOptions, page: Int = 1): Map<String, List<Movie>> {
return when(titleOptions){
TitleOptions.HOLLYWOOD -> {
val response = mutableListOf<Movie>()
repeat(2){
val list = movieRepo.getMovie(TitleOptions.HOLLYWOOD,it+1)
response.addAll(list)
}
val list = movieRepo.getMovie(TitleOptions.HOLLYWOOD, page)
mapOf(
"Hollywood" to response,
TitleOptions.HOLLYWOOD.toString() to list,
)
}
TitleOptions.HOME -> {
Expand All @@ -33,33 +28,19 @@ class GetTitlesUseCase(private val movieRepo: MovieRepo) {
)
}
TitleOptions.NOLLYWOOD -> {
val response = mutableListOf<Movie>()
repeat(2){
val list = movieRepo.getMovie(TitleOptions.NOLLYWOOD,it+1)
response.addAll(list)
}
mapOf(
"Nollywood" to response,
TitleOptions.NOLLYWOOD.toString() to movieRepo.getMovie(TitleOptions.NOLLYWOOD, page),
)
}
TitleOptions.TVSERIES -> {
val response = mutableListOf<Movie>()
repeat(2){
val list = movieRepo.getMovie(TitleOptions.TVSERIES,it+1)
response.addAll(list)
}
val list = movieRepo.getMovie(TitleOptions.TVSERIES, page)
mapOf(
"Tv Series" to response,
TitleOptions.TVSERIES.toString() to list,
)
}
TitleOptions.YOLLYWOOD -> {
val response = mutableListOf<Movie>()
repeat(2){
val list = movieRepo.getMovie(TitleOptions.YOLLYWOOD,it+1)
response.addAll(list)
}
mapOf(
"Yollywood" to response,
TitleOptions.YOLLYWOOD.toString() to movieRepo.getMovie(TitleOptions.YOLLYWOOD, page),
)
}
}
Expand All @@ -68,10 +49,10 @@ class GetTitlesUseCase(private val movieRepo: MovieRepo) {


sealed interface TitleOptions{
object HOME: TitleOptions
object HOLLYWOOD: TitleOptions
object NOLLYWOOD: TitleOptions
object TVSERIES: TitleOptions
object YOLLYWOOD: TitleOptions
data object HOME: TitleOptions
data object HOLLYWOOD: TitleOptions
data object NOLLYWOOD: TitleOptions
data object TVSERIES: TitleOptions
data object YOLLYWOOD: TitleOptions
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package presentation.details

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down
33 changes: 28 additions & 5 deletions desktop/src/jvmMain/kotlin/presentation/home/AppViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import data.remote.Series
import domain.usecase.GetTitleDetailsUseCase
import domain.usecase.GetTitlesUseCase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import org.koin.core.component.KoinComponent
import org.orbitmvi.orbit.ContainerHost
import org.orbitmvi.orbit.container
import org.orbitmvi.orbit.syntax.simple.intent
import org.orbitmvi.orbit.syntax.simple.reduce
import java.time.Month

class AppViewModel(
private val scope: CoroutineScope,
Expand All @@ -26,7 +24,17 @@ class AppViewModel(
if (!state.isMovieDetails) {
reduce { state.copy(loading = true) }
val data = getTitlesUseCase(titleOptions = sidePaneOptions.toTitle())
reduce { state.copy(loading = false, titles = data) }
if (sidePaneOptions == SidePaneOptions.HOME){
reduce { state.copy(loading = false, homeTitle = data) }
}else {
reduce {
state.copy(
loading = false,
titles = data[sidePaneOptions.toTitle().toString()].orEmpty(),
currentPage = sidePaneOptions.toTitle().toString()
)
}
}
}
}
fun openMovieDetails(movie: Movie) = intent {
Expand Down Expand Up @@ -102,6 +110,18 @@ class AppViewModel(
SidePaneOptions.YOLLYWOOD -> GetTitlesUseCase.TitleOptions.YOLLYWOOD
}

fun loadMore(options: SidePaneOptions, page: Int) = intent {
println("Page: $page")
if (!state.isMovieDetails) {
reduce { state.copy(isLoadingMore = true) }
val data = getTitlesUseCase(titleOptions = options.toTitle(), page)
val newMovies = state.titles + data[options.toTitle().toString()].orEmpty()
reduce {
state.copy(isLoadingMore = false, titles = newMovies, currentPage = options.toTitle().toString())
}
}
}

companion object: KoinComponent{
fun getViewModel() = getKoin().get<AppViewModel>()
}
Expand All @@ -110,10 +130,13 @@ class AppViewModel(

data class HomeState(
val loading: Boolean = false,
val titles: Map<String, List<Movie>>? = null,
val titles: List<Movie> = emptyList(),
val homeTitle: Map<String, List<Movie>> = mapOf(),
val isMovieDetails: Boolean = false,
val movie: Movie? = null,
val isDetailsLoading: Boolean = false,
val isMoviePlaying: Boolean = false,
val series: List<Series> = emptyList()
val series: List<Series> = emptyList(),
val isLoadingMore: Boolean = false,
val currentPage: String = ""
)
16 changes: 12 additions & 4 deletions desktop/src/jvmMain/kotlin/presentation/home/FullPane.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,18 @@ fun FullPane(
CircularProgressIndicator(Modifier.size(50.dp).align(Alignment.Center), color = Color.White)
}
}
!state.loading && state.titles != null && !state.isMovieDetails && !state.isMoviePlaying ->{
MovieListScreen(sidePaneOptions, state.titles){
viewModel.openMovieDetails(it)
}
!state.loading && (state.titles.isNotEmpty() || state.homeTitle.isNotEmpty()) && !state.isMovieDetails && !state.isMoviePlaying ->{
MovieListScreen(
currentPage = state.currentPage,
homeTitles = state.homeTitle,
isLoadingMore = state.isLoadingMore,
sidePaneOptions,
state.titles,
onLoadMore = { options, page ->
viewModel.loadMore(options, page)
},
onClick = { viewModel.openMovieDetails(it) }
)
}
state.isMovieDetails && state.movie != null -> {
MovieDetailsScreen(
Expand Down
2 changes: 0 additions & 2 deletions desktop/src/jvmMain/kotlin/presentation/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ package presentation.home

import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
Expand Down
54 changes: 38 additions & 16 deletions desktop/src/jvmMain/kotlin/presentation/home/MovieListScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.*
import androidx.compose.foundation.lazy.items
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
Expand All @@ -28,11 +25,32 @@ import presentation.ui.DARK_L

@Composable
fun MovieListScreen(
currentPage: String,
homeTitles: Map<String, List<Movie>>,
isLoadingMore: Boolean,
sidePaneOptions: SidePaneOptions,
data: Map<String,List<Movie>>,
data: List<Movie>,
onLoadMore: (SidePaneOptions, Int) -> Unit,
onClick: (Movie) -> Unit
) {
//val list = listOf("Popular", "Watched", "Nollywood", "Yollywood", "Tvseries", "Hollywood", "My list")
val lazyState = rememberLazyGridState()
val isScrolledToEnd by remember(data) {
derivedStateOf {
val totalItems = data.size
val lastVisibleItem = lazyState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
lastVisibleItem == totalItems //- threshold
}
}

LaunchedEffect(isScrolledToEnd, data.size){
if (isScrolledToEnd){
val page = (data.size) / 18 + 1
onLoadMore(sidePaneOptions, page)
}

}

when(sidePaneOptions){
SidePaneOptions.HOME -> {
LazyColumn(
Expand All @@ -41,37 +59,41 @@ fun MovieListScreen(
verticalArrangement = Arrangement.spacedBy(40.dp)
) {

items(data.toList()) {
items(homeTitles.toList()) {
MovieList(it.first, it.second, onClick = onClick)
}
}
}
else -> {
LazyVerticalGrid(
GridCells.Fixed(5),
modifier = Modifier.fillMaxWidth()
.background(color = DARK_L).padding(bottom = 10.dp),
state = lazyState,
columns = GridCells.Fixed(5),
modifier = Modifier.fillMaxWidth().background(color = DARK_L).padding(bottom = 10.dp),
horizontalArrangement = Arrangement.spacedBy(5.dp)
) {
item(span = {
GridItemSpan(currentLineSpan = 5)
}) {
item(span = { GridItemSpan(currentLineSpan = 5) }) {
Text(
data.toList().first().first.uppercase(),
currentPage.uppercase(),
color = Color.White,
fontSize = 14.sp,
fontWeight = FontWeight.W500,
modifier = Modifier.padding(15.dp)
)
}
items(data.toList().first().second){
items(data, key = { it.id }){
MovieItem(
movie = it,
modifier = Modifier.padding(5.dp),
onClick = onClick
)
}

if (isLoadingMore) {
item(span = { GridItemSpan(currentLineSpan = 5) }) {
Box(modifier = Modifier.fillMaxWidth()) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions desktop/src/jvmMain/kotlin/presentation/home/SidePane.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Home
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
Expand Down
2 changes: 0 additions & 2 deletions desktop/src/jvmMain/kotlin/presentation/player/VideoPlayer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import uk.co.caprica.vlcj.player.embedded.videosurface.callback.format.RV32Buffe
import java.net.URL
import java.nio.ByteBuffer
import java.util.*
import java.util.logging.Level
import java.util.logging.Logger
import javax.swing.JComponent
import kotlin.time.Duration
import kotlin.time.DurationUnit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.*
import androidx.compose.ui.input.pointer.*
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand Down
1 change: 0 additions & 1 deletion desktop/src/jvmMain/kotlin/presentation/ui/AppColor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package presentation.ui

import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color

Expand Down
Loading

0 comments on commit f8bac5d

Please sign in to comment.