diff --git a/app/build.gradle b/app/build.gradle index df24a64..59ca28f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -83,13 +83,13 @@ dependencies { // Android core implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' + implementation 'com.google.android.material:material:1.6.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1" implementation 'androidx.activity:activity-compose:1.4.0' // Android Compose - implementation "androidx.compose.ui:ui:1.2.0-alpha08" + implementation "androidx.compose.ui:ui:1.2.0-beta01" implementation "androidx.compose.material:material:1.1.1" implementation "androidx.compose.runtime:runtime-livedata:1.1.1" implementation "androidx.compose.ui:ui-tooling-preview:1.1.1" diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/Picture.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/Picture.kt new file mode 100644 index 0000000..ab34f37 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/ui/composable/Picture.kt @@ -0,0 +1,21 @@ +package com.pixelized.biblib.ui.composable + +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio + +@Composable +fun Cover( + modifier: Modifier = Modifier, + cover: CoverUio, + contentDescription: String? = null, +) { + Image( + modifier = modifier, + painter = cover.painter, + contentScale = cover.contentScale, + colorFilter = cover.colorFilter, + contentDescription = contentDescription, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt b/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt index 3224051..7c3fbb3 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/navigation/screen/ScreenNavHost.kt @@ -46,7 +46,11 @@ fun ScreenNavHost( fun NavHostController.navigateToHome() { navigate(Screen.Home.route) { launchSingleTop = true - popUpTo(0) { inclusive = true } + restoreState = true + popUpTo(0) { + saveState = true + inclusive = true + } } } diff --git a/app/src/main/java/com/pixelized/biblib/ui/scaffold/BottomDetailScaffold.kt b/app/src/main/java/com/pixelized/biblib/ui/scaffold/BottomDetailScaffold.kt index 5c57aaa..c6edf47 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/scaffold/BottomDetailScaffold.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/scaffold/BottomDetailScaffold.kt @@ -1,13 +1,21 @@ package com.pixelized.biblib.ui.scaffold -import androidx.compose.animation.ExperimentalAnimationApi +import android.content.Context import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.material.* +import androidx.compose.material.BottomSheetScaffold +import androidx.compose.material.BottomSheetScaffoldState +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.rememberBottomSheetScaffoldState import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import com.pixelized.biblib.R +import com.pixelized.biblib.ui.composable.StateUio import com.pixelized.biblib.ui.screen.detail.BookDetailViewModel import com.pixelized.biblib.ui.screen.detail.DetailScreen +import com.pixelized.biblib.ui.screen.home.common.uio.BookUio +import com.pixelized.biblib.utils.extention.showToast import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -15,7 +23,7 @@ val LocalBottomDetailController = staticCompositionLocalOf(null) + var bookDetail = mutableStateOf(null) + private set - fun expandBookDetail(bookId: Int) { + fun expandBookDetail(id: Int) { scope.launch { - detail.value = bookId - scaffoldState.bottomSheetState.expand() + when (val book = viewModel.getDetail(id)) { + is StateUio.Failure -> { + context.showToast(message = context.getString(R.string.error_generic)) + } + is StateUio.Success -> { + bookDetail.value = book.value + scaffoldState.bottomSheetState.expand() + } + else -> Unit + } } } fun collapse() { scope.launch { scaffoldState.bottomSheetState.collapse() - detail.value = null } } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/detail/BookDetailViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/detail/BookDetailViewModel.kt index 4c995fe..46a4a74 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/detail/BookDetailViewModel.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/detail/BookDetailViewModel.kt @@ -2,19 +2,13 @@ package com.pixelized.biblib.ui.screen.detail import android.app.Application import android.util.Log -import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.layout.ContentScale -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.viewModelScope import com.pixelized.biblib.R import com.pixelized.biblib.model.book.Book 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.ui.navigation.screen.Screen import com.pixelized.biblib.ui.screen.home.common.uio.BookUio import com.pixelized.biblib.ui.screen.home.common.uio.CoverUio import com.pixelized.biblib.ui.screen.home.common.viewModel.ACoverViewModel @@ -25,7 +19,7 @@ import com.pixelized.biblib.utils.extention.shortDate import com.pixelized.biblib.utils.painterResource import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import java.net.URL import javax.inject.Inject @@ -33,25 +27,17 @@ import javax.inject.Inject class BookDetailViewModel @Inject constructor( application: Application, bookCoverCache: BookCoverCache, - savedStateHandle: SavedStateHandle, private val client: IBibLibClient, ) : ACoverViewModel(application, bookCoverCache) { - private val _state = mutableStateOf>(StateUio.Progress()) - val state: State> get() = _state - - val book: State = derivedStateOf { - state.value.let { if (it is StateUio.Success) it.value else null } - } - - fun getDetail(id: Int) { - viewModelScope.launch(Dispatchers.IO) { + suspend fun getDetail(id: Int): StateUio { + return withContext(Dispatchers.IO) { try { val book = getBookDetail(id = id) - _state.value = StateUio.Success(book) + StateUio.Success(book) } catch (exception: Exception) { Log.e("BookDetailViewModel", exception.message, exception) - _state.value = StateUio.Failure(exception) + StateUio.Failure(exception) } } } @@ -83,7 +69,7 @@ class BookDetailViewModel @Inject constructor( date = releaseDate.shortDate(), series = series?.name, description = synopsis ?: "", - cover = cover( + coverState = cover( placeHolder = thumbnailCover, type = CoverUio.Type.DETAIL, contentScale = ContentScale.FillHeight, @@ -91,7 +77,4 @@ class BookDetailViewModel @Inject constructor( ) ) } - - private val SavedStateHandle.bookId: Int - get() = get(Screen.BookDetail.ARG_BOOK_ID) ?: error("") } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt index cb0d52c..75aec7c 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/detail/DetailScreen.kt @@ -3,7 +3,6 @@ package com.pixelized.biblib.ui.screen.detail import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.activity.compose.BackHandler -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState @@ -16,7 +15,6 @@ 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.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -30,8 +28,8 @@ 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 com.pixelized.biblib.R +import com.pixelized.biblib.ui.composable.Cover import com.pixelized.biblib.ui.composable.SpannedText import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer import com.pixelized.biblib.ui.composable.animation.AnimatedOffset @@ -45,29 +43,27 @@ import com.pixelized.biblib.utils.extention.todo @Composable fun DetailScreen( - viewModel: BookDetailViewModel = hiltViewModel() + detail: BookUio? = null, ) { - val bottomDetailState = LocalBottomDetailController.current + if (detail != null) { + DetailScreenContent( + modifier = Modifier.fillMaxSize(), + book = detail, + ) + } else { + Box(modifier = Modifier.fillMaxSize()) + } - Box { - val book by viewModel.book - book?.let { - DetailScreenContent( - book = it, - onClose = { - bottomDetailState.collapse() - }, - ) - } + val bottomDetailState = LocalBottomDetailController.current + BackHandler { + bottomDetailState.collapse() } } -@OptIn(ExperimentalAnimationApi::class) @Composable private fun DetailScreenContent( modifier: Modifier = Modifier, book: BookUio, - onClose: () -> Unit = todo(), onMobi: () -> Unit = todo(), onEpub: () -> Unit = todo(), onSend: () -> Unit = todo(), @@ -87,36 +83,15 @@ private fun DetailScreenContent( .height(MaterialTheme.bibLib.dimen.detail.cover), contentAlignment = Alignment.Center ) { - val cover by book.cover - when (cover.type) { - CoverUio.Type.PLACE_HOLDER -> { - Image( - modifier = Modifier.size(MaterialTheme.bibLib.dimen.detail.placeHolder), - painter = cover.painter, - contentScale = cover.contentScale, - colorFilter = cover.colorFilter, - contentDescription = null, - ) - } - CoverUio.Type.THUMBNAIL -> { - Image( - modifier = Modifier.fillMaxSize(), - painter = cover.painter, - contentScale = cover.contentScale, - colorFilter = cover.colorFilter, - contentDescription = null, - ) - } - CoverUio.Type.DETAIL -> { - Image( - modifier = Modifier.fillMaxSize(), - painter = cover.painter, - contentScale = cover.contentScale, - colorFilter = cover.colorFilter, - contentDescription = null, - ) - } + val sizeModifier = if (book.cover.type == CoverUio.Type.PLACE_HOLDER) { + Modifier.size(MaterialTheme.bibLib.dimen.detail.placeHolder) + } else { + Modifier.fillMaxSize() } + Cover( + modifier = sizeModifier, + cover = book.cover, + ) } Row(modifier = Modifier.padding(vertical = MaterialTheme.bibLib.dimen.medium)) { @@ -195,7 +170,7 @@ private fun DetailScreenContent( AnimatedOffset(modifier = Modifier.weight(1f)) { TitleLabel( title = stringResource(id = R.string.detail_rating), - label = book.rating.toString(), + label = book.rating?.toString(), ) } @@ -206,13 +181,11 @@ private fun DetailScreenContent( ) } - book.date?.let { - AnimatedOffset(modifier = Modifier.weight(1f)) { - TitleLabel( - title = stringResource(id = R.string.detail_release), - label = it, - ) - } + AnimatedOffset(modifier = Modifier.weight(1f)) { + TitleLabel( + title = stringResource(id = R.string.detail_release), + label = book.date ?: "-" , + ) } } @@ -237,8 +210,6 @@ private fun DetailScreenContent( } } } - - BackHandler(onBack = onClose) } @Composable @@ -288,7 +259,7 @@ private fun DetailScreenContentPreview() { 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 = cover, + coverState = cover, ) BibLibTheme { DetailScreenContent(book = book) diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/composable/LazyBookThumbnailColumn.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/composable/LazyBookThumbnailColumn.kt index 455ac3b..1782c09 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/composable/LazyBookThumbnailColumn.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/composable/LazyBookThumbnailColumn.kt @@ -39,7 +39,10 @@ fun LazyBookThumbnailColumn( contentPadding = contentPadding, state = state, ) { - items(books) { thumbnail -> + items( + items = books, + key = { it.id }, + ) { thumbnail -> BookThumbnail( thumbnail = thumbnail, onClick = currentOnItemClick, diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/uio/BookUio.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/uio/BookUio.kt index db83ff4..3dc1531 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/uio/BookUio.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/common/uio/BookUio.kt @@ -1,6 +1,7 @@ package com.pixelized.biblib.ui.screen.home.common.uio import androidx.compose.runtime.State +import androidx.compose.runtime.getValue data class BookUio( val id: Int, @@ -11,5 +12,7 @@ data class BookUio( val date: String?, val series: String?, val description: String, - val cover: State, -) + private val coverState: State, +) { + val cover by coverState +} diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/books/BooksPage.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/books/BooksPage.kt index 5fe99af..dba5f99 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/books/BooksPage.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/books/BooksPage.kt @@ -24,7 +24,7 @@ fun BooksPage( contentPadding = PaddingValues(all = MaterialTheme.bibLib.dimen.thumbnail.padding), books = booksViewModel.books, onItemClick = { - bottomDetailState.expandBookDetail(bookId = it.id) + bottomDetailState.expandBookDetail(id = it.id) }, ) } diff --git a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/news/NewsPage.kt b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/news/NewsPage.kt index 2d3e16b..a7a5d0d 100644 --- a/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/news/NewsPage.kt +++ b/app/src/main/java/com/pixelized/biblib/ui/screen/home/page/news/NewsPage.kt @@ -23,7 +23,7 @@ fun NewsPage( contentPadding = PaddingValues(all = MaterialTheme.bibLib.dimen.thumbnail.padding), books = booksViewModel.news, onItemClick = { - bottomDetail.expandBookDetail(bookId = it.id) + bottomDetail.expandBookDetail(id = it.id) }, ) }