Skip to content

Commit 7a746c7

Browse files
authored
Merge pull request #109 from YAPP-Github/BOOK-214-feature/#99
feat: 기록 상세 화면 API 연동
2 parents 6698a94 + 49511ed commit 7a746c7

File tree

19 files changed

+363
-151
lines changed

19 files changed

+363
-151
lines changed

core/common/src/main/kotlin/com/ninecraft/booket/core/common/extensions/String.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package com.ninecraft.booket.core.common.extensions
22

3+
import java.time.LocalDateTime
4+
import java.time.format.DateTimeFormatter
5+
import java.time.format.DateTimeParseException
6+
37
fun String.decodeHtmlEntities(): String {
48
return this
59
.replace("&lt;", "<")
@@ -12,3 +16,14 @@ fun String.decodeHtmlEntities(): String {
1216
.replace("&#39;", "'")
1317
.replace("&nbsp;", " ")
1418
}
19+
20+
fun String.toFormattedDate(): String {
21+
return try {
22+
val inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS")
23+
val outputFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd")
24+
val parsedDate = LocalDateTime.parse(this, inputFormatter)
25+
parsedDate.format(outputFormatter)
26+
} catch (e: DateTimeParseException) {
27+
""
28+
}
29+
}

core/data/api/src/main/kotlin/com/ninecraft/booket/core/data/api/repository/RecordRepository.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.ninecraft.booket.core.data.api.repository
22

3+
import com.ninecraft.booket.core.model.RecordDetailModel
34
import com.ninecraft.booket.core.model.RecordRegisterModel
45

56
interface RecordRepository {
@@ -10,4 +11,8 @@ interface RecordRepository {
1011
emotionTags: List<String>,
1112
review: String,
1213
): Result<RecordRegisterModel>
14+
15+
suspend fun getRecordDetail(
16+
readingRecordId: String,
17+
): Result<RecordDetailModel>
1318
}

core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/mapper/ResponseToModel.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ninecraft.booket.core.data.impl.mapper
22

33
import com.ninecraft.booket.core.common.extensions.decodeHtmlEntities
4+
import com.ninecraft.booket.core.common.extensions.toFormattedDate
45
import com.ninecraft.booket.core.model.BookDetailModel
56
import com.ninecraft.booket.core.model.BookSearchModel
67
import com.ninecraft.booket.core.model.BookSummaryModel
@@ -11,6 +12,7 @@ import com.ninecraft.booket.core.model.LibraryBooksModel
1112
import com.ninecraft.booket.core.model.LibraryModel
1213
import com.ninecraft.booket.core.model.PageInfoModel
1314
import com.ninecraft.booket.core.model.RecentBookModel
15+
import com.ninecraft.booket.core.model.RecordDetailModel
1416
import com.ninecraft.booket.core.model.RecordRegisterModel
1517
import com.ninecraft.booket.core.model.UserProfileModel
1618
import com.ninecraft.booket.core.network.response.BookDetailResponse
@@ -23,6 +25,7 @@ import com.ninecraft.booket.core.network.response.LibraryBooks
2325
import com.ninecraft.booket.core.network.response.LibraryResponse
2426
import com.ninecraft.booket.core.network.response.PageInfo
2527
import com.ninecraft.booket.core.network.response.RecentBook
28+
import com.ninecraft.booket.core.network.response.RecordDetailResponse
2629
import com.ninecraft.booket.core.network.response.RecordRegisterResponse
2730
import com.ninecraft.booket.core.network.response.UserProfileResponse
2831

@@ -156,6 +159,23 @@ internal fun RecordRegisterResponse.toModel(): RecordRegisterModel {
156159
)
157160
}
158161

162+
internal fun RecordDetailResponse.toModel(): RecordDetailModel {
163+
return RecordDetailModel(
164+
id = id,
165+
userBookId = userBookId,
166+
pageNumber = pageNumber,
167+
quote = quote,
168+
review = review,
169+
emotionTags = emotionTags,
170+
createdAt = createdAt.toFormattedDate(),
171+
updatedAt = updatedAt.toFormattedDate(),
172+
bookTitle = bookTitle,
173+
bookPublisher = bookPublisher,
174+
bookCoverImageUrl = bookCoverImageUrl,
175+
author = author,
176+
)
177+
}
178+
159179
internal fun HomeResponse.toModel(): HomeModel {
160180
return HomeModel(
161181
recentBooks = recentBooks.map { it.toModel() },

core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/repository/DefaultRecordRepository.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ class DefaultRecordRepository @Inject constructor(
1919
) = runSuspendCatching {
2020
service.postRecord(userBookId, RecordRegisterRequest(pageNumber, quote, emotionTags, review)).toModel()
2121
}
22+
23+
override suspend fun getRecordDetail(readingRecordId: String) = runSuspendCatching {
24+
service.getRecordDetail(readingRecordId).toModel()
25+
}
2226
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.ninecraft.booket.core.model
2+
3+
import androidx.compose.runtime.Stable
4+
5+
@Stable
6+
data class RecordDetailModel(
7+
val id: String = "",
8+
val userBookId: String = "",
9+
val pageNumber: Int = 0,
10+
val quote: String = "",
11+
val review: String = "",
12+
val emotionTags: List<String> = emptyList(),
13+
val createdAt: String = "",
14+
val updatedAt: String = "",
15+
val bookTitle: String = "",
16+
val bookPublisher: String = "",
17+
val bookCoverImageUrl: String = "",
18+
val author: String = "",
19+
)

core/network/src/main/kotlin/com/ninecraft/booket/core/network/response/LibraryResponse.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ data class LibraryResponse(
88
@SerialName("books")
99
val books: LibraryBooks,
1010
@SerialName("totalCount")
11-
val totalCount: Int = 0,
11+
val totalCount: Int,
1212
@SerialName("beforeReadingCount")
1313
val beforeReadingCount: Int,
1414
@SerialName("readingCount")
@@ -40,7 +40,7 @@ data class LibraryBookSummary(
4040
@SerialName("status")
4141
val status: String,
4242
@SerialName("recordCount")
43-
val recordCount: Int = 0,
43+
val recordCount: Int,
4444
@SerialName("coverImageUrl")
4545
val coverImageUrl: String,
4646
@SerialName("publisher")
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.ninecraft.booket.core.network.response
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
data class RecordDetailResponse(
8+
@SerialName("id")
9+
val id: String,
10+
@SerialName("userBookId")
11+
val userBookId: String,
12+
@SerialName("pageNumber")
13+
val pageNumber: Int,
14+
@SerialName("quote")
15+
val quote: String,
16+
@SerialName("review")
17+
val review: String,
18+
@SerialName("emotionTags")
19+
val emotionTags: List<String>,
20+
@SerialName("createdAt")
21+
val createdAt: String,
22+
@SerialName("updatedAt")
23+
val updatedAt: String,
24+
@SerialName("bookTitle")
25+
val bookTitle: String,
26+
@SerialName("bookPublisher")
27+
val bookPublisher: String,
28+
@SerialName("bookCoverImageUrl")
29+
val bookCoverImageUrl: String,
30+
@SerialName("author")
31+
val author: String,
32+
)

core/network/src/main/kotlin/com/ninecraft/booket/core/network/service/ReedService.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.ninecraft.booket.core.network.response.BookUpsertResponse
1111
import com.ninecraft.booket.core.network.response.HomeResponse
1212
import com.ninecraft.booket.core.network.response.LibraryResponse
1313
import com.ninecraft.booket.core.network.response.LoginResponse
14+
import com.ninecraft.booket.core.network.response.RecordDetailResponse
1415
import com.ninecraft.booket.core.network.response.RecordRegisterResponse
1516
import com.ninecraft.booket.core.network.response.RefreshTokenResponse
1617
import com.ninecraft.booket.core.network.response.TermsAgreementResponse
@@ -79,6 +80,11 @@ interface ReedService {
7980
@Body recordRegisterRequest: RecordRegisterRequest,
8081
): RecordRegisterResponse
8182

83+
@GET("api/v1/reading-records/detail/{readingRecordId}")
84+
suspend fun getRecordDetail(
85+
@Path("readingRecordId") readingRecordId: String,
86+
): RecordDetailResponse
87+
8288
// Home (auth required)
8389
@GET("api/v1/home")
8490
suspend fun getHome(

feature/detail/src/main/kotlin/com/ninecraft/booket/feature/detail/record/RecordDetailPresenter.kt

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
package com.ninecraft.booket.feature.detail.record
22

33
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.LaunchedEffect
45
import androidx.compose.runtime.getValue
56
import androidx.compose.runtime.mutableStateOf
67
import androidx.compose.runtime.rememberCoroutineScope
78
import androidx.compose.runtime.setValue
9+
import com.ninecraft.booket.core.common.utils.handleException
10+
import com.ninecraft.booket.core.data.api.repository.RecordRepository
11+
import com.ninecraft.booket.core.model.RecordDetailModel
12+
import com.ninecraft.booket.feature.screens.LoginScreen
813
import com.ninecraft.booket.feature.screens.RecordDetailScreen
14+
import com.orhanobut.logger.Logger
915
import com.slack.circuit.codegen.annotations.CircuitInject
1016
import com.slack.circuit.retained.rememberRetained
1117
import com.slack.circuit.runtime.Navigator
@@ -14,26 +20,58 @@ import dagger.assisted.Assisted
1420
import dagger.assisted.AssistedFactory
1521
import dagger.assisted.AssistedInject
1622
import dagger.hilt.android.components.ActivityRetainedComponent
23+
import kotlinx.coroutines.launch
1724

1825
class RecordDetailPresenter @AssistedInject constructor(
26+
@Assisted private val screen: RecordDetailScreen,
1927
@Assisted private val navigator: Navigator,
28+
private val repository: RecordRepository,
2029
) : Presenter<RecordDetailUiState> {
2130

22-
@Suppress("unused")
2331
@Composable
2432
override fun present(): RecordDetailUiState {
2533
val scope = rememberCoroutineScope()
2634

35+
var recordDetailInfo by rememberRetained { mutableStateOf(RecordDetailModel()) }
2736
var sideEffect by rememberRetained { mutableStateOf<RecordDetailSideEffect?>(null) }
2837

38+
fun getRecordDetail(readingRecordId: String) {
39+
scope.launch {
40+
repository.getRecordDetail(readingRecordId = readingRecordId)
41+
.onSuccess { result ->
42+
recordDetailInfo = result
43+
}
44+
.onFailure { exception ->
45+
val handleErrorMessage = { message: String ->
46+
Logger.e(message)
47+
sideEffect = RecordDetailSideEffect.ShowToast(message)
48+
}
49+
50+
handleException(
51+
exception = exception,
52+
onError = handleErrorMessage,
53+
onLoginRequired = {
54+
navigator.resetRoot(LoginScreen)
55+
},
56+
)
57+
}
58+
}
59+
}
60+
2961
fun handleEvent(event: RecordDetailUiEvent) {
3062
when (event) {
3163
RecordDetailUiEvent.OnCloseClicked -> {
3264
navigator.pop()
3365
}
3466
}
3567
}
68+
69+
LaunchedEffect(Unit) {
70+
getRecordDetail(screen.recordId)
71+
}
72+
3673
return RecordDetailUiState(
74+
recordDetailInfo = recordDetailInfo,
3775
sideEffect = sideEffect,
3876
eventSink = ::handleEvent,
3977
)
@@ -43,5 +81,8 @@ class RecordDetailPresenter @AssistedInject constructor(
4381
@CircuitInject(RecordDetailScreen::class, ActivityRetainedComponent::class)
4482
@AssistedFactory
4583
fun interface Factory {
46-
fun create(navigator: Navigator): RecordDetailPresenter
84+
fun create(
85+
screen: RecordDetailScreen,
86+
navigator: Navigator,
87+
): RecordDetailPresenter
4788
}

0 commit comments

Comments
 (0)