Small UI Clean.

This commit is contained in:
Thomas Andres Gomez 2022-07-19 14:21:15 +02:00
parent d65d0bc3d5
commit d82efe7f5d
14 changed files with 134 additions and 77 deletions

View file

@ -70,11 +70,12 @@ fun Search(
contentDescription = null
)
}
TextField(
modifier = Modifier
.focusRequester(focusRequester = focusRequester)
.weight(1f),
label = {
placeholder = {
Text(
color = MaterialTheme.colors.onSurface,
text = if (state.content != ContentState.PROFILE) {

View file

@ -12,7 +12,6 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.Color
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.R
import com.pixelized.biblib.ui.screen.home.page.search.CategorySearchPage
@ -25,20 +24,20 @@ import java.io.Serializable
val LocalSearchViewModel = staticCompositionLocalOf<SearchViewModel> {
error("SearchViewModel is not ready yet")
}
val LocalSearchBottomSheetState = staticCompositionLocalOf<SearchBottomSheetState> {
val LocalCategorySearchBottomSheetState = staticCompositionLocalOf<SearchBottomSheetState> {
error("BottomSearchState is not ready yet")
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SearchBottomSheet(
fun CategorySearchBottomSheet(
state: SearchBottomSheetState = rememberSearchBottomSheetState(),
searchViewModel: SearchViewModel = hiltViewModel(),
content: @Composable () -> Unit,
) {
CompositionLocalProvider(
LocalSearchViewModel provides searchViewModel,
LocalSearchBottomSheetState provides state,
LocalCategorySearchBottomSheetState provides state,
) {
ModalBottomSheetLayout(
modifier = Modifier.statusBarsPadding(),

View file

@ -9,7 +9,6 @@ import androidx.compose.material.ModalBottomSheetValue.Hidden
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.biblib.R

View file

@ -102,7 +102,8 @@ class SearchScaffoldState(
expended: Boolean,
state: ContentState = ContentState.INITIAL,
) {
private var isExpended: Boolean by mutableStateOf(expended)
var isExpended: Boolean by mutableStateOf(expended)
private set
var content: ContentState by mutableStateOf(state)
private set

View file

@ -27,8 +27,8 @@ import com.pixelized.biblib.R
import com.pixelized.biblib.ui.composable.Search
import com.pixelized.biblib.ui.scaffold.*
import com.pixelized.biblib.ui.scaffold.SearchScaffoldState.ContentState
import com.pixelized.biblib.ui.screen.home.common.connectivity.ConnectivityViewModel
import com.pixelized.biblib.ui.screen.home.common.connectivity.ConnectivityHeader
import com.pixelized.biblib.ui.screen.home.common.connectivity.ConnectivityViewModel
import com.pixelized.biblib.ui.screen.home.page.Page
import com.pixelized.biblib.ui.screen.home.page.books.BooksPage
import com.pixelized.biblib.ui.screen.home.page.news.NewsPage
@ -50,36 +50,36 @@ fun HomeScreen(
accountViewModel: HomeViewModel = hiltViewModel(),
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
keyboard: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current,
state: SearchScaffoldState = rememberSearchScaffoldState(),
searchScaffoldState: SearchScaffoldState = rememberSearchScaffoldState(),
) {
val scope = rememberCoroutineScope()
val focusManager: FocusManager = LocalFocusManager.current
val focusRequester: FocusRequester = remember { FocusRequester() }
DetailBottomSheet {
SearchBottomSheet {
CategorySearchBottomSheet {
SearchScaffold(
modifier = Modifier.statusBarsPadding(),
state = state,
state = searchScaffoldState,
topBar = {
val viewModel = LocalSearchViewModel.current
val search by viewModel.filterFlow.collectAsState(initial = "")
Search(
state = state,
state = searchScaffoldState,
avatar = accountViewModel.avatar,
focusRequester = focusRequester,
onCloseTap = {
focusManager.clearFocus(force = true)
keyboard?.hide()
state.collapse()
searchScaffoldState.collapse()
},
searchValue = search,
onSearchValueChange = {
viewModel.filter(criteria = it)
},
onSearchTap = {
if (state.content != ContentState.SEARCH || state.isCollapsed()) {
state.expand(ContentState.SEARCH)
if (searchScaffoldState.content != ContentState.SEARCH || searchScaffoldState.isCollapsed()) {
searchScaffoldState.expand(ContentState.SEARCH)
scope.launch {
delay(100) // let the animation play before requesting the focus
focusRequester.requestFocus()
@ -87,16 +87,16 @@ fun HomeScreen(
} else {
focusManager.clearFocus(force = true)
keyboard?.hide()
state.collapse()
searchScaffoldState.collapse()
}
},
onAvatarTap = {
if (state.content != ContentState.PROFILE || state.isCollapsed()) {
if (searchScaffoldState.content != ContentState.PROFILE || searchScaffoldState.isCollapsed()) {
focusManager.clearFocus(force = true)
keyboard?.hide()
state.expand(ContentState.PROFILE)
searchScaffoldState.expand(ContentState.PROFILE)
} else {
state.collapse()
searchScaffoldState.collapse()
}
}
)
@ -104,7 +104,7 @@ fun HomeScreen(
search = {
Box(modifier = Modifier.fillMaxSize()) {
AnimatedContent(
targetState = state.content,
targetState = searchScaffoldState.content,
transitionSpec = {
when {
targetState == ContentState.INITIAL -> {
@ -139,12 +139,16 @@ fun HomeScreen(
},
)
val bottomSearchState = LocalSearchBottomSheetState.current
val bottomSearchState = LocalCategorySearchBottomSheetState.current
val bottomDetailState = LocalDetailBottomSheetState.current
BackHandler(
state.isCollapsed().not() && bottomSearchState.bottomSheetState.isVisible.not()
enabled = searchScaffoldState.isExpended || bottomSearchState.bottomSheetState.isVisible || bottomDetailState.bottomSheetState.isVisible
) {
state.collapse()
when {
bottomSearchState.bottomSheetState.isVisible -> bottomSearchState.collapse()
bottomDetailState.bottomSheetState.isVisible -> bottomDetailState.collapse()
searchScaffoldState.isExpended -> searchScaffoldState.collapse()
}
}
}
}

View file

@ -1,6 +1,7 @@
package com.pixelized.biblib.ui.screen.home.common.item
import android.content.res.Configuration
import android.text.Layout
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -10,7 +11,9 @@ import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -64,6 +67,7 @@ private fun SmallBookThumbnailContent(
.clickable { onClick(thumbnail) }
.fillMaxWidth()
.wrapContentHeight(),
shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverSmall,
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
@ -77,6 +81,7 @@ private fun SmallBookThumbnailContent(
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
}
.clip(shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverSmall)
.size(size = MaterialTheme.bibLib.dimen.thumbnail.cover),
previewPlaceholder = R.drawable.ic_fondation_thumbnail,
imageModel = thumbnail.cover,
@ -90,7 +95,7 @@ private fun SmallBookThumbnailContent(
width = Dimension.fillToConstraints
},
style = MaterialTheme.typography.h6,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.medium,
text = thumbnail.title,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
@ -106,7 +111,7 @@ private fun SmallBookThumbnailContent(
height = Dimension.fillToConstraints
},
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.easy,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = thumbnail.author
@ -122,7 +127,7 @@ private fun SmallBookThumbnailContent(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.easy,
text = thumbnail.genre
)
@ -132,7 +137,7 @@ private fun SmallBookThumbnailContent(
end.linkTo(parent.end, margin = dimen.dp8)
},
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.easy,
text = thumbnail.date ?: ""
)
}
@ -148,6 +153,7 @@ private fun SmallBookThumbnailPlaceHolder(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverSmall,
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
@ -163,8 +169,8 @@ private fun SmallBookThumbnailPlaceHolder(
}
.size(size = MaterialTheme.bibLib.dimen.thumbnail.cover)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverSmall,
),
)
@ -176,7 +182,7 @@ private fun SmallBookThumbnailPlaceHolder(
}
.size(width = 120.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -189,7 +195,7 @@ private fun SmallBookThumbnailPlaceHolder(
}
.size(width = 100.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -202,7 +208,7 @@ private fun SmallBookThumbnailPlaceHolder(
}
.size(width = 80.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -215,7 +221,7 @@ private fun SmallBookThumbnailPlaceHolder(
}
.size(width = 40.dp, height = 16.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -229,16 +235,23 @@ fun LargeBookThumbnail(
thumbnail: BookThumbnailUio?,
onClick: (BookThumbnailUio) -> Unit = default<BookThumbnailUio>(),
) {
if (thumbnail != null) {
LargeBookThumbnailContent(
modifier = modifier,
thumbnail = thumbnail,
onClick = onClick,
)
} else {
LargeBookThumbnailPlaceHolder(
modifier = modifier,
)
Card(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight(),
shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverLarge,
) {
if (thumbnail != null) {
LargeBookThumbnailContent(
modifier = modifier,
thumbnail = thumbnail,
onClick = onClick,
)
} else {
LargeBookThumbnailPlaceHolder(
modifier = modifier,
)
}
}
}
@ -253,6 +266,7 @@ private fun LargeBookThumbnailContent(
) {
GlideImage(
modifier = Modifier
.clip(shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverLarge)
.fillMaxWidth()
.aspectRatio(64f / 102f),
previewPlaceholder = R.drawable.ic_fondatoin_cover,
@ -260,22 +274,30 @@ private fun LargeBookThumbnailContent(
)
Text(
modifier = Modifier
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.padding(horizontal = MaterialTheme.bibLib.dimen.dp8),
style = MaterialTheme.typography.body1,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.medium,
text = thumbnail.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp8),
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.easy,
text = thumbnail.author
)
Text(
modifier = Modifier
.align(alignment = Alignment.End)
.padding(horizontal = MaterialTheme.bibLib.dimen.dp8)
.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
style = MaterialTheme.typography.caption,
color = MaterialTheme.colors.onSurface,
color = MaterialTheme.bibLib.colors.typography.easy,
text = thumbnail.date ?: ""
)
}
@ -291,8 +313,8 @@ private fun LargeBookThumbnailPlaceHolder(
.fillMaxWidth()
.aspectRatio(64f / 102f)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
shape = MaterialTheme.shapes.medium,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverLarge,
),
)
@ -301,7 +323,7 @@ private fun LargeBookThumbnailPlaceHolder(
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.size(width = 80.dp, height = 12.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -311,7 +333,7 @@ private fun LargeBookThumbnailPlaceHolder(
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.size(width = 60.dp, height = 12.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -321,7 +343,7 @@ private fun LargeBookThumbnailPlaceHolder(
.padding(top = MaterialTheme.bibLib.dimen.dp4)
.size(width = 40.dp, height = 12.dp)
.background(
color = MaterialTheme.bibLib.color.placeHolder,
color = MaterialTheme.bibLib.colors.placeHolder,
shape = CircleShape,
),
)
@ -371,7 +393,7 @@ private fun LargeBookThumbnailPreview() {
genre = "Sci-Fi",
title = "Foundation",
author = "Asimov",
date = "1951",
date = "February 1951",
isNew = false,
cover = "",
),

View file

@ -2,7 +2,6 @@ package com.pixelized.biblib.ui.screen.home.detail
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.activity.compose.BackHandler
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -18,7 +17,6 @@ import androidx.compose.material.icons.filled.Send
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@ -111,10 +109,6 @@ fun DetailScreen(
}
},
)
BackHandler(enabled = sheet.isVisible) {
scope.launch { sheet.hide() }
}
}
@Composable

View file

@ -21,7 +21,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.paging.PagingData
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.items
import com.pixelized.biblib.ui.scaffold.LocalSearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalCategorySearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.SearchFilter
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.utils.extention.bibLib
@ -37,7 +37,7 @@ fun CategorySearchPage(
filter: SearchFilter?,
onClose: () -> Unit = default(),
) {
val bottomSearchState = LocalSearchBottomSheetState.current
val bottomSearchState = LocalCategorySearchBottomSheetState.current
CategorySearchPageContent(
focusRequester = focusRequester,

View file

@ -16,9 +16,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.paging.LoadState
@ -26,8 +29,8 @@ import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.items
import com.pixelized.biblib.ui.scaffold.LocalCategorySearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalSearchBottomSheetState
import com.pixelized.biblib.ui.scaffold.LocalSearchViewModel
import com.pixelized.biblib.ui.scaffold.SearchFilter
import com.pixelized.biblib.ui.screen.home.common.item.BookThumbnailUio
@ -39,11 +42,15 @@ import com.pixelized.biblib.utils.extention.navigationBarsHeight
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun SearchPage(
searchViewModel: SearchViewModel = LocalSearchViewModel.current
) {
val bottomSearchState = LocalSearchBottomSheetState.current
val keyboard = LocalSoftwareKeyboardController.current
val detail = LocalDetailBottomSheetState.current
val search = LocalCategorySearchBottomSheetState.current
val focus = LocalFocusManager.current
val filters = rememberSearchFilter()
SearchPageContent(
@ -51,7 +58,12 @@ fun SearchPage(
search = searchViewModel.search,
filters = filters,
onFilter = {
bottomSearchState.expandSearch(it)
search.expandSearch(it)
},
onDetail = {
focus.clearFocus(force = true)
keyboard?.hide()
detail.expandBookDetail(id = it.id)
}
)
}
@ -62,9 +74,9 @@ private fun SearchPageContent(
search: Flow<PagingData<BookThumbnailUio>> = emptyFlow(),
filters: List<SearchFilter> = SearchFilter.all,
onFilter: (filter: SearchFilter) -> Unit = default<SearchFilter>(),
onDetail: (item: BookThumbnailUio) -> Unit = default<BookThumbnailUio>()
) {
val items = search.collectAsLazyPagingItems()
val detail = LocalDetailBottomSheetState.current
LazyColumn(
modifier = modifier,
@ -82,7 +94,7 @@ private fun SearchPageContent(
SmallBookThumbnail(
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.thumbnail.padding),
thumbnail = item,
onClick = { item?.let { detail.expandBookDetail(it.id) } }
onClick = { onDetail(it) }
)
}
}

View file

@ -15,8 +15,8 @@ val LocalBibLibTheme = compositionLocalOf<BibLibTheme> {
@Stable
@Immutable
data class BibLibTheme(
val color: BibLibColor,
val colors: BibLibColor,
val dimen: BibLibDimen,
val typography: BibLibTypography,
val shape: BibLibShape,
val shapes: BibLibShape,
)

View file

@ -16,16 +16,16 @@ fun BibLibTheme(
content: @Composable () -> Unit
) {
val theme = BibLibTheme(
color = if (darkTheme) bibLibDarkColors() else bibLibLightColors(),
colors = if (darkTheme) bibLibDarkColors() else bibLibLightColors(),
dimen = BibLibDimen(),
typography = BibLibTypography(),
shape = BibLibShape(),
shapes = BibLibShape(),
)
CompositionLocalProvider(LocalBibLibTheme provides theme) {
MaterialTheme(
colors = theme.color.base,
colors = theme.colors.base,
typography = theme.typography.base,
shapes = theme.shape.base,
shapes = theme.shapes.base,
content = content
)
}

View file

@ -12,8 +12,18 @@ import javax.annotation.concurrent.Immutable
@Immutable
data class BibLibColor(
val base: Colors,
val typography: Typography,
val placeHolder: Color,
)
) {
@Stable
@Immutable
data class Typography(
val easy: Color,
val medium: Color,
val strong: Color,
)
}
fun bibLibDarkColors(
base: Colors = darkColors(
@ -24,9 +34,16 @@ fun bibLibDarkColors(
error = BibLibColorPalette.Red,
onError = Color.White,
),
typography: BibLibColor.Typography = BibLibColor.Typography(
easy = base.onSurface.copy(alpha = 0.67f),
medium = base.onSurface,
strong = base.primary,
),
placeHolder: Color = BibLibColorPalette.DarkGrey,
) = BibLibColor(
base = base,
placeHolder = BibLibColorPalette.DarkGrey,
typography = typography,
placeHolder = placeHolder,
)
fun bibLibLightColors(
@ -37,8 +54,15 @@ fun bibLibLightColors(
onSecondary = Color.White,
error = BibLibColorPalette.Red,
onError = Color.White,
)
),
typography: BibLibColor.Typography = BibLibColor.Typography(
easy = base.onSurface.copy(alpha = 0.67f),
medium = base.onSurface,
strong = base.primary,
),
placeHolder: Color = BibLibColorPalette.LightGrey,
) = BibLibColor(
base = base,
placeHolder = BibLibColorPalette.LightGrey,
typography = typography,
placeHolder = placeHolder,
)

View file

@ -36,7 +36,6 @@ data class BibLibDimen(
val padding: Dp = 16.dp,
val arrangement: Dp = 16.dp,
val cover: DpSize = DpSize(width = 72.dp, height = 115.dp), // ratio 1.6
val corner: Dp = 8.dp,
)
@Stable

View file

@ -15,4 +15,6 @@ data class BibLibShape(
medium = RoundedCornerShape(4.dp),
large = RoundedCornerShape(0.dp),
),
val bookThumbnailCoverSmall: Shape = RoundedCornerShape(4.dp),
val bookThumbnailCoverLarge: Shape = RoundedCornerShape(4.dp),
)