Skip to content

Commit ef92249

Browse files
authored
Merge pull request #63 from YAPP-Github/BOOK-144-feature/#49
feat: 내서재 화면 UI 구현
2 parents d440a17 + 34bd976 commit ef92249

File tree

15 files changed

+589
-98
lines changed

15 files changed

+589
-98
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.ninecraft.booket.core.data.api.repository
33
import com.ninecraft.booket.core.model.BookDetailModel
44
import com.ninecraft.booket.core.model.BookSearchModel
55
import com.ninecraft.booket.core.model.BookUpsertModel
6+
import com.ninecraft.booket.core.model.LibraryModel
67

78
interface BookRepository {
89
suspend fun searchBook(
@@ -16,4 +17,6 @@ interface BookRepository {
1617
bookIsbn: String,
1718
bookStatus: String,
1819
): Result<BookUpsertModel>
20+
21+
suspend fun getLibrary(): Result<LibraryModel>
1922
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import com.ninecraft.booket.core.model.BookDetailModel
55
import com.ninecraft.booket.core.model.BookSearchModel
66
import com.ninecraft.booket.core.model.BookSummaryModel
77
import com.ninecraft.booket.core.model.BookUpsertModel
8+
import com.ninecraft.booket.core.model.LibraryModel
89
import com.ninecraft.booket.core.model.UserProfileModel
910
import com.ninecraft.booket.core.network.response.BookDetailResponse
1011
import com.ninecraft.booket.core.network.response.BookSearchResponse
1112
import com.ninecraft.booket.core.network.response.BookSummary
1213
import com.ninecraft.booket.core.network.response.BookUpsertResponse
14+
import com.ninecraft.booket.core.network.response.LibraryResponse
1315
import com.ninecraft.booket.core.network.response.UserProfileResponse
1416

1517
internal fun UserProfileResponse.toModel(): UserProfileModel {
@@ -84,3 +86,18 @@ internal fun BookUpsertResponse.toModel(): BookUpsertModel {
8486
updatedAt = updatedAt,
8587
)
8688
}
89+
90+
internal fun LibraryResponse.toModel(): LibraryModel {
91+
return LibraryModel(
92+
userBookId = userBookId,
93+
userId = userId,
94+
bookIsbn = bookIsbn,
95+
bookTitle = bookTitle,
96+
bookAuthor = bookAuthor,
97+
status = status,
98+
coverImageUrl = coverImageUrl,
99+
publisher = publisher,
100+
createdAt = createdAt,
101+
updatedAt = updatedAt,
102+
)
103+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ internal class DefaultBookRepository @Inject constructor(
2727
override suspend fun upsertBook(bookIsbn: String, bookStatus: String) = runSuspendCatching {
2828
service.upsertBook(BookUpsertRequest(bookIsbn, bookStatus)).toModel()
2929
}
30+
31+
override suspend fun getLibrary() = runSuspendCatching {
32+
service.getLibrary().toModel()
33+
}
3034
}

core/designsystem/src/main/kotlin/com/ninecraft/booket/core/designsystem/component/appbar/ReedTopAppBar.kt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import androidx.compose.foundation.background
55
import androidx.compose.foundation.layout.Arrangement
66
import androidx.compose.foundation.layout.Row
77
import androidx.compose.foundation.layout.Spacer
8-
import androidx.compose.foundation.layout.fillMaxHeight
98
import androidx.compose.foundation.layout.fillMaxWidth
109
import androidx.compose.foundation.layout.height
10+
import androidx.compose.foundation.layout.padding
1111
import androidx.compose.foundation.layout.width
1212
import androidx.compose.material3.Icon
1313
import androidx.compose.material3.IconButton
@@ -37,25 +37,23 @@ fun ReedTopAppBar(
3737
Row(
3838
modifier = modifier
3939
.fillMaxWidth()
40-
.height(56.dp)
41-
.background(color = White),
40+
.height(60.dp)
41+
.background(color = White)
42+
.padding(horizontal = ReedTheme.spacing.spacing2),
4243
horizontalArrangement = Arrangement.Start,
4344
verticalAlignment = Alignment.CenterVertically,
4445
) {
4546
if (startIconRes != null) {
4647
IconButton(
4748
onClick = { startIconOnClick() },
48-
modifier = Modifier
49-
.fillMaxHeight()
50-
.width(72.dp),
5149
) {
5250
Icon(
5351
painter = painterResource(id = startIconRes),
5452
contentDescription = startIconDescription,
5553
)
5654
}
5755
} else {
58-
Spacer(modifier = Modifier.width(72.dp))
56+
Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing12))
5957
}
6058

6159
Text(
@@ -68,17 +66,14 @@ fun ReedTopAppBar(
6866
if (endIconRes != null) {
6967
IconButton(
7068
onClick = { endIconOnClick() },
71-
modifier = Modifier
72-
.fillMaxHeight()
73-
.width(72.dp),
7469
) {
7570
Icon(
7671
painter = painterResource(id = endIconRes),
7772
contentDescription = endIconDescription,
7873
)
7974
}
8075
} else {
81-
Spacer(modifier = Modifier.width(72.dp))
76+
Spacer(modifier = Modifier.width(ReedTheme.spacing.spacing12))
8277
}
8378
}
8479
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.ninecraft.booket.core.model
2+
3+
import androidx.compose.runtime.Stable
4+
5+
@Stable
6+
data class LibraryModel(
7+
val userBookId: String = "",
8+
val userId: String = "",
9+
val bookIsbn: String = "",
10+
val bookTitle: String = "",
11+
val bookAuthor: String = "",
12+
val status: String = "",
13+
val coverImageUrl: String = "",
14+
val publisher: String = "",
15+
val createdAt: String = "",
16+
val updatedAt: String = "",
17+
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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 LibraryResponse(
8+
@SerialName("userBookId")
9+
val userBookId: String,
10+
@SerialName("userId")
11+
val userId: String,
12+
@SerialName("bookIsbn")
13+
val bookIsbn: String,
14+
@SerialName("bookTitle")
15+
val bookTitle: String,
16+
@SerialName("bookAuthor")
17+
val bookAuthor: String,
18+
@SerialName("status")
19+
val status: String,
20+
@SerialName("coverImageUrl")
21+
val coverImageUrl: String,
22+
@SerialName("publisher")
23+
val publisher: String,
24+
@SerialName("createdAt")
25+
val createdAt: String,
26+
@SerialName("updatedAt")
27+
val updatedAt: String,
28+
)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import com.ninecraft.booket.core.network.request.RefreshTokenRequest
66
import com.ninecraft.booket.core.network.response.BookDetailResponse
77
import com.ninecraft.booket.core.network.response.BookSearchResponse
88
import com.ninecraft.booket.core.network.response.BookUpsertResponse
9+
import com.ninecraft.booket.core.network.response.LibraryResponse
910
import com.ninecraft.booket.core.network.response.LoginResponse
1011
import com.ninecraft.booket.core.network.response.RefreshTokenResponse
1112
import com.ninecraft.booket.core.network.response.UserProfileResponse
@@ -52,4 +53,7 @@ interface ReedService {
5253

5354
@PUT("api/v1/books/upsert")
5455
suspend fun upsertBook(@Body bookUpsertRequest: BookUpsertRequest): BookUpsertResponse
56+
57+
@GET("api/v1/books/my-library")
58+
suspend fun getLibrary(): LibraryResponse
5559
}

feature/library/src/main/kotlin/com/ninecraft/booket/feature/library/LibraryPresenter.kt

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,9 @@ package com.ninecraft.booket.feature.library
33
import androidx.compose.runtime.Composable
44
import androidx.compose.runtime.getValue
55
import androidx.compose.runtime.mutableStateOf
6-
import androidx.compose.runtime.rememberCoroutineScope
76
import androidx.compose.runtime.setValue
8-
import com.ninecraft.booket.core.common.utils.handleException
9-
import com.ninecraft.booket.core.data.api.repository.UserRepository
107
import com.ninecraft.booket.feature.screens.LibraryScreen
11-
import com.ninecraft.booket.feature.screens.LoginScreen
128
import com.ninecraft.booket.feature.screens.SettingsScreen
13-
import com.orhanobut.logger.Logger
14-
import com.skydoves.compose.effects.RememberedEffect
159
import com.slack.circuit.codegen.annotations.CircuitInject
1610
import com.slack.circuit.retained.rememberRetained
1711
import com.slack.circuit.runtime.Navigator
@@ -20,52 +14,42 @@ import dagger.assisted.Assisted
2014
import dagger.assisted.AssistedFactory
2115
import dagger.assisted.AssistedInject
2216
import dagger.hilt.android.components.ActivityRetainedComponent
23-
import kotlinx.coroutines.launch
17+
import kotlinx.collections.immutable.persistentListOf
18+
import kotlinx.collections.immutable.toPersistentList
2419

2520
class LibraryPresenter @AssistedInject constructor(
2621
@Assisted private val navigator: Navigator,
27-
private val userRepository: UserRepository,
2822
) : Presenter<LibraryUiState> {
2923

3024
@Composable
3125
override fun present(): LibraryUiState {
32-
val scope = rememberCoroutineScope()
3326
var isLoading by rememberRetained { mutableStateOf(false) }
3427
var sideEffect by rememberRetained { mutableStateOf<LibrarySideEffect?>(null) }
35-
var nickname by rememberRetained { mutableStateOf("") }
36-
var email by rememberRetained { mutableStateOf("") }
37-
38-
fun getUserProfile() {
39-
scope.launch {
40-
try {
41-
isLoading = true
42-
userRepository.getUserProfile()
43-
.onSuccess { user ->
44-
nickname = user.nickname
45-
email = user.email
46-
}
47-
.onFailure { exception ->
48-
val handleErrorMessage = { message: String ->
49-
Logger.e(message)
50-
sideEffect = LibrarySideEffect.ShowToast(message)
51-
}
52-
53-
handleException(
54-
exception = exception,
55-
onError = handleErrorMessage,
56-
onLoginRequired = {
57-
navigator.resetRoot(LoginScreen)
58-
},
59-
)
60-
}
61-
} finally {
62-
isLoading = false
63-
}
64-
}
65-
}
66-
67-
RememberedEffect(Unit) {
68-
getUserProfile()
28+
var chipElements by rememberRetained {
29+
mutableStateOf(
30+
persistentListOf(
31+
FilterChipState(
32+
title = BookStatus.TOTAL,
33+
count = 10,
34+
isSelected = true,
35+
),
36+
FilterChipState(
37+
title = BookStatus.BEFORE_READING,
38+
count = 15,
39+
isSelected = false,
40+
),
41+
FilterChipState(
42+
title = BookStatus.READING,
43+
count = 2,
44+
isSelected = false,
45+
),
46+
FilterChipState(
47+
title = BookStatus.COMPLETED,
48+
count = 5,
49+
isSelected = false,
50+
),
51+
),
52+
)
6953
}
7054

7155
fun handleEvent(event: LibraryUiEvent) {
@@ -77,13 +61,23 @@ class LibraryPresenter @AssistedInject constructor(
7761
is LibraryUiEvent.OnSettingsClick -> {
7862
navigator.goTo(SettingsScreen)
7963
}
64+
65+
is LibraryUiEvent.OnFilterClick -> {
66+
chipElements = chipElements.map {
67+
if (it.title == event.bookStatus) {
68+
it.copy(isSelected = true)
69+
} else {
70+
it.copy(isSelected = false)
71+
}
72+
}.toPersistentList()
73+
// TODO: 필터에 해당하는 도서 목록을 불러오는 로직이 들어가야 함
74+
}
8075
}
8176
}
8277

8378
return LibraryUiState(
8479
isLoading = isLoading,
85-
nickname = nickname,
86-
email = email,
80+
filterElements = chipElements,
8781
sideEffect = sideEffect,
8882
eventSink = ::handleEvent,
8983
)

0 commit comments

Comments
 (0)