Add email bottom sheet choice in book detail.
This commit is contained in:
parent
f6739f60ee
commit
3137c358c7
6 changed files with 131 additions and 22 deletions
|
|
@ -15,6 +15,8 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import com.pixelized.biblib.ui.composable.dialog.ErrorCard
|
import com.pixelized.biblib.ui.composable.dialog.ErrorCard
|
||||||
import com.pixelized.biblib.ui.composable.dialog.LoadingCard
|
import com.pixelized.biblib.ui.composable.dialog.LoadingCard
|
||||||
|
import kotlin.contracts.ExperimentalContracts
|
||||||
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
sealed class StateUio<T> {
|
sealed class StateUio<T> {
|
||||||
class Progress<T>(val progress: Float? = null) : StateUio<T>()
|
class Progress<T>(val progress: Float? = null) : StateUio<T>()
|
||||||
|
|
@ -22,6 +24,14 @@ sealed class StateUio<T> {
|
||||||
class Success<T>(val value: T) : StateUio<T>()
|
class Success<T>(val value: T) : StateUio<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
fun <T> StateUio<T>.isSuccessful(): Boolean {
|
||||||
|
contract {
|
||||||
|
returns(true) implies (this@isSuccessful is StateUio.Success<T>)
|
||||||
|
}
|
||||||
|
return this is StateUio.Success<T>
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun <T> StateUioHandler(
|
fun <T> StateUioHandler(
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import com.pixelized.biblib.utils.extention.showToast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
val LocalBottomDetailController = staticCompositionLocalOf<BottomDetailState> {
|
val LocalBottomDetailState = staticCompositionLocalOf<BottomDetailState> {
|
||||||
error("LocalBottomDetailController is not ready yet")
|
error("LocalBottomDetailController is not ready yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ fun BottomDetailScaffold(
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalBottomDetailController provides bottomDetailState
|
LocalBottomDetailState provides bottomDetailState
|
||||||
) {
|
) {
|
||||||
ModalBottomSheetLayout(
|
ModalBottomSheetLayout(
|
||||||
scrimColor = Color.Black.copy(alpha = 0.37f),
|
scrimColor = Color.Black.copy(alpha = 0.37f),
|
||||||
|
|
@ -89,7 +89,6 @@ class BottomDetailState constructor(
|
||||||
}
|
}
|
||||||
is StateUio.Success -> {
|
is StateUio.Success -> {
|
||||||
bookDetail.value = book.value
|
bookDetail.value = book.value
|
||||||
// bottomSheetState.animateTo(ModalBottomSheetValue.Expanded)
|
|
||||||
bottomSheetState.show()
|
bottomSheetState.show()
|
||||||
}
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,21 @@ package com.pixelized.biblib.ui.screen.detail
|
||||||
|
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.Button
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.Icon
|
|
||||||
import androidx.compose.material.MaterialTheme
|
|
||||||
import androidx.compose.material.Text
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.material.icons.filled.Download
|
||||||
import androidx.compose.material.icons.filled.Send
|
import androidx.compose.material.icons.filled.Send
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
@ -26,29 +26,77 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.core.text.toSpannable
|
import androidx.core.text.toSpannable
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.biblib.R
|
import com.pixelized.biblib.R
|
||||||
import com.pixelized.biblib.ui.composable.Cover
|
import com.pixelized.biblib.ui.composable.Cover
|
||||||
import com.pixelized.biblib.ui.composable.SpannedText
|
import com.pixelized.biblib.ui.composable.SpannedText
|
||||||
|
import com.pixelized.biblib.ui.composable.StateUio
|
||||||
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
|
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
|
||||||
import com.pixelized.biblib.ui.composable.animation.AnimatedOffset
|
import com.pixelized.biblib.ui.composable.animation.AnimatedOffset
|
||||||
|
import com.pixelized.biblib.ui.composable.isSuccessful
|
||||||
|
import com.pixelized.biblib.ui.scaffold.BottomDetailState
|
||||||
|
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailState
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.BookUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.BookUio
|
||||||
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio
|
||||||
|
import com.pixelized.biblib.ui.screen.profile.ProfileViewModel
|
||||||
|
import com.pixelized.biblib.ui.screen.profile.UserUio
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
import com.pixelized.biblib.utils.extention.bibLib
|
import com.pixelized.biblib.utils.extention.bibLib
|
||||||
import com.pixelized.biblib.utils.extention.todo
|
import com.pixelized.biblib.utils.extention.todo
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailScreen(
|
fun DetailScreen(
|
||||||
|
detailState: BottomDetailState = LocalBottomDetailState.current,
|
||||||
|
profileViewModel: ProfileViewModel = hiltViewModel(),
|
||||||
detail: BookUio? = null,
|
detail: BookUio? = null,
|
||||||
) {
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val sheet = rememberModalBottomSheetState(
|
||||||
|
initialValue = ModalBottomSheetValue.Hidden,
|
||||||
|
skipHalfExpanded = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
ModalBottomSheetLayout(
|
||||||
|
sheetState = sheet,
|
||||||
|
scrimColor = Color.Black.copy(alpha = 0.37f),
|
||||||
|
sheetContent = {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(min = 1.dp)
|
||||||
|
.animateContentSize()
|
||||||
|
) {
|
||||||
|
val user by derivedStateOf { profileViewModel.user }
|
||||||
|
if (user.isSuccessful()) {
|
||||||
|
DetailScreenSendContent(
|
||||||
|
modifier = Modifier.navigationBarsPadding(),
|
||||||
|
emails = (user as StateUio.Success<UserUio>).value.amazonEmails,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content = {
|
||||||
|
if (detailState.bottomSheetState.isVisible) {
|
||||||
if (detail != null) {
|
if (detail != null) {
|
||||||
DetailScreenContent(
|
DetailScreenContent(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
book = detail,
|
book = detail,
|
||||||
|
onSend = { scope.launch { sheet.show() } }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Box(modifier = Modifier.fillMaxSize())
|
Box(modifier = Modifier.fillMaxSize())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
LaunchedEffect(key1 = "email bottom sheet close") { sheet.hide() }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
BackHandler(enabled = sheet.isVisible) {
|
||||||
|
scope.launch { sheet.hide() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -61,7 +109,6 @@ private fun DetailScreenContent(
|
||||||
) {
|
) {
|
||||||
AnimatedDelayer(
|
AnimatedDelayer(
|
||||||
targetState = book,
|
targetState = book,
|
||||||
delay = 300,
|
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -230,6 +277,44 @@ private fun TitleLabel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DetailScreenSendContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
emails: List<String>,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(
|
||||||
|
vertical = MaterialTheme.bibLib.dimen.dp8,
|
||||||
|
horizontal = MaterialTheme.bibLib.dimen.dp16,
|
||||||
|
),
|
||||||
|
color = MaterialTheme.colors.primary,
|
||||||
|
style = MaterialTheme.typography.caption,
|
||||||
|
text = "Send this eBook to:",
|
||||||
|
)
|
||||||
|
|
||||||
|
LazyColumn {
|
||||||
|
items(items = emails) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(height = MaterialTheme.bibLib.dimen.dp48)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.bibLib.dimen.dp16),
|
||||||
|
style = MaterialTheme.typography.body1,
|
||||||
|
color = MaterialTheme.colors.onSurface,
|
||||||
|
text = it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||||
|
|
@ -259,3 +344,17 @@ private fun DetailScreenContentPreview() {
|
||||||
DetailScreenContent(book = book)
|
DetailScreenContent(book = book)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||||
|
private fun DetailScreenSendPreview() {
|
||||||
|
BibLibTheme {
|
||||||
|
DetailScreenSendContent(
|
||||||
|
emails = listOf(
|
||||||
|
"R.Giskard.Reventlov.Kindle@gmailcom",
|
||||||
|
"R.Daneel.Olivaw.Kindle@gmailcom",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailController
|
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailState
|
||||||
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
||||||
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
|
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
|
||||||
import com.pixelized.biblib.ui.screen.home.page.news.NewsPage
|
import com.pixelized.biblib.ui.screen.home.page.news.NewsPage
|
||||||
|
|
@ -18,7 +18,7 @@ fun BooksPage(
|
||||||
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
|
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
|
||||||
booksViewModel: BooksViewModel = hiltViewModel()
|
booksViewModel: BooksViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val bottomDetailState = LocalBottomDetailController.current
|
val bottomDetailState = LocalBottomDetailState.current
|
||||||
|
|
||||||
LazyBookThumbnailColumn(
|
LazyBookThumbnailColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailController
|
import com.pixelized.biblib.ui.scaffold.LocalBottomDetailState
|
||||||
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
import com.pixelized.biblib.ui.screen.connectivity.ConnectivityViewModel
|
||||||
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
|
import com.pixelized.biblib.ui.screen.home.common.composable.LazyBookThumbnailColumn
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
|
|
@ -17,7 +17,7 @@ fun NewsPage(
|
||||||
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
|
connectivityViewModel: ConnectivityViewModel = hiltViewModel(),
|
||||||
booksViewModel: NewsBookViewModel = hiltViewModel()
|
booksViewModel: NewsBookViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val bottomDetail = LocalBottomDetailController.current
|
val bottomDetail = LocalBottomDetailState.current
|
||||||
|
|
||||||
LazyBookThumbnailColumn(
|
LazyBookThumbnailColumn(
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.bibLib.dimen.thumbnail.arrangement),
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ data class BibLibDimen(
|
||||||
val dp8: Dp = 8.dp,
|
val dp8: Dp = 8.dp,
|
||||||
val dp16: Dp = 16.dp,
|
val dp16: Dp = 16.dp,
|
||||||
val dp32: Dp = 32.dp,
|
val dp32: Dp = 32.dp,
|
||||||
|
val dp48: Dp = 48.dp,
|
||||||
val dp64: Dp = 64.dp,
|
val dp64: Dp = 64.dp,
|
||||||
val dialog: Dialog = Dialog(),
|
val dialog: Dialog = Dialog(),
|
||||||
val thumbnail: BookThumbnail = BookThumbnail(),
|
val thumbnail: BookThumbnail = BookThumbnail(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue