Skip to content

Commit

Permalink
redesign favorite screen
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Mar 16, 2024
1 parent cc4a75e commit 3273109
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 112 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.datlag.burningseries.shared.common

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
Expand All @@ -18,6 +19,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.PathBuilder
import androidx.compose.ui.input.pointer.pointerInput
Expand Down Expand Up @@ -250,3 +253,18 @@ fun LocalPadding(horizontal: Dp = 0.dp, vertical: Dp = 0.dp): PaddingValues {
PaddingValues(horizontal = horizontal, vertical = vertical)
) ?: PaddingValues(horizontal = horizontal, vertical = vertical)
}

fun Modifier.bottomShadowBrush(color: Color, alpha: Float = 1F): Modifier {
val maxAlpha = kotlin.math.min(alpha, 1F)

return this.background(
brush = Brush.verticalGradient(
0.0f to Color.Transparent,
0.1f to color.copy(alpha = 0.35f * maxAlpha),
0.3f to color.copy(alpha = 0.55f * maxAlpha),
0.5f to color.copy(alpha = 0.75f * maxAlpha),
0.7f to color.copy(alpha = 0.95f * maxAlpha),
0.9f to color.copy(alpha = 1f * maxAlpha)
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
Expand All @@ -22,15 +26,16 @@ import kotlinx.coroutines.delay

@Composable
fun FloatingSearchButton(
icon: ImageVector,
contentDescription: String?,
clearIcon: ImageVector,
closeIcon: ImageVector,
icon: ImageVector = Icons.Default.Search,
contentDescription: String? = stringResource(SharedRes.strings.search),
clearIcon: ImageVector = Icons.Default.Clear,
closeIcon: ImageVector = Icons.AutoMirrored.Default.KeyboardArrowRight,
modifier: Modifier = Modifier,
onTextChange: (String) -> Unit
) {
val focusRequester = remember { FocusRequester() }
var opened by remember { mutableStateOf(false) }
val textState = remember { mutableStateOf("") }

Surface(
color = MaterialTheme.colorScheme.primaryContainer,
Expand All @@ -54,6 +59,7 @@ fun FloatingSearchButton(
clearIcon = clearIcon,
closeIcon = closeIcon,
focusRequester = focusRequester,
textState = textState,
onTextChange = onTextChange
)

Expand Down Expand Up @@ -81,18 +87,17 @@ fun FloatingSearchButton(
@Composable
private fun SearchBar(
focusRequester: FocusRequester,
textState: MutableState<String>,
close: () -> Unit,
clearIcon: ImageVector,
closeIcon: ImageVector,
onTextChange: (String) -> Unit
) {
var text by remember { mutableStateOf("") }

TextField(
value = text,
value = textState.value,
onValueChange = {
text = it
onTextChange(text)
textState.value = it
onTextChange(textState.value)
},
modifier = Modifier.focusRequester(focusRequester),
placeholder = {
Expand All @@ -117,8 +122,8 @@ private fun SearchBar(
trailingIcon = {
IconButton(
onClick = {
text = ""
onTextChange(text)
textState.value = ""
onTextChange(textState.value)
}
) {
Icon(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSiz
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.onSizeChanged
Expand All @@ -30,10 +31,13 @@ import dev.datlag.burningseries.shared.SharedRes
import dev.datlag.burningseries.shared.common.LocalPadding
import dev.datlag.burningseries.shared.common.header
import dev.datlag.burningseries.shared.common.lifecycle.collectAsStateWithLifecycle
import dev.datlag.burningseries.shared.common.localPadding
import dev.datlag.burningseries.shared.common.onClick
import dev.datlag.burningseries.shared.rememberIsTv
import dev.datlag.burningseries.shared.ui.custom.FloatingSearchButton
import dev.datlag.burningseries.shared.ui.custom.VerticalScrollbar
import dev.datlag.burningseries.shared.ui.custom.rememberScrollbarAdapter
import dev.datlag.burningseries.shared.ui.screen.initial.favorite.component.SeriesCard
import dev.datlag.burningseries.shared.ui.screen.initial.home.component.SeriesItem
import dev.icerock.moko.resources.compose.stringResource

Expand Down Expand Up @@ -96,92 +100,41 @@ private fun ExpandedView(component: FavoriteComponent) {

@Composable
private fun MainView(component: FavoriteComponent, modifier: Modifier = Modifier) {
Row(
modifier = modifier.fillMaxWidth().padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(2.dp)
Box(
modifier = modifier
) {
val favorites by component.favorites.collectAsStateWithLifecycle()
val listState = rememberLazyGridState()

LazyVerticalGrid(
columns = GridCells.Adaptive(400.dp),
modifier = Modifier.weight(1F).haze(state = LocalHaze.current),
state = listState,
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = LocalPadding()
Row(
modifier = Modifier.fillMaxSize().padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
header {
SearchBar(component)
}
items(favorites, key = { it.hrefPrimary }) { fav ->
SeriesItem(fav) {
component.itemClicked(FavoriteConfig.Series(fav))
}
}
}
VerticalScrollbar(rememberScrollbarAdapter(listState))
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SearchBar(component: FavoriteComponent) {
val items by component.searchItems.collectAsStateWithLifecycle()
var queryComp by remember { mutableStateOf(String()) }
val favorites by component.searchItems.collectAsStateWithLifecycle()
val listState = rememberLazyGridState()

DockedSearchBar(
query = queryComp,
onQueryChange = {
queryComp = it
},
onSearch = {
queryComp = it
},
modifier = Modifier.fillMaxWidth().animateContentSize(),
active = queryComp.isNotBlank() && items.isNotEmpty(),
onActiveChange = {},
placeholder = {
Text(text = stringResource(SharedRes.strings.search))
},
leadingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = stringResource(SharedRes.strings.search)
)
},
trailingIcon = {
AnimatedVisibility(
visible = queryComp.isNotBlank(),
enter = fadeIn(),
exit = fadeOut()
LazyVerticalGrid(
columns = GridCells.Adaptive(150.dp),
modifier = Modifier.weight(1F).haze(state = LocalHaze.current),
state = listState,
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = LocalPadding()
) {
IconButton(
onClick = {
queryComp = String()
}
) {
Icon(
imageVector = Icons.Default.Clear,
contentDescription = stringResource(SharedRes.strings.clear)
items(favorites, key = { it.hrefPrimary }) { fav ->
SeriesCard(
series = fav,
modifier = Modifier.width(150.dp).height(230.dp),
onClick = {
component.itemClicked(FavoriteConfig.Series(fav))
}
)
}
}
VerticalScrollbar(rememberScrollbarAdapter(listState))
}
) {
items.forEach { item ->
Text(
modifier = Modifier.fillMaxWidth().clip(MaterialTheme.shapes.extraSmall).onClick {
component.itemClicked(FavoriteConfig.Series(item))
}.padding(12.dp),
text = item.title,
softWrap = true,
overflow = TextOverflow.Ellipsis
)
}
}

LaunchedEffect(queryComp) {
component.searchQuery(queryComp)
FloatingSearchButton(
modifier = Modifier.align(Alignment.BottomEnd).localPadding(16.dp),
onTextChange = {
component.searchQuery(it)
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dev.datlag.burningseries.model.common.safeSubList
import dev.datlag.burningseries.shared.LocalDI
import dev.datlag.burningseries.shared.common.ioDispatcher
import dev.datlag.burningseries.shared.common.ioScope
import dev.datlag.burningseries.shared.common.launchIO
import dev.datlag.burningseries.shared.other.Crashlytics
import dev.datlag.burningseries.shared.ui.navigation.Component
import dev.datlag.burningseries.shared.ui.screen.initial.series.SeriesScreenComponent
Expand Down Expand Up @@ -43,10 +44,10 @@ class FavoriteScreenComponent(
.flowOn(ioDispatcher())
.stateIn(ioScope(), SharingStarted.WhileSubscribed(), emptyList())

private val searchQuery: MutableStateFlow<String> = MutableStateFlow(String())
private val searchQuery: MutableStateFlow<String> = MutableStateFlow("")
override val searchItems: StateFlow<List<Series>> = combine(favorites, searchQuery) { t1, t2 ->
if (t2.isBlank()) {
emptyList()
t1
} else {
coroutineScope {
t1.map {
Expand Down Expand Up @@ -107,7 +108,9 @@ class FavoriteScreenComponent(
}

override fun searchQuery(text: String) {
searchQuery.value = text.trim()
ioScope().launchIO {
searchQuery.emit(text)
}
}

override fun dismissHoldingSeries() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package dev.datlag.burningseries.shared.ui.screen.initial.favorite.component

import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import dev.datlag.burningseries.database.Series
import dev.datlag.burningseries.database.common.allTitles
import dev.datlag.burningseries.database.common.bestTitle
import dev.datlag.burningseries.database.common.mainTitle
import dev.datlag.burningseries.database.common.subTitle
import dev.datlag.burningseries.model.BSUtil
import dev.datlag.burningseries.shared.common.bottomShadowBrush
import dev.datlag.burningseries.shared.ui.custom.Cover
import dev.datlag.burningseries.shared.ui.theme.SchemeTheme
import dev.datlag.burningseries.shared.ui.theme.rememberSchemeThemeDominantColorState

@Composable
fun SeriesCard(
series: Series,
modifier: Modifier = Modifier,
onClick: (Series) -> Unit
) {
SchemeTheme(
key = series.hrefPrimary
) {
Card(
modifier = modifier,
onClick = {
onClick(series)
}
) {
Box(
modifier = Modifier.fillMaxSize()
) {
val scope = rememberCoroutineScope()
val colorState = rememberSchemeThemeDominantColorState(
key = series.hrefPrimary,
applyMinContrast = true,
minContrastBackgroundColor = MaterialTheme.colorScheme.surfaceVariant
)
val animatedColor by animateColorAsState(
targetValue = colorState.color
)

Cover(
key = series.coverHref,
data = series.coverHref?.let { BSUtil.getBurningSeriesLink(it) },
contentDescription = series.bestTitle,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
onSuccess = { state ->
SchemeTheme.update(
key = series.hrefPrimary,
input = state.painter,
scope = scope
)
}
)

Column(
modifier = Modifier
.align(Alignment.BottomStart)
.fillMaxWidth()
.bottomShadowBrush(animatedColor)
.padding(16.dp)
.padding(top = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically)
) {
Text(
text = series.mainTitle,
style = MaterialTheme.typography.titleLarge,
maxLines = 2,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth(),
color = colorState.onColor
)
series.subTitle?.let {
Text(
text = it,
modifier = Modifier.fillMaxWidth(),
color = colorState.onColor,
maxLines = 2
)
}
}
}
}
}
}
Loading

0 comments on commit 3273109

Please sign in to comment.