Add drawer menu.
This commit is contained in:
parent
01d635ca1a
commit
bb9e1b36f7
3 changed files with 204 additions and 61 deletions
|
|
@ -2,19 +2,28 @@ package com.pixelized.biblib.ui.composable.screen
|
||||||
|
|
||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.animation.core.tween
|
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.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CutCornerShape
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
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.ArrowBack
|
||||||
import androidx.compose.material.icons.sharp.LocalLibrary
|
import androidx.compose.material.icons.sharp.Menu
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
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.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.paging.compose.LazyPagingItems
|
import androidx.paging.compose.LazyPagingItems
|
||||||
import androidx.paging.compose.collectAsLazyPagingItems
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import com.pixelized.biblib.BuildConfig
|
||||||
import com.pixelized.biblib.R
|
import com.pixelized.biblib.R
|
||||||
import com.pixelized.biblib.ui.composable.pages.DetailPageComposable
|
import com.pixelized.biblib.ui.composable.pages.DetailPageComposable
|
||||||
import com.pixelized.biblib.ui.composable.pages.HomePageComposable
|
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
|
||||||
import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page
|
import com.pixelized.biblib.ui.viewmodel.navigation.INavigationViewModel.Navigable.Page
|
||||||
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
|
|
@ -34,20 +45,39 @@ fun MainScreenComposable(
|
||||||
navigationViewModel: INavigationViewModel = viewModel<NavigationViewModel>(),
|
navigationViewModel: INavigationViewModel = viewModel<NavigationViewModel>(),
|
||||||
booksViewModel: IBooksViewModel = viewModel<BooksViewModel>(),
|
booksViewModel: IBooksViewModel = viewModel<BooksViewModel>(),
|
||||||
) {
|
) {
|
||||||
val lazyBooks: LazyPagingItems<BookThumbnailUio> =
|
// navigation
|
||||||
booksViewModel.books.collectAsLazyPagingItems()
|
|
||||||
val lazyListState = rememberLazyListState()
|
|
||||||
val scrollableState = rememberScrollState()
|
|
||||||
val page by navigationViewModel.page.observeAsState()
|
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 = {
|
LaunchedEffect(key1 = "MainScreen", block = {
|
||||||
navigationViewModel.navigateTo(Page.HomePage, true)
|
navigationViewModel.navigateTo(Page.HomePage, true)
|
||||||
})
|
})
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { ToolbarComposable(navigationViewModel) },
|
scaffoldState = scaffoldState,
|
||||||
drawerContent = {}
|
topBar = {
|
||||||
|
ToolbarComposable(
|
||||||
|
canNavigateBack,
|
||||||
|
onBackPress = {
|
||||||
|
navigationViewModel.navigateBack()
|
||||||
|
},
|
||||||
|
onDrawerPress = {
|
||||||
|
coroutineScope.launch { scaffoldState.drawerState.open() }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
drawerContent = { DrawerContentComposable() }
|
||||||
) {
|
) {
|
||||||
|
Box {
|
||||||
|
val lazyBooks: LazyPagingItems<BookThumbnailUio> =
|
||||||
|
booksViewModel.books.collectAsLazyPagingItems()
|
||||||
|
val lazyListState = rememberLazyListState()
|
||||||
|
val scrollableState = rememberScrollState()
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = page is Page.HomePage,
|
visible = page is Page.HomePage,
|
||||||
initiallyVisible = true,
|
initiallyVisible = true,
|
||||||
|
|
@ -67,6 +97,8 @@ fun MainScreenComposable(
|
||||||
lazyBooks
|
lazyBooks
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Box {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = page is Page.Detail,
|
visible = page is Page.Detail,
|
||||||
initiallyVisible = false,
|
initiallyVisible = false,
|
||||||
|
|
@ -85,45 +117,153 @@ fun MainScreenComposable(
|
||||||
currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) }
|
currentPage?.let { DetailPageComposable(booksViewModel, it.bookId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ToolbarComposable(navigationViewModel: INavigationViewModel) {
|
fun ToolbarComposable(
|
||||||
|
canNavigateBack: Boolean,
|
||||||
|
onBackPress: () -> Unit,
|
||||||
|
onDrawerPress: () -> Unit,
|
||||||
|
) {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(stringResource(id = R.string.app_name)) },
|
title = {
|
||||||
navigationIcon = { NavigationIcon(navigationViewModel) }
|
Text(stringResource(id = R.string.app_name))
|
||||||
)
|
},
|
||||||
}
|
navigationIcon = {
|
||||||
|
NavigationIcon(
|
||||||
@Composable
|
canNavigateBack,
|
||||||
fun NavigationIcon(navigationViewModel: INavigationViewModel) {
|
onBackPress,
|
||||||
val page: Page? by navigationViewModel.page.observeAsState()
|
onDrawerPress,
|
||||||
|
|
||||||
Crossfade(targetState = page) {
|
|
||||||
when (it) {
|
|
||||||
is Page.HomePage -> IconButton(onClick = {}) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Sharp.LocalLibrary,
|
|
||||||
contentDescription = ""
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> IconButton(onClick = {
|
)
|
||||||
navigationViewModel.navigateBack()
|
}
|
||||||
}) {
|
|
||||||
|
@Composable
|
||||||
|
fun NavigationIcon(
|
||||||
|
canNavigateBack: Boolean,
|
||||||
|
onBackPress: () -> Unit,
|
||||||
|
onDrawerPress: () -> Unit,
|
||||||
|
) {
|
||||||
|
Crossfade(targetState = canNavigateBack) {
|
||||||
|
if (it) {
|
||||||
|
IconButton(onClick = onBackPress) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Sharp.ArrowBack,
|
imageVector = Icons.Sharp.ArrowBack,
|
||||||
contentDescription = "back"
|
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
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun ToolbarComposableDarkPreview() {
|
fun ToolbarComposableDarkPreview() {
|
||||||
BibLibTheme(darkTheme = false) {
|
BibLibTheme(darkTheme = false) {
|
||||||
ToolbarComposable(navigationViewModel = INavigationViewModel.Mock())
|
ToolbarComposable(false, {}, {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +271,7 @@ fun ToolbarComposableDarkPreview() {
|
||||||
@Composable
|
@Composable
|
||||||
fun ToolbarComposableLightPreview() {
|
fun ToolbarComposableLightPreview() {
|
||||||
BibLibTheme(darkTheme = true) {
|
BibLibTheme(darkTheme = true) {
|
||||||
ToolbarComposable(navigationViewModel = INavigationViewModel.Mock())
|
ToolbarComposable(true, {}, {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package com.pixelized.biblib.ui.viewmodel.navigation
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.pixelized.biblib.ui.data.BookUio
|
|
||||||
|
|
||||||
interface INavigationViewModel {
|
interface INavigationViewModel {
|
||||||
val screen: LiveData<Navigable.Screen>
|
val screen: LiveData<Navigable.Screen>
|
||||||
|
|
@ -10,6 +9,7 @@ interface INavigationViewModel {
|
||||||
|
|
||||||
fun navigateTo(navigable: Navigable, addToBackStack: Boolean = false): Boolean
|
fun navigateTo(navigable: Navigable, addToBackStack: Boolean = false): Boolean
|
||||||
fun navigateBack(): Boolean
|
fun navigateBack(): Boolean
|
||||||
|
fun canNavigateBack(): Boolean
|
||||||
|
|
||||||
sealed class Navigable {
|
sealed class Navigable {
|
||||||
sealed class Screen : Navigable() {
|
sealed class Screen : Navigable() {
|
||||||
|
|
@ -33,5 +33,6 @@ interface INavigationViewModel {
|
||||||
|
|
||||||
override fun navigateTo(navigable: Navigable, addToBackStack: Boolean): Boolean = false
|
override fun navigateTo(navigable: Navigable, addToBackStack: Boolean): Boolean = false
|
||||||
override fun navigateBack(): Boolean = false
|
override fun navigateBack(): Boolean = false
|
||||||
|
override fun canNavigateBack(): Boolean = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,6 +43,8 @@ class NavigationViewModel : ViewModel(), INavigationViewModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun canNavigateBack(): Boolean = stack.any { it != null }
|
||||||
|
|
||||||
private fun popBackStack(): Navigable? {
|
private fun popBackStack(): Navigable? {
|
||||||
return if (stack.isEmpty()) {
|
return if (stack.isEmpty()) {
|
||||||
null
|
null
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue