From f2aa7100a489904423486d612cb14be961a30730 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Thu, 15 Jan 2026 22:45:59 +0900 Subject: [PATCH 01/10] =?UTF-8?q?[BOOK-490]=20feat:=20=EB=8F=84=EC=84=9C?= =?UTF-8?q?=20=EC=83=81=EC=84=B8=20API=20=EB=B3=80=EA=B2=BD=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=B0=98=EC=98=81=20=EB=B0=8F=20V2=20suffix=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v1 -> v2 --- .../data/api/repository/RecordRepository.kt | 8 +++--- .../core/data/impl/mapper/ResponseToModel.kt | 19 ------------- .../booket/core/model/ReadingRecordsModel.kt | 19 +------------ .../response/ReadingRecordsResponse.kt | 28 ------------------- .../core/network/service/ReedService.kt | 10 +++---- .../detail/book/BookDetailPresenter.kt | 2 +- .../feature/detail/book/BookDetailUi.kt | 13 +++++---- .../detail/book/component/RecordItem.kt | 8 +++--- .../detail/record/RecordDetailPresenter.kt | 4 +-- .../feature/detail/record/RecordDetailUi.kt | 6 ++-- .../detail/record/RecordDetailUiState.kt | 4 +-- 11 files changed, 29 insertions(+), 92 deletions(-) diff --git a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt index 2c13ec33..8d23e487 100644 --- a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt +++ b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt @@ -1,6 +1,6 @@ package com.ninecraft.booket.core.data.api.repository -import com.ninecraft.booket.core.model.ReadingRecordModelV2 +import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.model.ReadingRecordsModel interface RecordRepository { @@ -11,7 +11,7 @@ interface RecordRepository { review: String, primaryEmotion: String, detailEmotionTagIds: List, - ): Result + ): Result suspend fun getReadingRecords( userBookId: String, @@ -22,7 +22,7 @@ interface RecordRepository { suspend fun getRecordDetail( readingRecordId: String, - ): Result + ): Result suspend fun editRecord( readingRecordId: String, @@ -31,7 +31,7 @@ interface RecordRepository { review: String, primaryEmotion: String, detailEmotionTagIds: List, - ): Result + ): Result suspend fun deleteRecord( readingRecordId: String, diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt index 822eb87e..defb3da4 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt @@ -18,7 +18,6 @@ import com.ninecraft.booket.core.model.LibraryModel import com.ninecraft.booket.core.model.PageInfoModel import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel -import com.ninecraft.booket.core.model.ReadingRecordModelV2 import com.ninecraft.booket.core.model.ReadingRecordsModel import com.ninecraft.booket.core.model.RecentBookModel import com.ninecraft.booket.core.model.RecordRegisterModel @@ -42,7 +41,6 @@ import com.ninecraft.booket.core.network.response.LibraryResponse import com.ninecraft.booket.core.network.response.PageInfo import com.ninecraft.booket.core.network.response.PrimaryEmotion import com.ninecraft.booket.core.network.response.ReadingRecord -import com.ninecraft.booket.core.network.response.ReadingRecordV2 import com.ninecraft.booket.core.network.response.ReadingRecordsResponse import com.ninecraft.booket.core.network.response.RecentBook import com.ninecraft.booket.core.network.response.RecordRegisterResponse @@ -242,23 +240,6 @@ internal fun ReadingRecordsResponse.toModel(): ReadingRecordsModel { internal fun ReadingRecord.toModel(): ReadingRecordModel { return ReadingRecordModel( - id = id, - userBookId = userBookId, - pageNumber = pageNumber, - quote = quote, - review = review ?: "", - emotionTags = emotionTags, - createdAt = createdAt, - updatedAt = updatedAt, - bookTitle = bookTitle ?: "", - bookPublisher = bookPublisher ?: "", - bookCoverImageUrl = bookCoverImageUrl ?: "", - author = author ?: "", - ) -} - -internal fun ReadingRecordV2.toModel(): ReadingRecordModelV2 { - return ReadingRecordModelV2( id = id, userBookId = userBookId, pageNumber = pageNumber, diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt index 8d6f7140..666fc5b3 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt @@ -1,6 +1,5 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable data class ReadingRecordsModel( @@ -11,24 +10,8 @@ data class ReadingRecordsModel( val readingRecords: List = emptyList(), ) -@Immutable -data class ReadingRecordModel( - val id: String = "", - val userBookId: String = "", - val pageNumber: Int = 0, - val quote: String = "", - val review: String = "", - val emotionTags: List = emptyList(), - val createdAt: String = "", - val updatedAt: String = "", - val bookTitle: String = "", - val bookPublisher: String = "", - val bookCoverImageUrl: String = "", - val author: String = "", -) - @Stable -data class ReadingRecordModelV2( +data class ReadingRecordModel( val id: String = "", val userBookId: String = "", val pageNumber: Int? = null, diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt index d17c3c90..3cbb8cda 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt @@ -19,34 +19,6 @@ data class ReadingRecordsResponse( @Serializable data class ReadingRecord( - @SerialName("id") - val id: String, - @SerialName("userBookId") - val userBookId: String, - @SerialName("pageNumber") - val pageNumber: Int, - @SerialName("quote") - val quote: String, - @SerialName("review") - val review: String?, - @SerialName("emotionTags") - val emotionTags: List, - @SerialName("createdAt") - val createdAt: String, - @SerialName("updatedAt") - val updatedAt: String, - @SerialName("bookTitle") - val bookTitle: String?, - @SerialName("bookPublisher") - val bookPublisher: String?, - @SerialName("bookCoverImageUrl") - val bookCoverImageUrl: String?, - @SerialName("author") - val author: String?, -) - -@Serializable -data class ReadingRecordV2( @SerialName("id") val id: String, @SerialName("userBookId") diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt index 8b8e6261..4d06ede2 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt @@ -15,7 +15,7 @@ import com.ninecraft.booket.core.network.response.GuestBookSearchResponse import com.ninecraft.booket.core.network.response.HomeResponse import com.ninecraft.booket.core.network.response.LibraryResponse import com.ninecraft.booket.core.network.response.LoginResponse -import com.ninecraft.booket.core.network.response.ReadingRecordV2 +import com.ninecraft.booket.core.network.response.ReadingRecord import com.ninecraft.booket.core.network.response.ReadingRecordsResponse import com.ninecraft.booket.core.network.response.RefreshTokenResponse import com.ninecraft.booket.core.network.response.SeedResponse @@ -114,9 +114,9 @@ interface ReedService { suspend fun postRecord( @Path("userBookId") userBookId: String, @Body recordRegisterRequest: RecordRegisterRequest, - ): ReadingRecordV2 + ): ReadingRecord - @GET("api/v1/reading-records/{userBookId}") + @GET("api/v2/reading-records/{userBookId}") suspend fun getReadingRecords( @Path("userBookId") userBookId: String, @Query("sort") sort: String = "CREATED_DATE_DESC", @@ -132,13 +132,13 @@ interface ReedService { @GET("api/v2/reading-records/detail/{readingRecordId}") suspend fun getRecordDetail( @Path("readingRecordId") readingRecordId: String, - ): ReadingRecordV2 + ): ReadingRecord @PUT("api/v2/reading-records/{readingRecordId}") suspend fun editRecord( @Path("readingRecordId") readingRecordId: String, @Body recordRegisterRequest: RecordRegisterRequest, - ): ReadingRecordV2 + ): ReadingRecord @DELETE("api/v2/reading-records/{readingRecordId}") suspend fun deleteRecord( 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 25d44ccb..17aac677 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 @@ -70,7 +70,7 @@ class BookDetailPresenter( private fun getRecordComparator(sortType: RecordSort): Comparator { return when (sortType) { - RecordSort.PAGE_NUMBER_ASC -> compareBy { it.pageNumber } + RecordSort.PAGE_NUMBER_ASC -> compareBy(nullsLast()) { it.pageNumber } RecordSort.CREATED_DATE_DESC -> compareByDescending { LocalDateTime.parse(it.createdAt) } } } 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 83c7e25c..3133f9e3 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 @@ -38,6 +38,7 @@ import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.model.BookDetailModel import com.ninecraft.booket.core.model.Emotion import com.ninecraft.booket.core.model.EmotionModel +import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.InfinityLazyColumn @@ -418,7 +419,7 @@ private fun BookDetailSeedStatsPreview() { pageNumber = 42, quote = "새는 알에서 나오려고 투쟁한다. 알은 세계이다.", review = "정말 인상 깊은 구절이었다.", - emotionTags = listOf("깨달음", "따뜻함"), + primaryEmotion = PrimaryEmotionModel(displayName = "깨달음"), createdAt = "2024-01-15T10:30:00.000000", ), ReadingRecordModel( @@ -426,7 +427,7 @@ private fun BookDetailSeedStatsPreview() { pageNumber = 78, quote = "나는 더 이상 꿈을 꾸지 않으려 했다.", review = "성장통을 느끼는 부분", - emotionTags = listOf("슬픔"), + primaryEmotion = PrimaryEmotionModel(displayName = "슬픔"), createdAt = "2024-01-20T14:20:00.000000", ), ReadingRecordModel( @@ -434,7 +435,7 @@ private fun BookDetailSeedStatsPreview() { pageNumber = 156, quote = "운명과 성향은 같은 개념의 두 이름이다.", review = "내 삶을 돌아보게 되었다.", - emotionTags = listOf("깨달음", "즐거움"), + primaryEmotion = PrimaryEmotionModel(displayName = "깨달음"), createdAt = "2024-01-25T09:15:00.000000", ), ), @@ -472,7 +473,7 @@ private fun BookDetailSeedsStatsExpandedPreview() { pageNumber = 42, quote = "새는 알에서 나오려고 투쟁한다. 알은 세계이다.", review = "정말 인상 깊은 구절이었다.", - emotionTags = listOf("깨달음", "따뜻함"), + primaryEmotion = PrimaryEmotionModel(displayName = "깨달음"), createdAt = "2024-01-15T10:30:00.000000", ), ReadingRecordModel( @@ -480,7 +481,7 @@ private fun BookDetailSeedsStatsExpandedPreview() { pageNumber = 78, quote = "나는 더 이상 꿈을 꾸지 않으려 했다.", review = "성장통을 느끼는 부분", - emotionTags = listOf("슬픔"), + primaryEmotion = PrimaryEmotionModel(displayName = "슬픔"), createdAt = "2024-01-20T14:20:00.000000", ), ReadingRecordModel( @@ -488,7 +489,7 @@ private fun BookDetailSeedsStatsExpandedPreview() { pageNumber = 156, quote = "운명과 성향은 같은 개념의 두 이름이다.", review = "내 삶을 돌아보게 되었다.", - emotionTags = listOf("깨달음", "즐거움"), + primaryEmotion = PrimaryEmotionModel(displayName = "깨달음"), createdAt = "2024-01-25T09:15:00.000000", ), ), 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 a5f8f05e..31f551f8 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 @@ -24,9 +24,9 @@ import androidx.compose.ui.text.style.TextOverflow import com.ninecraft.booket.core.common.extensions.toFormattedDate import com.ninecraft.booket.core.designsystem.ComponentPreview import com.ninecraft.booket.core.designsystem.theme.ReedTheme +import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.feature.detail.R -import kotlinx.collections.immutable.persistentListOf import com.ninecraft.booket.core.designsystem.R as designR @Composable @@ -54,7 +54,7 @@ internal fun RecordItem( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = if (recordInfo.pageNumber != 0) "${recordInfo.pageNumber}p" + text = if (recordInfo.pageNumber != null) "${recordInfo.pageNumber}p" else "-p", color = ReedTheme.colors.contentBrand, style = ReedTheme.typography.label1Medium, @@ -86,7 +86,7 @@ internal fun RecordItem( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = "#${recordInfo.emotionTags[0]}", + text = "#${recordInfo.primaryEmotion.displayName}", color = ReedTheme.colors.contentTertiary, style = ReedTheme.typography.label1Medium, ) @@ -118,7 +118,7 @@ private fun RecordItemPreview() { RecordItem( recordInfo = ReadingRecordModel( quote = "소설가들은 늘 소재를 찾아 떠도는 존재 같지만, 실은 그 반대인 경우가 더 잦다.", - emotionTags = persistentListOf("따뜻함"), + primaryEmotion = PrimaryEmotionModel(displayName = "따뜻함"), pageNumber = 12, createdAt = "2025-06-25T10:30:00.000000", ), diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt index 1c0d3157..20e11826 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt @@ -8,7 +8,7 @@ import androidx.compose.runtime.setValue import com.ninecraft.booket.core.common.analytics.AnalyticsHelper import com.ninecraft.booket.core.common.utils.handleException import com.ninecraft.booket.core.data.api.repository.RecordRepository -import com.ninecraft.booket.core.model.ReadingRecordModelV2 +import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.RecordCardScreen import com.ninecraft.booket.feature.screens.RecordDetailScreen @@ -52,7 +52,7 @@ class RecordDetailPresenter( override fun present(): RecordDetailUiState { val scope = rememberCoroutineScope() var uiState by rememberRetained { mutableStateOf(UiState.Idle) } - var recordDetailInfo by rememberRetained { mutableStateOf(ReadingRecordModelV2()) } + var recordDetailInfo by rememberRetained { mutableStateOf(ReadingRecordModel()) } var isRecordMenuBottomSheetVisible by rememberRetained { mutableStateOf(false) } var isRecordDeleteDialogVisible by rememberRetained { mutableStateOf(false) } var sideEffect by rememberRetained { mutableStateOf(null) } diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt index e73ba02a..d3330310 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUi.kt @@ -19,7 +19,7 @@ import com.ninecraft.booket.core.designsystem.ComponentPreview import com.ninecraft.booket.core.designsystem.component.ReedDivider import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.White -import com.ninecraft.booket.core.model.ReadingRecordModelV2 +import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.ui.ReedScaffold import com.ninecraft.booket.core.ui.component.ReedDialog import com.ninecraft.booket.core.ui.component.ReedErrorUi @@ -189,7 +189,7 @@ private fun ReviewDetailPreview() { RecordDetailUi( state = RecordDetailUiState( uiState = UiState.Success, - recordDetailInfo = ReadingRecordModelV2( + recordDetailInfo = ReadingRecordModel( id = "", userBookId = "", pageNumber = 90, @@ -215,7 +215,7 @@ private fun ReviewDetailEmptyPreview() { RecordDetailUi( state = RecordDetailUiState( uiState = UiState.Success, - recordDetailInfo = ReadingRecordModelV2( + recordDetailInfo = ReadingRecordModel( id = "", userBookId = "", pageNumber = 90, diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUiState.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUiState.kt index 6a228b51..4d845ea2 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUiState.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailUiState.kt @@ -1,7 +1,7 @@ package com.ninecraft.booket.feature.detail.record import androidx.compose.runtime.Immutable -import com.ninecraft.booket.core.model.ReadingRecordModelV2 +import com.ninecraft.booket.core.model.ReadingRecordModel import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState import java.util.UUID @@ -16,7 +16,7 @@ sealed interface UiState { data class RecordDetailUiState( val uiState: UiState = UiState.Idle, - val recordDetailInfo: ReadingRecordModelV2 = ReadingRecordModelV2(), + val recordDetailInfo: ReadingRecordModel = ReadingRecordModel(), val isRecordMenuBottomSheetVisible: Boolean = false, val isRecordDeleteDialogVisible: Boolean = false, val sideEffect: RecordDetailSideEffect? = null, From d89ed4b5cf038dea451e806239a43d8c77f798b4 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Fri, 16 Jan 2026 15:34:59 +0900 Subject: [PATCH 02/10] =?UTF-8?q?[BOOK-490]=20feat:=20=EB=8F=85=EC=84=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=A1=9D=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20(V2)=20DTO=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 대표 감정을 representativeEmotion으로 내려주는 관계로, 네이티브 코드내에서 대표 감정을 선출하는 로직 제거 --- .../core/common/utils/EmotionAnalyzer.kt | 34 ---- .../data/api/repository/RecordRepository.kt | 2 +- .../core/data/impl/mapper/ResponseToModel.kt | 5 +- .../booket/core/model/ReadingRecordsModel.kt | 1 + .../response/ReadingRecordsResponse.kt | 2 + .../detail/book/BookDetailPresenter.kt | 4 + .../feature/detail/book/BookDetailUiState.kt | 2 + .../detail/book/component/CollectedSeeds.kt | 69 +++---- .../component/EmotionAnalysisResultText.kt | 172 ------------------ 9 files changed, 47 insertions(+), 244 deletions(-) delete mode 100644 core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/EmotionAnalyzer.kt delete mode 100644 feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/EmotionAnalysisResultText.kt diff --git a/core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/EmotionAnalyzer.kt b/core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/EmotionAnalyzer.kt deleted file mode 100644 index 06be275c..00000000 --- a/core/common/src/main/kotlin/com/ninecraft/booket/core/common/utils/EmotionAnalyzer.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.ninecraft.booket.core.common.utils - -import com.ninecraft.booket.core.model.EmotionModel - -data class EmotionAnalysisResult( - val topEmotions: List, - val displayType: EmotionDisplayType, -) - -enum class EmotionDisplayType { - NONE, // 모든 감정의 count가 0 - SINGLE, // 1개 감정이 1위 - DUAL, // 2개 감정이 공동 1위 - BALANCED, // 3개 이상 감정이 공동 1위 -} - -fun analyzeEmotions(emotions: List): EmotionAnalysisResult { - val maxCount = emotions.maxOf { it.count } - - // 모든 감정의 count가 0인 경우 - if (maxCount == 0) { - return EmotionAnalysisResult(emptyList(), EmotionDisplayType.NONE) - } - - val topEmotions = emotions.filter { it.count == maxCount } - - val displayType = when (topEmotions.size) { - 1 -> EmotionDisplayType.SINGLE - 2 -> EmotionDisplayType.DUAL - else -> EmotionDisplayType.BALANCED - } - - return EmotionAnalysisResult(topEmotions, displayType) -} diff --git a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt index 8d23e487..37064766 100644 --- a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt +++ b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt @@ -18,7 +18,7 @@ interface RecordRepository { sort: String, page: Int, size: Int, - ): Result // TODO: V2로 변경 필요 + ): Result suspend fun getRecordDetail( readingRecordId: String, diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt index defb3da4..1f9eb186 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt @@ -6,7 +6,6 @@ import com.ninecraft.booket.core.model.BookSearchModel import com.ninecraft.booket.core.model.BookSummaryModel import com.ninecraft.booket.core.model.BookUpsertModel import com.ninecraft.booket.core.model.DetailEmotionModel -import com.ninecraft.booket.core.model.Emotion import com.ninecraft.booket.core.model.EmotionCode import com.ninecraft.booket.core.model.EmotionGroupModel import com.ninecraft.booket.core.model.EmotionGroupsModel @@ -230,6 +229,7 @@ internal fun RecordRegisterResponse.toModel(): RecordRegisterModel { internal fun ReadingRecordsResponse.toModel(): ReadingRecordsModel { return ReadingRecordsModel( + representativeEmotion = representativeEmotion.toModel(), lastPage = lastPage, totalResults = totalResults, startIndex = startIndex, @@ -290,9 +290,8 @@ internal fun SeedResponse.toModel(): SeedModel { } internal fun Category.toEmotionModel(): EmotionModel? { - val emotion = Emotion.fromDisplayName(name) ?: return null return EmotionModel( - name = emotion, + code = EmotionCode.fromDisplayName(name) ?: return null, count = count, ) } diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt index 666fc5b3..e6ddc379 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt @@ -3,6 +3,7 @@ package com.ninecraft.booket.core.model import androidx.compose.runtime.Stable data class ReadingRecordsModel( + val representativeEmotion: PrimaryEmotionModel = PrimaryEmotionModel(), val lastPage: Boolean = true, val totalResults: Int = 0, val startIndex: Int = 0, diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt index 3cbb8cda..925ad9af 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt @@ -5,6 +5,8 @@ import kotlinx.serialization.Serializable @Serializable data class ReadingRecordsResponse( + @SerialName("representativeEmotion") + val representativeEmotion: PrimaryEmotion, @SerialName("lastPage") val lastPage: Boolean, @SerialName("totalResults") 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 17aac677..fa0f63c5 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 @@ -14,6 +14,7 @@ import com.ninecraft.booket.core.data.api.repository.RecordRepository import com.ninecraft.booket.core.model.BookDetailModel import com.ninecraft.booket.core.model.EmotionCode import com.ninecraft.booket.core.model.EmotionModel +import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.ui.component.FooterState import com.ninecraft.booket.feature.screens.BookDetailScreen @@ -81,6 +82,7 @@ class BookDetailPresenter( var uiState by rememberRetained { mutableStateOf(UiState.Idle) } var footerState by rememberRetained { mutableStateOf(FooterState.Idle) } var bookDetail by rememberRetained { mutableStateOf(BookDetailModel()) } + var representativeEmotion by rememberRetained { mutableStateOf(PrimaryEmotionModel()) } var seedsStates by rememberRetained { mutableStateOf>(persistentListOf()) } var isStatsExpanded by rememberRetained { mutableStateOf(false) } var readingRecords by rememberRetained { mutableStateOf(persistentListOf()) } @@ -122,6 +124,7 @@ class BookDetailPresenter( bookDetail = detail currentBookStatus = BookStatus.fromValue(detail.userBookStatus) ?: BookStatus.BEFORE_READING selectedBookStatus = currentBookStatus + representativeEmotion = records.representativeEmotion seedsStates = seeds.categories.toImmutableList() readingRecords = records.readingRecords.toPersistentList() readingRecordsTotalCount = records.totalResults @@ -421,6 +424,7 @@ class BookDetailPresenter( uiState = uiState, footerState = footerState, bookDetail = bookDetail, + representativeEmotion = representativeEmotion, seedsStats = seedsStates, isStatsExpanded = isStatsExpanded, readingRecords = readingRecords, 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 2d0246ce..fdfc4e61 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.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel import com.ninecraft.booket.core.ui.component.FooterState import com.slack.circuit.runtime.CircuitUiEvent @@ -26,6 +27,7 @@ data class BookDetailUiState( val footerState: FooterState = FooterState.Idle, val isLoading: Boolean = false, val bookDetail: BookDetailModel = BookDetailModel(), + val representativeEmotion: PrimaryEmotionModel = PrimaryEmotionModel(), val seedsStats: ImmutableList = persistentListOf(), val isStatsExpanded: Boolean = false, val readingRecords: ImmutableList = persistentListOf(), 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 67e6ac94..2802663c 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 @@ -22,7 +22,6 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -30,13 +29,14 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp -import com.ninecraft.booket.core.common.utils.analyzeEmotions import com.ninecraft.booket.core.designsystem.ComponentPreview +import com.ninecraft.booket.core.designsystem.graphicRes import com.ninecraft.booket.core.designsystem.ratioBarColor import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.designsystem.theme.Yellow700 -import com.ninecraft.booket.core.model.Emotion +import com.ninecraft.booket.core.model.EmotionCode import com.ninecraft.booket.core.model.EmotionModel +import com.ninecraft.booket.core.model.PrimaryEmotionModel import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import com.ninecraft.booket.core.designsystem.R as designR @@ -44,13 +44,11 @@ import com.ninecraft.booket.core.designsystem.R as designR @Composable internal fun CollectedSeeds( seedsStats: ImmutableList, + representativeEmotion: PrimaryEmotionModel, isStatsExpanded: Boolean, onToggleClick: () -> Unit, modifier: Modifier = Modifier, ) { - val analysisResult = remember(seedsStats) { analyzeEmotions(seedsStats) } - val topEmotion = analysisResult.topEmotions.firstOrNull() - Column( modifier = modifier .fillMaxWidth() @@ -65,7 +63,7 @@ internal fun CollectedSeeds( .padding(ReedTheme.spacing.spacing4), ) { CollectedSeedsHeader( - topEmotion = topEmotion, + primaryEmotion = representativeEmotion, isStatsExpanded = isStatsExpanded, onToggleClick = onToggleClick, ) @@ -91,9 +89,9 @@ internal fun CollectedSeeds( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(ReedTheme.spacing.spacing1), ) { - Emotion.entries.forEach { emotion -> - val emotionModel = seedsStats.find { it.name == emotion } - ?: EmotionModel(emotion, 0) + EmotionCode.entries.forEach { emotionCode -> + val emotionModel = seedsStats.find { it.code == emotionCode } + ?: EmotionModel(emotionCode, 0) EmotionStatCard( emotion = emotionModel, modifier = Modifier.weight(1f), @@ -107,7 +105,7 @@ internal fun CollectedSeeds( @Composable private fun CollectedSeedsHeader( - topEmotion: EmotionModel?, + primaryEmotion: PrimaryEmotionModel, isStatsExpanded: Boolean, onToggleClick: () -> Unit, modifier: Modifier = Modifier, @@ -122,9 +120,9 @@ private fun CollectedSeedsHeader( Row( verticalAlignment = Alignment.CenterVertically, ) { - topEmotion?.let { emotion -> + primaryEmotion?.let { emotion -> Image( - painter = painterResource(id = getEmotionImageResourceByDisplayName(emotion.name.displayName)), + painter = painterResource(id = emotion.code.graphicRes), contentDescription = "Seed Image", modifier = Modifier .size(36.dp) @@ -136,7 +134,7 @@ private fun CollectedSeedsHeader( Row { Text( - text = "'${topEmotion?.name?.displayName ?: ""}'", + text = "'${primaryEmotion?.code?.displayName ?: ""}'", color = Yellow700, style = ReedTheme.typography.label1SemiBold, ) @@ -173,8 +171,8 @@ private fun EmotionRatioBar( .height(12.dp) .clip(RoundedCornerShape(ReedTheme.radius.full)), ) { - Emotion.entries.forEach { emotion -> - val emotionModel = seedsStats.find { it.name == emotion } + EmotionCode.entries.forEach { emotionCode -> + val emotionModel = seedsStats.find { it.code == emotionCode } val count = emotionModel?.count ?: 0 if (count > 0) { val weight = count.toFloat() / totalCount @@ -182,7 +180,7 @@ private fun EmotionRatioBar( modifier = Modifier .weight(weight) .height(12.dp) - .background(emotion.ratioBarColor), + .background(emotionCode.ratioBarColor), ) } } @@ -208,13 +206,13 @@ private fun EmotionStatCard( modifier = Modifier .size(10.dp) .clip(RoundedCornerShape(ReedTheme.radius.xs)) - .background(emotion.name.ratioBarColor), + .background(emotion.code.ratioBarColor), ) Spacer(modifier = Modifier.height(ReedTheme.spacing.spacing2)) Text( - text = emotion.name.displayName, + text = emotion.code.displayName, color = ReedTheme.colors.contentSecondary, style = ReedTheme.typography.label2Regular, ) @@ -233,12 +231,13 @@ private fun CollectedSeedsCollapsedPreview() { ReedTheme { CollectedSeeds( seedsStats = persistentListOf( - EmotionModel(Emotion.WARM, 4), - EmotionModel(Emotion.JOY, 2), - EmotionModel(Emotion.SAD, 2), - EmotionModel(Emotion.INSIGHT, 2), - EmotionModel(Emotion.ETC, 2), + EmotionModel(EmotionCode.WARMTH, 4), + EmotionModel(EmotionCode.JOY, 2), + EmotionModel(EmotionCode.SADNESS, 2), + EmotionModel(EmotionCode.INSIGHT, 2), + EmotionModel(EmotionCode.OTHER, 2), ), + representativeEmotion = PrimaryEmotionModel(EmotionCode.WARMTH, "기쁨"), isStatsExpanded = false, onToggleClick = {}, ) @@ -251,12 +250,13 @@ private fun CollectedSeedsExpandedPreview() { ReedTheme { CollectedSeeds( seedsStats = persistentListOf( - EmotionModel(Emotion.WARM, 4), - EmotionModel(Emotion.JOY, 2), - EmotionModel(Emotion.SAD, 2), - EmotionModel(Emotion.INSIGHT, 2), - EmotionModel(Emotion.ETC, 2), + EmotionModel(EmotionCode.WARMTH, 4), + EmotionModel(EmotionCode.JOY, 2), + EmotionModel(EmotionCode.SADNESS, 2), + EmotionModel(EmotionCode.INSIGHT, 2), + EmotionModel(EmotionCode.OTHER, 2), ), + representativeEmotion = PrimaryEmotionModel(EmotionCode.WARMTH, "기쁨"), isStatsExpanded = true, onToggleClick = {}, ) @@ -269,12 +269,13 @@ private fun CollectedSeedsExpandedDuplicatedPreview() { ReedTheme { CollectedSeeds( seedsStats = persistentListOf( - EmotionModel(Emotion.WARM, 4), - EmotionModel(Emotion.JOY, 4), - EmotionModel(Emotion.SAD, 2), - EmotionModel(Emotion.INSIGHT, 2), - EmotionModel(Emotion.ETC, 2), + EmotionModel(EmotionCode.WARMTH, 4), + EmotionModel(EmotionCode.JOY, 4), + EmotionModel(EmotionCode.SADNESS, 2), + EmotionModel(EmotionCode.INSIGHT, 2), + EmotionModel(EmotionCode.OTHER, 2), ), + representativeEmotion = PrimaryEmotionModel(EmotionCode.WARMTH, "기쁨"), isStatsExpanded = true, onToggleClick = {}, ) diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/EmotionAnalysisResultText.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/EmotionAnalysisResultText.kt deleted file mode 100644 index a0cd832b..00000000 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/EmotionAnalysisResultText.kt +++ /dev/null @@ -1,172 +0,0 @@ -package com.ninecraft.booket.feature.detail.book.component - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp -import com.ninecraft.booket.core.common.utils.EmotionDisplayType -import com.ninecraft.booket.core.common.utils.analyzeEmotions -import com.ninecraft.booket.core.designsystem.ComponentPreview -import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.model.Emotion -import com.ninecraft.booket.core.model.EmotionModel -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf - -@Composable -internal fun EmotionAnalysisResultText( - emotions: ImmutableList, - brandColor: Color, - secondaryColor: Color, - emotionTextStyle: TextStyle, - regularTextStyle: TextStyle, -): AnnotatedString? { - val analysisResult = remember(emotions) { analyzeEmotions(emotions) } - - return when (analysisResult.displayType) { - EmotionDisplayType.NONE -> null - - EmotionDisplayType.SINGLE -> { - val emotion = analysisResult.topEmotions.first() - buildAnnotatedString { - withStyle(SpanStyle(color = secondaryColor, fontSize = regularTextStyle.fontSize, fontWeight = regularTextStyle.fontWeight)) { - append("이 책에서 ") - } - withStyle(SpanStyle(color = brandColor, fontSize = emotionTextStyle.fontSize, fontWeight = emotionTextStyle.fontWeight)) { - append(emotion.name.displayName) - } - withStyle(SpanStyle(color = secondaryColor, fontSize = regularTextStyle.fontSize, fontWeight = regularTextStyle.fontWeight)) { - append(" 감정을 많이 느꼈어요") - } - } - } - - EmotionDisplayType.DUAL -> { - val emotions = analysisResult.topEmotions - buildAnnotatedString { - withStyle(SpanStyle(color = secondaryColor, fontSize = regularTextStyle.fontSize, fontWeight = regularTextStyle.fontWeight)) { - append("이 책에서 ") - } - emotions.forEachIndexed { index, emotion -> - if (index > 0) { - withStyle(SpanStyle(color = secondaryColor, fontSize = regularTextStyle.fontSize, fontWeight = regularTextStyle.fontWeight)) { - append(", ") - } - } - withStyle(SpanStyle(color = brandColor, fontSize = emotionTextStyle.fontSize, fontWeight = emotionTextStyle.fontWeight)) { - append(emotion.name.displayName) - } - } - withStyle(SpanStyle(color = secondaryColor, fontSize = regularTextStyle.fontSize, fontWeight = regularTextStyle.fontWeight)) { - append(" 감정을 많이 느꼈어요") - } - } - } - - EmotionDisplayType.BALANCED -> { - buildAnnotatedString { - withStyle(SpanStyle(color = secondaryColor, fontSize = regularTextStyle.fontSize, fontWeight = regularTextStyle.fontWeight)) { - append("이 책에서 ") - } - withStyle(SpanStyle(color = brandColor, fontSize = emotionTextStyle.fontSize, fontWeight = emotionTextStyle.fontWeight)) { - append("여러 감정이 고르게 담겼어요") - } - } - } - } -} - -@ComponentPreview -@Composable -private fun EmotionTextAllCasesPreview() { - ReedTheme { - Column(modifier = Modifier.padding(16.dp)) { - Text(text = "1개의 감정이 1위인 경우:") - EmotionAnalysisResultText( - emotions = persistentListOf( - EmotionModel(name = Emotion.WARM, count = 5), - EmotionModel(name = Emotion.JOY, count = 2), - ), - brandColor = ReedTheme.colors.contentBrand, - secondaryColor = ReedTheme.colors.contentSecondary, - emotionTextStyle = ReedTheme.typography.label2SemiBold, - regularTextStyle = ReedTheme.typography.label2Regular, - )?.let { annotatedString -> - Text( - text = annotatedString, - modifier = Modifier.padding(vertical = 8.dp), - ) - } - Spacer(modifier = Modifier.height(16.dp)) - Text(text = "2개의 감정이 공동 1위인 경우:") - EmotionAnalysisResultText( - emotions = persistentListOf( - EmotionModel(name = Emotion.WARM, count = 5), - EmotionModel(name = Emotion.JOY, count = 5), - EmotionModel(name = Emotion.SAD, count = 2), - ), - brandColor = ReedTheme.colors.contentBrand, - secondaryColor = ReedTheme.colors.contentSecondary, - emotionTextStyle = ReedTheme.typography.label2SemiBold, - regularTextStyle = ReedTheme.typography.label2Regular, - )?.let { annotatedString -> - Text( - text = annotatedString, - modifier = Modifier.padding(vertical = 8.dp), - ) - } - Spacer(modifier = Modifier.height(16.dp)) - Text(text = "3~4개의 감정이 공동 1위인 경우:") - EmotionAnalysisResultText( - emotions = persistentListOf( - EmotionModel(name = Emotion.WARM, count = 3), - EmotionModel(name = Emotion.JOY, count = 3), - EmotionModel(name = Emotion.SAD, count = 3), - EmotionModel(name = Emotion.INSIGHT, count = 3), - ), - brandColor = ReedTheme.colors.contentBrand, - secondaryColor = ReedTheme.colors.contentSecondary, - emotionTextStyle = ReedTheme.typography.label2SemiBold, - regularTextStyle = ReedTheme.typography.label2Regular, - )?.let { annotatedString -> - Text( - text = annotatedString, - modifier = Modifier.padding(vertical = 8.dp), - ) - } - Spacer(modifier = Modifier.height(16.dp)) - Text(text = "모든 감정의 count가 0인 경우:") - EmotionAnalysisResultText( - emotions = persistentListOf( - EmotionModel(name = Emotion.WARM, count = 0), - EmotionModel(name = Emotion.JOY, count = 0), - EmotionModel(name = Emotion.SAD, count = 0), - ), - brandColor = ReedTheme.colors.contentBrand, - secondaryColor = ReedTheme.colors.contentSecondary, - emotionTextStyle = ReedTheme.typography.label2SemiBold, - regularTextStyle = ReedTheme.typography.label2Regular, - )?.let { annotatedString -> - Text( - text = annotatedString, - modifier = Modifier.padding(vertical = 8.dp), - ) - } ?: Text( - text = "null 반환 - 표시되지 않음", - modifier = Modifier.padding(vertical = 8.dp), - color = ReedTheme.colors.contentSecondary, - ) - } - } -} From a278f6a939d9b198a345b8933966c55f598fa43b Mon Sep 17 00:00:00 2001 From: easyhooon Date: Fri, 16 Jan 2026 15:38:24 +0900 Subject: [PATCH 03/10] =?UTF-8?q?[BOOK-490]=20refactor:=20EmotionModel,=20?= =?UTF-8?q?EmotionCode=20=20->=20EmotionCode=EB=A1=9C=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 각 Emotion 네이밍 warmth, joy, sadness, insight, other로 통일(리소스 네이밍 수정) --- .../{Emotion.kt => EmotionCode.kt} | 41 +++++++++--------- .../booket/core/designsystem/theme/Color.kt | 4 +- .../booket/core/model/EmotionModel.kt | 15 ++++++- .../ninecraft/booket/core/model/SeedModel.kt | 19 +------- .../feature/detail/book/BookDetailUi.kt | 19 ++++---- .../detail/book/component/CollectedSeeds.kt | 4 +- .../detail/book/component/RecordItem.kt | 11 ----- .../feature/detail/book/component/SeedItem.kt | 24 +++------- .../detail/card/component/RecordCard.kt | 4 +- .../detail/src/main/res/drawable/img_etc.webp | Bin 7194 -> 0 bytes .../src/main/res/drawable/img_insight.webp | Bin 7648 -> 0 bytes .../detail/src/main/res/drawable/img_joy.webp | Bin 7640 -> 0 bytes ..._sad.webp => img_record_card_sadness.webp} | Bin ..._warm.webp => img_record_card_warmth.webp} | Bin .../detail/src/main/res/drawable/img_sad.webp | Bin 7508 -> 0 bytes .../src/main/res/drawable/img_warm.webp | Bin 7686 -> 0 bytes 16 files changed, 57 insertions(+), 84 deletions(-) rename core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/{Emotion.kt => EmotionCode.kt} (71%) delete mode 100644 feature/detail/src/main/res/drawable/img_etc.webp delete mode 100644 feature/detail/src/main/res/drawable/img_insight.webp delete mode 100644 feature/detail/src/main/res/drawable/img_joy.webp rename feature/detail/src/main/res/drawable/{img_record_card_sad.webp => img_record_card_sadness.webp} (100%) rename feature/detail/src/main/res/drawable/{img_record_card_warm.webp => img_record_card_warmth.webp} (100%) delete mode 100644 feature/detail/src/main/res/drawable/img_sad.webp delete mode 100644 feature/detail/src/main/res/drawable/img_warm.webp diff --git a/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/Emotion.kt b/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/EmotionCode.kt similarity index 71% rename from core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/Emotion.kt rename to core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/EmotionCode.kt index ef46f7c1..d071f8da 100644 --- a/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/Emotion.kt +++ b/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/EmotionCode.kt @@ -2,8 +2,8 @@ package com.ninecraft.booket.core.designsystem import androidx.compose.ui.graphics.Color import com.ninecraft.booket.core.designsystem.theme.Blue300 -import com.ninecraft.booket.core.designsystem.theme.EtcBgColor -import com.ninecraft.booket.core.designsystem.theme.EtcTextColor +import com.ninecraft.booket.core.designsystem.theme.OtherBgColor +import com.ninecraft.booket.core.designsystem.theme.OtherTextColor import com.ninecraft.booket.core.designsystem.theme.InsightBgColor import com.ninecraft.booket.core.designsystem.theme.InsightTextColor import com.ninecraft.booket.core.designsystem.theme.JoyBgColor @@ -16,34 +16,33 @@ import com.ninecraft.booket.core.designsystem.theme.Violet300 import com.ninecraft.booket.core.designsystem.theme.WarmthBgColor import com.ninecraft.booket.core.designsystem.theme.WarmthTextColor import com.ninecraft.booket.core.designsystem.theme.Yellow300 -import com.ninecraft.booket.core.model.Emotion import com.ninecraft.booket.core.model.EmotionCode -val Emotion.bgColor: Color +val EmotionCode.bgColor: Color get() = when (this) { - Emotion.WARM -> WarmthBgColor - Emotion.JOY -> JoyBgColor - Emotion.SAD -> SadnessBgColor - Emotion.INSIGHT -> InsightBgColor - Emotion.ETC -> EtcBgColor + EmotionCode.WARMTH -> WarmthBgColor + EmotionCode.JOY -> JoyBgColor + EmotionCode.SADNESS -> SadnessBgColor + EmotionCode.INSIGHT -> InsightBgColor + EmotionCode.OTHER -> OtherBgColor } -val Emotion.textColor: Color +val EmotionCode.textColor: Color get() = when (this) { - Emotion.WARM -> WarmthTextColor - Emotion.JOY -> JoyTextColor - Emotion.SAD -> SadnessTextColor - Emotion.INSIGHT -> InsightTextColor - Emotion.ETC -> EtcTextColor + EmotionCode.WARMTH -> WarmthTextColor + EmotionCode.JOY -> JoyTextColor + EmotionCode.SADNESS -> SadnessTextColor + EmotionCode.INSIGHT -> InsightTextColor + EmotionCode.OTHER -> OtherTextColor } -val Emotion.ratioBarColor: Color +val EmotionCode.ratioBarColor: Color get() = when (this) { - Emotion.WARM -> Yellow300 - Emotion.JOY -> Orange300 - Emotion.SAD -> Blue300 - Emotion.INSIGHT -> Violet300 - Emotion.ETC -> Neutral300 + EmotionCode.WARMTH -> Yellow300 + EmotionCode.JOY -> Orange300 + EmotionCode.SADNESS -> Blue300 + EmotionCode.INSIGHT -> Violet300 + EmotionCode.OTHER -> Neutral300 } val EmotionCode.graphicRes: Int diff --git a/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt b/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt index e3b2588f..e3ee5e09 100644 --- a/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt +++ b/core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/theme/Color.kt @@ -98,8 +98,8 @@ val InsightTextColor = Color(0xFF9A55E4) val InsightBgColor = Color(0xFFF3E8FF) val SadnessTextColor = Color(0xFF2872E9) val SadnessBgColor = Color(0xFFE1ECFF) -val EtcTextColor = Color(0xFF737373) -val EtcBgColor = Color(0xFFF5F5F5) +val OtherTextColor = Color(0xFF737373) +val OtherBgColor = Color(0xFFF5F5F5) @Immutable data class ReedColorScheme( diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt index 762852e3..4d80a0a6 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt @@ -20,12 +20,23 @@ data class DetailEmotionModel( val name: String, ) -enum class EmotionCode { - WARMTH, JOY, SADNESS, INSIGHT, OTHER; +enum class EmotionCode( + val displayName: String, +) { + WARMTH("따뜻함"), + JOY("즐거움"), + SADNESS("슬픔"), + INSIGHT("깨달음"), + OTHER("기타"), + ; companion object { fun fromCode(code: String): EmotionCode? { return EmotionCode.entries.find { it.name == code } } + + fun fromDisplayName(displayName: String): EmotionCode? { + return entries.find { it.displayName == displayName } + } } } diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt index 24ee2845..d6cb1f95 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt @@ -9,23 +9,6 @@ data class SeedModel( @Stable data class EmotionModel( - val name: Emotion, + val code: EmotionCode, val count: Int, ) - -enum class Emotion( - val displayName: String, -) { - WARM("따뜻함"), - JOY("즐거움"), - SAD("슬픔"), - INSIGHT("깨달음"), - ETC("기타"), - ; - - companion object { - fun fromDisplayName(displayName: String): Emotion? { - return entries.find { it.displayName == displayName } - } - } -} 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 3133f9e3..f1fa1a97 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 @@ -36,7 +36,7 @@ import com.ninecraft.booket.core.designsystem.component.button.ReedButtonColorSt import com.ninecraft.booket.core.designsystem.component.button.mediumButtonStyle import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.model.BookDetailModel -import com.ninecraft.booket.core.model.Emotion +import com.ninecraft.booket.core.model.EmotionCode import com.ninecraft.booket.core.model.EmotionModel import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel @@ -286,6 +286,7 @@ internal fun BookDetailContent( if (state.hasEmotionData()) { CollectedSeeds( seedsStats = state.seedsStats, + representativeEmotion = state.representativeEmotion, isStatsExpanded = state.isStatsExpanded, onToggleClick = { state.eventSink(BookDetailUiEvent.OnStatsToggleClick(!state.isStatsExpanded)) @@ -408,10 +409,10 @@ private fun BookDetailSeedStatsPreview() { coverImageUrl = "", ), seedsStats = persistentListOf( - EmotionModel(name = Emotion.WARM, count = 5), - EmotionModel(name = Emotion.JOY, count = 3), - EmotionModel(name = Emotion.SAD, count = 2), - EmotionModel(name = Emotion.INSIGHT, count = 7), + EmotionModel(code = EmotionCode.WARMTH, count = 5), + EmotionModel(code = EmotionCode.JOY, count = 3), + EmotionModel(code = EmotionCode.SADNESS, count = 2), + EmotionModel(code = EmotionCode.INSIGHT, count = 7), ), readingRecords = persistentListOf( ReadingRecordModel( @@ -461,10 +462,10 @@ private fun BookDetailSeedsStatsExpandedPreview() { coverImageUrl = "", ), seedsStats = persistentListOf( - EmotionModel(name = Emotion.WARM, count = 5), - EmotionModel(name = Emotion.JOY, count = 3), - EmotionModel(name = Emotion.SAD, count = 2), - EmotionModel(name = Emotion.INSIGHT, count = 7), + EmotionModel(code = EmotionCode.WARMTH, count = 5), + EmotionModel(code = EmotionCode.JOY, count = 3), + EmotionModel(code = EmotionCode.SADNESS, count = 2), + EmotionModel(code = EmotionCode.INSIGHT, count = 7), ), isStatsExpanded = true, readingRecords = persistentListOf( 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 2802663c..53bed2b9 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 @@ -120,7 +120,7 @@ private fun CollectedSeedsHeader( Row( verticalAlignment = Alignment.CenterVertically, ) { - primaryEmotion?.let { emotion -> + primaryEmotion.let { emotion -> Image( painter = painterResource(id = emotion.code.graphicRes), contentDescription = "Seed Image", @@ -134,7 +134,7 @@ private fun CollectedSeedsHeader( Row { Text( - text = "'${primaryEmotion?.code?.displayName ?: ""}'", + text = "'${primaryEmotion.code.displayName}'", color = Yellow700, style = ReedTheme.typography.label1SemiBold, ) 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 31f551f8..3e510292 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 @@ -100,17 +100,6 @@ internal fun RecordItem( } } -fun getEmotionImageResourceByDisplayName(displayName: String): Int { - return when (displayName) { - "따뜻함" -> R.drawable.img_warm - "즐거움" -> R.drawable.img_joy - "슬픔" -> R.drawable.img_sad - "깨달음" -> R.drawable.img_insight - "기타" -> R.drawable.img_etc - else -> R.drawable.img_warm - } -} - @ComponentPreview @Composable private fun RecordItemPreview() { diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt index b35e4965..3f422be1 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/book/component/SeedItem.kt @@ -18,11 +18,11 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.ninecraft.booket.core.designsystem.ComponentPreview import com.ninecraft.booket.core.designsystem.bgColor +import com.ninecraft.booket.core.designsystem.graphicRes import com.ninecraft.booket.core.designsystem.textColor import com.ninecraft.booket.core.designsystem.theme.ReedTheme -import com.ninecraft.booket.core.model.Emotion +import com.ninecraft.booket.core.model.EmotionCode import com.ninecraft.booket.core.model.EmotionModel -import com.ninecraft.booket.feature.detail.R @Composable internal fun SeedItem( @@ -34,7 +34,7 @@ internal fun SeedItem( horizontalAlignment = Alignment.CenterHorizontally, ) { Image( - painter = painterResource(id = getEmotionImageResource(emotion.name)), + painter = painterResource(id = emotion.code.graphicRes), contentDescription = "Seed Graphic Image", modifier = Modifier.size(50.dp), ) @@ -42,7 +42,7 @@ internal fun SeedItem( Box( modifier = Modifier .clip(RoundedCornerShape(ReedTheme.radius.full)) - .background(emotion.name.bgColor) + .background(emotion.code.bgColor) .padding( horizontal = ReedTheme.spacing.spacing2, vertical = ReedTheme.spacing.spacing1, @@ -50,8 +50,8 @@ internal fun SeedItem( contentAlignment = Alignment.Center, ) { Text( - text = emotion.name.displayName, - color = emotion.name.textColor, + text = emotion.code.displayName, + color = emotion.code.textColor, style = ReedTheme.typography.label2SemiBold, ) } @@ -64,23 +64,13 @@ internal fun SeedItem( } } -private fun getEmotionImageResource(emotion: Emotion): Int { - return when (emotion) { - Emotion.WARM -> R.drawable.img_warm - Emotion.JOY -> R.drawable.img_joy - Emotion.SAD -> R.drawable.img_sad - Emotion.INSIGHT -> R.drawable.img_insight - Emotion.ETC -> R.drawable.img_etc - } -} - @ComponentPreview @Composable private fun SeedItemPreview() { ReedTheme { SeedItem( emotion = EmotionModel( - name = Emotion.WARM, + code = EmotionCode.WARMTH, count = 3, ), ) diff --git a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/card/component/RecordCard.kt b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/card/component/RecordCard.kt index 3b64a480..82303602 100644 --- a/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/card/component/RecordCard.kt +++ b/feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/card/component/RecordCard.kt @@ -76,9 +76,9 @@ internal fun RecordCard( private fun getEmotionCardImage(emotionCode: EmotionCode): Int { return when (emotionCode) { - EmotionCode.WARMTH -> R.drawable.img_record_card_warm + EmotionCode.WARMTH -> R.drawable.img_record_card_warmth EmotionCode.JOY -> R.drawable.img_record_card_joy - EmotionCode.SADNESS -> R.drawable.img_record_card_sad + EmotionCode.SADNESS -> R.drawable.img_record_card_sadness EmotionCode.INSIGHT -> R.drawable.img_record_card_insight EmotionCode.OTHER -> R.drawable.img_record_card_other } diff --git a/feature/detail/src/main/res/drawable/img_etc.webp b/feature/detail/src/main/res/drawable/img_etc.webp deleted file mode 100644 index 0672c24317eb43a8cb17e4e11ca9cf01b9dc94b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7194 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 diff --git a/feature/detail/src/main/res/drawable/img_insight.webp b/feature/detail/src/main/res/drawable/img_insight.webp deleted file mode 100644 index b86a84417cbab281bd0111f80833dbc2e98ec93c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7648 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 A@Bjb+ From aece95cf551b225d0aaf139b4c1a980afbe1c950 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Fri, 16 Jan 2026 16:00:29 +0900 Subject: [PATCH 04/10] =?UTF-8?q?[BOOK-490]=20refactor:=20Domain=20Model?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=93=A4=20@Stable=20->=20@Immut?= =?UTF-8?q?able=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ninecraft/booket/core/model/EmotionModel.kt | 8 ++++---- .../ninecraft/booket/core/model/ReadingRecordsModel.kt | 7 ++++--- .../kotlin/com/ninecraft/booket/core/model/SeedModel.kt | 6 +++--- .../booket/feature/detail/book/component/RecordItem.kt | 1 - .../booket/feature/screens/arguments/RecordEditArgs.kt | 2 ++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt index 4d80a0a6..a0c084c8 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/EmotionModel.kt @@ -1,20 +1,20 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable -@Stable +@Immutable data class EmotionGroupsModel( val emotions: List, ) -@Stable +@Immutable data class EmotionGroupModel( val code: EmotionCode, val displayName: String, val detailEmotions: List, ) -@Stable +@Immutable data class DetailEmotionModel( val id: String, val name: String, diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt index e6ddc379..4c06fc93 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt @@ -1,7 +1,8 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable +@Immutable data class ReadingRecordsModel( val representativeEmotion: PrimaryEmotionModel = PrimaryEmotionModel(), val lastPage: Boolean = true, @@ -11,7 +12,7 @@ data class ReadingRecordsModel( val readingRecords: List = emptyList(), ) -@Stable +@Immutable data class ReadingRecordModel( val id: String = "", val userBookId: String = "", @@ -28,7 +29,7 @@ data class ReadingRecordModel( val author: String = "", ) -@Stable +@Immutable data class PrimaryEmotionModel( val code: EmotionCode = EmotionCode.OTHER, val displayName: String = "기타", diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt index d6cb1f95..db102d40 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/SeedModel.kt @@ -1,13 +1,13 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable -@Stable +@Immutable data class SeedModel( val categories: List = emptyList(), ) -@Stable +@Immutable data class EmotionModel( val code: EmotionCode, val count: Int, 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 3e510292..04a90ee4 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 @@ -26,7 +26,6 @@ import com.ninecraft.booket.core.designsystem.ComponentPreview import com.ninecraft.booket.core.designsystem.theme.ReedTheme import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel -import com.ninecraft.booket.feature.detail.R import com.ninecraft.booket.core.designsystem.R as designR @Composable diff --git a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt index cf18cad5..0ac61764 100644 --- a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt +++ b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt @@ -3,6 +3,8 @@ package com.ninecraft.booket.feature.screens.arguments import android.os.Parcelable import androidx.compose.runtime.Immutable import com.ninecraft.booket.core.model.EmotionCode +import com.ninecraft.booket.core.model.PrimaryEmotionModel +import com.ninecraft.booket.core.model.DetailEmotionModel import kotlinx.parcelize.Parcelize @Immutable From 793c6428ad2c7493de8eaa5713acc7704ea8fa01 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Fri, 16 Jan 2026 16:41:00 +0900 Subject: [PATCH 05/10] [BOOK-490] chore: code style check success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용하지 않는 dto 클래스 제거 --- .../booket/core/model/RecordDetailModel.kt | 19 ----------- .../network/response/RecordDetailResponse.kt | 32 ------------------- .../detail/book/BookDetailPresenter.kt | 13 ++++---- .../screens/arguments/RecordEditArgs.kt | 2 -- 4 files changed, 7 insertions(+), 59 deletions(-) delete mode 100644 core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordDetailModel.kt delete mode 100644 core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordDetailResponse.kt diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordDetailModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordDetailModel.kt deleted file mode 100644 index 19e49633..00000000 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordDetailModel.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.ninecraft.booket.core.model - -import androidx.compose.runtime.Stable - -@Stable -data class RecordDetailModel( - val id: String = "", - val userBookId: String = "", - val pageNumber: Int = 0, - val quote: String = "", - val review: String = "", - val emotionTags: List = emptyList(), - val createdAt: String = "", - val updatedAt: String = "", - val bookTitle: String = "", - val bookPublisher: String = "", - val bookCoverImageUrl: String = "", - val author: String = "", -) diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordDetailResponse.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordDetailResponse.kt deleted file mode 100644 index c78a219b..00000000 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordDetailResponse.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.ninecraft.booket.core.network.response - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class RecordDetailResponse( - @SerialName("id") - val id: String, - @SerialName("userBookId") - val userBookId: String, - @SerialName("pageNumber") - val pageNumber: Int, - @SerialName("quote") - val quote: String, - @SerialName("review") - val review: String?, - @SerialName("emotionTags") - val emotionTags: List, - @SerialName("createdAt") - val createdAt: String, - @SerialName("updatedAt") - val updatedAt: String, - @SerialName("bookTitle") - val bookTitle: String, - @SerialName("bookPublisher") - val bookPublisher: String, - @SerialName("bookCoverImageUrl") - val bookCoverImageUrl: String, - @SerialName("author") - val author: String, -) 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 fa0f63c5..8813a618 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 @@ -12,7 +12,6 @@ import com.ninecraft.booket.core.common.utils.handleException 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.EmotionCode import com.ninecraft.booket.core.model.EmotionModel import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel @@ -321,7 +320,7 @@ class BookDetailPresenter( RecordCardScreen( quote = selectedRecordInfo.quote, bookTitle = selectedRecordInfo.bookTitle, - emotionCode = EmotionCode.OTHER, // TODO: 고정값 임시 조치 + emotionCode = selectedRecordInfo.primaryEmotion.code, ), ) } @@ -336,10 +335,12 @@ class BookDetailPresenter( quote = selectedRecordInfo.quote, review = selectedRecordInfo.review, primaryEmotion = PrimaryEmotionArg( - code = EmotionCode.OTHER, - displayName = "기타", - ), // TODO: 고정값 임시 조치 - detailEmotions = listOf(DetailEmotionArg("", "")), // TODO: 고정값 임시 조치 + code = selectedRecordInfo.primaryEmotion.code, + displayName = selectedRecordInfo.primaryEmotion.displayName, + ), + detailEmotions = selectedRecordInfo.detailEmotions.map { + DetailEmotionArg(id = it.id, name = it.name) + }, bookTitle = selectedRecordInfo.bookTitle, bookPublisher = selectedRecordInfo.bookPublisher, bookCoverImageUrl = selectedRecordInfo.bookCoverImageUrl, diff --git a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt index 0ac61764..cf18cad5 100644 --- a/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt +++ b/feature/screens/src/main/kotlin/com/ninecraft/booket/feature/screens/arguments/RecordEditArgs.kt @@ -3,8 +3,6 @@ package com.ninecraft.booket.feature.screens.arguments import android.os.Parcelable import androidx.compose.runtime.Immutable import com.ninecraft.booket.core.model.EmotionCode -import com.ninecraft.booket.core.model.PrimaryEmotionModel -import com.ninecraft.booket.core.model.DetailEmotionModel import kotlinx.parcelize.Parcelize @Immutable From 1f62d87649aa6c70571b3a3788294e6d07ac62e0 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Fri, 16 Jan 2026 17:34:33 +0900 Subject: [PATCH 06/10] =?UTF-8?q?[BOOK-490]=20chore:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B2=8C=EB=90=9C=20registerResp?= =?UTF-8?q?onse=20DTO=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/data/impl/mapper/ResponseToModel.kt | 28 ------------------- .../response/RecordRegisterResponse.kt | 24 ---------------- 2 files changed, 52 deletions(-) delete mode 100644 core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordRegisterResponse.kt diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt index 1f9eb186..c3ac2220 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt @@ -17,9 +17,7 @@ import com.ninecraft.booket.core.model.LibraryModel import com.ninecraft.booket.core.model.PageInfoModel import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel -import com.ninecraft.booket.core.model.ReadingRecordsModel import com.ninecraft.booket.core.model.RecentBookModel -import com.ninecraft.booket.core.model.RecordRegisterModel import com.ninecraft.booket.core.model.SeedModel import com.ninecraft.booket.core.model.TermsAgreementModel import com.ninecraft.booket.core.model.UserProfileModel @@ -40,9 +38,7 @@ import com.ninecraft.booket.core.network.response.LibraryResponse import com.ninecraft.booket.core.network.response.PageInfo import com.ninecraft.booket.core.network.response.PrimaryEmotion import com.ninecraft.booket.core.network.response.ReadingRecord -import com.ninecraft.booket.core.network.response.ReadingRecordsResponse import com.ninecraft.booket.core.network.response.RecentBook -import com.ninecraft.booket.core.network.response.RecordRegisterResponse import com.ninecraft.booket.core.network.response.SeedResponse import com.ninecraft.booket.core.network.response.TermsAgreementResponse import com.ninecraft.booket.core.network.response.UserProfileResponse @@ -214,30 +210,6 @@ internal fun DetailEmotion.toModel(): DetailEmotionModel { ) } -internal fun RecordRegisterResponse.toModel(): RecordRegisterModel { - return RecordRegisterModel( - id = id, - userBookId = userBookId, - pageNumber = pageNumber, - quote = quote, - emotionTags = emotionTags, - review = review ?: "", - createdAt = createdAt, - updatedAt = updatedAt, - ) -} - -internal fun ReadingRecordsResponse.toModel(): ReadingRecordsModel { - return ReadingRecordsModel( - representativeEmotion = representativeEmotion.toModel(), - lastPage = lastPage, - totalResults = totalResults, - startIndex = startIndex, - itemsPerPage = itemsPerPage, - readingRecords = readingRecords.map { it.toModel() }, - ) -} - internal fun ReadingRecord.toModel(): ReadingRecordModel { return ReadingRecordModel( id = id, diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordRegisterResponse.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordRegisterResponse.kt deleted file mode 100644 index a6a4c2b5..00000000 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/RecordRegisterResponse.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.ninecraft.booket.core.network.response - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class RecordRegisterResponse( - @SerialName("id") - val id: String, - @SerialName("userBookId") - val userBookId: String, - @SerialName("pageNumber") - val pageNumber: Int, - @SerialName("quote") - val quote: String, - @SerialName("emotionTags") - val emotionTags: List, - @SerialName("review") - val review: String?, - @SerialName("createdAt") - val createdAt: String, - @SerialName("updatedAt") - val updatedAt: String, -) From 9779ef2db26ebb4742f945c631c8a1a715136d3a Mon Sep 17 00:00:00 2001 From: easyhooon Date: Sun, 18 Jan 2026 12:29:46 +0900 Subject: [PATCH 07/10] [BOOK-490] chore: run build success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 누락된 model 모듈 @Stable -> @Immutable 추가 적용 --- .../core/data/impl/mapper/ResponseToModel.kt | 13 ++ .../impl/repository/DefaultAuthRepository.kt | 2 +- .../datasource/DefaultOnboardingDataSource.kt | 4 +- .../stability/designsystem.stability | 37 ----- .../booket/core/model/AutoLoginState.kt | 3 + .../booket/core/model/BookDetailModel.kt | 4 +- .../booket/core/model/BookSearchModel.kt | 6 +- .../ninecraft/booket/core/model/HomeModel.kt | 6 +- .../booket/core/model/LibraryModel.kt | 10 +- .../booket/core/model/OnboardingState.kt | 3 + .../booket/core/model/RecordRegisterModel.kt | 12 -- .../ninecraft/booket/core/model/UserState.kt | 3 + feature/detail/stability/detail.stability | 18 +-- feature/home/stability/home.stability | 57 -------- feature/library/stability/library.stability | 69 ---------- feature/login/stability/login.stability | 28 ++-- feature/record/stability/record.stability | 22 ++- feature/search/stability/search.stability | 126 ------------------ feature/settings/stability/settings.stability | 114 ---------------- feature/splash/stability/splash.stability | 16 --- feature/webview/stability/webview.stability | 14 +- 21 files changed, 62 insertions(+), 505 deletions(-) delete mode 100644 core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordRegisterModel.kt diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt index c3ac2220..20d4e11c 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt @@ -17,6 +17,7 @@ import com.ninecraft.booket.core.model.LibraryModel import com.ninecraft.booket.core.model.PageInfoModel import com.ninecraft.booket.core.model.PrimaryEmotionModel import com.ninecraft.booket.core.model.ReadingRecordModel +import com.ninecraft.booket.core.model.ReadingRecordsModel import com.ninecraft.booket.core.model.RecentBookModel import com.ninecraft.booket.core.model.SeedModel import com.ninecraft.booket.core.model.TermsAgreementModel @@ -38,6 +39,7 @@ import com.ninecraft.booket.core.network.response.LibraryResponse import com.ninecraft.booket.core.network.response.PageInfo import com.ninecraft.booket.core.network.response.PrimaryEmotion import com.ninecraft.booket.core.network.response.ReadingRecord +import com.ninecraft.booket.core.network.response.ReadingRecordsResponse import com.ninecraft.booket.core.network.response.RecentBook import com.ninecraft.booket.core.network.response.SeedResponse import com.ninecraft.booket.core.network.response.TermsAgreementResponse @@ -210,6 +212,17 @@ internal fun DetailEmotion.toModel(): DetailEmotionModel { ) } +internal fun ReadingRecordsResponse.toModel(): ReadingRecordsModel { + return ReadingRecordsModel( + representativeEmotion = representativeEmotion.toModel(), + lastPage = lastPage, + totalResults = totalResults, + startIndex = startIndex, + itemsPerPage = itemsPerPage, + readingRecords = readingRecords.map { it.toModel() }, + ) +} + internal fun ReadingRecord.toModel(): ReadingRecordModel { return ReadingRecordModel( id = id, diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt index 4172d9e0..92d2ab04 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt @@ -3,12 +3,12 @@ package com.ninecraft.booket.core.data.impl.repository import com.ninecraft.booket.core.common.utils.runSuspendCatching import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.datastore.api.datasource.TokenDataSource +import com.ninecraft.booket.core.di.DataScope import com.ninecraft.booket.core.model.AutoLoginState import com.ninecraft.booket.core.model.UserState import com.ninecraft.booket.core.network.request.LoginRequest import com.ninecraft.booket.core.network.service.ReedService import dev.zacsweers.metro.Inject -import com.ninecraft.booket.core.di.DataScope import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.map diff --git a/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt b/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt index c4c4dfd2..dd5e3ad0 100644 --- a/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt +++ b/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt @@ -5,11 +5,11 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import com.ninecraft.booket.core.datastore.api.datasource.OnboardingDataSource -import com.ninecraft.booket.core.model.OnboardingState import com.ninecraft.booket.core.datastore.impl.di.OnboardingDataStore import com.ninecraft.booket.core.datastore.impl.util.handleIOException -import dev.zacsweers.metro.Inject import com.ninecraft.booket.core.di.DataScope +import com.ninecraft.booket.core.model.OnboardingState +import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/core/designsystem/stability/designsystem.stability b/core/designsystem/stability/designsystem.stability index 7a2aa49f..a44f1681 100644 --- a/core/designsystem/stability/designsystem.stability +++ b/core/designsystem/stability/designsystem.stability @@ -232,40 +232,3 @@ public fun com.ninecraft.booket.core.designsystem.component.textfield.ReedTextFi - borderStroke: STABLE (marked @Stable or @Immutable) - searchIconTint: STABLE (marked @Stable or @Immutable) -@Composable -public fun com.ninecraft.booket.core.designsystem.theme.ReedTheme(content: @[Composable] androidx.compose.runtime.internal.ComposableFunction0): kotlin.Unit - skippable: true - restartable: true - params: - - content: STABLE (composable function type) - -@Composable -public fun com.ninecraft.booket.core.designsystem.theme.ReedTheme.border(): com.ninecraft.booket.core.designsystem.theme.ReedBorder - skippable: true - restartable: true - params: - -@Composable -public fun com.ninecraft.booket.core.designsystem.theme.ReedTheme.colors(): com.ninecraft.booket.core.designsystem.theme.ReedColorScheme - skippable: true - restartable: true - params: - -@Composable -public fun com.ninecraft.booket.core.designsystem.theme.ReedTheme.radius(): com.ninecraft.booket.core.designsystem.theme.ReedRadius - skippable: true - restartable: true - params: - -@Composable -public fun com.ninecraft.booket.core.designsystem.theme.ReedTheme.spacing(): com.ninecraft.booket.core.designsystem.theme.ReedSpacing - skippable: true - restartable: true - params: - -@Composable -public fun com.ninecraft.booket.core.designsystem.theme.ReedTheme.typography(): com.ninecraft.booket.core.designsystem.theme.ReedTypography - skippable: true - restartable: true - params: - diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt index fead4e57..00ece077 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt @@ -1,5 +1,8 @@ package com.ninecraft.booket.core.model +import androidx.compose.runtime.Stable + +@Stable enum class AutoLoginState { IDLE, LOGGED_IN, diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookDetailModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookDetailModel.kt index c398445a..0fd424a2 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookDetailModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookDetailModel.kt @@ -1,8 +1,8 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable -@Stable +@Immutable data class BookDetailModel( val version: String = "", val title: String = "", diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookSearchModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookSearchModel.kt index f1c8b05c..9e25a219 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookSearchModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/BookSearchModel.kt @@ -1,8 +1,8 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable -@Stable +@Immutable data class BookSearchModel( val version: String = "", val title: String = "", @@ -17,7 +17,7 @@ data class BookSearchModel( val books: List = emptyList(), ) -@Stable +@Immutable data class BookSummaryModel( val isbn13: String = "", val title: String = "", diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/HomeModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/HomeModel.kt index 7d55b27b..2970d4a3 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/HomeModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/HomeModel.kt @@ -1,13 +1,13 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable -@Stable +@Immutable data class HomeModel( val recentBooks: List = emptyList(), ) -@Stable +@Immutable data class RecentBookModel( val userBookId: String = "", val isbn13: String = "", diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/LibraryModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/LibraryModel.kt index 39dad91a..9bb4b94b 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/LibraryModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/LibraryModel.kt @@ -1,8 +1,8 @@ package com.ninecraft.booket.core.model -import androidx.compose.runtime.Stable +import androidx.compose.runtime.Immutable -@Stable +@Immutable data class LibraryModel( val books: LibraryBooksModel = LibraryBooksModel(), val totalCount: Int = 0, @@ -11,13 +11,13 @@ data class LibraryModel( val completedCount: Int = 0, ) -@Stable +@Immutable data class LibraryBooksModel( val content: List = emptyList(), val page: PageInfoModel = PageInfoModel(), ) -@Stable +@Immutable data class LibraryBookSummaryModel( val userBookId: String = "", val userId: String = "", @@ -32,7 +32,7 @@ data class LibraryBookSummaryModel( val updatedAt: String = "", ) -@Stable +@Immutable data class PageInfoModel( val size: Int = 0, val number: Int = 0, diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt index 1d478929..540042b3 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt @@ -1,5 +1,8 @@ package com.ninecraft.booket.core.model +import androidx.compose.runtime.Stable + +@Stable enum class OnboardingState { IDLE, NOT_COMPLETED, diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordRegisterModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordRegisterModel.kt deleted file mode 100644 index c60ac203..00000000 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/RecordRegisterModel.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.ninecraft.booket.core.model - -data class RecordRegisterModel( - val id: String = "", - val userBookId: String = "", - val pageNumber: Int = 0, - val quote: String = "", - val emotionTags: List = emptyList(), - val review: String = "", - val createdAt: String = "", - val updatedAt: String = "", -) diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt index 43ec0824..08bb7edb 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt @@ -1,5 +1,8 @@ package com.ninecraft.booket.core.model +import androidx.compose.runtime.Stable + +@Stable sealed interface UserState { data object Guest : UserState data object LoggedIn : UserState diff --git a/feature/detail/stability/detail.stability b/feature/detail/stability/detail.stability index 1722293b..3d59a3b1 100644 --- a/feature/detail/stability/detail.stability +++ b/feature/detail/stability/detail.stability @@ -70,21 +70,22 @@ internal fun com.ninecraft.booket.feature.detail.book.component.BookUpdateBottom - modifier: STABLE (marked @Stable or @Immutable) @Composable -internal fun com.ninecraft.booket.feature.detail.book.component.CollectedSeeds(seedsStats: kotlinx.collections.immutable.ImmutableList, isStatsExpanded: kotlin.Boolean, onToggleClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit +internal fun com.ninecraft.booket.feature.detail.book.component.CollectedSeeds(seedsStats: kotlinx.collections.immutable.ImmutableList, representativeEmotion: com.ninecraft.booket.core.model.PrimaryEmotionModel, isStatsExpanded: kotlin.Boolean, onToggleClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit skippable: true restartable: true params: - seedsStats: STABLE (known stable type) + - representativeEmotion: STABLE (marked @Stable or @Immutable) - isStatsExpanded: STABLE (primitive type) - onToggleClick: STABLE (function type) - modifier: STABLE (marked @Stable or @Immutable) @Composable -private fun com.ninecraft.booket.feature.detail.book.component.CollectedSeedsHeader(topEmotion: com.ninecraft.booket.core.model.EmotionModel?, isStatsExpanded: kotlin.Boolean, onToggleClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit +private fun com.ninecraft.booket.feature.detail.book.component.CollectedSeedsHeader(primaryEmotion: com.ninecraft.booket.core.model.PrimaryEmotionModel, isStatsExpanded: kotlin.Boolean, onToggleClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit skippable: true restartable: true params: - - topEmotion: STABLE (marked @Stable or @Immutable) + - primaryEmotion: STABLE (marked @Stable or @Immutable) - isStatsExpanded: STABLE (primitive type) - onToggleClick: STABLE (function type) - modifier: STABLE (marked @Stable or @Immutable) @@ -111,17 +112,6 @@ private fun com.ninecraft.booket.feature.detail.book.component.DetailMenuItem(ic - onClick: STABLE (function type) - modifier: STABLE (marked @Stable or @Immutable) -@Composable -internal fun com.ninecraft.booket.feature.detail.book.component.EmotionAnalysisResultText(emotions: kotlinx.collections.immutable.ImmutableList, brandColor: androidx.compose.ui.graphics.Color, secondaryColor: androidx.compose.ui.graphics.Color, emotionTextStyle: androidx.compose.ui.text.TextStyle, regularTextStyle: androidx.compose.ui.text.TextStyle): androidx.compose.ui.text.AnnotatedString? - skippable: true - restartable: true - params: - - emotions: STABLE (known stable type) - - brandColor: STABLE (marked @Stable or @Immutable) - - secondaryColor: STABLE (marked @Stable or @Immutable) - - emotionTextStyle: STABLE (marked @Stable or @Immutable) - - regularTextStyle: STABLE (marked @Stable or @Immutable) - @Composable private fun com.ninecraft.booket.feature.detail.book.component.EmotionRatioBar(seedsStats: kotlinx.collections.immutable.ImmutableList, modifier: androidx.compose.ui.Modifier): kotlin.Unit skippable: true diff --git a/feature/home/stability/home.stability b/feature/home/stability/home.stability index 7c83f45e..3df12376 100644 --- a/feature/home/stability/home.stability +++ b/feature/home/stability/home.stability @@ -4,66 +4,9 @@ // Do not edit this file directly. To update it, run: // ./gradlew :home:stabilityDump -@Composable -internal fun com.ninecraft.booket.feature.home.HandleHomeSideEffects(state: com.ninecraft.booket.feature.home.HomeUiState): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - -@Composable -internal fun com.ninecraft.booket.feature.home.HomeContent(state: com.ninecraft.booket.feature.home.HomeUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - @Composable public fun com.ninecraft.booket.feature.home.HomePresenter.present(): com.ninecraft.booket.feature.home.HomeUiState skippable: true restartable: true params: -@Composable -internal fun com.ninecraft.booket.feature.home.HomeUi(state: com.ninecraft.booket.feature.home.HomeUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.home.component.BookCard(recentBookInfo: com.ninecraft.booket.core.model.RecentBookModel, onBookDetailClick: kotlin.Function0, onRecordButtonClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - recentBookInfo: STABLE (marked @Stable or @Immutable) - - onBookDetailClick: STABLE (function type) - - onRecordButtonClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.home.component.EmptyBookCard(onBookRegisterClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onBookRegisterClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.home.component.HomeBanner(onBookRegisterClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onBookRegisterClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.home.component.HomeHeader(onSettingsClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onSettingsClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - diff --git a/feature/library/stability/library.stability b/feature/library/stability/library.stability index 1138df11..e3d809a3 100644 --- a/feature/library/stability/library.stability +++ b/feature/library/stability/library.stability @@ -4,78 +4,9 @@ // Do not edit this file directly. To update it, run: // ./gradlew :library:stabilityDump -@Composable -private fun com.ninecraft.booket.feature.library.EmptyResult(): kotlin.Unit - skippable: true - restartable: true - params: - -@Composable -internal fun com.ninecraft.booket.feature.library.HandleLibrarySideEffects(state: com.ninecraft.booket.feature.library.LibraryUiState, eventSink: kotlin.Function1): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - eventSink: STABLE (function type) - -@Composable -internal fun com.ninecraft.booket.feature.library.LibraryContent(state: com.ninecraft.booket.feature.library.LibraryUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - @Composable public fun com.ninecraft.booket.feature.library.LibraryPresenter.present(): com.ninecraft.booket.feature.library.LibraryUiState skippable: true restartable: true params: -@Composable -internal fun com.ninecraft.booket.feature.library.LibraryUi(state: com.ninecraft.booket.feature.library.LibraryUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.library.component.FilterChip(option: com.ninecraft.booket.feature.library.LibraryFilterOption, count: kotlin.Int, isSelected: kotlin.Boolean, onChipClick: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - option: STABLE (class with no mutable properties) - - count: STABLE (primitive type) - - isSelected: STABLE (primitive type) - - onChipClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.library.component.FilterChipGroup(filterList: kotlinx.collections.immutable.ImmutableList, selectedChipOption: com.ninecraft.booket.feature.library.LibraryFilterOption, onChipClick: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - filterList: STABLE (known stable type) - - selectedChipOption: STABLE (class with no mutable properties) - - onChipClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.library.component.LibraryBookItem(book: com.ninecraft.booket.core.model.LibraryBookSummaryModel, onBookClick: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - book: STABLE (marked @Stable or @Immutable) - - onBookClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.library.component.LibraryHeader(onSearchClick: kotlin.Function0, onSettingClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onSearchClick: STABLE (function type) - - onSettingClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - diff --git a/feature/login/stability/login.stability b/feature/login/stability/login.stability index 005d619f..58cad8b4 100644 --- a/feature/login/stability/login.stability +++ b/feature/login/stability/login.stability @@ -6,45 +6,33 @@ @Composable internal fun com.ninecraft.booket.feature.login.HandleLoginSideEffects(state: com.ninecraft.booket.feature.login.LoginUiState, eventSink: kotlin.Function1): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - eventSink: STABLE (function type) -@Composable -public fun com.ninecraft.booket.feature.login.LoginPresenter.present(): com.ninecraft.booket.feature.login.LoginUiState - skippable: true - restartable: true - params: - @Composable internal fun com.ninecraft.booket.feature.login.LoginUi(state: com.ninecraft.booket.feature.login.LoginUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - modifier: STABLE (marked @Stable or @Immutable) @Composable internal fun com.ninecraft.booket.feature.termsagreement.HandleTermsAgreementSideEffects(state: com.ninecraft.booket.feature.termsagreement.TermsAgreementUiState): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - -@Composable -public fun com.ninecraft.booket.feature.termsagreement.TermsAgreementPresenter.present(): com.ninecraft.booket.feature.termsagreement.TermsAgreementUiState - skippable: true + skippable: false restartable: true params: + - state: UNSTABLE (has mutable properties or unstable members) @Composable internal fun com.ninecraft.booket.feature.termsagreement.TermsAgreementUi(state: com.ninecraft.booket.feature.termsagreement.TermsAgreementUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - modifier: STABLE (marked @Stable or @Immutable) @Composable diff --git a/feature/record/stability/record.stability b/feature/record/stability/record.stability index 498b04db..953962fc 100644 --- a/feature/record/stability/record.stability +++ b/feature/record/stability/record.stability @@ -6,39 +6,33 @@ @Composable private fun com.ninecraft.booket.feature.record.ocr.CameraPreview(state: com.ninecraft.booket.feature.record.ocr.OcrUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - modifier: STABLE (marked @Stable or @Immutable) @Composable internal fun com.ninecraft.booket.feature.record.ocr.HandleOcrSideEffects(state: com.ninecraft.booket.feature.record.ocr.OcrUiState): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - -@Composable -public fun com.ninecraft.booket.feature.record.ocr.OcrPresenter.present(): com.ninecraft.booket.feature.record.ocr.OcrUiState - skippable: true + skippable: false restartable: true params: + - state: UNSTABLE (has mutable properties or unstable members) @Composable internal fun com.ninecraft.booket.feature.record.ocr.OcrUi(state: com.ninecraft.booket.feature.record.ocr.OcrUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - modifier: STABLE (marked @Stable or @Immutable) @Composable private fun com.ninecraft.booket.feature.record.ocr.TextScanResult(state: com.ninecraft.booket.feature.record.ocr.OcrUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - modifier: STABLE (marked @Stable or @Immutable) @Composable diff --git a/feature/search/stability/search.stability b/feature/search/stability/search.stability index 89be1232..1eff2c36 100644 --- a/feature/search/stability/search.stability +++ b/feature/search/stability/search.stability @@ -4,135 +4,9 @@ // Do not edit this file directly. To update it, run: // ./gradlew :search:stabilityDump -@Composable -internal fun com.ninecraft.booket.feature.search.book.BookSearchContent(state: com.ninecraft.booket.feature.search.book.BookSearchUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - @Composable public fun com.ninecraft.booket.feature.search.book.BookSearchPresenter.present(): com.ninecraft.booket.feature.search.book.BookSearchUiState skippable: true restartable: true params: -@Composable -internal fun com.ninecraft.booket.feature.search.book.BookSearchUi(state: com.ninecraft.booket.feature.search.book.BookSearchUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -internal fun com.ninecraft.booket.feature.search.book.HandleBookSearchSideEffects(state: com.ninecraft.booket.feature.search.book.BookSearchUiState, eventSink: kotlin.Function1): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - eventSink: STABLE (function type) - -@Composable -public fun com.ninecraft.booket.feature.search.book.component.BookItem(book: com.ninecraft.booket.core.model.BookSummaryModel, onBookClick: kotlin.Function1, modifier: androidx.compose.ui.Modifier, enabled: kotlin.Boolean): kotlin.Unit - skippable: true - restartable: true - params: - - book: STABLE (marked @Stable or @Immutable) - - onBookClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - - enabled: STABLE (primitive type) - -@Composable -public fun com.ninecraft.booket.feature.search.book.component.BookRegisterBottomSheet(onDismissRequest: kotlin.Function0, sheetState: androidx.compose.material3.SheetState, onCloseButtonClick: kotlin.Function0, bookStatuses: kotlinx.collections.immutable.ImmutableList, currentBookStatus: com.ninecraft.booket.core.common.constants.BookStatus?, onItemSelected: kotlin.Function1, onBookRegisterButtonClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onDismissRequest: STABLE (function type) - - sheetState: STABLE (marked @Stable or @Immutable) - - onCloseButtonClick: STABLE (function type) - - bookStatuses: STABLE (known stable type) - - currentBookStatus: STABLE (class with no mutable properties) - - onItemSelected: STABLE (function type) - - onBookRegisterButtonClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.search.book.component.BookRegisterSuccessBottomSheet(onDismissRequest: kotlin.Function0, sheetState: androidx.compose.material3.SheetState, upsertedBookStatus: com.ninecraft.booket.core.common.constants.BookStatus, onCancelButtonClick: kotlin.Function0, onOKButtonClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onDismissRequest: STABLE (function type) - - sheetState: STABLE (marked @Stable or @Immutable) - - upsertedBookStatus: STABLE (class with no mutable properties) - - onCancelButtonClick: STABLE (function type) - - onOKButtonClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.search.book.component.BookStatusItem(item: com.ninecraft.booket.core.common.constants.BookStatus, selected: kotlin.Boolean, onClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - item: STABLE (class with no mutable properties) - - selected: STABLE (primitive type) - - onClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -internal fun com.ninecraft.booket.feature.search.common.component.RecentSearchTitle(modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.search.common.component.SearchItem(query: kotlin.String, onQueryClick: kotlin.Function1, onDeleteIconClick: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - query: STABLE (String is immutable) - - onQueryClick: STABLE (function type) - - onDeleteIconClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -internal fun com.ninecraft.booket.feature.search.library.HandlingLibrarySearchSideEffect(state: com.ninecraft.booket.feature.search.library.LibrarySearchUiState): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - -@Composable -internal fun com.ninecraft.booket.feature.search.library.LibrarySearchContent(state: com.ninecraft.booket.feature.search.library.LibrarySearchUiState, innerPadding: androidx.compose.foundation.layout.PaddingValues, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - innerPadding: STABLE (marked @Stable or @Immutable) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.search.library.LibrarySearchPresenter.present(): com.ninecraft.booket.feature.search.library.LibrarySearchUiState - skippable: true - restartable: true - params: - -@Composable -internal fun com.ninecraft.booket.feature.search.library.LibrarySearchUi(state: com.ninecraft.booket.feature.search.library.LibrarySearchUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.search.library.component.LibraryBookItem(book: com.ninecraft.booket.core.model.LibraryBookSummaryModel, onBookClick: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - book: STABLE (marked @Stable or @Immutable) - - onBookClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - diff --git a/feature/settings/stability/settings.stability b/feature/settings/stability/settings.stability index 8fdafda6..849b8b18 100644 --- a/feature/settings/stability/settings.stability +++ b/feature/settings/stability/settings.stability @@ -4,123 +4,9 @@ // Do not edit this file directly. To update it, run: // ./gradlew :settings:stabilityDump -@Composable -internal fun com.ninecraft.booket.feature.settings.HandleSettingsSideEffects(state: com.ninecraft.booket.feature.settings.SettingsUiState, eventSink: kotlin.Function1): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - eventSink: STABLE (function type) - @Composable public fun com.ninecraft.booket.feature.settings.SettingsPresenter.present(): com.ninecraft.booket.feature.settings.SettingsUiState skippable: true restartable: true params: -@Composable -internal fun com.ninecraft.booket.feature.settings.SettingsUi(state: com.ninecraft.booket.feature.settings.SettingsUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -internal fun com.ninecraft.booket.feature.settings.component.ReedSwitch(checked: kotlin.Boolean, onCheckedChange: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - checked: STABLE (primitive type) - - onCheckedChange: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -internal fun com.ninecraft.booket.feature.settings.component.SettingItem(title: kotlin.String, modifier: androidx.compose.ui.Modifier, isClickable: kotlin.Boolean, onItemClick: kotlin.Function0, action: @[Composable] androidx.compose.runtime.internal.ComposableFunction0, description: @[Composable] androidx.compose.runtime.internal.ComposableFunction0): kotlin.Unit - skippable: true - restartable: true - params: - - title: STABLE (String is immutable) - - modifier: STABLE (marked @Stable or @Immutable) - - isClickable: STABLE (primitive type) - - onItemClick: STABLE (function type) - - action: STABLE (composable function type) - - description: STABLE (composable function type) - -@Composable -internal fun com.ninecraft.booket.feature.settings.component.ToggleItem(title: kotlin.String, description: kotlin.String, isChecked: kotlin.Boolean, onCheckedChange: kotlin.Function1, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - title: STABLE (String is immutable) - - description: STABLE (String is immutable) - - isChecked: STABLE (primitive type) - - onCheckedChange: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.settings.component.WithdrawConfirmationBottomSheet(onDismissRequest: kotlin.Function0, sheetState: androidx.compose.material3.SheetState, isCheckBoxChecked: kotlin.Boolean, onCheckBoxCheckedChange: kotlin.Function0, onCancelButtonClick: kotlin.Function0, onWithdrawButtonClick: kotlin.Function0): kotlin.Unit - skippable: true - restartable: true - params: - - onDismissRequest: STABLE (function type) - - sheetState: STABLE (marked @Stable or @Immutable) - - isCheckBoxChecked: STABLE (primitive type) - - onCheckBoxCheckedChange: STABLE (function type) - - onCancelButtonClick: STABLE (function type) - - onWithdrawButtonClick: STABLE (function type) - -@Composable -internal fun com.ninecraft.booket.feature.settings.notification.HandleNotificationSideEffects(state: com.ninecraft.booket.feature.settings.notification.NotificationUiState, eventSink: kotlin.Function1): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - eventSink: STABLE (function type) - -@Composable -internal fun com.ninecraft.booket.feature.settings.notification.NotificationGuideItem(onClick: kotlin.Function0, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - onClick: STABLE (function type) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.settings.notification.NotificationPresenter.present(): com.ninecraft.booket.feature.settings.notification.NotificationUiState - skippable: true - restartable: true - params: - -@Composable -internal fun com.ninecraft.booket.feature.settings.notification.NotificationUi(state: com.ninecraft.booket.feature.settings.notification.NotificationUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -private fun com.ninecraft.booket.feature.settings.osslicenses.OssLicenseItem(name: kotlin.String, license: kotlin.String, url: kotlin.String, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - name: STABLE (String is immutable) - - license: STABLE (String is immutable) - - url: STABLE (String is immutable) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -internal fun com.ninecraft.booket.feature.settings.osslicenses.OssLicenses(state: com.ninecraft.booket.feature.settings.osslicenses.OssLicensesUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - -@Composable -public fun com.ninecraft.booket.feature.settings.osslicenses.OssLicensesPresenter.present(): com.ninecraft.booket.feature.settings.osslicenses.OssLicensesUiState - skippable: true - restartable: true - params: - diff --git a/feature/splash/stability/splash.stability b/feature/splash/stability/splash.stability index 0791787f..c82c8721 100644 --- a/feature/splash/stability/splash.stability +++ b/feature/splash/stability/splash.stability @@ -4,25 +4,9 @@ // Do not edit this file directly. To update it, run: // ./gradlew :splash:stabilityDump -@Composable -internal fun com.ninecraft.booket.splash.HandleSplashSideEffects(state: com.ninecraft.booket.splash.SplashUiState, eventSink: kotlin.Function1): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - eventSink: STABLE (function type) - @Composable public fun com.ninecraft.booket.splash.SplashPresenter.present(): com.ninecraft.booket.splash.SplashUiState skippable: true restartable: true params: -@Composable -public fun com.ninecraft.booket.splash.SplashUi(state: com.ninecraft.booket.splash.SplashUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true - restartable: true - params: - - state: STABLE (class with no mutable properties) - - modifier: STABLE (marked @Stable or @Immutable) - diff --git a/feature/webview/stability/webview.stability b/feature/webview/stability/webview.stability index 5a8b8867..dbfdc6b0 100644 --- a/feature/webview/stability/webview.stability +++ b/feature/webview/stability/webview.stability @@ -6,24 +6,18 @@ @Composable internal fun com.ninecraft.booket.feature.webview.WebViewContent(state: com.ninecraft.booket.feature.webview.WebViewUiState, innerPadding: androidx.compose.foundation.layout.PaddingValues, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - innerPadding: STABLE (marked @Stable or @Immutable) - modifier: STABLE (marked @Stable or @Immutable) -@Composable -public fun com.ninecraft.booket.feature.webview.WebViewPresenter.present(): com.ninecraft.booket.feature.webview.WebViewUiState - skippable: true - restartable: true - params: - @Composable internal fun com.ninecraft.booket.feature.webview.WebViewUi(state: com.ninecraft.booket.feature.webview.WebViewUiState, modifier: androidx.compose.ui.Modifier): kotlin.Unit - skippable: true + skippable: false restartable: true params: - - state: STABLE (class with no mutable properties) + - state: UNSTABLE (has mutable properties or unstable members) - modifier: STABLE (marked @Stable or @Immutable) From 149d18eca409f3512e316545419766c176998755 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Sun, 18 Jan 2026 12:30:42 +0900 Subject: [PATCH 08/10] =?UTF-8?q?[BOOK-490]=20chore:=20model=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EB=82=B4=20state=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=93=A4=20=EB=B3=84=EB=8F=84=20state=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booket/core/data/api/repository/AuthRepository.kt | 4 ++-- .../booket/core/data/api/repository/UserRepository.kt | 2 +- .../booket/core/data/impl/repository/DefaultAuthRepository.kt | 4 ++-- .../core/datastore/api/datasource/OnboardingDataSource.kt | 2 +- .../datastore/impl/datasource/DefaultOnboardingDataSource.kt | 2 +- .../ninecraft/booket/core/model/{ => state}/AutoLoginState.kt | 2 +- .../booket/core/model/{ => state}/OnboardingState.kt | 2 +- .../com/ninecraft/booket/core/model/{ => state}/UserState.kt | 2 +- .../kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt | 2 +- .../com/ninecraft/booket/feature/library/LibraryPresenter.kt | 2 +- .../booket/feature/search/book/BookSearchPresenter.kt | 2 +- .../ninecraft/booket/feature/settings/SettingsPresenter.kt | 2 +- .../kotlin/com/ninecraft/booket/splash/SplashPresenter.kt | 4 ++-- 13 files changed, 16 insertions(+), 16 deletions(-) rename core/model/src/main/kotlin/com/ninecraft/booket/core/model/{ => state}/AutoLoginState.kt (72%) rename core/model/src/main/kotlin/com/ninecraft/booket/core/model/{ => state}/OnboardingState.kt (72%) rename core/model/src/main/kotlin/com/ninecraft/booket/core/model/{ => state}/UserState.kt (76%) diff --git a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/AuthRepository.kt b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/AuthRepository.kt index c8943668..c1ca3cb5 100644 --- a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/AuthRepository.kt +++ b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/AuthRepository.kt @@ -1,7 +1,7 @@ package com.ninecraft.booket.core.data.api.repository -import com.ninecraft.booket.core.model.AutoLoginState -import com.ninecraft.booket.core.model.UserState +import com.ninecraft.booket.core.model.state.AutoLoginState +import com.ninecraft.booket.core.model.state.UserState import kotlinx.coroutines.flow.Flow interface AuthRepository { diff --git a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt index 20618cc1..9ea1524c 100644 --- a/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt +++ b/core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/UserRepository.kt @@ -1,6 +1,6 @@ package com.ninecraft.booket.core.data.api.repository -import com.ninecraft.booket.core.model.OnboardingState +import com.ninecraft.booket.core.model.state.OnboardingState import com.ninecraft.booket.core.model.TermsAgreementModel import com.ninecraft.booket.core.model.UserProfileModel import kotlinx.coroutines.flow.Flow diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt index 92d2ab04..9f8fe63b 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultAuthRepository.kt @@ -4,8 +4,8 @@ import com.ninecraft.booket.core.common.utils.runSuspendCatching import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.datastore.api.datasource.TokenDataSource import com.ninecraft.booket.core.di.DataScope -import com.ninecraft.booket.core.model.AutoLoginState -import com.ninecraft.booket.core.model.UserState +import com.ninecraft.booket.core.model.state.AutoLoginState +import com.ninecraft.booket.core.model.state.UserState import com.ninecraft.booket.core.network.request.LoginRequest import com.ninecraft.booket.core.network.service.ReedService import dev.zacsweers.metro.Inject diff --git a/core/datastore/api/src/main/kotlin/com/ninecraft/booket/core/datastore/api/datasource/OnboardingDataSource.kt b/core/datastore/api/src/main/kotlin/com/ninecraft/booket/core/datastore/api/datasource/OnboardingDataSource.kt index e43f92c7..dfff6f75 100644 --- a/core/datastore/api/src/main/kotlin/com/ninecraft/booket/core/datastore/api/datasource/OnboardingDataSource.kt +++ b/core/datastore/api/src/main/kotlin/com/ninecraft/booket/core/datastore/api/datasource/OnboardingDataSource.kt @@ -1,6 +1,6 @@ package com.ninecraft.booket.core.datastore.api.datasource -import com.ninecraft.booket.core.model.OnboardingState +import com.ninecraft.booket.core.model.state.OnboardingState import kotlinx.coroutines.flow.Flow interface OnboardingDataSource { diff --git a/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt b/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt index dd5e3ad0..1c0521bd 100644 --- a/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt +++ b/core/datastore/impl/src/main/kotlin/com/ninecraft/booket/core/datastore/impl/datasource/DefaultOnboardingDataSource.kt @@ -8,7 +8,7 @@ import com.ninecraft.booket.core.datastore.api.datasource.OnboardingDataSource import com.ninecraft.booket.core.datastore.impl.di.OnboardingDataStore import com.ninecraft.booket.core.datastore.impl.util.handleIOException import com.ninecraft.booket.core.di.DataScope -import com.ninecraft.booket.core.model.OnboardingState +import com.ninecraft.booket.core.model.state.OnboardingState import dev.zacsweers.metro.Inject import dev.zacsweers.metro.SingleIn import kotlinx.coroutines.flow.Flow diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/AutoLoginState.kt similarity index 72% rename from core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt rename to core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/AutoLoginState.kt index 00ece077..e1816d2b 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/AutoLoginState.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/AutoLoginState.kt @@ -1,4 +1,4 @@ -package com.ninecraft.booket.core.model +package com.ninecraft.booket.core.model.state import androidx.compose.runtime.Stable diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/OnboardingState.kt similarity index 72% rename from core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt rename to core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/OnboardingState.kt index 540042b3..8b4c332a 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/OnboardingState.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/OnboardingState.kt @@ -1,4 +1,4 @@ -package com.ninecraft.booket.core.model +package com.ninecraft.booket.core.model.state import androidx.compose.runtime.Stable diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/UserState.kt similarity index 76% rename from core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt rename to core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/UserState.kt index 08bb7edb..777c5efb 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/UserState.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/state/UserState.kt @@ -1,4 +1,4 @@ -package com.ninecraft.booket.core.model +package com.ninecraft.booket.core.model.state import androidx.compose.runtime.Stable diff --git a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt index 9b65a95f..8f9ddbab 100644 --- a/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt +++ b/feature/home/src/main/kotlin/com/ninecraft/booket/feature/home/HomePresenter.kt @@ -12,7 +12,7 @@ import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.BookRepository import com.ninecraft.booket.core.data.api.repository.UserRepository import com.ninecraft.booket.core.model.RecentBookModel -import com.ninecraft.booket.core.model.UserState +import com.ninecraft.booket.core.model.state.UserState import com.ninecraft.booket.feature.screens.BookDetailScreen import com.ninecraft.booket.feature.screens.BookSearchScreen import com.ninecraft.booket.feature.screens.HomeScreen diff --git a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt index 54aebc08..80df03e5 100644 --- a/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt +++ b/feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt @@ -12,7 +12,7 @@ import com.ninecraft.booket.core.common.utils.handleException import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.BookRepository import com.ninecraft.booket.core.model.LibraryBookSummaryModel -import com.ninecraft.booket.core.model.UserState +import com.ninecraft.booket.core.model.state.UserState import com.ninecraft.booket.core.ui.component.FooterState import com.ninecraft.booket.feature.screens.BookDetailScreen import com.ninecraft.booket.feature.screens.LibraryScreen diff --git a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt index 3d6b29d2..01aa176d 100644 --- a/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt +++ b/feature/search/src/main/kotlin/com/ninecraft/booket/feature/search/book/BookSearchPresenter.kt @@ -17,7 +17,7 @@ import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.BookRepository import com.ninecraft.booket.core.model.BookSearchModel import com.ninecraft.booket.core.model.BookSummaryModel -import com.ninecraft.booket.core.model.UserState +import com.ninecraft.booket.core.model.state.UserState import com.ninecraft.booket.core.ui.component.FooterState import com.ninecraft.booket.feature.screens.BookSearchScreen import com.ninecraft.booket.feature.screens.LoginScreen diff --git a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt index 613c2d6a..47de5d50 100644 --- a/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt +++ b/feature/settings/src/main/kotlin/com/ninecraft/booket/feature/settings/SettingsPresenter.kt @@ -11,7 +11,7 @@ import com.ninecraft.booket.core.common.utils.handleException import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.RemoteConfigRepository import com.ninecraft.booket.core.data.api.repository.UserRepository -import com.ninecraft.booket.core.model.UserState +import com.ninecraft.booket.core.model.state.UserState import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.NotificationScreen import com.ninecraft.booket.feature.screens.OssLicensesScreen diff --git a/feature/splash/src/main/kotlin/com/ninecraft/booket/splash/SplashPresenter.kt b/feature/splash/src/main/kotlin/com/ninecraft/booket/splash/SplashPresenter.kt index 398affca..b1cb8ae1 100644 --- a/feature/splash/src/main/kotlin/com/ninecraft/booket/splash/SplashPresenter.kt +++ b/feature/splash/src/main/kotlin/com/ninecraft/booket/splash/SplashPresenter.kt @@ -12,8 +12,8 @@ import com.ninecraft.booket.core.common.event.postErrorDialog import com.ninecraft.booket.core.data.api.repository.AuthRepository import com.ninecraft.booket.core.data.api.repository.RemoteConfigRepository import com.ninecraft.booket.core.data.api.repository.UserRepository -import com.ninecraft.booket.core.model.AutoLoginState -import com.ninecraft.booket.core.model.OnboardingState +import com.ninecraft.booket.core.model.state.AutoLoginState +import com.ninecraft.booket.core.model.state.OnboardingState import com.ninecraft.booket.feature.screens.HomeScreen import com.ninecraft.booket.feature.screens.LoginScreen import com.ninecraft.booket.feature.screens.OnboardingScreen From 05750856e5d1c748792e113387feecbf6b388085 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Thu, 22 Jan 2026 22:16:12 +0900 Subject: [PATCH 09/10] =?UTF-8?q?[BOOK-490]=20feat:=20=EC=84=A0=ED=83=9D?= =?UTF-8?q?=ED=95=9C=20=EA=B8=B0=EB=A1=9D,=20=EA=B8=B0=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=ED=99=94=EB=A9=B4=20=EC=A0=84=EB=8B=AC?= =?UTF-8?q?=EC=8B=9C=20=EB=8F=84=EC=84=9C=20=EC=A0=95=EB=B3=B4=20fallback?= =?UTF-8?q?=20=EC=B2=98=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booket/feature/detail/book/BookDetailPresenter.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 8813a618..c7bb10e3 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 @@ -341,10 +341,10 @@ class BookDetailPresenter( detailEmotions = selectedRecordInfo.detailEmotions.map { DetailEmotionArg(id = it.id, name = it.name) }, - bookTitle = selectedRecordInfo.bookTitle, - bookPublisher = selectedRecordInfo.bookPublisher, - bookCoverImageUrl = selectedRecordInfo.bookCoverImageUrl, - author = selectedRecordInfo.author, + bookTitle = selectedRecordInfo.bookTitle.ifEmpty { bookDetail.title }, + bookPublisher = selectedRecordInfo.bookPublisher.ifEmpty { bookDetail.publisher }, + bookCoverImageUrl = selectedRecordInfo.bookCoverImageUrl.ifEmpty { bookDetail.coverImageUrl }, + author = selectedRecordInfo.author.ifEmpty { bookDetail.author }, ), ), ) From c012c78f70097dbca8f86cd2cd46b989ca5db606 Mon Sep 17 00:00:00 2001 From: easyhooon Date: Sat, 24 Jan 2026 22:03:18 +0900 Subject: [PATCH 10/10] =?UTF-8?q?[BOOK-490]=20fix:=20representativeEmotion?= =?UTF-8?q?=20nullable=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 책만 등록하고 기록을 안 할 경우, 대표 감정은 null로 내려옴 --- .../ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt | 2 +- .../com/ninecraft/booket/core/model/ReadingRecordsModel.kt | 2 +- .../booket/core/network/response/ReadingRecordsResponse.kt | 2 +- .../ninecraft/booket/feature/detail/book/BookDetailPresenter.kt | 2 +- .../com/ninecraft/booket/feature/detail/book/BookDetailUi.kt | 2 +- .../ninecraft/booket/feature/detail/book/BookDetailUiState.kt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt index 20d4e11c..d3ffa85e 100644 --- a/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt +++ b/core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt @@ -214,7 +214,7 @@ internal fun DetailEmotion.toModel(): DetailEmotionModel { internal fun ReadingRecordsResponse.toModel(): ReadingRecordsModel { return ReadingRecordsModel( - representativeEmotion = representativeEmotion.toModel(), + representativeEmotion = representativeEmotion?.toModel(), lastPage = lastPage, totalResults = totalResults, startIndex = startIndex, diff --git a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt index 4c06fc93..912091c0 100644 --- a/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt +++ b/core/model/src/main/kotlin/com/ninecraft/booket/core/model/ReadingRecordsModel.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Immutable @Immutable data class ReadingRecordsModel( - val representativeEmotion: PrimaryEmotionModel = PrimaryEmotionModel(), + val representativeEmotion: PrimaryEmotionModel? = null, val lastPage: Boolean = true, val totalResults: Int = 0, val startIndex: Int = 0, diff --git a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt index 925ad9af..67f85469 100644 --- a/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt +++ b/core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/ReadingRecordsResponse.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable @Serializable data class ReadingRecordsResponse( @SerialName("representativeEmotion") - val representativeEmotion: PrimaryEmotion, + val representativeEmotion: PrimaryEmotion?, @SerialName("lastPage") val lastPage: Boolean, @SerialName("totalResults") 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 c7bb10e3..2a457532 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 @@ -81,7 +81,7 @@ class BookDetailPresenter( var uiState by rememberRetained { mutableStateOf(UiState.Idle) } var footerState by rememberRetained { mutableStateOf(FooterState.Idle) } var bookDetail by rememberRetained { mutableStateOf(BookDetailModel()) } - var representativeEmotion by rememberRetained { mutableStateOf(PrimaryEmotionModel()) } + var representativeEmotion by rememberRetained { mutableStateOf(null) } var seedsStates by rememberRetained { mutableStateOf>(persistentListOf()) } var isStatsExpanded by rememberRetained { mutableStateOf(false) } var readingRecords by rememberRetained { mutableStateOf(persistentListOf()) } 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 f1fa1a97..1e0ebee1 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 @@ -286,7 +286,7 @@ internal fun BookDetailContent( if (state.hasEmotionData()) { CollectedSeeds( seedsStats = state.seedsStats, - representativeEmotion = state.representativeEmotion, + representativeEmotion = state.representativeEmotion!!, isStatsExpanded = state.isStatsExpanded, onToggleClick = { state.eventSink(BookDetailUiEvent.OnStatsToggleClick(!state.isStatsExpanded)) 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 fdfc4e61..1f65bed8 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 @@ -27,7 +27,7 @@ data class BookDetailUiState( val footerState: FooterState = FooterState.Idle, val isLoading: Boolean = false, val bookDetail: BookDetailModel = BookDetailModel(), - val representativeEmotion: PrimaryEmotionModel = PrimaryEmotionModel(), + val representativeEmotion: PrimaryEmotionModel? = null, val seedsStats: ImmutableList = persistentListOf(), val isStatsExpanded: Boolean = false, val readingRecords: ImmutableList = persistentListOf(),