diff --git a/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt b/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt new file mode 100644 index 0000000..b964459 --- /dev/null +++ b/app/src/main/java/hu/bbara/purefin/app/content/ContentMockData.kt @@ -0,0 +1,162 @@ +package hu.bbara.purefin.app.content + +import hu.bbara.purefin.app.content.episode.EpisodeUiModel +import hu.bbara.purefin.app.content.movie.MovieUiModel +import hu.bbara.purefin.app.content.series.SeriesCastMemberUiModel +import hu.bbara.purefin.app.content.series.SeriesEpisodeUiModel +import hu.bbara.purefin.app.content.series.SeriesSeasonUiModel +import hu.bbara.purefin.app.content.series.SeriesUiModel +import org.jellyfin.sdk.model.UUID +import hu.bbara.purefin.app.content.episode.CastMember as EpisodeCastMember +import hu.bbara.purefin.app.content.movie.CastMember as MovieCastMember + +object ContentMockData { + fun series(): SeriesUiModel { + val heroUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuD3hBjDpw00tDCQsK5xNcnJra301k1T4LksWVZzHieH9KHQItEQkVzhwevJvf8RkaQKdVKvObzRlfDDqa3_PNwLUlUQc1LpDih8p94VTGobEV62qi7QrmNyQm_o55KRMNWiTG3zLLpblGqo3uUNQcYmPFqfNML95dClXQ4lQNl85-zgerPPAbGPr23dswbIYCigyTAaXgrmdV_nbNQ5LdDB0Wh5cMHtP0uxz6k3ARjNom6clhphGIUF9e6YSvKuwuiZ-1lMYFg8C_4" + val episode1 = SeriesEpisodeUiModel( + id = "1", + title = "E1: The Beginning", + description = "The crew assembles for the first time as the anomaly begins to expand rapidly near Saturn's rings.", + duration = "58m", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" + ) + val episode2 = SeriesEpisodeUiModel( + id = "2", + title = "E2: Event Horizon", + description = "Dr. Cole discovers a frequency embedded in the rift's radiation that suggests intelligent design.", + duration = "54m", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" + ) + val episode3 = SeriesEpisodeUiModel( + id = "3", + title = "E3: Singularity", + description = "Tension rises as the ship approaches the event horizon, and the AI begins to behave erratically.", + duration = "1h 02m", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U" + ) + return SeriesUiModel( + title = "Interstellar Horizon: The Series", + year = "2024", + rating = "TV-MA", + seasons = "3 Seasons", + format = "4K HDR", + synopsis = "When a mysterious cosmic rift appears near Saturn, a team of seasoned astronauts and theoretical physicists must embark on a high-stakes voyage across dimensions. They seek to unlock the secrets of time-dilated anomalies that threaten the very fabric of human existence on Earth.", + heroImageUrl = heroUrl, + seasonTabs = listOf( + SeriesSeasonUiModel( + name = "Season 1", + isSelected = true, + episodes = listOf(episode1, episode2, episode3) + ), + SeriesSeasonUiModel( + name = "Season 2", + isSelected = false, + episodes = listOf(episode1, episode2, episode3) + ), + SeriesSeasonUiModel( + name = "Season 3", + isSelected = false, + episodes = listOf(episode1, episode2, episode3) + ) + ), + cast = listOf( + SeriesCastMemberUiModel( + name = "Marcus Thorne", + role = "Cmdr. Vance", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" + ), + SeriesCastMemberUiModel( + name = "Elena Rossi", + role = "Dr. Sarah Cole", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" + ), + SeriesCastMemberUiModel( + name = "Julian Chen", + role = "Tech Officer Lin", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U" + ), + SeriesCastMemberUiModel( + name = "Sarah Jenkins", + role = "Mission Pilot", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBN6_72VggBdNx7ITLvEvIA6OSre5iJI6kQiUVMpKAlYgd8TpT-Jx6DzZwGsGACLnAXOUuzT2R7mx9A9DNZcqi5BF_jSaEdeYpfcBvJttmVPAwiCiq1_PI2BwoZZH_Ccmq2AHV5lQqcYaA2rPkf4e7YLLLgpmVbGjKhncTotQtxiZvmLNzCbLUdlEb7XLgHKfjS6FU6djV9ocOo9bxZ_YtrQj-mMFvYGzCxeFYC8OF0kIV2NN3kQYH8x1X-rYMqu2-d7klJfQdhKHw" + ), + SeriesCastMemberUiModel( + name = "David Wu", + role = "The AI (Voice)", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuCnNkjaBc2hU2zJ5hAF8iZZ_ZZvMlU79o4JtPNCP2MEfttpF0fe_BHWsMMl6h3S37FJ1dTLk8AQuvRQ_ggy1u-71xlQWULB76rT8pdZiRE7TkInQ8gwpigs84KNWbTRxVUI7Nia9RPyJeFE7egZqnT46TQWUeN8llWF9EDQ6mpfVLH0vHhKUlko39iDgMnBIequYntugSFgWJQc1jH-AxZ4OpJr_-uZGkwtQ_CVYNV69u9y107gk5BwaUFwPeipe8Bn9I655kyHIuQ" + ), + SeriesCastMemberUiModel( + name = "Alex Reed", + role = "Engineer", + imageUrl = null + ) + ) + ) + } + + fun movie(): MovieUiModel { + val castMembers = listOf( + MovieCastMember( + name = "Elena Rossi", + role = "Dr. Sarah Cole", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" + ), + MovieCastMember( + name = "Marcus Thorne", + role = "Cmdr. Vance", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" + ), + MovieCastMember( + name = "Julian Chen", + role = "Tech Officer Lin", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U" + ) + ) + return MovieUiModel( + id = UUID.randomUUID(), + title = "Interstellar Horizon", + year = "2024", + rating = "PG-13", + runtime = "2h 14m", + format = "4K HDR", + synopsis = "A deep-space rescue crew is dispatched to intercept a derelict vessel drifting back from a temporal rift. As timelines fracture around them, they must decide which reality is worth saving.", + heroImageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuD3hBjDpw00tDCQsK5xNcnJra301k1T4LksWVZzHieH9KHQItEQkVzhwevJvf8RkaQKdVKvObzRlfDDqa3_PNwLUlUQc1LpDih8p94VTGobEV62qi7QrmNyQm_o55KRMNWiTG3zLLpblGqo3uUNQcYmPFqfNML95dClXQ4lQNl85-zgerPPAbGPr23dswbIYCigyTAaXgrmdV_nbNQ5LdDB0Wh5cMHtP0uxz6k3ARjNom6clhphGIUF9e6YSvKuwuiZ-1lMYFg8C_4", + audioTrack = "English (Dolby Atmos)", + subtitles = "English, Spanish, French", + cast = castMembers + ) + } + + fun episode(): EpisodeUiModel { + val castMembers = listOf( + EpisodeCastMember( + name = "Elena Rossi", + role = "Dr. Sarah Cole", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" + ), + EpisodeCastMember( + name = "Marcus Thorne", + role = "Cmdr. Vance", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" + ), + EpisodeCastMember( + name = "David Wu", + role = "The AI (Voice)", + imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuCnNkjaBc2hU2zJ5hAF8iZZ_ZZvMlU79o4JtPNCP2MEfttpF0fe_BHWsMMl6h3S37FJ1dTLk8AQuvRQ_ggy1u-71xlQWULB76rT8pdZiRE7TkInQ8gwpigs84KNWbTRxVUI7Nia9RPyJeFE7egZqnT46TQWUeN8llWF9EDQ6mpfVLH0vHhKUlko39iDgMnBIequYntugSFgWJQc1jH-AxZ4OpJr_-uZGkwtQ_CVYNV69u9y107gk5BwaUFwPeipe8Bn9I655kyHIuQ" + ) + ) + return EpisodeUiModel( + id = UUID.randomUUID(), + title = "S1E1 ยท Event Horizon", + releaseDate = "Oct 12, 2024", + rating = "TV-MA", + runtime = "58m", + format = "4K HDR", + synopsis = "As the anomaly near Saturn destabilizes, the crew boards the research vessel Helios to intercept what might be a distress signal from another timeline.", + heroImageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0", + audioTrack = "English (Dolby Atmos)", + subtitles = "English, Spanish, German", + cast = castMembers + ) + } +} diff --git a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt index 7ef2d40..d0fe9ea 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt @@ -1,34 +1,20 @@ package hu.bbara.purefin.app.content.episode -import android.content.Intent -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Cast import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -36,13 +22,10 @@ import androidx.hilt.navigation.compose.hiltViewModel import hu.bbara.purefin.common.ui.MediaActionButtons import hu.bbara.purefin.common.ui.MediaCastMember import hu.bbara.purefin.common.ui.MediaCastRow -import hu.bbara.purefin.common.ui.MediaFloatingPlayButton import hu.bbara.purefin.common.ui.MediaGhostIconButton import hu.bbara.purefin.common.ui.MediaMetaChip import hu.bbara.purefin.common.ui.MediaPlaybackSettings -import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.common.ui.toMediaDetailColors -import hu.bbara.purefin.player.PlayerActivity @Composable internal fun EpisodeTopBar( @@ -140,99 +123,6 @@ internal fun EpisodeDetails( } } -@Composable -fun EpisodeCard( - episode: EpisodeUiModel, - backGroundColor: Color, - modifier: Modifier = Modifier, -) { - val colors = rememberEpisodeColors().toMediaDetailColors() - val context = LocalContext.current - val playAction = remember(episode.id) { - { - val intent = Intent(context, PlayerActivity::class.java) - intent.putExtra("MEDIA_ID", episode.id.toString()) - context.startActivity(intent) - } - } - - BoxWithConstraints( - modifier = modifier - .fillMaxSize() - .background(colors.background) - ) { - val isWide = maxWidth >= 900.dp - val contentPadding = if (isWide) 32.dp else 20.dp - - Box(modifier = Modifier.fillMaxSize()) { - if (isWide) { - Row(modifier = Modifier.fillMaxSize()) { - MediaHero( - imageUrl = episode.heroImageUrl, - height = 300.dp, - backgroundColor = backGroundColor, - modifier = Modifier - .fillMaxHeight() - .weight(0.5f) - ) - EpisodeDetails( - episode = episode, - modifier = Modifier - .weight(0.5f) - .fillMaxHeight() - .verticalScroll(rememberScrollState()) - .padding( - start = contentPadding, - end = contentPadding, - top = 96.dp, - bottom = 32.dp - ) - ) - } - } else { - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - MediaHero( - imageUrl = episode.heroImageUrl, - backgroundColor = backGroundColor, - height = 400.dp, - modifier = Modifier.fillMaxWidth() - ) - EpisodeDetails( - episode = episode, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = contentPadding) - .offset(y = (-48).dp) - .padding(bottom = 96.dp) - ) - } - } - - EpisodeTopBar( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp) - ) - - if (!isWide) { - MediaFloatingPlayButton( - containerColor = colors.primary, - onContainerColor = colors.onPrimary, - onClick = playAction, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(20.dp) - ) - } - } - } - -} - private fun CastMember.toMediaCastMember() = MediaCastMember( name = name, role = role, diff --git a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt index f3e5059..960068e 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt @@ -1,13 +1,37 @@ package hu.bbara.purefin.app.content.episode +import android.content.Intent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import hu.bbara.purefin.app.content.ContentMockData +import hu.bbara.purefin.common.ui.MediaFloatingPlayButton import hu.bbara.purefin.common.ui.PurefinWaitingScreen +import hu.bbara.purefin.common.ui.components.MediaHero +import hu.bbara.purefin.common.ui.toMediaDetailColors import hu.bbara.purefin.navigation.ItemDto +import hu.bbara.purefin.player.PlayerActivity @Composable fun EpisodeScreen( @@ -22,13 +46,115 @@ fun EpisodeScreen( val episode = viewModel.episode.collectAsState() - if (episode.value != null) { - EpisodeCard( - episode = episode.value!!, - modifier = modifier, - backGroundColor = MaterialTheme.colorScheme.background - ) - } else { + if (episode.value == null) { PurefinWaitingScreen() + return + } + + EpisodeScreenInternal( + episode = episode.value!!, + modifier = modifier, + backGroundColor = MaterialTheme.colorScheme.background + ) +} + +@Composable +private fun EpisodeScreenInternal( + episode: EpisodeUiModel, + backGroundColor: Color, + modifier: Modifier = Modifier, +) { + val colors = rememberEpisodeColors().toMediaDetailColors() + val context = LocalContext.current + val playAction = remember(episode.id) { + { + val intent = Intent(context, PlayerActivity::class.java) + intent.putExtra("MEDIA_ID", episode.id.toString()) + context.startActivity(intent) + } + } + + BoxWithConstraints( + modifier = modifier + .fillMaxSize() + .background(colors.background) + ) { + val isWide = maxWidth >= 900.dp + val contentPadding = if (isWide) 32.dp else 20.dp + + Box(modifier = Modifier.fillMaxSize()) { + if (isWide) { + Row(modifier = Modifier.fillMaxSize()) { + MediaHero( + imageUrl = episode.heroImageUrl, + height = 300.dp, + backgroundColor = backGroundColor, + modifier = Modifier + .fillMaxHeight() + .weight(0.5f) + ) + EpisodeDetails( + episode = episode, + modifier = Modifier + .weight(0.5f) + .fillMaxHeight() + .verticalScroll(rememberScrollState()) + .padding( + start = contentPadding, + end = contentPadding, + top = 96.dp, + bottom = 32.dp + ) + ) + } + } else { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + MediaHero( + imageUrl = episode.heroImageUrl, + backgroundColor = backGroundColor, + height = 400.dp, + modifier = Modifier.fillMaxWidth() + ) + EpisodeDetails( + episode = episode, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = contentPadding) + .offset(y = (-48).dp) + .padding(bottom = 96.dp) + ) + } + } + + EpisodeTopBar( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 16.dp) + ) + + if (!isWide) { + MediaFloatingPlayButton( + containerColor = colors.primary, + onContainerColor = colors.onPrimary, + onClick = playAction, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(20.dp) + ) + } + } } } + +@Preview +@Composable +fun EpisodeScreenPreview() { + EpisodeScreenInternal( + episode = ContentMockData.episode(), + backGroundColor = MaterialTheme.colorScheme.background + ) +} diff --git a/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt b/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt index e48d38f..705bd47 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt @@ -1,34 +1,20 @@ package hu.bbara.purefin.app.content.movie -import android.content.Intent -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Cast import androidx.compose.material.icons.outlined.MoreVert -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -36,13 +22,10 @@ import androidx.hilt.navigation.compose.hiltViewModel import hu.bbara.purefin.common.ui.MediaActionButtons import hu.bbara.purefin.common.ui.MediaCastMember import hu.bbara.purefin.common.ui.MediaCastRow -import hu.bbara.purefin.common.ui.MediaFloatingPlayButton import hu.bbara.purefin.common.ui.MediaGhostIconButton import hu.bbara.purefin.common.ui.MediaMetaChip import hu.bbara.purefin.common.ui.MediaPlaybackSettings -import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.common.ui.toMediaDetailColors -import hu.bbara.purefin.player.PlayerActivity @Composable internal fun MovieTopBar( @@ -140,100 +123,6 @@ internal fun MovieDetails( } } - - -@Composable -fun MovieCard( - movie: MovieUiModel, - modifier: Modifier = Modifier, -) { - val context = LocalContext.current - val playAction = remember(movie.id) { - { - val intent = Intent(context, PlayerActivity::class.java) - intent.putExtra("MEDIA_ID", movie.id.toString()) - context.startActivity(intent) - } - } - - BoxWithConstraints( - modifier = modifier - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - ) { - val isWide = maxWidth >= 900.dp - val contentPadding = if (isWide) 32.dp else 20.dp - - Box(modifier = Modifier.fillMaxSize()) { - if (isWide) { - Row(modifier = Modifier.fillMaxSize()) { - MediaHero( - imageUrl = movie.heroImageUrl, - backgroundColor = MaterialTheme.colorScheme.background, - height = 300.dp, - modifier = Modifier - .fillMaxHeight() - .weight(0.5f) - ) - MovieDetails( - movie = movie, - modifier = Modifier - .weight(0.5f) - .fillMaxHeight() - .verticalScroll(rememberScrollState()) - .padding( - start = contentPadding, - end = contentPadding, - top = 96.dp, - bottom = 32.dp - ) - ) - } - } else { - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - MediaHero( - imageUrl = movie.heroImageUrl, - height = 400.dp, - backgroundColor = MaterialTheme.colorScheme.background, - modifier = Modifier.fillMaxWidth() - ) - MovieDetails( - movie = movie, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = contentPadding) - .offset(y = (-48).dp) - .padding(bottom = 96.dp) - ) - } - } - - MovieTopBar( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp) - ) - - if (!isWide) { - MediaFloatingPlayButton( - containerColor = MaterialTheme.colorScheme.primary, - onContainerColor = MaterialTheme.colorScheme.onPrimary, - - onClick = playAction, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(20.dp) - ) - } - } - } - -} - private fun CastMember.toMediaCastMember() = MediaCastMember( name = name, role = role, diff --git a/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieScreen.kt b/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieScreen.kt index 8553e83..24a4093 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieScreen.kt @@ -1,18 +1,39 @@ package hu.bbara.purefin.app.content.movie +import android.content.Intent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import hu.bbara.purefin.app.content.ContentMockData +import hu.bbara.purefin.common.ui.MediaFloatingPlayButton import hu.bbara.purefin.common.ui.PurefinWaitingScreen +import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.navigation.ItemDto +import hu.bbara.purefin.player.PlayerActivity @Composable fun MovieScreen( - movie: ItemDto, - viewModel: MovieScreenViewModel = hiltViewModel(), - modifier: Modifier = Modifier + movie: ItemDto, viewModel: MovieScreenViewModel = hiltViewModel(), modifier: Modifier = Modifier ) { LaunchedEffect(movie.id) { viewModel.selectMovie(movie.id) @@ -21,11 +42,107 @@ fun MovieScreen( val movieItem = viewModel.movie.collectAsState() if (movieItem.value != null) { - MovieCard( - movie = movieItem.value!!, - modifier = modifier + MovieScreenInternal( + movie = movieItem.value!!, modifier = modifier ) } else { PurefinWaitingScreen() } } + +@Composable +private fun MovieScreenInternal( + movie: MovieUiModel, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + val playAction = remember(movie.id) { + { + val intent = Intent(context, PlayerActivity::class.java) + intent.putExtra("MEDIA_ID", movie.id.toString()) + context.startActivity(intent) + } + } + + BoxWithConstraints( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + val isWide = maxWidth >= 900.dp + val contentPadding = if (isWide) 32.dp else 20.dp + + Box(modifier = Modifier.fillMaxSize()) { + if (isWide) { + Row(modifier = Modifier.fillMaxSize()) { + MediaHero( + imageUrl = movie.heroImageUrl, + backgroundColor = MaterialTheme.colorScheme.background, + height = 300.dp, + modifier = Modifier + .fillMaxHeight() + .weight(0.5f) + ) + MovieDetails( + movie = movie, + modifier = Modifier + .weight(0.5f) + .fillMaxHeight() + .verticalScroll(rememberScrollState()) + .padding( + start = contentPadding, + end = contentPadding, + top = 96.dp, + bottom = 32.dp + ) + ) + } + } else { + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + MediaHero( + imageUrl = movie.heroImageUrl, + height = 400.dp, + backgroundColor = MaterialTheme.colorScheme.background, + modifier = Modifier.fillMaxWidth() + ) + MovieDetails( + movie = movie, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = contentPadding) + .offset(y = (-48).dp) + .padding(bottom = 96.dp) + ) + } + } + + MovieTopBar( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 16.dp) + ) + + if (!isWide) { + MediaFloatingPlayButton( + containerColor = MaterialTheme.colorScheme.primary, + onContainerColor = MaterialTheme.colorScheme.onPrimary, + + onClick = playAction, + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(20.dp) + ) + } + } + } +} + +@Preview +@Composable +fun MovieScreenPreview() { + MovieScreenInternal(movie = ContentMockData.movie()) +} diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesCard.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesCard.kt deleted file mode 100644 index 9ab7726..0000000 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesCard.kt +++ /dev/null @@ -1,128 +0,0 @@ -package hu.bbara.purefin.app.content.series - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp - -@Composable -fun SeriesCard( - series: SeriesUiModel, - modifier: Modifier = Modifier, -) { - val colors = rememberSeriesColors() - - BoxWithConstraints( - modifier = modifier - .fillMaxSize() - .background(colors.background) - ) { - val heroHeight = maxHeight * 0.4f - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - ) { - SeriesHero( - imageUrl = series.heroImageUrl, - height = heroHeight - ) - Column( - modifier = Modifier - .fillMaxWidth() - .offset(y = (-96).dp) - ) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 20.dp) - ) { - Text( - text = series.title, - color = colors.textPrimary, - fontSize = 30.sp, - fontWeight = FontWeight.Bold, - lineHeight = 36.sp - ) - Spacer(modifier = Modifier.height(16.dp)) - SeriesMetaChips(series = series) - Spacer(modifier = Modifier.height(24.dp)) - SeriesActionButtons() - Spacer(modifier = Modifier.height(24.dp)) - Text( - text = "Synopsis", - color = colors.textPrimary, - fontSize = 18.sp, - fontWeight = FontWeight.Bold - ) - Spacer(modifier = Modifier.height(8.dp)) - Text( - text = series.synopsis, - color = colors.textMutedStrong, - fontSize = 13.sp, - ) - Spacer(modifier = Modifier.height(28.dp)) - Text( - text = "Episodes", - color = colors.textPrimary, - fontSize = 18.sp, - fontWeight = FontWeight.Bold - ) - Spacer(modifier = Modifier.height(28.dp)) - SeasonTabs(seasons = series.seasonTabs) - Spacer(modifier = Modifier.height(16.dp)) - } - - EpisodeCarousel( - episodes = series.seasonTabs.firstOrNull { it.isSelected }?.episodes - ?: series.seasonTabs.firstOrNull()?.episodes - ?: emptyList() - ) - Spacer(modifier = Modifier.height(32.dp)) - Column( - modifier = Modifier - .fillMaxWidth() - .padding(top = 0.dp, bottom = 0.dp) - ) { - Text( - text = "Cast", - color = colors.textPrimary, - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(horizontal = 20.dp) - ) - Spacer(modifier = Modifier.height(12.dp)) - CastRow(cast = series.cast) - } - } - } - - SeriesTopBar( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 16.dp) - .align(Alignment.TopCenter) - ) - } -} - -@Preview -@Composable -fun SeriesCardPreview() { - SeriesCard(series = SeriesMockData.series()) -} diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt index 10293eb..17d2087 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesModels.kt @@ -31,88 +31,3 @@ data class SeriesUiModel( val seasonTabs: List, val cast: List ) - -internal object SeriesMockData { - fun series(): SeriesUiModel { - val heroUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuD3hBjDpw00tDCQsK5xNcnJra301k1T4LksWVZzHieH9KHQItEQkVzhwevJvf8RkaQKdVKvObzRlfDDqa3_PNwLUlUQc1LpDih8p94VTGobEV62qi7QrmNyQm_o55KRMNWiTG3zLLpblGqo3uUNQcYmPFqfNML95dClXQ4lQNl85-zgerPPAbGPr23dswbIYCigyTAaXgrmdV_nbNQ5LdDB0Wh5cMHtP0uxz6k3ARjNom6clhphGIUF9e6YSvKuwuiZ-1lMYFg8C_4" - val episode1 = SeriesEpisodeUiModel( - id = "1", - title = "E1: The Beginning", - description = "The crew assembles for the first time as the anomaly begins to expand rapidly near Saturn's rings.", - duration = "58m", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" - ) - val episode2 = SeriesEpisodeUiModel( - id = "2", - title = "E2: Event Horizon", - description = "Dr. Cole discovers a frequency embedded in the rift's radiation that suggests intelligent design.", - duration = "54m", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" - ) - val episode3 = SeriesEpisodeUiModel( - id = "3", - title = "E3: Singularity", - description = "Tension rises as the ship approaches the event horizon, and the AI begins to behave erratically.", - duration = "1h 02m", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U" - ) - return SeriesUiModel( - title = "Interstellar Horizon: The Series", - year = "2024", - rating = "TV-MA", - seasons = "3 Seasons", - format = "4K HDR", - synopsis = "When a mysterious cosmic rift appears near Saturn, a team of seasoned astronauts and theoretical physicists must embark on a high-stakes voyage across dimensions. They seek to unlock the secrets of time-dilated anomalies that threaten the very fabric of human existence on Earth.", - heroImageUrl = heroUrl, - seasonTabs = listOf( - SeriesSeasonUiModel( - name = "Season 1", - isSelected = true, - episodes = listOf(episode1, episode2, episode3) - ), - SeriesSeasonUiModel( - name = "Season 2", - isSelected = false, - episodes = listOf(episode1, episode2, episode3) - ), - SeriesSeasonUiModel( - name = "Season 3", - isSelected = false, - episodes = listOf(episode1, episode2, episode3) - ) - ), - cast = listOf( - SeriesCastMemberUiModel( - name = "Marcus Thorne", - role = "Cmdr. Vance", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuC6OPszCXCIP_FMO3BJJUrjpCtDNw9aeHYOGyOAXdqF078hDFNrH7KXbaQ7qtipz6aIPLivd8VBBffNMbeAiYIjjWjn5GMb6Xn9iiJz0D2rzhCKi0TBeFrN6tC1IXJkzQyQKJNhTnyokWy9dd-YtN65V7er7RT6hP5jdVBXhtK1xZMjlgrm1bk_FTTmKd8Afu3zPtJCaaC98Z608vav5zhYlkrdA1wKNSTWTpzwMSyDIY3pNQNPFauWf0n-iEu7QsYTAwhCG_zfxz0" - ), - SeriesCastMemberUiModel( - name = "Elena Rossi", - role = "Dr. Sarah Cole", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBExsf-wEzAVjMxasU2ImGhlreqQo9biBSN1yHyAbW8MyuhuppRw9ho7OD3vsbySSJ3kNluEgH1Qun45PmLnZWixZsFU4Qc7UGGJNKMS5Nkm4GZAsKdFvb3z_i1tkCvaXXvGpqmwI0qjFuo1QyjjhYPA5Yp3I8ZhrnDYdQv_GxbhR6Vl3mY1rbxd2BIUEE5oMTwTF-QmJztUEaViZkSGSG2VgVXZ5VAREn4xWE902OH2sysllvXQJQIaj439JIC2_Vg61m0-F-F1Vc" - ), - SeriesCastMemberUiModel( - name = "Julian Chen", - role = "Tech Officer Lin", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuA5CFDWsWYO4YxdRoLd2QfH5Su2KLhtj5xSDb8qmzWHvPE888ac_HAAj1wu1uqdFNSncdmmJ-bWsc--h6NYKxVXkhd4vHaFWi0XTJXgsR0F3cBu_l2SynSX4TMNSy5C3XWDurgeSH789byOe1HvoxHCHTJYaSf3OyEbil-NOp9g_9mZ24CIZOI79nx57CRzmooxoswycqssPpfTNkrnoYrrAczt5qbncwLM9NVU442YxyBFisr2Ds9H-CNBOakiCtaKnoJ6npznM7U" - ), - SeriesCastMemberUiModel( - name = "Sarah Jenkins", - role = "Mission Pilot", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuBN6_72VggBdNx7ITLvEvIA6OSre5iJI6kQiUVMpKAlYgd8TpT-Jx6DzZwGsGACLnAXOUuzT2R7mx9A9DNZcqi5BF_jSaEdeYpfcBvJttmVPAwiCiq1_PI2BwoZZH_Ccmq2AHV5lQqcYaA2rPkf4e7YLLLgpmVbGjKhncTotQtxiZvmLNzCbLUdlEb7XLgHKfjS6FU6djV9ocOo9bxZ_YtrQj-mMFvYGzCxeFYC8OF0kIV2NN3kQYH8x1X-rYMqu2-d7klJfQdhKHw" - ), - SeriesCastMemberUiModel( - name = "David Wu", - role = "The AI (Voice)", - imageUrl = "https://lh3.googleusercontent.com/aida-public/AB6AXuCnNkjaBc2hU2zJ5hAF8iZZ_ZZvMlU79o4JtPNCP2MEfttpF0fe_BHWsMMl6h3S37FJ1dTLk8AQuvRQ_ggy1u-71xlQWULB76rT8pdZiRE7TkInQ8gwpigs84KNWbTRxVUI7Nia9RPyJeFE7egZqnT46TQWUeN8llWF9EDQ6mpfVLH0vHhKUlko39iDgMnBIequYntugSFgWJQc1jH-AxZ4OpJr_-uZGkwtQ_CVYNV69u9y107gk5BwaUFwPeipe8Bn9I655kyHIuQ" - ), - SeriesCastMemberUiModel( - name = "Alex Reed", - role = "Engineer", - imageUrl = null - ) - ) - ) - } -} diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt index 45f15b3..58d3f78 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt @@ -1,10 +1,28 @@ package hu.bbara.purefin.app.content.series +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import hu.bbara.purefin.app.content.ContentMockData import hu.bbara.purefin.common.ui.PurefinWaitingScreen import hu.bbara.purefin.navigation.ItemDto @@ -21,7 +39,7 @@ fun SeriesScreen( val series = viewModel.series.collectAsState() if (series.value != null) { - SeriesCard( + SeriesScreenInternal( series = series.value!!, modifier = modifier ) @@ -29,3 +47,110 @@ fun SeriesScreen( PurefinWaitingScreen() } } + +@Composable +private fun SeriesScreenInternal( + series: SeriesUiModel, + modifier: Modifier = Modifier, +) { + val colors = rememberSeriesColors() + + BoxWithConstraints( + modifier = modifier + .fillMaxSize() + .background(colors.background) + ) { + val heroHeight = maxHeight * 0.4f + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + ) { + SeriesHero( + imageUrl = series.heroImageUrl, + height = heroHeight + ) + Column( + modifier = Modifier + .fillMaxWidth() + .offset(y = (-96).dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp) + ) { + Text( + text = series.title, + color = colors.textPrimary, + fontSize = 30.sp, + fontWeight = FontWeight.Bold, + lineHeight = 36.sp + ) + Spacer(modifier = Modifier.height(16.dp)) + SeriesMetaChips(series = series) + Spacer(modifier = Modifier.height(24.dp)) + SeriesActionButtons() + Spacer(modifier = Modifier.height(24.dp)) + Text( + text = "Synopsis", + color = colors.textPrimary, + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = series.synopsis, + color = colors.textMutedStrong, + fontSize = 13.sp, + ) + Spacer(modifier = Modifier.height(28.dp)) + Text( + text = "Episodes", + color = colors.textPrimary, + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(28.dp)) + SeasonTabs(seasons = series.seasonTabs) + Spacer(modifier = Modifier.height(16.dp)) + } + + EpisodeCarousel( + episodes = series.seasonTabs.firstOrNull { it.isSelected }?.episodes + ?: series.seasonTabs.firstOrNull()?.episodes + ?: emptyList() + ) + Spacer(modifier = Modifier.height(32.dp)) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 0.dp, bottom = 0.dp) + ) { + Text( + text = "Cast", + color = colors.textPrimary, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(horizontal = 20.dp) + ) + Spacer(modifier = Modifier.height(12.dp)) + CastRow(cast = series.cast) + } + } + } + + SeriesTopBar( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 16.dp) + .align(Alignment.TopCenter) + ) + } +} + +@Preview +@Composable +fun SeriesScreenPreview() { + SeriesScreenInternal(series = ContentMockData.series()) +}