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-drawablepainter:0.26.5-rc"
|
||||||
implementation "com.google.accompanist:accompanist-insets: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-pager:0.26.5-rc"
|
||||||
|
implementation "com.google.accompanist:accompanist-placeholder-material:0.26.5-rc"
|
||||||
|
|
||||||
// Landscapist
|
// Landscapist
|
||||||
implementation "com.github.skydoves:landscapist-glide:1.5.2"
|
implementation "com.github.skydoves:landscapist-glide:1.5.2"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ interface BookDao {
|
||||||
@Query("SELECT * FROM ${BookDbo.TABLE} ORDER BY ${BookDbo.SORT}")
|
@Query("SELECT * FROM ${BookDbo.TABLE} ORDER BY ${BookDbo.SORT}")
|
||||||
fun getBook(): DataSource.Factory<Int, BookRelation>
|
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)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insert(vararg books: BookDbo)
|
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.BookAuthorCrossRef
|
||||||
import com.pixelized.biblib.database.crossref.BookGenreCrossRef
|
import com.pixelized.biblib.database.crossref.BookGenreCrossRef
|
||||||
import com.pixelized.biblib.database.data.*
|
import com.pixelized.biblib.database.data.*
|
||||||
import com.pixelized.biblib.model.book.*
|
|
||||||
import com.pixelized.biblib.database.factory.*
|
import com.pixelized.biblib.database.factory.*
|
||||||
|
import com.pixelized.biblib.model.book.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class BookRepository @Inject constructor(
|
class BookRepository @Inject constructor(
|
||||||
|
|
@ -17,6 +17,9 @@ class BookRepository @Inject constructor(
|
||||||
override fun getAll(): List<Book> =
|
override fun getAll(): List<Book> =
|
||||||
database.bookDao().getAll().map { it.toBook() }
|
database.bookDao().getAll().map { it.toBook() }
|
||||||
|
|
||||||
|
override fun getBook(id: Int): Book =
|
||||||
|
database.bookDao().getBook(id = id).toBook()
|
||||||
|
|
||||||
override fun getBookCount(): Int =
|
override fun getBookCount(): Int =
|
||||||
database.bookDao().count()
|
database.bookDao().count()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ interface IBookRepository {
|
||||||
|
|
||||||
fun getAll(): List<Book>
|
fun getAll(): List<Book>
|
||||||
|
|
||||||
|
fun getBook(id: Int): Book
|
||||||
|
|
||||||
fun getBookCount(): Int
|
fun getBookCount(): Int
|
||||||
|
|
||||||
fun getNewsSource(): DataSource.Factory<Int, Book>
|
fun getNewsSource(): DataSource.Factory<Int, Book>
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ import kotlin.contracts.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
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, val cache : T? = null) : StateUio<T>()
|
||||||
class Failure<T>(val exception: Exception) : StateUio<T>()
|
class Failure<T>(val exception: Exception, val cache: T? = null) : StateUio<T>()
|
||||||
class Success<T>(val value: T) : 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.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
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.BookDetailViewModel
|
||||||
import com.pixelized.biblib.ui.screen.home.detail.DetailScreen
|
import com.pixelized.biblib.ui.screen.home.detail.DetailScreen
|
||||||
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
||||||
import com.pixelized.biblib.utils.extention.showToast
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
val LocalDetailBottomSheetState = staticCompositionLocalOf<DetailBottomSheetState> {
|
val LocalDetailBottomSheetState = staticCompositionLocalOf<DetailBottomSheetState> {
|
||||||
|
|
@ -39,10 +35,7 @@ fun DetailBottomSheet(
|
||||||
scrimColor = ShadowPalette.scrim,
|
scrimColor = ShadowPalette.scrim,
|
||||||
sheetState = bottomDetailState.bottomSheetState,
|
sheetState = bottomDetailState.bottomSheetState,
|
||||||
sheetContent = {
|
sheetContent = {
|
||||||
DetailScreen(
|
DetailScreen(bookId = bottomDetailState.bookId)
|
||||||
detailViewModel = bottomDetailState.viewModel,
|
|
||||||
detail = bottomDetailState.bookDetail,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
content = content,
|
content = content,
|
||||||
)
|
)
|
||||||
|
|
@ -58,48 +51,34 @@ fun DetailBottomSheet(
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberDetailBottomSheetState(
|
fun rememberDetailBottomSheetState(
|
||||||
viewModel: BookDetailViewModel = hiltViewModel(),
|
|
||||||
bottomSheetState: ModalBottomSheetState = rememberModalBottomSheetState(
|
bottomSheetState: ModalBottomSheetState = rememberModalBottomSheetState(
|
||||||
initialValue = Hidden,
|
initialValue = Hidden,
|
||||||
skipHalfExpanded = true,
|
skipHalfExpanded = true,
|
||||||
),
|
),
|
||||||
): DetailBottomSheetState {
|
): DetailBottomSheetState {
|
||||||
val context: Context = LocalContext.current
|
val detail = rememberSaveable(bottomSheetState) {
|
||||||
val detail = rememberSaveable(viewModel, bottomSheetState) {
|
mutableStateOf<Int?>(null)
|
||||||
mutableStateOf<BookDetailUio?>(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)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Stable
|
@Stable
|
||||||
class DetailBottomSheetState constructor(
|
class DetailBottomSheetState constructor(
|
||||||
private val context: Context,
|
|
||||||
val viewModel: BookDetailViewModel,
|
|
||||||
val bottomSheetState: ModalBottomSheetState,
|
val bottomSheetState: ModalBottomSheetState,
|
||||||
bookDetail: MutableState<BookDetailUio?>,
|
bookDetail: MutableState<Int?>,
|
||||||
) {
|
) {
|
||||||
var bookDetail: BookDetailUio? by bookDetail
|
var bookId: Int? by bookDetail
|
||||||
private set
|
private set
|
||||||
|
|
||||||
suspend fun expandBookDetail(id: Int) {
|
suspend fun expandBookDetail(id: Int) {
|
||||||
when (val book = viewModel.getDetail(id)) {
|
bookId = id
|
||||||
is StateUio.Failure -> {
|
bottomSheetState.show()
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun collapse() {
|
suspend fun collapse() {
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,62 @@
|
||||||
package com.pixelized.biblib.ui.screen.home.detail
|
package com.pixelized.biblib.ui.screen.home.detail
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.biblib.network.client.IBibLibClient
|
import com.pixelized.biblib.network.client.IBibLibClient
|
||||||
import com.pixelized.biblib.network.factory.BookFactory
|
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 com.pixelized.biblib.utils.extention.toDetailUio
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
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
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class BookDetailViewModel @Inject constructor(
|
class BookDetailViewModel @Inject constructor(
|
||||||
|
private val bookRepository: BookRepository,
|
||||||
private val client: IBibLibClient,
|
private val client: IBibLibClient,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
suspend fun getDetail(id: Int): StateUio<BookDetailUio> {
|
private val _error = MutableSharedFlow<BookDetailUioErrorUio>()
|
||||||
return withContext(Dispatchers.IO) {
|
val error: Flow<BookDetailUioErrorUio> get() = _error
|
||||||
try {
|
|
||||||
val book = getBookDetail(id = id)
|
fun getDetail(id: Int?): State<BookDetailUio?> {
|
||||||
StateUio.Success(book)
|
return mutableStateOf<BookDetailUio?>(null).apply {
|
||||||
} catch (exception: Exception) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
Log.e("BookDetailViewModel", exception.message, exception)
|
try {
|
||||||
StateUio.Failure(exception)
|
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) {
|
suspend fun send(bookId: Int, email: String): State<Boolean?> {
|
||||||
val data = client.service.send(bookId = bookId, mail = email)
|
return mutableStateOf<Boolean?>(null).apply {
|
||||||
Log.d("send", data.toString())
|
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 {
|
private suspend fun getBookDetail(id: Int): BookDetailUio {
|
||||||
|
|
@ -39,4 +65,9 @@ class BookDetailViewModel @Inject constructor(
|
||||||
val book = factory.fromDetailResponseToBook(response)
|
val book = factory.fromDetailResponseToBook(response)
|
||||||
return book.toDetailUio()
|
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
|
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.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
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 dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ConfirmDialogViewModel @Inject constructor() : ViewModel() {
|
class ConfirmDialogViewModel @Inject constructor(
|
||||||
|
application: Application,
|
||||||
val shouldDisplayConfirmDialog by derivedStateOf { dialog != null }
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
var dialog by mutableStateOf<ConfirmDialogUio?>(null)
|
var dialog by mutableStateOf<ConfirmDialogUio?>(null)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
fun show(
|
fun show(email: String) {
|
||||||
email: String,
|
this.dialog = ConfirmDialogUio(
|
||||||
title: String? = null,
|
|
||||||
description: String? = null,
|
|
||||||
help: String? = null,
|
|
||||||
options: List<ConfirmDialogUio.Option>,
|
|
||||||
) = show(
|
|
||||||
ConfirmDialogUio(
|
|
||||||
email = email,
|
email = email,
|
||||||
title = title,
|
title = context.getString(R.string.detail_send_confirm_title),
|
||||||
description = description,
|
description = context.getString(R.string.detail_send_confirm_description),
|
||||||
help = help,
|
help = context.getString(R.string.detail_send_confirm_help),
|
||||||
options = options,
|
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() {
|
fun hide() {
|
||||||
|
|
|
||||||
|
|
@ -1,49 +1,23 @@
|
||||||
package com.pixelized.biblib.ui.screen.home.detail
|
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.animation.animateContentSize
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.*
|
import androidx.compose.material.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.material.icons.filled.Download
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
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.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
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.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.biblib.R
|
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.composable.isSuccessful
|
||||||
import com.pixelized.biblib.ui.scaffold.DetailBottomSheetState
|
|
||||||
import com.pixelized.biblib.ui.scaffold.LocalDetailBottomSheetState
|
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.screen.home.page.profile.ProfileViewModel
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
|
||||||
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
import com.pixelized.biblib.ui.theme.color.ShadowPalette
|
||||||
import com.pixelized.biblib.utils.extention.bibLib
|
import com.pixelized.biblib.utils.extention.bibLib
|
||||||
import com.pixelized.biblib.utils.extention.default
|
|
||||||
import com.pixelized.biblib.utils.extention.showToast
|
import com.pixelized.biblib.utils.extention.showToast
|
||||||
import com.skydoves.landscapist.CircularReveal
|
|
||||||
import com.skydoves.landscapist.glide.GlideImage
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
|
|
@ -59,332 +33,138 @@ data class BookDetailUio(
|
||||||
val series: String?,
|
val series: String?,
|
||||||
val description: String,
|
val description: String,
|
||||||
val cover: 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
|
@Stable
|
||||||
private const val CONFIRM_OPTION_NO = 1
|
@Immutable
|
||||||
|
data class BookDetailUioErrorUio(
|
||||||
|
val type: Type,
|
||||||
|
val message: String,
|
||||||
|
) {
|
||||||
|
enum class Type {
|
||||||
|
GET_DETAIL,
|
||||||
|
SEND_BOOK,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailScreen(
|
fun DetailScreen(
|
||||||
detailState: DetailBottomSheetState = LocalDetailBottomSheetState.current,
|
detailViewModel: BookDetailViewModel = hiltViewModel(),
|
||||||
confirmViewModel: ConfirmDialogViewModel = hiltViewModel(),
|
confirmViewModel: ConfirmDialogViewModel = hiltViewModel(),
|
||||||
profileViewModel: ProfileViewModel = hiltViewModel(),
|
profileViewModel: ProfileViewModel = hiltViewModel(),
|
||||||
detailViewModel: BookDetailViewModel = hiltViewModel(),
|
bookId: Int? = null,
|
||||||
detail: BookDetailUio? = null,
|
|
||||||
) {
|
) {
|
||||||
|
val detailState = LocalDetailBottomSheetState.current
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val emailSheetState = rememberModalBottomSheetState(
|
val emailSheetState = rememberModalBottomSheetState(
|
||||||
initialValue = ModalBottomSheetValue.Hidden,
|
initialValue = ModalBottomSheetValue.Hidden,
|
||||||
skipHalfExpanded = true,
|
skipHalfExpanded = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
ModalBottomSheetLayout(
|
val bookDetailState: State<BookDetailUio?> = rememberSaveable(bookId) {
|
||||||
sheetState = emailSheetState,
|
detailViewModel.getDetail(bookId)
|
||||||
scrimColor = ShadowPalette.scrim,
|
}
|
||||||
sheetContent = {
|
|
||||||
Box(
|
when (val detail = bookDetailState.value) {
|
||||||
modifier = Modifier
|
null -> EmptyDetail()
|
||||||
.fillMaxWidth()
|
else -> {
|
||||||
.heightIn(min = 1.dp)
|
ModalBottomSheetLayout(
|
||||||
.animateContentSize()
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
sheetState = emailSheetState,
|
||||||
val user = profileViewModel.user
|
scrimColor = ShadowPalette.scrim,
|
||||||
if (user.isSuccessful()) {
|
sheetContent = {
|
||||||
DetailScreenEmailList(
|
DetailScreenEmailList(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||||
emails = user.value.amazonEmails,
|
profileViewModel = profileViewModel,
|
||||||
onEmail = { mail ->
|
onEmail = { mail ->
|
||||||
confirmViewModel.show(email = mail)
|
confirmViewModel.show(email = mail)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
}
|
content = {
|
||||||
},
|
if (detailState.bottomSheetState.isVisible) {
|
||||||
content = {
|
DetailScreenContent(
|
||||||
if (detailState.bottomSheetState.isVisible && detail != null) {
|
modifier = Modifier
|
||||||
DetailScreenContent(
|
.fillMaxSize()
|
||||||
modifier = Modifier.fillMaxSize(),
|
.verticalScroll(rememberScrollState())
|
||||||
book = detail,
|
.systemBarsPadding()
|
||||||
onSend = {
|
.padding(all = MaterialTheme.bibLib.dimen.dp16)
|
||||||
// check
|
.animateContentSize(),
|
||||||
val user = profileViewModel.user
|
book = detail,
|
||||||
if (user.isSuccessful()) {
|
onSend = {
|
||||||
val mails = user.value.amazonEmails
|
// check
|
||||||
when {
|
val user = profileViewModel.user
|
||||||
mails.isEmpty() -> {
|
if (user.isSuccessful()) {
|
||||||
context.showToast(context.getString(R.string.error_no_amazon_email))
|
val mails = user.value.amazonEmails
|
||||||
}
|
when {
|
||||||
mails.size == 1 -> {
|
mails.isEmpty() -> {
|
||||||
confirmViewModel.show(email = mails.first())
|
context.showToast(context.getString(R.string.error_no_amazon_email))
|
||||||
}
|
}
|
||||||
else -> {
|
mails.size == 1 -> {
|
||||||
scope.launch { emailSheetState.show() }
|
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)) {
|
ConfirmDialog(
|
||||||
AnimatedOffset(
|
modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp32),
|
||||||
modifier = Modifier.weight(1f),
|
confirmViewModel = confirmViewModel,
|
||||||
) {
|
detail = detail,
|
||||||
Button(
|
onConfirm = { id, email ->
|
||||||
modifier = Modifier.fillMaxSize(),
|
scope.launch {
|
||||||
onClick = onMobi,
|
confirmViewModel.hide()
|
||||||
) {
|
emailSheetState.hide()
|
||||||
Icon(imageVector = Icons.Default.Download, contentDescription = null)
|
detailViewModel.send(bookId = id, email = email)
|
||||||
Spacer(modifier = Modifier.width(MaterialTheme.bibLib.dimen.dp4))
|
|
||||||
Text(text = stringResource(id = R.string.action_mobi))
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onDismiss = { confirmViewModel.hide() },
|
||||||
|
onHelp = { url -> uriHandler.openUri(url) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp4))
|
LaunchedEffect(key1 = "DetailScreenError") {
|
||||||
|
detailViewModel.error.collect {
|
||||||
AnimatedOffset(
|
context.showToast(message = it.message)
|
||||||
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(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TitleLabel(
|
private fun EmptyDetail(
|
||||||
title: String,
|
modifier : Modifier = Modifier,
|
||||||
label: String,
|
) = Box(
|
||||||
) {
|
modifier = modifier.fillMaxSize()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,11 +7,18 @@ import androidx.compose.material.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.Stable
|
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.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.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
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.ui.theme.BibLibTheme
|
||||||
import com.pixelized.biblib.utils.extention.bibLib
|
import com.pixelized.biblib.utils.extention.bibLib
|
||||||
import com.pixelized.biblib.utils.extention.default
|
import com.pixelized.biblib.utils.extention.default
|
||||||
|
|
@ -20,99 +27,133 @@ import com.pixelized.biblib.utils.extention.default
|
||||||
@Immutable
|
@Immutable
|
||||||
data class ConfirmDialogUio(
|
data class ConfirmDialogUio(
|
||||||
val email: String,
|
val email: String,
|
||||||
val title: String? = null,
|
val title: String,
|
||||||
val description: String? = null,
|
val description: String,
|
||||||
val help: String? = null,
|
val help: String,
|
||||||
val options: List<Option>,
|
val helpUrl: String,
|
||||||
|
val confirm: String,
|
||||||
|
val cancel: String,
|
||||||
) {
|
) {
|
||||||
@Stable
|
|
||||||
@Immutable
|
|
||||||
data class Option(
|
|
||||||
val id: Int,
|
|
||||||
val label: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun option(
|
@Composable
|
||||||
id: Int,
|
fun preview() = ConfirmDialogUio(
|
||||||
label: String,
|
email = "R.Daneel.Olivaw.Kindle@gmail.com",
|
||||||
) = Option(
|
title = stringResource(R.string.detail_send_confirm_title),
|
||||||
id = id,
|
description = stringResource(R.string.detail_send_confirm_description),
|
||||||
label = label
|
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
|
@Composable
|
||||||
fun ConfirmDialog(
|
fun ConfirmDialog(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
uio: ConfirmDialogUio,
|
uio: ConfirmDialogUio,
|
||||||
onDismissRequest: () -> Unit = default(),
|
onDismissRequest: () -> Unit = default(),
|
||||||
onOption: (ConfirmDialogUio.Option) -> Unit = default<ConfirmDialogUio.Option>(),
|
onHelp: (url: String) -> Unit = default<String>(),
|
||||||
onHelp: () -> Unit = default(),
|
onConfirm: (mail: String) -> Unit = default<String>(),
|
||||||
) {
|
) {
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
|
||||||
) {
|
) {
|
||||||
Card(modifier = modifier) {
|
ConfirmDialogContent(
|
||||||
Column(modifier = Modifier.padding(all = MaterialTheme.bibLib.dimen.dp16)) {
|
modifier = modifier,
|
||||||
if (uio.title != null) {
|
uio = uio,
|
||||||
Text(
|
onDismissRequest = onDismissRequest,
|
||||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
onHelp = onHelp,
|
||||||
style = MaterialTheme.typography.h6,
|
onConfirm = onConfirm,
|
||||||
color = MaterialTheme.bibLib.colors.typography.medium,
|
)
|
||||||
text = uio.title
|
}
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (uio.description != null) {
|
@Composable
|
||||||
Text(
|
fun ConfirmDialogContent(
|
||||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
modifier: Modifier = Modifier,
|
||||||
style = MaterialTheme.typography.body1,
|
uio: ConfirmDialogUio,
|
||||||
color = MaterialTheme.bibLib.colors.typography.medium,
|
onDismissRequest: () -> Unit = default(),
|
||||||
text = uio.description
|
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(
|
Text(
|
||||||
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
||||||
style = MaterialTheme.typography.caption,
|
style = MaterialTheme.typography.body1,
|
||||||
color = MaterialTheme.bibLib.colors.typography.easy,
|
color = MaterialTheme.bibLib.colors.typography.medium,
|
||||||
text = uio.email
|
text = uio.description
|
||||||
)
|
)
|
||||||
|
|
||||||
if (uio.help != null) {
|
Text(
|
||||||
Text(
|
modifier = Modifier.padding(bottom = MaterialTheme.bibLib.dimen.dp8),
|
||||||
modifier = Modifier
|
style = MaterialTheme.typography.caption,
|
||||||
.clickable(onClick = onHelp)
|
color = MaterialTheme.bibLib.colors.typography.easy,
|
||||||
.padding(bottom = MaterialTheme.bibLib.dimen.dp16),
|
text = uio.email
|
||||||
style = MaterialTheme.typography.caption,
|
)
|
||||||
color = MaterialTheme.bibLib.colors.typography.strong,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
maxLines = 1,
|
|
||||||
text = uio.help
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
Text(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
horizontalArrangement = Arrangement.End,
|
.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 ->
|
Text(text = uio.cancel)
|
||||||
Button(
|
}
|
||||||
modifier = when (index) {
|
Button(
|
||||||
uio.options.lastIndex -> Modifier
|
colors = ButtonDefaults.buttonColors(),
|
||||||
else -> Modifier.padding(end = MaterialTheme.bibLib.dimen.dp8)
|
onClick = { onConfirm(uio.email) }
|
||||||
},
|
) {
|
||||||
colors = when (index) {
|
Text(text = uio.confirm)
|
||||||
uio.options.lastIndex -> ButtonDefaults.buttonColors()
|
|
||||||
else -> ButtonDefaults.outlinedButtonColors()
|
|
||||||
},
|
|
||||||
onClick = { onOption(option) }
|
|
||||||
) {
|
|
||||||
Text(text = option.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -124,17 +165,8 @@ fun ConfirmDialog(
|
||||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
private fun DetailConfirmPreview() {
|
private fun DetailConfirmPreview() {
|
||||||
BibLibTheme {
|
BibLibTheme {
|
||||||
ConfirmDialog(
|
ConfirmDialogContent(
|
||||||
uio = ConfirmDialogUio(
|
uio = ConfirmDialogUio.preview(),
|
||||||
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")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
package com.pixelized.biblib.ui.screen.home.detail
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
|
@ -15,10 +16,37 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
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.ui.theme.BibLibTheme
|
||||||
import com.pixelized.biblib.utils.extention.bibLib
|
import com.pixelized.biblib.utils.extention.bibLib
|
||||||
import com.pixelized.biblib.utils.extention.default
|
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
|
@Composable
|
||||||
fun DetailScreenEmailList(
|
fun DetailScreenEmailList(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ data class BibLibColor(
|
||||||
val base: Colors,
|
val base: Colors,
|
||||||
val typography: Typography,
|
val typography: Typography,
|
||||||
val placeHolder: Color,
|
val placeHolder: Color,
|
||||||
|
val dialogBackground: Color,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -40,10 +41,12 @@ fun bibLibDarkColors(
|
||||||
strong = base.primary,
|
strong = base.primary,
|
||||||
),
|
),
|
||||||
placeHolder: Color = BibLibColorPalette.DarkGrey,
|
placeHolder: Color = BibLibColorPalette.DarkGrey,
|
||||||
|
dialogBackground: Color = BibLibColorPalette.VeryDarkGrey,
|
||||||
) = BibLibColor(
|
) = BibLibColor(
|
||||||
base = base,
|
base = base,
|
||||||
typography = typography,
|
typography = typography,
|
||||||
placeHolder = placeHolder,
|
placeHolder = placeHolder,
|
||||||
|
dialogBackground = dialogBackground,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun bibLibLightColors(
|
fun bibLibLightColors(
|
||||||
|
|
@ -61,8 +64,10 @@ fun bibLibLightColors(
|
||||||
strong = base.primary,
|
strong = base.primary,
|
||||||
),
|
),
|
||||||
placeHolder: Color = BibLibColorPalette.LightGrey,
|
placeHolder: Color = BibLibColorPalette.LightGrey,
|
||||||
|
dialogBackground: Color = Color.White,
|
||||||
) = BibLibColor(
|
) = BibLibColor(
|
||||||
base = base,
|
base = base,
|
||||||
typography = typography,
|
typography = typography,
|
||||||
placeHolder = placeHolder,
|
placeHolder = placeHolder,
|
||||||
|
dialogBackground = dialogBackground,
|
||||||
)
|
)
|
||||||
|
|
@ -15,6 +15,7 @@ data class BibLibDimen(
|
||||||
val dp8: Dp = 8.dp,
|
val dp8: Dp = 8.dp,
|
||||||
val dp12: Dp = 12.dp,
|
val dp12: Dp = 12.dp,
|
||||||
val dp16: Dp = 16.dp,
|
val dp16: Dp = 16.dp,
|
||||||
|
val dp24: Dp = 24.dp,
|
||||||
val dp32: Dp = 32.dp,
|
val dp32: Dp = 32.dp,
|
||||||
val dp48: Dp = 48.dp,
|
val dp48: Dp = 48.dp,
|
||||||
val dp52: Dp = 52.dp,
|
val dp52: Dp = 52.dp,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,15 @@
|
||||||
package com.pixelized.biblib.utils.extention
|
package com.pixelized.biblib.utils.extention
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.lifecycle.AndroidViewModel
|
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_series">Séries</string>
|
||||||
<string name="detail_emails_title">Envoyer cet eBook à :</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_title">Rechercher sur Biblib</string>
|
||||||
<string name="search_filter_title">Rechercher</string>
|
<string name="search_filter_title">Rechercher</string>
|
||||||
<string name="search_filter_param">%1$s : %2$s</string>
|
<string name="search_filter_param">%1$s : %2$s</string>
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,13 @@
|
||||||
<string name="detail_series">Series</string>
|
<string name="detail_series">Series</string>
|
||||||
<string name="detail_emails_title">Send this eBook to:</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_title">Search on Biblib</string>
|
||||||
<string name="search_filter_title">Search</string>
|
<string name="search_filter_title">Search</string>
|
||||||
<string name="search_filter_param">%1$s: %2$s</string>
|
<string name="search_filter_param">%1$s: %2$s</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue