mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
Refactor home screen UI and top bar components
- Replace `HomeDiscoveryTopBar` with a simpler `HomeTopBar` using `TopAppBar`. - Introduce `DefaultTopBar` as a reusable component with integrated search and profile actions. - Update `HomeScreen` and `LibrariesScreen` to use the new top bar implementations. - Simplify `HomeContent` background and remove `ContentBadge` from featured and next-up items. - Refine `HomeSections` UI including library item shapes, padding, and text styling. - Expose internal preview data functions for broader use in UI previews.
This commit is contained in:
@@ -6,8 +6,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -15,14 +13,19 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import hu.bbara.purefin.app.home.ui.HomeContent
|
||||
import hu.bbara.purefin.app.home.ui.HomeDiscoveryTopBar
|
||||
import hu.bbara.purefin.app.home.ui.HomeSearchOverlay
|
||||
import hu.bbara.purefin.app.home.ui.HomeTopBar
|
||||
import hu.bbara.purefin.app.home.ui.homePreviewContinueWatching
|
||||
import hu.bbara.purefin.app.home.ui.homePreviewLibraries
|
||||
import hu.bbara.purefin.app.home.ui.homePreviewLibraryContent
|
||||
import hu.bbara.purefin.app.home.ui.homePreviewNextUp
|
||||
import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem
|
||||
import hu.bbara.purefin.feature.shared.home.LibraryItem
|
||||
import hu.bbara.purefin.feature.shared.home.NextUpItem
|
||||
import hu.bbara.purefin.feature.shared.home.PosterItem
|
||||
import hu.bbara.purefin.ui.theme.AppTheme
|
||||
import org.jellyfin.sdk.model.UUID
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -45,9 +48,6 @@ fun HomeScreen(
|
||||
onTabSelected: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
|
||||
rememberTopAppBarState()
|
||||
)
|
||||
var isSearchVisible by rememberSaveable { mutableStateOf(false) }
|
||||
val subtitle = remember(continueWatching, nextUp, libraries) {
|
||||
when {
|
||||
@@ -60,15 +60,13 @@ fun HomeScreen(
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
.fillMaxSize(),
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
topBar = {
|
||||
HomeDiscoveryTopBar(
|
||||
HomeTopBar(
|
||||
title = "Watch now",
|
||||
subtitle = subtitle,
|
||||
scrollBehavior = scrollBehavior,
|
||||
onSearchClick = { isSearchVisible = true },
|
||||
onProfileClick = onProfileClick,
|
||||
onSettingsClick = onSettingsClick,
|
||||
@@ -116,3 +114,27 @@ fun HomeScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Home Screen", showBackground = true, widthDp = 412, heightDp = 915)
|
||||
@Composable
|
||||
private fun HomeScreenPreview() {
|
||||
AppTheme(darkTheme = true) {
|
||||
HomeScreen(
|
||||
libraries = homePreviewLibraries(),
|
||||
libraryContent = homePreviewLibraryContent(),
|
||||
continueWatching = homePreviewContinueWatching(),
|
||||
nextUp = homePreviewNextUp(),
|
||||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
onMovieSelected = {},
|
||||
onSeriesSelected = {},
|
||||
onEpisodeSelected = { _, _, _ -> },
|
||||
onLibrarySelected = {},
|
||||
onProfileClick = {},
|
||||
onSettingsClick = {},
|
||||
onLogoutClick = {},
|
||||
selectedTab = 0,
|
||||
onTabSelected = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import hu.bbara.purefin.app.home.ui.HomeNavItem
|
||||
import hu.bbara.purefin.app.home.ui.HomeTopBar
|
||||
import hu.bbara.purefin.app.home.ui.DefaultTopBar
|
||||
import hu.bbara.purefin.app.home.ui.LibrariesContent
|
||||
|
||||
@Composable
|
||||
@@ -26,7 +26,7 @@ fun LibrariesScreen(
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||
topBar = {
|
||||
HomeTopBar(
|
||||
DefaultTopBar(
|
||||
onProfileClick = onProfileClick,
|
||||
onSettingsClick = onSettingsClick,
|
||||
onLogoutClick = onLogoutClick
|
||||
|
||||
123
app/src/main/java/hu/bbara/purefin/app/home/ui/DefaultTopBar.kt
Normal file
123
app/src/main/java/hu/bbara/purefin/app/home/ui/DefaultTopBar.kt
Normal file
@@ -0,0 +1,123 @@
|
||||
package hu.bbara.purefin.app.home.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Person
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import hu.bbara.purefin.common.ui.components.PurefinSearchBar
|
||||
import hu.bbara.purefin.feature.shared.search.SearchViewModel
|
||||
|
||||
@Composable
|
||||
fun DefaultTopBar(
|
||||
modifier: Modifier = Modifier,
|
||||
searchViewModel: SearchViewModel = hiltViewModel(),
|
||||
onProfileClick: () -> Unit = {},
|
||||
onSettingsClick: () -> Unit = {},
|
||||
onLogoutClick: () -> Unit = {},
|
||||
) {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
val searchResult = searchViewModel.searchResult.collectAsState()
|
||||
var isProfileMenuExpanded by remember { mutableStateOf(false) }
|
||||
var isSearchExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(scheme.background.copy(alpha = 0.95f))
|
||||
.zIndex(1f)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.CenterEnd
|
||||
) {
|
||||
PurefinSearchBar(
|
||||
onQueryChange = {
|
||||
searchViewModel.search(it)
|
||||
},
|
||||
onSearch = {
|
||||
searchViewModel.search(it)
|
||||
},
|
||||
onExpandedChange = { expanded ->
|
||||
isSearchExpanded = expanded
|
||||
if (expanded) {
|
||||
isProfileMenuExpanded = false
|
||||
}
|
||||
},
|
||||
searchResults = searchResult.value,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = if (isSearchExpanded) 0.dp else 72.dp),
|
||||
)
|
||||
if (!isSearchExpanded) {
|
||||
Box {
|
||||
IconButton(
|
||||
onClick = { isProfileMenuExpanded = true },
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.clip(CircleShape),
|
||||
) {
|
||||
HomeAvatar(
|
||||
size = 56.dp,
|
||||
borderWidth = 1.dp,
|
||||
borderColor = scheme.outlineVariant,
|
||||
backgroundColor = scheme.secondaryContainer,
|
||||
icon = Icons.Outlined.Person,
|
||||
iconTint = scheme.onSecondaryContainer
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = isProfileMenuExpanded,
|
||||
onDismissRequest = { isProfileMenuExpanded = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Profile") },
|
||||
onClick = {
|
||||
isProfileMenuExpanded = false
|
||||
onProfileClick()
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Settings") },
|
||||
onClick = {
|
||||
isProfileMenuExpanded = false
|
||||
onSettingsClick()
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Logout") },
|
||||
onClick = {
|
||||
isProfileMenuExpanded = false
|
||||
onLogoutClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import hu.bbara.purefin.core.model.Episode
|
||||
@@ -85,15 +84,7 @@ fun HomeContent(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
scheme.primaryContainer.copy(alpha = 0.24f),
|
||||
scheme.surface.copy(alpha = 0.92f),
|
||||
scheme.background
|
||||
)
|
||||
)
|
||||
)
|
||||
.background(scheme.background)
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
@@ -358,10 +349,10 @@ private fun openHomeDestination(
|
||||
private fun HomeContentPreview() {
|
||||
AppTheme(darkTheme = true) {
|
||||
HomeContent(
|
||||
libraries = previewLibraries(),
|
||||
libraryContent = previewLibraryContent(),
|
||||
continueWatching = previewContinueWatching(),
|
||||
nextUp = previewNextUp(),
|
||||
libraries = homePreviewLibraries(),
|
||||
libraryContent = homePreviewLibraryContent(),
|
||||
continueWatching = homePreviewContinueWatching(),
|
||||
nextUp = homePreviewNextUp(),
|
||||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
onMovieSelected = {},
|
||||
@@ -378,8 +369,8 @@ private fun HomeContentPreview() {
|
||||
private fun HomeLibrariesOnlyPreview() {
|
||||
AppTheme(darkTheme = true) {
|
||||
HomeContent(
|
||||
libraries = previewLibraries(),
|
||||
libraryContent = previewLibraryContent(),
|
||||
libraries = homePreviewLibraries(),
|
||||
libraryContent = homePreviewLibraryContent(),
|
||||
continueWatching = emptyList(),
|
||||
nextUp = emptyList(),
|
||||
isRefreshing = false,
|
||||
@@ -413,7 +404,7 @@ private fun HomeEmptyPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun previewLibraries(): List<LibraryItem> {
|
||||
internal fun homePreviewLibraries(): List<LibraryItem> {
|
||||
return listOf(
|
||||
LibraryItem(
|
||||
id = JavaUuid.fromString("11111111-1111-1111-1111-111111111111"),
|
||||
@@ -432,8 +423,8 @@ private fun previewLibraries(): List<LibraryItem> {
|
||||
)
|
||||
}
|
||||
|
||||
private fun previewLibraryContent(): Map<UUID, List<PosterItem>> {
|
||||
val movie = previewMovie(
|
||||
internal fun homePreviewLibraryContent(): Map<UUID, List<PosterItem>> {
|
||||
val movie = homePreviewMovie(
|
||||
id = "33333333-3333-3333-3333-333333333333",
|
||||
title = "Blade Runner 2049",
|
||||
year = "2017",
|
||||
@@ -445,7 +436,7 @@ private fun previewLibraryContent(): Map<UUID, List<PosterItem>> {
|
||||
progress = 42.0,
|
||||
watched = false
|
||||
)
|
||||
val secondMovie = previewMovie(
|
||||
val secondMovie = homePreviewMovie(
|
||||
id = "44444444-4444-4444-4444-444444444444",
|
||||
title = "Arrival",
|
||||
year = "2016",
|
||||
@@ -457,8 +448,8 @@ private fun previewLibraryContent(): Map<UUID, List<PosterItem>> {
|
||||
progress = null,
|
||||
watched = false
|
||||
)
|
||||
val series = previewSeries()
|
||||
val episode = previewEpisode(
|
||||
val series = homePreviewSeries()
|
||||
val episode = homePreviewEpisode(
|
||||
id = "66666666-6666-6666-6666-666666666666",
|
||||
title = "Signals",
|
||||
index = 2,
|
||||
@@ -472,22 +463,22 @@ private fun previewLibraryContent(): Map<UUID, List<PosterItem>> {
|
||||
)
|
||||
|
||||
return mapOf(
|
||||
previewLibraries()[0].id to listOf(
|
||||
homePreviewLibraries()[0].id to listOf(
|
||||
PosterItem(type = BaseItemKind.MOVIE, movie = movie),
|
||||
PosterItem(type = BaseItemKind.MOVIE, movie = secondMovie)
|
||||
),
|
||||
previewLibraries()[1].id to listOf(
|
||||
homePreviewLibraries()[1].id to listOf(
|
||||
PosterItem(type = BaseItemKind.SERIES, series = series),
|
||||
PosterItem(type = BaseItemKind.EPISODE, episode = episode)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun previewContinueWatching(): List<ContinueWatchingItem> {
|
||||
internal fun homePreviewContinueWatching(): List<ContinueWatchingItem> {
|
||||
return listOf(
|
||||
ContinueWatchingItem(
|
||||
type = BaseItemKind.MOVIE,
|
||||
movie = previewMovie(
|
||||
movie = homePreviewMovie(
|
||||
id = "77777777-7777-7777-7777-777777777777",
|
||||
title = "Dune: Part Two",
|
||||
year = "2024",
|
||||
@@ -502,7 +493,7 @@ private fun previewContinueWatching(): List<ContinueWatchingItem> {
|
||||
),
|
||||
ContinueWatchingItem(
|
||||
type = BaseItemKind.EPISODE,
|
||||
episode = previewEpisode(
|
||||
episode = homePreviewEpisode(
|
||||
id = "88888888-8888-8888-8888-888888888888",
|
||||
title = "A Fresh Start",
|
||||
index = 1,
|
||||
@@ -518,10 +509,10 @@ private fun previewContinueWatching(): List<ContinueWatchingItem> {
|
||||
)
|
||||
}
|
||||
|
||||
private fun previewNextUp(): List<NextUpItem> {
|
||||
internal fun homePreviewNextUp(): List<NextUpItem> {
|
||||
return listOf(
|
||||
NextUpItem(
|
||||
episode = previewEpisode(
|
||||
episode = homePreviewEpisode(
|
||||
id = "99999999-9999-9999-9999-999999999999",
|
||||
title = "Return Window",
|
||||
index = 3,
|
||||
@@ -537,7 +528,7 @@ private fun previewNextUp(): List<NextUpItem> {
|
||||
)
|
||||
}
|
||||
|
||||
private fun previewMovie(
|
||||
internal fun homePreviewMovie(
|
||||
id: String,
|
||||
title: String,
|
||||
year: String,
|
||||
@@ -567,7 +558,7 @@ private fun previewMovie(
|
||||
)
|
||||
}
|
||||
|
||||
private fun previewSeries(): Series {
|
||||
internal fun homePreviewSeries(): Series {
|
||||
return Series(
|
||||
id = JavaUuid.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
|
||||
libraryId = JavaUuid.fromString("cccccccc-cccc-cccc-cccc-cccccccccccc"),
|
||||
@@ -582,7 +573,7 @@ private fun previewSeries(): Series {
|
||||
)
|
||||
}
|
||||
|
||||
private fun previewEpisode(
|
||||
internal fun homePreviewEpisode(
|
||||
id: String,
|
||||
title: String,
|
||||
index: Int,
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
package hu.bbara.purefin.app.home.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Person
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomeDiscoveryTopBar(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
onSearchClick: () -> Unit,
|
||||
onProfileClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit,
|
||||
onLogoutClick: () -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
var isProfileMenuExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
LargeTopAppBar(
|
||||
title = {
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = scheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onSearchClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Search,
|
||||
contentDescription = "Search"
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { isProfileMenuExpanded = true },
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape),
|
||||
) {
|
||||
HomeAvatar(
|
||||
size = 40.dp,
|
||||
borderWidth = 1.dp,
|
||||
borderColor = scheme.outlineVariant,
|
||||
backgroundColor = scheme.secondaryContainer,
|
||||
icon = Icons.Outlined.Person,
|
||||
iconTint = scheme.onSecondaryContainer
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = isProfileMenuExpanded,
|
||||
onDismissRequest = { isProfileMenuExpanded = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Profile") },
|
||||
onClick = {
|
||||
isProfileMenuExpanded = false
|
||||
onProfileClick()
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Settings") },
|
||||
onClick = {
|
||||
isProfileMenuExpanded = false
|
||||
onSettingsClick()
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Logout") },
|
||||
onClick = {
|
||||
isProfileMenuExpanded = false
|
||||
onLogoutClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||
containerColor = scheme.background,
|
||||
scrolledContainerColor = scheme.surface.copy(alpha = 0.96f),
|
||||
navigationIconContentColor = scheme.onSurface,
|
||||
actionIconContentColor = scheme.onSurface,
|
||||
titleContentColor = scheme.onSurface
|
||||
),
|
||||
scrollBehavior = scrollBehavior,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowForward
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.outlined.Collections
|
||||
import androidx.compose.material.icons.outlined.Refresh
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
@@ -161,12 +160,18 @@ private fun HomeFeaturedCard(
|
||||
.fillMaxSize()
|
||||
.padding(24.dp)
|
||||
) {
|
||||
ContentBadge(
|
||||
text = item.badge,
|
||||
containerColor = scheme.surface.copy(alpha = 0.88f),
|
||||
contentColor = scheme.onSurface
|
||||
)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Text(
|
||||
text = item.title
|
||||
)
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (item.metadata.isNotEmpty()) {
|
||||
Text(
|
||||
text = item.metadata.joinToString(" • "),
|
||||
@@ -176,14 +181,6 @@ private fun HomeFeaturedCard(
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = Color.White,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
if (description.isNotBlank()) {
|
||||
Text(
|
||||
text = description,
|
||||
@@ -194,14 +191,6 @@ private fun HomeFeaturedCard(
|
||||
modifier = Modifier.widthIn(max = 520.dp)
|
||||
)
|
||||
}
|
||||
FilledTonalButton(onClick = onClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PlayArrow,
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = item.ctaLabel)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.progress != null && item.progress > 0f) {
|
||||
@@ -275,7 +264,7 @@ private fun ContinueWatchingCard(
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(26.dp),
|
||||
color = scheme.surfaceContainerLow,
|
||||
color = scheme.surfaceContainer,
|
||||
tonalElevation = 3.dp,
|
||||
modifier = modifier.width(320.dp)
|
||||
) {
|
||||
@@ -298,7 +287,7 @@ private fun ContinueWatchingCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(16f / 9f)
|
||||
.background(scheme.surfaceVariant)
|
||||
.background(scheme.surfaceContainer)
|
||||
) {
|
||||
if (imageUrl != null) {
|
||||
PurefinAsyncImage(
|
||||
@@ -321,12 +310,6 @@ private fun ContinueWatchingCard(
|
||||
)
|
||||
)
|
||||
)
|
||||
ContentBadge(
|
||||
text = "Continue",
|
||||
containerColor = scheme.surface.copy(alpha = 0.9f),
|
||||
contentColor = scheme.onSurface,
|
||||
modifier = Modifier.padding(14.dp)
|
||||
)
|
||||
MediaProgressBar(
|
||||
progress = (item.progress.toFloat() / 100f).coerceIn(0f, 1f),
|
||||
foregroundColor = scheme.primary,
|
||||
@@ -406,9 +389,7 @@ private fun NextUpCard(
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
onEpisodeSelected(
|
||||
item.episode.seriesId,
|
||||
item.episode.seasonId,
|
||||
item.episode.id
|
||||
item.episode.seriesId, item.episode.seasonId, item.episode.id
|
||||
)
|
||||
}
|
||||
) {
|
||||
@@ -437,12 +418,6 @@ private fun NextUpCard(
|
||||
)
|
||||
)
|
||||
)
|
||||
ContentBadge(
|
||||
text = "Up next",
|
||||
containerColor = scheme.secondaryContainer.copy(alpha = 0.9f),
|
||||
contentColor = scheme.onSecondaryContainer,
|
||||
modifier = Modifier.padding(12.dp)
|
||||
)
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
@@ -535,9 +510,8 @@ private fun HomeBrowseCard(
|
||||
}
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = scheme.surfaceContainer,
|
||||
tonalElevation = 1.dp,
|
||||
modifier = modifier.width(188.dp)
|
||||
) {
|
||||
Column(
|
||||
@@ -555,14 +529,15 @@ private fun HomeBrowseCard(
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(16f / 10f)
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.border(1.dp, scheme.outlineVariant.copy(alpha = 0.35f), RoundedCornerShape(18.dp))
|
||||
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
|
||||
.border(
|
||||
1.dp, scheme.outlineVariant.copy(alpha = 0.35f), RoundedCornerShape(18.dp)
|
||||
)
|
||||
.background(scheme.surfaceVariant)
|
||||
) {
|
||||
PurefinAsyncImage(
|
||||
@@ -610,6 +585,7 @@ private fun HomeBrowseCard(
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Column(modifier = modifier.padding(12.dp)) {
|
||||
Text(
|
||||
text = item.title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
@@ -629,6 +605,7 @@ private fun HomeBrowseCard(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -1,88 +1,77 @@
|
||||
package hu.bbara.purefin.app.home.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Person
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import hu.bbara.purefin.common.ui.components.PurefinSearchBar
|
||||
import hu.bbara.purefin.feature.shared.search.SearchViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomeTopBar(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
onSearchClick: () -> Unit,
|
||||
onProfileClick: () -> Unit,
|
||||
onSettingsClick: () -> Unit,
|
||||
onLogoutClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
searchViewModel: SearchViewModel = hiltViewModel(),
|
||||
onProfileClick: () -> Unit = {},
|
||||
onSettingsClick: () -> Unit = {},
|
||||
onLogoutClick: () -> Unit = {},
|
||||
) {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
val searchResult = searchViewModel.searchResult.collectAsState()
|
||||
var isProfileMenuExpanded by remember { mutableStateOf(false) }
|
||||
var isSearchExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(scheme.background.copy(alpha = 0.95f))
|
||||
.zIndex(1f)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.statusBarsPadding()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.CenterEnd
|
||||
) {
|
||||
PurefinSearchBar(
|
||||
onQueryChange = {
|
||||
searchViewModel.search(it)
|
||||
},
|
||||
onSearch = {
|
||||
searchViewModel.search(it)
|
||||
},
|
||||
onExpandedChange = { expanded ->
|
||||
isSearchExpanded = expanded
|
||||
if (expanded) {
|
||||
isProfileMenuExpanded = false
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = scheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
},
|
||||
searchResults = searchResult.value,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = if (isSearchExpanded) 0.dp else 72.dp),
|
||||
actions = {
|
||||
IconButton(onClick = onSearchClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Search,
|
||||
contentDescription = "Search"
|
||||
)
|
||||
if (!isSearchExpanded) {
|
||||
Box {
|
||||
}
|
||||
IconButton(
|
||||
onClick = { isProfileMenuExpanded = true },
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.size(48.dp)
|
||||
.clip(CircleShape),
|
||||
) {
|
||||
HomeAvatar(
|
||||
size = 56.dp,
|
||||
size = 40.dp,
|
||||
borderWidth = 1.dp,
|
||||
borderColor = scheme.outlineVariant,
|
||||
backgroundColor = scheme.secondaryContainer,
|
||||
@@ -116,8 +105,14 @@ fun HomeTopBar(
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||
containerColor = scheme.background,
|
||||
scrolledContainerColor = scheme.surface.copy(alpha = 0.96f),
|
||||
navigationIconContentColor = scheme.onSurface,
|
||||
actionIconContentColor = scheme.onSurface,
|
||||
titleContentColor = scheme.onSurface
|
||||
),
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user