diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt index d8163766..622c5150 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailPresenter.kt @@ -15,6 +15,7 @@ import com.ninecraft.booket.core.data.api.repository.BookRepository import com.ninecraft.booket.core.data.api.repository.RecordRepository import com.ninecraft.booket.core.model.BookDetailModel import com.ninecraft.booket.core.model.EmotionModel +import com.ninecraft.booket.core.model.PageInfoModel import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.ui.component.FooterState import com.ninecraft.booket.feature.screens.BookDetailScreen @@ -34,9 +35,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.CancellationException import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import java.time.LocalDateTime @@ -66,20 +65,22 @@ class BookDetailPresenter @AssistedInject constructor( var bookDetail by rememberRetained { mutableStateOf(BookDetailModel()) } var seedsStates by rememberRetained { mutableStateOf>(persistentListOf()) } var readingRecords by rememberRetained { mutableStateOf(persistentListOf()) } + var readingRecordsPageInfo by rememberRetained { mutableStateOf(PageInfoModel()) } var currentStartIndex by rememberRetained { mutableIntStateOf(START_INDEX) } var isLastPage by rememberRetained { mutableStateOf(false) } var currentBookStatus by rememberRetained { mutableStateOf(BookStatus.READING) } + var selectedBookStatus by rememberRetained { mutableStateOf(BookStatus.READING) } var currentRecordSort by rememberRetained { mutableStateOf(RecordSort.PAGE_NUMBER_ASC) } var isBookUpdateBottomSheetVisible by rememberRetained { mutableStateOf(false) } var isRecordSortBottomSheetVisible by rememberRetained { mutableStateOf(false) } var sideEffect by rememberRetained { mutableStateOf(null) } @Suppress("TooGenericExceptionCaught") - suspend fun initialLoad() { + fun initialLoad() { uiState = UiState.Loading try { - coroutineScope { + scope.launch { val bookDetailDef = async { bookRepository.getBookDetail(screen.isbn13).getOrThrow() } val seedsDef = async { bookRepository.getSeedsStats(screen.userBookId).getOrThrow() } val readingRecordsDef = async { @@ -95,16 +96,17 @@ class BookDetailPresenter @AssistedInject constructor( val records = readingRecordsDef.await() bookDetail = detail + currentBookStatus = BookStatus.fromValue(detail.userBookStatus) ?: BookStatus.BEFORE_READING + selectedBookStatus = currentBookStatus seedsStates = seeds.categories.toImmutableList() readingRecords = records.content.toPersistentList() + readingRecordsPageInfo = records.page isLastPage = records.content.size < PAGE_SIZE currentStartIndex = START_INDEX uiState = UiState.Success } - } catch (ce: CancellationException) { - throw ce } catch (e: Throwable) { uiState = UiState.Error(e) @@ -211,11 +213,11 @@ class BookDetailPresenter @AssistedInject constructor( } is BookDetailUiEvent.OnBookStatusItemSelected -> { - currentBookStatus = event.bookStatus + selectedBookStatus = event.bookStatus } is BookDetailUiEvent.OnBookStatusUpdateButtonClick -> { - upsertBook(screen.isbn13, currentBookStatus.value) + upsertBook(screen.isbn13, selectedBookStatus.value) } is BookDetailUiEvent.OnRecordSortBottomSheetDismiss -> { @@ -252,9 +254,11 @@ class BookDetailPresenter @AssistedInject constructor( bookDetail = bookDetail, seedsStats = seedsStates, readingRecords = readingRecords, + readingRecordsPageInfo = readingRecordsPageInfo, isBookUpdateBottomSheetVisible = isBookUpdateBottomSheetVisible, isRecordSortBottomSheetVisible = isRecordSortBottomSheetVisible, currentBookStatus = currentBookStatus, + selectedBookStatus = selectedBookStatus, currentRecordSort = currentRecordSort, sideEffect = sideEffect, eventSink = ::handleEvent, diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt index 16ea3a20..106620d3 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUi.kt @@ -37,6 +37,7 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButton import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorStyle import com.ninecraft.booket.core.designsystem.component.button.largeButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme +import com.ninecraft.booket.core.model.BookDetailModel import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.InfinityLazyColumn import com.ninecraft.booket.core.ui.component.LoadStateFooter @@ -95,6 +96,7 @@ internal fun BookDetailUi( }, bookStatuses = BookStatus.entries.toTypedArray().toImmutableList(), currentBookStatus = state.currentBookStatus, + selectedBookStatus = state.selectedBookStatus, onItemSelected = { state.eventSink(BookDetailUiEvent.OnBookStatusItemSelected(it)) }, @@ -221,7 +223,7 @@ internal fun BookDetailContent( ) { Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing6)) ReadingRecordsHeader( - readingRecords = state.readingRecords, + pageInfo = state.readingRecordsPageInfo, currentRecordSort = state.currentRecordSort, onReadingRecordClick = { state.eventSink(BookDetailUiEvent.OnRecordSortButtonClick) @@ -297,6 +299,13 @@ private fun BookDetailPreview() { BookDetailUi( state = BookDetailUiState( uiState = UiState.Success, + bookDetail = BookDetailModel( + title = "데미안", + author = "헤르만 헤세", + publisher = "민음사", + pubDate = "2023-01-01", + coverImageUrl = "", + ), eventSink = {}, ), ) diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt index 4b58683e..72d548f9 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/BookDetailUiState.kt @@ -5,6 +5,7 @@ import com.ninecraft.booket.core.common.R import com.ninecraft.booket.core.common.constants.BookStatus import com.ninecraft.booket.core.model.BookDetailModel import com.ninecraft.booket.core.model.EmotionModel +import com.ninecraft.booket.core.model.PageInfoModel import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.ui.component.FooterState import com.slack.circuit.runtime.CircuitUiEvent @@ -27,9 +28,11 @@ data class BookDetailUiState( val bookDetail: BookDetailModel = BookDetailModel(), val seedsStats: ImmutableList = persistentListOf(), val readingRecords: ImmutableList = persistentListOf(), + val readingRecordsPageInfo: PageInfoModel = PageInfoModel(), val currentStartIndex: Int = 1, val isLastPage: Boolean = false, val currentBookStatus: BookStatus = BookStatus.BEFORE_READING, + val selectedBookStatus: BookStatus = BookStatus.BEFORE_READING, val currentRecordSort: RecordSort = RecordSort.PAGE_NUMBER_ASC, val isBookUpdateBottomSheetVisible: Boolean = false, val isRecordSortBottomSheetVisible: Boolean = false, diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookItem.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookItem.kt index 383b6111..da452ed5 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookItem.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookItem.kt @@ -1,5 +1,7 @@ package com.ninecraft.booket.feature.detail.book.component +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -7,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.material3.VerticalDivider @@ -18,6 +21,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.ninecraft.booket.core.common.extensions.formatPublishYear +import com.ninecraft.booket.core.designsystem.ComponentPreview import com.ninecraft.booket.core.designsystem.component.NetworkImage import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.model.BookDetailModel @@ -41,7 +45,12 @@ internal fun BookItem( .padding(end = ReedTheme.spacing.spacing4) .width(70.dp) .height(99.dp) - .clip(RoundedCornerShape(size = ReedTheme.radius.xs)), + .clip(RoundedCornerShape(size = ReedTheme.radius.xs)) + .border( + width = 1.dp, + color = ReedTheme.colors.borderPrimary, + shape = RoundedCornerShape(ReedTheme.radius.xs), + ), placeholder = painterResource(designR.drawable.ic_placeholder), ) Column(modifier = Modifier.weight(1f)) { @@ -53,35 +62,38 @@ internal fun BookItem( style = ReedTheme.typography.headline1SemiBold, ) Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing2)) - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = bookDetail.author, - color = ReedTheme.colors.contentTertiary, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = ReedTheme.typography.label2Regular, - modifier = Modifier.weight(0.7f, fill = false), - ) - Spacer(Modifier.width(ReedTheme.spacing.spacing1)) - VerticalDivider( - modifier = Modifier.height(14.dp), - thickness = 1.dp, - color = ReedTheme.colors.contentTertiary, - ) - Spacer(Modifier.width(ReedTheme.spacing.spacing1)) - Text( - text = bookDetail.publisher, - color = ReedTheme.colors.contentTertiary, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = ReedTheme.typography.label2Regular, - modifier = Modifier.weight(0.3f, fill = false), - ) + BoxWithConstraints { + val authorMaxWidth = maxWidth * 0.7f + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = bookDetail.author, + color = ReedTheme.colors.contentTertiary, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = ReedTheme.typography.label2Regular, + modifier = Modifier.widthIn(max = authorMaxWidth), + ) + Spacer(Modifier.width(ReedTheme.spacing.spacing1)) + VerticalDivider( + modifier = Modifier.height(14.dp), + thickness = 1.dp, + color = ReedTheme.colors.contentTertiary, + ) + Spacer(Modifier.width(ReedTheme.spacing.spacing1)) + Text( + text = bookDetail.publisher, + color = ReedTheme.colors.contentTertiary, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = ReedTheme.typography.label2Regular, + modifier = Modifier.weight(1f, fill = false), + ) + } } - Spacer(Modifier.width(ReedTheme.spacing.spacing05)) Text( text = bookDetail.pubDate.formatPublishYear(), color = ReedTheme.colors.contentTertiary, @@ -92,3 +104,19 @@ internal fun BookItem( } } } + +@ComponentPreview +@Composable +private fun BookItemPreview() { + ReedTheme { + BookItem( + bookDetail = BookDetailModel( + title = "데미안", + author = "헤르만 헤세", + publisher = "민음사", + pubDate = "2023-01-01", + coverImageUrl = "", + ), + ) + } +} diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookUpdateBottomSheet.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookUpdateBottomSheet.kt index 6cafde4b..d324c67a 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookUpdateBottomSheet.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/BookUpdateBottomSheet.kt @@ -48,6 +48,7 @@ internal fun BookUpdateBottomSheet( onCloseButtonClick: () -> Unit, bookStatuses: ImmutableList, currentBookStatus: BookStatus?, + selectedBookStatus: BookStatus, onItemSelected: (BookStatus) -> Unit, onBookUpdateButtonClick: () -> Unit, modifier: Modifier = Modifier, @@ -94,9 +95,9 @@ internal fun BookUpdateBottomSheet( bookStatuses.forEach { item -> BookStatusItem( item = item, - selected = item == currentBookStatus, + selected = item == selectedBookStatus, onClick = { - if (item != currentBookStatus) { + if (item != selectedBookStatus) { onItemSelected(item) } }, @@ -112,7 +113,7 @@ internal fun BookUpdateBottomSheet( sizeStyle = largeButtonStyle, colorStyle = ReedButtonColorStyle.PRIMARY, modifier = Modifier.fillMaxWidth(), - enabled = currentBookStatus != null, + enabled = currentBookStatus != selectedBookStatus, text = stringResource(R.string.book_update_ok), ) Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing4)) @@ -167,6 +168,7 @@ private fun BookUpdateBottomSheetPreview() { onCloseButtonClick = {}, bookStatuses = BookStatus.entries.toImmutableList(), currentBookStatus = null, + selectedBookStatus = BookStatus.BEFORE_READING, onItemSelected = {}, onBookUpdateButtonClick = {}, ) diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt index dfa83df1..db940fd5 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/CollectedSeeds.kt @@ -66,12 +66,12 @@ internal fun CollectedSeeds( .padding(horizontal = ReedTheme.spacing.spacing4) .clip(RoundedCornerShape(ReedTheme.radius.sm)) .background(ReedTheme.colors.basePrimary) - .padding(ReedTheme.spacing.spacing3) .border( width = 1.dp, - color = ReedTheme.colors.basePrimary, + color = ReedTheme.colors.borderPrimary, shape = RoundedCornerShape(ReedTheme.radius.sm), - ), + ) + .padding(ReedTheme.spacing.spacing3), ) { EmotionAnalysisResultText( emotions = seedsStats, diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/ReadingRecordsHeader.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/ReadingRecordsHeader.kt index 511f2c81..2868a472 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/ReadingRecordsHeader.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/ReadingRecordsHeader.kt @@ -15,15 +15,14 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.model.ReadingRecordModel +import com.ninecraft.booket.core.model.PageInfoModel import com.ninecraft.booket.feature.detail.R import com.ninecraft.booket.feature.detail.book.RecordSort -import kotlinx.collections.immutable.ImmutableList import com.ninecraft.booket.core.designsystem.R as designR @Composable internal fun ReadingRecordsHeader( - readingRecords: ImmutableList, + pageInfo: PageInfoModel, currentRecordSort: RecordSort, onReadingRecordClick: () -> Unit, modifier: Modifier = Modifier, @@ -41,7 +40,7 @@ internal fun ReadingRecordsHeader( ) Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing1)) Text( - text = "${readingRecords.size}", + text = "${pageInfo.totalElements}", color = ReedTheme.colors.contentBrand, style = ReedTheme.typography.headline2SemiBold, ) diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt index 44d88ef6..c8af70a1 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/RecordItem.kt @@ -81,7 +81,7 @@ internal fun RecordItem( } Spacer(modifier = Modifier.weight(1f)) Text( - text = "${pageNumber}P", + text = "${pageNumber}p", color = ReedTheme.colors.contentBrand, style = ReedTheme.typography.body2Medium, )