Prefil BookDetail with availlable detail from the list.
This commit is contained in:
parent
af89e153ef
commit
2774f93b6c
17 changed files with 585 additions and 478 deletions
|
|
@ -123,6 +123,7 @@ dependencies {
|
|||
implementation "com.google.accompanist:accompanist-drawablepainter:0.26.5-rc"
|
||||
implementation "com.google.accompanist:accompanist-insets:0.26.5-rc"
|
||||
implementation "com.google.accompanist:accompanist-pager:0.26.5-rc"
|
||||
implementation "com.google.accompanist:accompanist-placeholder-material:0.26.5-rc"
|
||||
|
||||
// Landscapist
|
||||
implementation "com.github.skydoves:landscapist-glide:1.5.2"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ interface BookDao {
|
|||
@Query("SELECT * FROM ${BookDbo.TABLE} ORDER BY ${BookDbo.SORT}")
|
||||
fun getBook(): DataSource.Factory<Int, BookRelation>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM ${BookDbo.TABLE} WHERE ${BookDbo.ID} LIKE :id")
|
||||
fun getBook(id: Int): BookRelation
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insert(vararg books: BookDbo)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import com.pixelized.biblib.database.BibLibDatabase
|
|||
import com.pixelized.biblib.database.crossref.BookAuthorCrossRef
|
||||
import com.pixelized.biblib.database.crossref.BookGenreCrossRef
|
||||
import com.pixelized.biblib.database.data.*
|
||||
import com.pixelized.biblib.model.book.*
|
||||
import com.pixelized.biblib.database.factory.*
|
||||
import com.pixelized.biblib.model.book.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class BookRepository @Inject constructor(
|
||||
|
|
@ -17,6 +17,9 @@ class BookRepository @Inject constructor(
|
|||
override fun getAll(): List<Book> =
|
||||
database.bookDao().getAll().map { it.toBook() }
|
||||
|
||||
override fun getBook(id: Int): Book =
|
||||
database.bookDao().getBook(id = id).toBook()
|
||||
|
||||
override fun getBookCount(): Int =
|
||||
database.bookDao().count()
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ interface IBookRepository {
|
|||
|
||||
fun getAll(): List<Book>
|
||||
|
||||
fun getBook(id: Int): Book
|
||||
|
||||
fun getBookCount(): Int
|
||||
|
||||
fun getNewsSource(): DataSource.Factory<Int, Book>
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ import kotlin.contracts.ExperimentalContracts
|
|||
import kotlin.contracts.contract
|
||||
|
||||
sealed class StateUio<T> {
|
||||
class Progress<T>(val progress: Float? = null) : StateUio<T>()
|
||||
class Failure<T>(val exception: Exception) : StateUio<T>()
|
||||
class Progress<T>(val progress: Float? = null, val cache : T? = null) : StateUio<T>()
|
||||
class Failure<T>(val exception: Exception, val cache: T? = null) : StateUio<T>()
|
||||
class Success<T>(val value: T) : StateUio<T>()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,13 +11,9 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.composable.StateUio
|
||||
import com.pixelized.biblib.ui.screen.home.detail.BookDetailUio
|
||||
import com.pixelized.biblib.ui.screen.home.detail.BookDetailViewModel
|
||||
import com.pixelized.biblib.ui.screen.home.detail.DetailScreen
|
||||
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
||||
import com.pixelized.biblib.utils.extention.showToast
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
val LocalDetailBottomSheetState = staticCompositionLocalOf<DetailBottomSheetState> {
|
||||
|
|
@ -39,10 +35,7 @@ fun DetailBottomSheet(
|
|||
scrimColor = ShadowPalette.scrim,
|
||||
sheetState = bottomDetailState.bottomSheetState,
|
||||
sheetContent = {
|
||||
DetailScreen(
|
||||
detailViewModel = bottomDetailState.viewModel,
|
||||
detail = bottomDetailState.bookDetail,
|
||||
)
|
||||
DetailScreen(bookId = bottomDetailState.bookId)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
|
|
@ -58,48 +51,34 @@ fun DetailBottomSheet(
|
|||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun rememberDetailBottomSheetState(
|
||||
viewModel: BookDetailViewModel = hiltViewModel(),
|
||||
bottomSheetState: ModalBottomSheetState = rememberModalBottomSheetState(
|
||||
initialValue = Hidden,
|
||||
skipHalfExpanded = true,
|
||||
),
|
||||
): DetailBottomSheetState {
|
||||
val context: Context = LocalContext.current
|
||||
val detail = rememberSaveable(viewModel, bottomSheetState) {
|
||||
mutableStateOf<BookDetailUio?>(null)
|
||||
val detail = rememberSaveable(bottomSheetState) {
|
||||
mutableStateOf<Int?>(null)
|
||||
}
|
||||
return remember(bottomSheetState) {
|
||||
DetailBottomSheetState(
|
||||
bottomSheetState = bottomSheetState,
|
||||
bookDetail = detail,
|
||||
)
|
||||
}
|
||||
val controller = DetailBottomSheetState(
|
||||
context = context,
|
||||
viewModel = viewModel,
|
||||
bottomSheetState = bottomSheetState,
|
||||
bookDetail = detail,
|
||||
)
|
||||
return remember(viewModel, bottomSheetState) { controller }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Stable
|
||||
class DetailBottomSheetState constructor(
|
||||
private val context: Context,
|
||||
val viewModel: BookDetailViewModel,
|
||||
val bottomSheetState: ModalBottomSheetState,
|
||||
bookDetail: MutableState<BookDetailUio?>,
|
||||
bookDetail: MutableState<Int?>,
|
||||
) {
|
||||
var bookDetail: BookDetailUio? by bookDetail
|
||||
var bookId: Int? by bookDetail
|
||||
private set
|
||||
|
||||
suspend fun expandBookDetail(id: Int) {
|
||||
when (val book = viewModel.getDetail(id)) {
|
||||
is StateUio.Failure -> {
|
||||
val mes = book.exception.message ?: context.getString(R.string.error_generic)
|
||||
context.showToast(message = mes)
|
||||
}
|
||||
is StateUio.Success -> {
|
||||
bookDetail = book.value
|
||||
bottomSheetState.show()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
bookId = id
|
||||
bottomSheetState.show()
|
||||
}
|
||||
|
||||
suspend fun collapse() {
|
||||
|
|
|
|||
|
|
@ -1,36 +1,62 @@
|
|||
package com.pixelized.biblib.ui.screen.home.detail
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.biblib.network.client.IBibLibClient
|
||||
import com.pixelized.biblib.network.factory.BookFactory
|
||||
import com.pixelized.biblib.ui.composable.StateUio
|
||||
import com.pixelized.biblib.repository.book.BookRepository
|
||||
import com.pixelized.biblib.ui.screen.home.detail.BookDetailUioErrorUio.Type
|
||||
import com.pixelized.biblib.utils.extention.toDetailUio
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class BookDetailViewModel @Inject constructor(
|
||||
private val bookRepository: BookRepository,
|
||||
private val client: IBibLibClient,
|
||||
) : ViewModel() {
|
||||
|
||||
suspend fun getDetail(id: Int): StateUio<BookDetailUio> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val book = getBookDetail(id = id)
|
||||
StateUio.Success(book)
|
||||
} catch (exception: Exception) {
|
||||
Log.e("BookDetailViewModel", exception.message, exception)
|
||||
StateUio.Failure(exception)
|
||||
private val _error = MutableSharedFlow<BookDetailUioErrorUio>()
|
||||
val error: Flow<BookDetailUioErrorUio> get() = _error
|
||||
|
||||
fun getDetail(id: Int?): State<BookDetailUio?> {
|
||||
return mutableStateOf<BookDetailUio?>(null).apply {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
requireNotNull(id)
|
||||
value = getCacheBookDetail(id = id)
|
||||
value = getBookDetail(id = id)
|
||||
} catch (exception: Exception) {
|
||||
_error.emit(exception.toUio(Type.GET_DETAIL))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun send(bookId: Int, email: String) {
|
||||
val data = client.service.send(bookId = bookId, mail = email)
|
||||
Log.d("send", data.toString())
|
||||
suspend fun send(bookId: Int, email: String): State<Boolean?> {
|
||||
return mutableStateOf<Boolean?>(null).apply {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val data = client.service.send(bookId = bookId, mail = email)
|
||||
Log.d("send", data.toString())
|
||||
} catch (exception: Exception) {
|
||||
Log.d("send", exception.message, exception)
|
||||
_error.emit(exception.toUio(Type.SEND_BOOK))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCacheBookDetail(id: Int): BookDetailUio {
|
||||
return bookRepository.getBook(id = id).toDetailUio()
|
||||
}
|
||||
|
||||
private suspend fun getBookDetail(id: Int): BookDetailUio {
|
||||
|
|
@ -39,4 +65,9 @@ class BookDetailViewModel @Inject constructor(
|
|||
val book = factory.fromDetailResponseToBook(response)
|
||||
return book.toDetailUio()
|
||||
}
|
||||
|
||||
private fun Exception.toUio(type: Type) = BookDetailUioErrorUio(
|
||||
type = type,
|
||||
message = this.message ?: "An error occurred."
|
||||
)
|
||||
}
|
||||
|
|
@ -1,39 +1,33 @@
|
|||
package com.pixelized.biblib.ui.screen.home.detail
|
||||
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import android.app.Application
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.utils.extention.context
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ConfirmDialogViewModel @Inject constructor() : ViewModel() {
|
||||
|
||||
val shouldDisplayConfirmDialog by derivedStateOf { dialog != null }
|
||||
class ConfirmDialogViewModel @Inject constructor(
|
||||
application: Application,
|
||||
) : AndroidViewModel(application) {
|
||||
|
||||
var dialog by mutableStateOf<ConfirmDialogUio?>(null)
|
||||
private set
|
||||
|
||||
fun show(
|
||||
email: String,
|
||||
title: String? = null,
|
||||
description: String? = null,
|
||||
help: String? = null,
|
||||
options: List<ConfirmDialogUio.Option>,
|
||||
) = show(
|
||||
ConfirmDialogUio(
|
||||
fun show(email: String) {
|
||||
this.dialog = ConfirmDialogUio(
|
||||
email = email,
|
||||
title = title,
|
||||
description = description,
|
||||
help = help,
|
||||
options = options,
|
||||
title = context.getString(R.string.detail_send_confirm_title),
|
||||
description = context.getString(R.string.detail_send_confirm_description),
|
||||
help = context.getString(R.string.detail_send_confirm_help),
|
||||
helpUrl = context.getString(R.string.detail_send_confirm_help_url),
|
||||
confirm = context.getString(R.string.detail_send_confirm_confirm_action),
|
||||
cancel = context.getString(R.string.detail_send_confirm_cancel_action),
|
||||
)
|
||||
)
|
||||
|
||||
fun show(dialog: ConfirmDialogUio?) {
|
||||
this.dialog = dialog
|
||||
}
|
||||
|
||||
fun hide() {
|
||||
|
|
|
|||
|
|
@ -1,49 +1,23 @@
|
|||
package com.pixelized.biblib.ui.screen.home.detail
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.net.Uri
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.composable.SpannedText
|
||||
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
|
||||
import com.pixelized.biblib.ui.composable.animation.AnimatedOffset
|
||||
import com.pixelized.biblib.ui.composable.isSuccessful
|
||||
import com.pixelized.biblib.ui.scaffold.DetailBottomSheetState
|
||||
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
|
||||
import com.pixelized.biblib.ui.screen.home.detail.ConfirmDialogUio.Companion.option
|
||||
import com.pixelized.biblib.ui.screen.home.page.profile.ProfileViewModel
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
import com.pixelized.biblib.utils.extention.showToast
|
||||
import com.skydoves.landscapist.CircularReveal
|
||||
import com.skydoves.landscapist.glide.GlideImage
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.Serializable
|
||||
|
||||
|
|
@ -59,332 +33,138 @@ data class BookDetailUio(
|
|||
val series: String?,
|
||||
val description: String,
|
||||
val cover: String,
|
||||
) : Serializable
|
||||
) : Serializable {
|
||||
companion object {
|
||||
fun preview() = BookDetailUio(
|
||||
id = 90,
|
||||
title = "Foundation",
|
||||
author = "Asimov",
|
||||
date = "1951",
|
||||
series = "Foundation - 1",
|
||||
description = "", // "En ce début de treizième millénaire, l'Empire n'a jamais été aussi puissant, aussi étendu à travers toute la galaxie. C'est dans sa capitale, Trantor, que l'éminent savant Hari Seldon invente la psychohistoire, une science nouvelle permettant de prédire l'avenir. Grâce à elle, Seldon prévoit l'effondrement de l'Empire d'ici cinq siècles, suivi d'une ère de ténèbres de trente mille ans. Réduire cette période à mille ans est peut-être possible, à condition de mener à terme son projet : la Fondation, chargée de rassembler toutes les connaissances humaines. Une entreprise visionnaire qui rencontre de nombreux et puissants détracteurs...",
|
||||
rating = 4.5f,
|
||||
language = "Français",
|
||||
cover = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val CONFIRM_OPTION_YES = 0
|
||||
private const val CONFIRM_OPTION_NO = 1
|
||||
@Stable
|
||||
@Immutable
|
||||
data class BookDetailUioErrorUio(
|
||||
val type: Type,
|
||||
val message: String,
|
||||
) {
|
||||
enum class Type {
|
||||
GET_DETAIL,
|
||||
SEND_BOOK,
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun DetailScreen(
|
||||
detailState: DetailBottomSheetState = LocalDetailBottomSheetState.current,
|
||||
detailViewModel: BookDetailViewModel = hiltViewModel(),
|
||||
confirmViewModel: ConfirmDialogViewModel = hiltViewModel(),
|
||||
profileViewModel: ProfileViewModel = hiltViewModel(),
|
||||
detailViewModel: BookDetailViewModel = hiltViewModel(),
|
||||
detail: BookDetailUio? = null,
|
||||
bookId: Int? = null,
|
||||
) {
|
||||
val detailState = LocalDetailBottomSheetState.current
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val context = LocalContext.current
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val emailSheetState = rememberModalBottomSheetState(
|
||||
initialValue = ModalBottomSheetValue.Hidden,
|
||||
skipHalfExpanded = true,
|
||||
)
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
sheetState = emailSheetState,
|
||||
scrimColor = ShadowPalette.scrim,
|
||||
sheetContent = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 1.dp)
|
||||
.animateContentSize()
|
||||
) {
|
||||
val user = profileViewModel.user
|
||||
if (user.isSuccessful()) {
|
||||
val bookDetailState: State<BookDetailUio?> = rememberSaveable(bookId) {
|
||||
detailViewModel.getDetail(bookId)
|
||||
}
|
||||
|
||||
when (val detail = bookDetailState.value) {
|
||||
null -> EmptyDetail()
|
||||
else -> {
|
||||
ModalBottomSheetLayout(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
sheetState = emailSheetState,
|
||||
scrimColor = ShadowPalette.scrim,
|
||||
sheetContent = {
|
||||
DetailScreenEmailList(
|
||||
modifier = Modifier
|
||||
.navigationBarsPadding()
|
||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
emails = user.value.amazonEmails,
|
||||
profileViewModel = profileViewModel,
|
||||
onEmail = { mail ->
|
||||
confirmViewModel.show(email = mail)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = {
|
||||
if (detailState.bottomSheetState.isVisible && detail != null) {
|
||||
DetailScreenContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
book = detail,
|
||||
onSend = {
|
||||
// check
|
||||
val user = profileViewModel.user
|
||||
if (user.isSuccessful()) {
|
||||
val mails = user.value.amazonEmails
|
||||
when {
|
||||
mails.isEmpty() -> {
|
||||
context.showToast(context.getString(R.string.error_no_amazon_email))
|
||||
}
|
||||
mails.size == 1 -> {
|
||||
confirmViewModel.show(email = mails.first())
|
||||
}
|
||||
else -> {
|
||||
scope.launch { emailSheetState.show() }
|
||||
},
|
||||
content = {
|
||||
if (detailState.bottomSheetState.isVisible) {
|
||||
DetailScreenContent(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.systemBarsPadding()
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16)
|
||||
.animateContentSize(),
|
||||
book = detail,
|
||||
onSend = {
|
||||
// check
|
||||
val user = profileViewModel.user
|
||||
if (user.isSuccessful()) {
|
||||
val mails = user.value.amazonEmails
|
||||
when {
|
||||
mails.isEmpty() -> {
|
||||
context.showToast(context.getString(R.string.error_no_amazon_email))
|
||||
}
|
||||
mails.size == 1 -> {
|
||||
confirmViewModel.show(email = mails.first())
|
||||
}
|
||||
else -> {
|
||||
scope.launch { emailSheetState.show() }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO()
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Box(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
ConfirmDialogHandler(
|
||||
detail = detail,
|
||||
confirmViewModel = confirmViewModel,
|
||||
onConfirm = { bookId, email ->
|
||||
scope.launch {
|
||||
confirmViewModel.hide()
|
||||
emailSheetState.hide()
|
||||
detailViewModel.send(bookId = bookId, email = email)
|
||||
}
|
||||
},
|
||||
onDismiss = {
|
||||
confirmViewModel.hide()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialogHandler(
|
||||
detail: BookDetailUio? = null,
|
||||
confirmViewModel: ConfirmDialogViewModel,
|
||||
onConfirm: (bookId: Int, mail: String) -> Unit = { _, _ -> },
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
confirmViewModel.dialog?.let { dialog ->
|
||||
ConfirmDialog(
|
||||
uio = dialog,
|
||||
onDismissRequest = onDismiss,
|
||||
onOption = { option ->
|
||||
when (option.id) {
|
||||
CONFIRM_OPTION_YES -> detail?.id?.let { onConfirm(it, dialog.email) }
|
||||
else -> onDismiss()
|
||||
}
|
||||
},
|
||||
onHelp = {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(dialog.help))
|
||||
context.startActivity(intent)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DetailScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
book: BookDetailUio,
|
||||
onMobi: () -> Unit = default(),
|
||||
onEpub: () -> Unit = default(),
|
||||
onSend: () -> Unit = default(),
|
||||
) {
|
||||
AnimatedDelayer(
|
||||
targetState = book,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.systemBarsPadding()
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16)
|
||||
.then(modifier)
|
||||
) {
|
||||
GlideImage(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = MaterialTheme.bibLib.dimen.dp16)
|
||||
.height(MaterialTheme.bibLib.dimen.detail.cover),
|
||||
loading = {
|
||||
Box(modifier = Modifier.matchParentSize()) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
} else {
|
||||
EmptyDetail()
|
||||
}
|
||||
},
|
||||
previewPlaceholder = R.drawable.ic_fondatoin_cover,
|
||||
circularReveal = CircularReveal(duration = 1000),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
imageModel = book.cover,
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.padding(vertical = MaterialTheme.bibLib.dimen.dp16)) {
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onClick = onMobi,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_mobi))
|
||||
ConfirmDialog(
|
||||
modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp32),
|
||||
confirmViewModel = confirmViewModel,
|
||||
detail = detail,
|
||||
onConfirm = { id, email ->
|
||||
scope.launch {
|
||||
confirmViewModel.hide()
|
||||
emailSheetState.hide()
|
||||
detailViewModel.send(bookId = id, email = email)
|
||||
}
|
||||
}
|
||||
},
|
||||
onDismiss = { confirmViewModel.hide() },
|
||||
onHelp = { url -> uriHandler.openUri(url) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onClick = onEpub,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_epub))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onClick = onSend,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Send, contentDescription = "")
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_send))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(bottom = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h5,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = book.title,
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
Text(
|
||||
fontWeight = FontWeight.Bold,
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = book.author,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
|
||||
) {
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
title = stringResource(id = R.string.detail_rating),
|
||||
label = book.rating.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
title = stringResource(id = R.string.detail_language),
|
||||
label = book.language,
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
title = stringResource(id = R.string.detail_release),
|
||||
label = book.date ?: "-",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
TitleLabel(
|
||||
title = stringResource(id = R.string.detail_series),
|
||||
label = book.series ?: "-",
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset {
|
||||
SpannedText(
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = HtmlCompat.fromHtml(
|
||||
book.description,
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
).toSpannable(),
|
||||
)
|
||||
}
|
||||
LaunchedEffect(key1 = "DetailScreenError") {
|
||||
detailViewModel.error.collect {
|
||||
context.showToast(message = it.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TitleLabel(
|
||||
title: String,
|
||||
label: String,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body2,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = title,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = label,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConfirmDialogViewModel.show(email: String) = show(
|
||||
email = email,
|
||||
title = "Envoyer sur votre Kindle",
|
||||
description = "Assurez-vous que votre Kindle dispose d'une connexion Internet et que l'adresse e-mail suivante est correctement configurée.",
|
||||
help = "https://www.amazon.fr/gp/help/customer/display.html?nodeId=G7NECT4B4ZWHQ8WV",
|
||||
options = listOf(
|
||||
option(id = CONFIRM_OPTION_YES, label = "Oui"),
|
||||
option(id = CONFIRM_OPTION_NO, label = "Non")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
|
||||
private fun DetailScreenContentPreview() {
|
||||
val book = BookDetailUio(
|
||||
id = 90,
|
||||
title = "Foundation",
|
||||
author = "Asimov",
|
||||
date = "1951",
|
||||
series = "Foundation - 1",
|
||||
description = "En ce début de treizième millénaire, l'Empire n'a jamais été aussi puissant, aussi étendu à travers toute la galaxie. C'est dans sa capitale, Trantor, que l'éminent savant Hari Seldon invente la psychohistoire, une science nouvelle permettant de prédire l'avenir. Grâce à elle, Seldon prévoit l'effondrement de l'Empire d'ici cinq siècles, suivi d'une ère de ténèbres de trente mille ans. Réduire cette période à mille ans est peut-être possible, à condition de mener à terme son projet : la Fondation, chargée de rassembler toutes les connaissances humaines. Une entreprise visionnaire qui rencontre de nombreux et puissants détracteurs...",
|
||||
rating = 4.5f,
|
||||
language = "Français",
|
||||
cover = "",
|
||||
)
|
||||
BibLibTheme {
|
||||
DetailScreenContent(book = book)
|
||||
}
|
||||
}
|
||||
private fun EmptyDetail(
|
||||
modifier : Modifier = Modifier,
|
||||
) = Box(
|
||||
modifier = modifier.fillMaxSize()
|
||||
)
|
||||
|
|
@ -7,11 +7,18 @@ import androidx.compose.material.*
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import com.pixelized.biblib.ui.screen.home.detail.ConfirmDialogUio.Companion.option
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
|
|
@ -20,99 +27,133 @@ import com.pixelized.biblib.utils.extention.default
|
|||
@Immutable
|
||||
data class ConfirmDialogUio(
|
||||
val email: String,
|
||||
val title: String? = null,
|
||||
val description: String? = null,
|
||||
val help: String? = null,
|
||||
val options: List<Option>,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val help: String,
|
||||
val helpUrl: String,
|
||||
val confirm: String,
|
||||
val cancel: String,
|
||||
) {
|
||||
@Stable
|
||||
@Immutable
|
||||
data class Option(
|
||||
val id: Int,
|
||||
val label: String,
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun option(
|
||||
id: Int,
|
||||
label: String,
|
||||
) = Option(
|
||||
id = id,
|
||||
label = label
|
||||
@Composable
|
||||
fun preview() = ConfirmDialogUio(
|
||||
email = "R.Daneel.Olivaw.Kindle@gmail.com",
|
||||
title = stringResource(R.string.detail_send_confirm_title),
|
||||
description = stringResource(R.string.detail_send_confirm_description),
|
||||
help = stringResource(R.string.detail_send_confirm_help),
|
||||
helpUrl = stringResource(R.string.detail_send_confirm_help_url),
|
||||
confirm = stringResource(R.string.detail_send_confirm_confirm_action),
|
||||
cancel = stringResource(R.string.detail_send_confirm_cancel_action),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
confirmViewModel: ConfirmDialogViewModel,
|
||||
detail: BookDetailUio,
|
||||
onConfirm: (bookId: Int, mail: String) -> Unit = { _, _ -> },
|
||||
onHelp: (url: String) -> Unit = default<String>(),
|
||||
onDismiss: () -> Unit
|
||||
) {
|
||||
confirmViewModel.dialog?.let { dialog ->
|
||||
ConfirmDialog(
|
||||
modifier = modifier,
|
||||
uio = dialog,
|
||||
onDismissRequest = onDismiss,
|
||||
onConfirm = { onConfirm(detail.id, dialog.email) },
|
||||
onHelp = onHelp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun ConfirmDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
uio: ConfirmDialogUio,
|
||||
onDismissRequest: () -> Unit = default(),
|
||||
onOption: (ConfirmDialogUio.Option) -> Unit = default<ConfirmDialogUio.Option>(),
|
||||
onHelp: () -> Unit = default(),
|
||||
onHelp: (url: String) -> Unit = default<String>(),
|
||||
onConfirm: (mail: String) -> Unit = default<String>(),
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
|
||||
) {
|
||||
Card(modifier = modifier) {
|
||||
Column(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp16)) {
|
||||
if (uio.title != null) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.bibLib.colors.typography.medium,
|
||||
text = uio.title
|
||||
)
|
||||
}
|
||||
ConfirmDialogContent(
|
||||
modifier = modifier,
|
||||
uio = uio,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onHelp = onHelp,
|
||||
onConfirm = onConfirm,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (uio.description != null) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.bibLib.colors.typography.medium,
|
||||
text = uio.description
|
||||
)
|
||||
}
|
||||
@Composable
|
||||
fun ConfirmDialogContent(
|
||||
modifier: Modifier = Modifier,
|
||||
uio: ConfirmDialogUio,
|
||||
onDismissRequest: () -> Unit = default(),
|
||||
onHelp: (url: String) -> Unit = default<String>(),
|
||||
onConfirm: (mail: String) -> Unit = default<String>(),
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
backgroundColor = MaterialTheme.bibLib.colors.dialogBackground,
|
||||
) {
|
||||
Column(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp16)) {
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.bibLib.colors.typography.medium,
|
||||
text = uio.title
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.bibLib.colors.typography.easy,
|
||||
text = uio.email
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.bibLib.colors.typography.medium,
|
||||
text = uio.description
|
||||
)
|
||||
|
||||
if (uio.help != null) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onHelp)
|
||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.bibLib.colors.typography.strong,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = uio.help
|
||||
)
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.bibLib.colors.typography.easy,
|
||||
text = uio.email
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = { onHelp(uio.helpUrl) })
|
||||
.padding(vertical = MaterialTheme.bibLib.dimen.dp8),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.caption,
|
||||
color = MaterialTheme.bibLib.colors.typography.strong,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
text = uio.help
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = MaterialTheme.bibLib.dimen.dp8),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
|
||||
) {
|
||||
Button(
|
||||
colors = ButtonDefaults.outlinedButtonColors(),
|
||||
onClick = onDismissRequest,
|
||||
) {
|
||||
uio.options.reversed().forEachIndexed { index, option ->
|
||||
Button(
|
||||
modifier = when (index) {
|
||||
uio.options.lastIndex -> Modifier
|
||||
else -> Modifier.padding(end = MaterialTheme.bibLib.dimen.dp8)
|
||||
},
|
||||
colors = when (index) {
|
||||
uio.options.lastIndex -> ButtonDefaults.buttonColors()
|
||||
else -> ButtonDefaults.outlinedButtonColors()
|
||||
},
|
||||
onClick = { onOption(option) }
|
||||
) {
|
||||
Text(text = option.label)
|
||||
}
|
||||
}
|
||||
Text(text = uio.cancel)
|
||||
}
|
||||
Button(
|
||||
colors = ButtonDefaults.buttonColors(),
|
||||
onClick = { onConfirm(uio.email) }
|
||||
) {
|
||||
Text(text = uio.confirm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -124,17 +165,8 @@ fun ConfirmDialog(
|
|||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun DetailConfirmPreview() {
|
||||
BibLibTheme {
|
||||
ConfirmDialog(
|
||||
uio = ConfirmDialogUio(
|
||||
email = "R.Daneel.Olivaw.Kindle@gmail.com",
|
||||
title = "Envoyer sur votre Kindle",
|
||||
description = "Assurez-vous que votre Kindle dispose d'une connexion Internet et que l'adresse e-mail suivante est correctement configurée.",
|
||||
help = "https://www.amazon.fr/gp/help/customer/display.html?nodeId=G7NECT4B4ZWHQ8WV",
|
||||
options = listOf(
|
||||
option(id = 0, "Oui"),
|
||||
option(id = 1, "Non")
|
||||
)
|
||||
)
|
||||
ConfirmDialogContent(
|
||||
uio = ConfirmDialogUio.preview(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
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.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Button
|
||||
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.filled.Download
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.toSpannable
|
||||
import com.google.accompanist.placeholder.material.placeholder
|
||||
import com.pixelized.biblib.R
|
||||
import com.pixelized.biblib.ui.composable.SpannedText
|
||||
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
|
||||
import com.pixelized.biblib.ui.composable.animation.AnimatedOffset
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
import com.skydoves.landscapist.CircularReveal
|
||||
import com.skydoves.landscapist.glide.GlideImage
|
||||
|
||||
@Composable
|
||||
fun DetailScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
book: BookDetailUio,
|
||||
onMobi: () -> Unit = default(),
|
||||
onEpub: () -> Unit = default(),
|
||||
onSend: () -> Unit = default(),
|
||||
) {
|
||||
AnimatedDelayer(
|
||||
targetState = book.id,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
GlideImage(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = MaterialTheme.bibLib.dimen.dp16)
|
||||
.height(MaterialTheme.bibLib.dimen.detail.cover),
|
||||
previewPlaceholder = R.drawable.ic_fondatoin_cover,
|
||||
circularReveal = CircularReveal(duration = 1000),
|
||||
contentScale = ContentScale.FillHeight,
|
||||
imageModel = book.cover,
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.padding(vertical = MaterialTheme.bibLib.dimen.dp16)) {
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onMobi,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_mobi))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onEpub,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_epub))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onSend,
|
||||
) {
|
||||
Icon(imageVector = Icons.Default.Send, contentDescription = "")
|
||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
||||
Text(text = stringResource(id = R.string.action_send))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(bottom = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.h5,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = book.title,
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier
|
||||
.align(alignment = Alignment.CenterHorizontally)
|
||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
Text(
|
||||
fontWeight = FontWeight.Bold,
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = book.author,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
|
||||
) {
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
title = stringResource(id = R.string.detail_rating),
|
||||
label = book.rating.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
title = stringResource(id = R.string.detail_language),
|
||||
label = book.language,
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset(modifier = Modifier.weight(1f)) {
|
||||
TitleLabel(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
title = stringResource(id = R.string.detail_release),
|
||||
label = book.date ?: "-",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedOffset(
|
||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||
) {
|
||||
TitleLabel(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
title = stringResource(id = R.string.detail_series),
|
||||
label = book.series ?: "-",
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedOffset {
|
||||
SpannedText(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.placeholder(visible = book.description.isEmpty()),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = HtmlCompat.fromHtml(
|
||||
book.description.ifEmpty { "placeholder" },
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT,
|
||||
).toSpannable(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TitleLabel(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
label: String,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
style = MaterialTheme.typography.body2,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = title,
|
||||
)
|
||||
Text(
|
||||
style = MaterialTheme.typography.body1,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colors.onSurface,
|
||||
text = label,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO, heightDp = 1000)
|
||||
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES, heightDp = 1000)
|
||||
private fun DetailScreenContentPreview() {
|
||||
BibLibTheme {
|
||||
DetailScreenContent(
|
||||
modifier = Modifier
|
||||
.verticalScroll(state = rememberScrollState())
|
||||
.padding(all = MaterialTheme.bibLib.dimen.dp16),
|
||||
book = BookDetailUio.preview(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.pixelized.biblib.ui.screen.home.detail
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
|
|
@ -15,10 +16,37 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.biblib.ui.composable.isSuccessful
|
||||
import com.pixelized.biblib.ui.screen.home.page.profile.ProfileViewModel
|
||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||
import com.pixelized.biblib.utils.extention.bibLib
|
||||
import com.pixelized.biblib.utils.extention.default
|
||||
|
||||
@Composable
|
||||
fun DetailScreenEmailList(
|
||||
modifier: Modifier = Modifier,
|
||||
profileViewModel: ProfileViewModel,
|
||||
onEmail: (email: String) -> Unit = default<String>(),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 1.dp)
|
||||
.animateContentSize()
|
||||
) {
|
||||
val user = profileViewModel.user
|
||||
if (user.isSuccessful()) {
|
||||
DetailScreenEmailList(
|
||||
modifier = modifier,
|
||||
emails = user.value.amazonEmails,
|
||||
onEmail = onEmail,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DetailScreenEmailList(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ data class BibLibColor(
|
|||
val base: Colors,
|
||||
val typography: Typography,
|
||||
val placeHolder: Color,
|
||||
val dialogBackground: Color,
|
||||
) {
|
||||
|
||||
@Stable
|
||||
|
|
@ -40,10 +41,12 @@ fun bibLibDarkColors(
|
|||
strong = base.primary,
|
||||
),
|
||||
placeHolder: Color = BibLibColorPalette.DarkGrey,
|
||||
dialogBackground: Color = BibLibColorPalette.VeryDarkGrey,
|
||||
) = BibLibColor(
|
||||
base = base,
|
||||
typography = typography,
|
||||
placeHolder = placeHolder,
|
||||
dialogBackground = dialogBackground,
|
||||
)
|
||||
|
||||
fun bibLibLightColors(
|
||||
|
|
@ -61,8 +64,10 @@ fun bibLibLightColors(
|
|||
strong = base.primary,
|
||||
),
|
||||
placeHolder: Color = BibLibColorPalette.LightGrey,
|
||||
dialogBackground: Color = Color.White,
|
||||
) = BibLibColor(
|
||||
base = base,
|
||||
typography = typography,
|
||||
placeHolder = placeHolder,
|
||||
dialogBackground = dialogBackground,
|
||||
)
|
||||
|
|
@ -15,6 +15,7 @@ data class BibLibDimen(
|
|||
val dp8: Dp = 8.dp,
|
||||
val dp12: Dp = 12.dp,
|
||||
val dp16: Dp = 16.dp,
|
||||
val dp24: Dp = 24.dp,
|
||||
val dp32: Dp = 32.dp,
|
||||
val dp48: Dp = 48.dp,
|
||||
val dp52: Dp = 52.dp,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
package com.pixelized.biblib.utils.extention
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
|
||||
val AndroidViewModel.context: Context get() = this.getApplication()
|
||||
val AndroidViewModel.context: Context get() = this.getApplication()
|
||||
|
||||
fun AndroidViewModel.stringResource(@StringRes id: Int): String {
|
||||
return context.getString(id)
|
||||
}
|
||||
|
||||
fun AndroidViewModel.stringResource(@StringRes id: Int, vararg formatArgs: Any): String {
|
||||
return context.getString(id, formatArgs)
|
||||
}
|
||||
|
|
@ -42,6 +42,13 @@
|
|||
<string name="detail_series">Séries</string>
|
||||
<string name="detail_emails_title">Envoyer cet eBook à :</string>
|
||||
|
||||
<string name="detail_send_confirm_title">Envoyer sur votre Kindle</string>
|
||||
<string name="detail_send_confirm_description">Assurez-vous que votre Kindle dispose d\'une connexion Internet et que l\'adresse e-mail suivante est correctement configurée.</string>
|
||||
<string name="detail_send_confirm_help">Aide à la configuration du kindle</string>
|
||||
<string name="detail_send_confirm_help_url">https://www.amazon.fr/gp/help/customer/display.html?nodeId=G7NECT4B4ZWHQ8WV</string>
|
||||
<string name="detail_send_confirm_confirm_action">Oui</string>
|
||||
<string name="detail_send_confirm_cancel_action">Annuler</string>
|
||||
|
||||
<string name="search_title">Rechercher sur Biblib</string>
|
||||
<string name="search_filter_title">Rechercher</string>
|
||||
<string name="search_filter_param">%1$s : %2$s</string>
|
||||
|
|
|
|||
|
|
@ -48,6 +48,13 @@
|
|||
<string name="detail_series">Series</string>
|
||||
<string name="detail_emails_title">Send this eBook to:</string>
|
||||
|
||||
<string name="detail_send_confirm_title">Send to your Kindle</string>
|
||||
<string name="detail_send_confirm_description">Make sure your Kindle has an internet connection and the following email address is correctly set up.</string>
|
||||
<string name="detail_send_confirm_help">Help me configure my kindle</string>
|
||||
<string name="detail_send_confirm_help_url">https://www.amazon.co.uk/gp/help/customer/display.html?nodeId=G7NECT4B4ZWHQ8WV</string>
|
||||
<string name="detail_send_confirm_confirm_action">Oui</string>
|
||||
<string name="detail_send_confirm_cancel_action">Annuler</string>
|
||||
|
||||
<string name="search_title">Search on Biblib</string>
|
||||
<string name="search_filter_title">Search</string>
|
||||
<string name="search_filter_param">%1$s: %2$s</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue