diff --git a/app/src/main/java/com/pixelized/biblib/ui/scaffold/SearchScaffold.kt b/app/src/main/java/com/pixelized/biblib/ui/scaffold/SearchScaffold.kt index a5c9897..39d9eaf 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/scaffold/SearchScaffold.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/scaffold/SearchScaffold.kt @@ -5,6 +5,7 @@ import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.Card import androidx.compose.material.MaterialTheme @@ -28,9 +29,10 @@ fun SearchScaffold( Layout( modifier = modifier, content = { + // TODO : remember transition. val transition = updateTransition( label = "Collapse transition", - targetState = state.isCollapsed() + targetState = state.isCollapsed ) val horizontal by transition.animateDp(label = "horizontal") { when (it) { @@ -52,7 +54,8 @@ fun SearchScaffold( Column { topBar() AnimatedVisibility( - visible = state.isCollapsed().not(), + modifier = Modifier.fillMaxWidth(), + visible = state.isExpended, enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { @@ -87,51 +90,34 @@ fun SearchScaffold( @Composable fun rememberSearchScaffoldState( expended: Boolean = false, - content: SearchScaffoldState.ContentState = SearchScaffoldState.ContentState.INITIAL, ): SearchScaffoldState { return rememberSaveable(saver = SearchScaffoldState.Saver) { - SearchScaffoldState( - expended = expended, - state = content, - ) + SearchScaffoldState(expended = expended) } } @Stable class SearchScaffoldState( expended: Boolean, - state: ContentState = ContentState.INITIAL, ) { var isExpended: Boolean by mutableStateOf(expended) private set - var content: ContentState by mutableStateOf(state) - private set + val isCollapsed: Boolean + get() = isExpended.not() - fun isCollapsed(): Boolean = isExpended.not() - - fun expand(state: ContentState) { - content = state + fun expand() { isExpended = true } fun collapse() { isExpended = false - content = ContentState.INITIAL } companion object { - val Saver: Saver> = Saver( - save = { it.isExpended to it.content.ordinal }, - restore = { SearchScaffoldState(it.first, ContentState.values()[it.second]) }, + val Saver: Saver = Saver( + save = { it.isExpended }, + restore = { SearchScaffoldState(it) }, ) } - - @Stable - @Immutable - enum class ContentState { - INITIAL, - SEARCH, - PROFILE, - } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt index 1460584..3ca7b00 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.SoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.window.Dialog import androidx.hilt.navigation.compose.hiltViewModel import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager @@ -25,7 +26,6 @@ import com.google.accompanist.pager.PagerState import com.google.accompanist.pager.rememberPagerState import com.pixelized.biblib.R import com.pixelized.biblib.ui.scaffold.* -import com.pixelized.biblib.ui.scaffold.SearchScaffoldState.ContentState 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 @@ -40,14 +40,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch -@OptIn( - ExperimentalComposeUiApi::class, - ExperimentalAnimationApi::class, - ExperimentalMaterialApi::class -) +@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterialApi::class) @Composable fun HomeScreen( - accountViewModel: HomeViewModel = hiltViewModel(), + homeViewModel: HomeViewModel = hiltViewModel(), connectivityViewModel: ConnectivityViewModel = hiltViewModel(), keyboard: SoftwareKeyboardController? = LocalSoftwareKeyboardController.current, searchScaffoldState: SearchScaffoldState = rememberSearchScaffoldState(), @@ -65,70 +61,33 @@ fun HomeScreen( val viewModel = LocalBookSearchViewModel.current Search( state = searchScaffoldState, - avatar = accountViewModel.avatar, focusRequester = focusRequester, + avatar = homeViewModel.avatar, + searchValue = viewModel.search ?: "", + onSearchValueChange = { + viewModel.filterSearch(criteria = it) + }, onCloseTap = { focusManager.clearFocus(force = true) keyboard?.hide() searchScaffoldState.collapse() }, - searchValue = viewModel.search ?: "", - onSearchValueChange = { - viewModel.filterSearch(criteria = it) + onAvatarTap = { + homeViewModel.showProfileDialog() }, onSearchTap = { - if (searchScaffoldState.content != ContentState.SEARCH || searchScaffoldState.isCollapsed()) { - searchScaffoldState.expand(ContentState.SEARCH) + if (searchScaffoldState.isCollapsed) { + searchScaffoldState.expand() scope.launch { delay(100) // let the animation play before requesting the focus focusRequester.requestFocus() } - } else { - focusManager.clearFocus(force = true) - keyboard?.hide() - searchScaffoldState.collapse() - } - }, - onAvatarTap = { - if (searchScaffoldState.content != ContentState.PROFILE || searchScaffoldState.isCollapsed()) { - focusManager.clearFocus(force = true) - keyboard?.hide() - searchScaffoldState.expand(ContentState.PROFILE) - } else { - searchScaffoldState.collapse() } } ) }, search = { - Box(modifier = Modifier.fillMaxSize()) { - AnimatedContent( - targetState = searchScaffoldState.content, - transitionSpec = { - when { - targetState == ContentState.INITIAL -> { - EnterTransition.None with fadeOut() - } - targetState.ordinal < initialState.ordinal -> { - val enter = slideInHorizontally { -it } + fadeIn() - val exit = slideOutHorizontally { +it } + fadeOut() - enter with exit - } - else -> { - val enter = slideInHorizontally { +it } + fadeIn() - val exit = slideOutHorizontally { -it } + fadeOut() - enter with exit - } - } - } - ) { - when (it) { - ContentState.SEARCH -> SearchPage() - ContentState.PROFILE -> ProfilePage() - else -> Unit - } - } - } + SearchPage() }, content = { HomeScreenContent( @@ -155,6 +114,21 @@ fun HomeScreen( } } } + + ProfileHandler(viewModel = homeViewModel) +} + +@Composable +fun ProfileHandler( + viewModel: HomeViewModel +) { + if (viewModel.shouldDisplayProfileDialog) { + Dialog( + onDismissRequest = { viewModel.dismissProfileDialog() } + ) { + ProfilePage() + } + } } @OptIn(ExperimentalPagerApi::class) diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeViewModel.kt index 66ce591..d9770e2 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/HomeViewModel.kt @@ -2,9 +2,14 @@ package com.pixelized.biblib.ui.screen.home import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.pixelized.biblib.repository.googleSignIn.IGoogleSingInRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -12,4 +17,15 @@ class HomeViewModel @Inject constructor( account: IGoogleSingInRepository, ) : ViewModel() { val avatar by mutableStateOf(account.account?.photoUrl?.toString()) + + var shouldDisplayProfileDialog by mutableStateOf(false) + private set + + fun showProfileDialog() { + shouldDisplayProfileDialog = true + } + + fun dismissProfileDialog() { + shouldDisplayProfileDialog = false + } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt index ee46cbc..07fe3b0 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/profile/ProfilePage.kt @@ -29,21 +29,25 @@ fun ProfilePage( val context = LocalContext.current val navigation = LocalScreenNavHostController.current - when (val user = viewModel.user) { - is StateUio.Progress -> Unit - is StateUio.Success -> ProfileScreenContent( - modifier = Modifier.padding(MaterialTheme.bibLib.dimen.dp16), - user = user.value, - onEditClick = { - val intent = Intent(Intent.ACTION_VIEW, Uri.parse(IBibLibClient.EDIT_PROFILE)) - context.startActivity(intent) - }, - onLogoutClick = { - viewModel.logout() - navigation.navigateToAuthentication() - } - ) - is StateUio.Failure -> Unit + Card { + when (val user = viewModel.user) { + is StateUio.Progress -> Unit + is StateUio.Success -> ProfileScreenContent( + modifier = Modifier + .fillMaxWidth() + .padding(MaterialTheme.bibLib.dimen.dp16), + user = user.value, + onEditClick = { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(IBibLibClient.EDIT_PROFILE)) + context.startActivity(intent) + }, + onLogoutClick = { + viewModel.logout() + navigation.navigateToAuthentication() + } + ) + is StateUio.Failure -> Unit + } } } @@ -54,14 +58,14 @@ private fun ProfileScreenContent( onEditClick: () -> Unit = default(), onLogoutClick: () -> Unit = default(), ) { - Column(modifier = modifier.fillMaxWidth()) { + Column(modifier = modifier) { Text( style = MaterialTheme.typography.body1, color = MaterialTheme.colors.onSurface, text = "Welcome" ) Text( - style = MaterialTheme.typography.h4, + style = MaterialTheme.typography.h6, color = MaterialTheme.colors.primary, text = user.username, ) @@ -94,7 +98,7 @@ private fun ProfileScreenContent( ) user.amazonEmails.forEach { Text( - style = MaterialTheme.typography.body1, + style = MaterialTheme.typography.caption, color = MaterialTheme.colors.onSurface, text = it, ) @@ -119,7 +123,7 @@ private fun ProfileScreenContent( Button( modifier = Modifier - .padding(top = MaterialTheme.bibLib.dimen.dp8) + .align(Alignment.End), colors = ButtonDefaults.outlinedButtonColors(), onClick = onLogoutClick, @@ -138,7 +142,9 @@ private fun ProfileScreenContentPreview() { BibLibTheme { Box { ProfileScreenContent( - modifier = Modifier.padding(MaterialTheme.bibLib.dimen.dp16), + modifier = Modifier + .fillMaxWidth() + .padding(all = MaterialTheme.bibLib.dimen.dp16), user = UserUio( username = "DefinitelyNotARobot", firstname = "R. Daneel", diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchPage.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchPage.kt index ae1b5ab..dddca53 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchPage.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchPage.kt @@ -2,10 +2,7 @@ package com.pixelized.biblib.ui.screen.home.page.search import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme @@ -50,7 +47,7 @@ fun SearchPage( val scope = rememberCoroutineScope() SearchPageContent( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxSize(), search = bookSearchViewModel.paging, filters = filters(bookSearchViewModel), onFilter = { diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchToolbar.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchToolbar.kt index 60b12e9..523e84b 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchToolbar.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/search/SearchToolbar.kt @@ -13,7 +13,9 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Search -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -25,7 +27,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.pixelized.biblib.R import com.pixelized.biblib.ui.scaffold.SearchScaffoldState -import com.pixelized.biblib.ui.scaffold.SearchScaffoldState.ContentState import com.pixelized.biblib.ui.scaffold.rememberSearchScaffoldState import com.pixelized.biblib.ui.theme.BibLibTheme import com.pixelized.biblib.utils.extention.bibLib @@ -46,7 +47,7 @@ fun Search( onSearchTap: () -> Unit = default(), ) { val horizontalPadding by animateDpAsState( - targetValue = when (state.isCollapsed()) { + targetValue = when (state.isCollapsed) { true -> MaterialTheme.bibLib.dimen.default else -> MaterialTheme.bibLib.dimen.dp4 } @@ -60,11 +61,12 @@ fun Search( ) { IconButton( modifier = Modifier.padding(start = horizontalPadding), - onClick = if (state.content != ContentState.INITIAL) onCloseTap else onSearchTap, + enabled = state.isExpended, + onClick = onCloseTap, ) { Icon( - imageVector = when (state.content) { - ContentState.INITIAL -> Icons.Default.Search + imageVector = when (state.isCollapsed) { + true -> Icons.Default.Search else -> Icons.Default.Close }, tint = MaterialTheme.colors.onSurface, @@ -79,17 +81,13 @@ fun Search( placeholder = { Text( color = MaterialTheme.colors.onSurface, - text = if (state.content != ContentState.PROFILE) { - stringResource(id = R.string.search_title) - } else { - stringResource(id = R.string.profile_title) - } + text = stringResource(id = R.string.search_title), ) }, - value = if (state.content == ContentState.SEARCH) searchValue else "", + value = if (state.isExpended) searchValue else "", singleLine = true, - enabled = state.content == ContentState.SEARCH, - readOnly = state.content != ContentState.SEARCH, + enabled = state.isExpended, + readOnly = state.isCollapsed, colors = TextFieldDefaults.outlinedTextFieldColors( focusedBorderColor = Color.Transparent, unfocusedBorderColor = Color.Transparent, @@ -141,10 +139,7 @@ private fun SearchContentEmptyDeployPreview() { BibLibTheme { Search( avatar = "", - state = rememberSearchScaffoldState( - expended = true, - content = ContentState.SEARCH, - ), + state = rememberSearchScaffoldState(expended = true), ) } } \ No newline at end of file