From 0e1682fd160db4d45ecf267b0c63b61fc4a0b81b Mon Sep 17 00:00:00 2001 From: Barnabas Balogh Date: Tue, 31 Mar 2026 11:29:02 +0200 Subject: [PATCH] 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. --- .../hu/bbara/purefin/app/home/HomeScreen.kt | 44 +++-- .../bbara/purefin/app/home/LibrariesScreen.kt | 4 +- .../purefin/app/home/ui/DefaultTopBar.kt | 123 ++++++++++++ .../bbara/purefin/app/home/ui/HomeContent.kt | 55 +++--- .../app/home/ui/HomeDiscoveryTopBar.kt | 121 ------------ .../bbara/purefin/app/home/ui/HomeSections.kt | 91 ++++----- .../bbara/purefin/app/home/ui/HomeTopBar.kt | 183 +++++++++--------- 7 files changed, 304 insertions(+), 317 deletions(-) create mode 100644 app/src/main/java/hu/bbara/purefin/app/home/ui/DefaultTopBar.kt delete mode 100644 app/src/main/java/hu/bbara/purefin/app/home/ui/HomeDiscoveryTopBar.kt diff --git a/app/src/main/java/hu/bbara/purefin/app/home/HomeScreen.kt b/app/src/main/java/hu/bbara/purefin/app/home/HomeScreen.kt index 06d8c6b..643b1ec 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/HomeScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/HomeScreen.kt @@ -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 = {} + ) + } +} diff --git a/app/src/main/java/hu/bbara/purefin/app/home/LibrariesScreen.kt b/app/src/main/java/hu/bbara/purefin/app/home/LibrariesScreen.kt index 33b6a40..bedfeaf 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/LibrariesScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/LibrariesScreen.kt @@ -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 diff --git a/app/src/main/java/hu/bbara/purefin/app/home/ui/DefaultTopBar.kt b/app/src/main/java/hu/bbara/purefin/app/home/ui/DefaultTopBar.kt new file mode 100644 index 0000000..37032e2 --- /dev/null +++ b/app/src/main/java/hu/bbara/purefin/app/home/ui/DefaultTopBar.kt @@ -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() + } + ) + } + } + } + } + } +} diff --git a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt index cd87483..b85ffc8 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeContent.kt @@ -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 { +internal fun homePreviewLibraries(): List { return listOf( LibraryItem( id = JavaUuid.fromString("11111111-1111-1111-1111-111111111111"), @@ -432,8 +423,8 @@ private fun previewLibraries(): List { ) } -private fun previewLibraryContent(): Map> { - val movie = previewMovie( +internal fun homePreviewLibraryContent(): Map> { + val movie = homePreviewMovie( id = "33333333-3333-3333-3333-333333333333", title = "Blade Runner 2049", year = "2017", @@ -445,7 +436,7 @@ private fun previewLibraryContent(): Map> { 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> { 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> { ) 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 { +internal fun homePreviewContinueWatching(): List { 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( 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 { ) } -private fun previewNextUp(): List { +internal fun homePreviewNextUp(): List { 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 { ) } -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, diff --git a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeDiscoveryTopBar.kt b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeDiscoveryTopBar.kt deleted file mode 100644 index 5e1a287..0000000 --- a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeDiscoveryTopBar.kt +++ /dev/null @@ -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 - ) -} diff --git a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeSections.kt b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeSections.kt index a45e5a2..e6031bd 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeSections.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeSections.kt @@ -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,22 +585,24 @@ private fun HomeBrowseCard( } } Spacer(modifier = Modifier.height(10.dp)) - Text( - text = item.title, - style = MaterialTheme.typography.bodyLarge, - fontWeight = FontWeight.SemiBold, - maxLines = 2, - overflow = TextOverflow.Ellipsis - ) - if (supportingText.isNotBlank()) { - Spacer(modifier = Modifier.height(4.dp)) + Column(modifier = modifier.padding(12.dp)) { Text( - text = supportingText, - style = MaterialTheme.typography.bodySmall, - color = scheme.onSurfaceVariant, - maxLines = 1, + text = item.title, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.SemiBold, + maxLines = 2, overflow = TextOverflow.Ellipsis ) + if (supportingText.isNotBlank()) { + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = supportingText, + style = MaterialTheme.typography.bodySmall, + color = scheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } } } } diff --git a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeTopBar.kt b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeTopBar.kt index 8b21820..aeff7ff 100644 --- a/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeTopBar.kt +++ b/app/src/main/java/hu/bbara/purefin/app/home/ui/HomeTopBar.kt @@ -1,123 +1,118 @@ 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 - } - }, - 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() - } - ) - } - } + 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 + ) } - } - } + }, + 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 + ), + modifier = modifier + ) }