diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt index ca3fea6..f92891a 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/MainScreenComposable.kt @@ -2,19 +2,28 @@ package com.pixelized.biblib.ui.composable.screen import androidx.compose.animation.* import androidx.compose.animation.core.tween +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.sharp.ArrowBack -import androidx.compose.material.icons.sharp.LocalLibrary +import androidx.compose.material.icons.sharp.Menu import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems +import com.pixelized.biblib.BuildConfig import com.pixelized.biblib.R import com.pixelized.biblib.ui.composable.pages.DetailPageComposable import com.pixelized.biblib.ui.composable.pages.HomePageComposable @@ -26,6 +35,8 @@ import com.pixelized.biblib.ui.viewmodel.book.IBooksViewModel import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel +import kotlinx.coroutines.launch +import java.util.* @OptIn(ExperimentalAnimationApi::class) @@ -34,96 +45,225 @@ fun MainScreenComposable( navigationViewModel: INavigationViewModel = viewModel(), booksViewModel: IBooksViewModel = viewModel(), ) { - val lazyBooks: LazyPagingItems = - booksViewModel.books.collectAsLazyPagingItems() - val lazyListState = rememberLazyListState() - val scrollableState = rememberScrollState() + // navigation val page by navigationViewModel.page.observeAsState() + // coroutine + val coroutineScope = rememberCoroutineScope() + // scaffold & toolbar + val canNavigateBack = page !is Page.HomePage + val scaffoldState = rememberScaffoldState() LaunchedEffect(key1 = "MainScreen", block = { navigationViewModel.navigateTo(Page.HomePage, true) }) Scaffold( - topBar = { ToolbarComposable(navigationViewModel) }, - drawerContent = {} - ) { - AnimatedVisibility( - visible = page is Page.HomePage, - initiallyVisible = true, - enter = slideInHorizontally( - animationSpec = tween(Animation.MEDIUM_DURATION), - initialOffsetX = { width -> -width } - ), - exit = slideOutHorizontally( - animationSpec = tween(Animation.MEDIUM_DURATION), - targetOffsetX = { width -> -width } - ), - ) { - HomePageComposable( - navigationViewModel, - lazyListState, - scrollableState, - lazyBooks + scaffoldState = scaffoldState, + topBar = { + ToolbarComposable( + canNavigateBack, + onBackPress = { + navigationViewModel.navigateBack() + }, + onDrawerPress = { + coroutineScope.launch { scaffoldState.drawerState.open() } + } ) + }, + drawerContent = { DrawerContentComposable() } + ) { + Box { + val lazyBooks: LazyPagingItems = + booksViewModel.books.collectAsLazyPagingItems() + val lazyListState = rememberLazyListState() + val scrollableState = rememberScrollState() + + AnimatedVisibility( + visible = page is Page.HomePage, + initiallyVisible = true, + enter = slideInHorizontally( + animationSpec = tween(Animation.MEDIUM_DURATION), + initialOffsetX = { width -> -width } + ), + exit = slideOutHorizontally( + animationSpec = tween(Animation.MEDIUM_DURATION), + targetOffsetX = { width -> -width } + ), + ) { + HomePageComposable( + navigationViewModel, + lazyListState, + scrollableState, + lazyBooks + ) + } } - AnimatedVisibility( - visible = page is Page.Detail, - initiallyVisible = false, - enter = slideInHorizontally( - animationSpec = tween(Animation.MEDIUM_DURATION), - initialOffsetX = { width -> width }, - ), - exit = slideOutHorizontally( - animationSpec = tween(Animation.MEDIUM_DURATION), - targetOffsetX = { width -> width }, - ), - ) { - // Small trick to display the detail page during animation exit. - var currentPage by remember { mutableStateOf(null) } - currentPage = page as? Page.Detail ?: currentPage - currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) } + Box { + AnimatedVisibility( + visible = page is Page.Detail, + initiallyVisible = false, + enter = slideInHorizontally( + animationSpec = tween(Animation.MEDIUM_DURATION), + initialOffsetX = { width -> width }, + ), + exit = slideOutHorizontally( + animationSpec = tween(Animation.MEDIUM_DURATION), + targetOffsetX = { width -> width }, + ), + ) { + // Small trick to display the detail page during animation exit. + var currentPage by remember { mutableStateOf(null) } + currentPage = page as? Page.Detail ?: currentPage + currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) } + } } } } @Composable -fun ToolbarComposable(navigationViewModel: INavigationViewModel) { +fun ToolbarComposable( + canNavigateBack: Boolean, + onBackPress: () -> Unit, + onDrawerPress: () -> Unit, +) { TopAppBar( - title = { Text(stringResource(id = R.string.app_name)) }, - navigationIcon = { NavigationIcon(navigationViewModel) } + title = { + Text(stringResource(id = R.string.app_name)) + }, + navigationIcon = { + NavigationIcon( + canNavigateBack, + onBackPress, + onDrawerPress, + ) + } ) } @Composable -fun NavigationIcon(navigationViewModel: INavigationViewModel) { - val page: Page? by navigationViewModel.page.observeAsState() - - Crossfade(targetState = page) { - when (it) { - is Page.HomePage -> IconButton(onClick = {}) { - Icon( - imageVector = Icons.Sharp.LocalLibrary, - contentDescription = "" - ) - } - else -> IconButton(onClick = { - navigationViewModel.navigateBack() - }) { +fun NavigationIcon( + canNavigateBack: Boolean, + onBackPress: () -> Unit, + onDrawerPress: () -> Unit, +) { + Crossfade(targetState = canNavigateBack) { + if (it) { + IconButton(onClick = onBackPress) { Icon( imageVector = Icons.Sharp.ArrowBack, contentDescription = "back" ) } + } else { + IconButton(onClick = onDrawerPress) { + Icon( + imageVector = Icons.Sharp.Menu, + contentDescription = "drawer" + ) + } } } } +@Composable +fun DrawerContentComposable() { + val typography = MaterialTheme.typography + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + ) { + Card( + shape = CutCornerShape(bottomEnd = 16.dp), + elevation = 8.dp + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + style = typography.body1, + text = "R. Daneel Olivaw" + ) + Text( + style = typography.caption, + text = "r.daneel.olivaw@biblib.com" + ) + } + } + Spacer(modifier = Modifier.height(8.dp)) + DrawerLink( + text = "Nouveautés", + imageVector = Icons.Default.NewReleases, + onClick = {}, + ) + DrawerLink( + text = "Livres", + imageVector = Icons.Default.AutoStories, + onClick = {}, + ) + DrawerLink( + text = "Séries", + imageVector = Icons.Default.AutoAwesomeMotion, + onClick = {}, + ) + DrawerLink( + text = "Auteurs", + imageVector = Icons.Default.SupervisorAccount, + onClick = {}, + ) + DrawerLink( + text = "Étiquettes", + imageVector = Icons.Default.LocalOffer, + onClick = {}, + ) + Spacer(modifier = Modifier.weight(1f)) + Text( + modifier = Modifier + .align(Alignment.End) + .padding(16.dp), + style = typography.caption, + text = stringResource( + R.string.app_version, + BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()), + BuildConfig.VERSION_NAME, + BuildConfig.VERSION_CODE + ) + ) + } +} + +@Composable +fun DrawerLink(text: String, imageVector: ImageVector, onClick: () -> Unit) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(52.dp) + .clickable { onClick() }) { + Icon( + modifier = Modifier.padding(horizontal = 16.dp), + imageVector = imageVector, + contentDescription = "" + ) + Text( + text = text, + ) + Spacer(modifier = Modifier.weight(1f)) + Icon( + modifier = Modifier.padding(end = 8.dp), + imageVector = Icons.Default.NavigateNext, + contentDescription = "" + ) + } +} + @Preview @Composable fun ToolbarComposableDarkPreview() { BibLibTheme(darkTheme = false) { - ToolbarComposable(navigationViewModel = INavigationViewModel.Mock()) + ToolbarComposable(false, {}, {}) } } @@ -131,7 +271,7 @@ fun ToolbarComposableDarkPreview() { @Composable fun ToolbarComposableLightPreview() { BibLibTheme(darkTheme = true) { - ToolbarComposable(navigationViewModel = INavigationViewModel.Mock()) + ToolbarComposable(true, {}, {}) } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/INavigationViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/INavigationViewModel.kt index 2f43dcc..d5bf44b 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/INavigationViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/INavigationViewModel.kt @@ -2,7 +2,6 @@ package com.pixelized.biblib.ui.viewmodel.navigation import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import com.pixelized.biblib.ui.data.BookUio interface INavigationViewModel { val screen: LiveData @@ -10,6 +9,7 @@ interface INavigationViewModel { fun navigateTo(navigable: Navigable, addToBackStack: Boolean = false): Boolean fun navigateBack(): Boolean + fun canNavigateBack(): Boolean sealed class Navigable { sealed class Screen : Navigable() { @@ -33,5 +33,6 @@ interface INavigationViewModel { override fun navigateTo(navigable: Navigable, addToBackStack: Boolean): Boolean = false override fun navigateBack(): Boolean = false + override fun canNavigateBack(): Boolean = false } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/NavigationViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/NavigationViewModel.kt index 3835507..8858ae9 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/NavigationViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/navigation/NavigationViewModel.kt @@ -43,6 +43,8 @@ class NavigationViewModel : ViewModel(), INavigationViewModel { } } + override fun canNavigateBack(): Boolean = stack.any { it != null } + private fun popBackStack(): Navigable? { return if (stack.isEmpty()) { null