Skip to content

Commit a2bacb3

Browse files
authored
Merge pull request #30 from YAPP-Github/BOOK-92-feature/#28
feat: 유저 프로필 조회 API 연동
2 parents 9d45323 + 88319c5 commit a2bacb3

File tree

11 files changed

+137
-13
lines changed

11 files changed

+137
-13
lines changed

build-logic/src/main/kotlin/AndroidFeatureConventionPlugin.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ internal class AndroidFeatureConventionPlugin : Plugin<Project> {
2424
implementation(project(path = ":core:model"))
2525
implementation(project(path = ":core:ui"))
2626

27+
implementation(libs.compose.effects)
28+
2729
implementation(libs.bundles.circuit)
2830

2931
api(libs.circuit.codegen.annotation)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.ninecraft.booket.core.data.api.repository
2+
3+
import com.ninecraft.booket.core.model.UserProfileModel
4+
5+
interface UserRepository {
6+
suspend fun getUserProfile(): Result<UserProfileModel>
7+
}

core/data/impl/src/main/kotlin/com/ninecraft/booket/core/data/impl/di/RepositoryModule.kt

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

33
import com.ninecraft.booket.core.data.api.repository.AuthRepository
4+
import com.ninecraft.booket.core.data.api.repository.UserRepository
45
import com.ninecraft.booket.core.data.impl.repository.DefaultAuthRepository
6+
import com.ninecraft.booket.core.data.impl.repository.DefaultUserRepository
57
import dagger.Binds
68
import dagger.Module
79
import dagger.hilt.InstallIn
@@ -15,4 +17,8 @@ internal abstract class RepositoryModule {
1517
@Binds
1618
@Singleton
1719
abstract fun bindAuthRepository(defaultAuthRepository: DefaultAuthRepository): AuthRepository
20+
21+
@Binds
22+
@Singleton
23+
abstract fun bindUserRepository(defaultUserRepository: DefaultUserRepository): UserRepository
1824
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
package com.ninecraft.booket.core.data.impl.mapper
22

33
import com.ninecraft.booket.core.model.LoginModel
4+
import com.ninecraft.booket.core.model.UserProfileModel
45
import com.ninecraft.booket.core.network.response.LoginResponse
6+
import com.ninecraft.booket.core.network.response.UserProfileResponse
57

68
internal fun LoginResponse.toModel(): LoginModel {
79
return LoginModel(
810
accessToken = accessToken,
911
refreshToken = refreshToken,
1012
)
1113
}
14+
15+
internal fun UserProfileResponse.toModel(): UserProfileModel {
16+
return UserProfileModel(
17+
id = id,
18+
email = email,
19+
nickname = nickname,
20+
provider = provider,
21+
)
22+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.ninecraft.booket.core.data.impl.repository
2+
3+
import com.ninecraft.booket.core.common.utils.runSuspendCatching
4+
import com.ninecraft.booket.core.data.api.repository.UserRepository
5+
import com.ninecraft.booket.core.data.impl.mapper.toModel
6+
import com.ninecraft.booket.core.network.service.AuthService
7+
import javax.inject.Inject
8+
9+
internal class DefaultUserRepository @Inject constructor(
10+
private val authService: AuthService,
11+
) : UserRepository {
12+
override suspend fun getUserProfile() = runSuspendCatching {
13+
authService.getUserProfile().toModel()
14+
}
15+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.ninecraft.booket.core.model
2+
3+
data class UserProfileModel(
4+
val id: String,
5+
val email: String,
6+
val nickname: String,
7+
val provider: String,
8+
)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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 UserProfileResponse(
8+
@SerialName("id")
9+
val id: String,
10+
@SerialName("email")
11+
val email: String,
12+
@SerialName("nickname")
13+
val nickname: String,
14+
@SerialName("provider")
15+
val provider: String,
16+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package com.ninecraft.booket.core.network.service
22

3+
import com.ninecraft.booket.core.network.response.UserProfileResponse
4+
import retrofit2.http.GET
35
import retrofit2.http.POST
46

57
interface AuthService {
68
@POST("api/v1/auth/signout")
79
suspend fun logout()
10+
11+
@GET("api/v1/auth/me")
12+
suspend fun getUserProfile(): UserProfileResponse
813
}

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

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import androidx.compose.runtime.rememberCoroutineScope
77
import androidx.compose.runtime.setValue
88
import com.ninecraft.booket.core.common.utils.handleException
99
import com.ninecraft.booket.core.data.api.repository.AuthRepository
10+
import com.ninecraft.booket.core.data.api.repository.UserRepository
1011
import com.ninecraft.booket.feature.login.LoginScreen
1112
import com.orhanobut.logger.Logger
13+
import com.skydoves.compose.effects.RememberedEffect
1214
import com.slack.circuit.codegen.annotations.CircuitInject
1315
import com.slack.circuit.retained.rememberRetained
1416
import com.slack.circuit.runtime.Navigator
@@ -21,14 +23,51 @@ import kotlinx.coroutines.launch
2123

2224
class LibraryPresenter @AssistedInject constructor(
2325
@Assisted private val navigator: Navigator,
24-
private val repository: AuthRepository,
26+
private val authRepository: AuthRepository,
27+
private val userRepository: UserRepository,
2528
) : Presenter<LibraryScreen.State> {
2629

2730
@Composable
2831
override fun present(): LibraryScreen.State {
2932
val scope = rememberCoroutineScope()
3033
var isLoading by rememberRetained { mutableStateOf(false) }
3134
var sideEffect by rememberRetained { mutableStateOf<LibraryScreen.SideEffect?>(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 = LibraryScreen.SideEffect.ShowToast(message)
51+
}
52+
53+
handleException(
54+
exception = exception,
55+
onServerError = handleErrorMessage,
56+
onNetworkError = handleErrorMessage,
57+
onLoginRequired = {
58+
navigator.resetRoot(LoginScreen)
59+
},
60+
)
61+
}
62+
} finally {
63+
isLoading = false
64+
}
65+
}
66+
}
67+
68+
RememberedEffect(Unit) {
69+
getUserProfile()
70+
}
3271

3372
fun handleEvent(event: LibraryScreen.Event) {
3473
when (event) {
@@ -40,9 +79,9 @@ class LibraryPresenter @AssistedInject constructor(
4079
scope.launch {
4180
try {
4281
isLoading = true
43-
repository.logout()
82+
authRepository.logout()
4483
.onSuccess {
45-
repository.clearTokens()
84+
authRepository.clearTokens()
4685
navigator.resetRoot(LoginScreen)
4786
}
4887
.onFailure { exception ->
@@ -70,6 +109,8 @@ class LibraryPresenter @AssistedInject constructor(
70109

71110
return LibraryScreen.State(
72111
isLoading = isLoading,
112+
nickname = nickname,
113+
email = email,
73114
sideEffect = sideEffect,
74115
eventSink = ::handleEvent,
75116
)

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

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.ninecraft.booket.feature.library
33
import androidx.compose.foundation.layout.Arrangement
44
import androidx.compose.foundation.layout.Box
55
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Spacer
67
import androidx.compose.foundation.layout.fillMaxSize
78
import androidx.compose.foundation.layout.fillMaxWidth
89
import androidx.compose.foundation.layout.height
@@ -31,6 +32,8 @@ import kotlinx.parcelize.Parcelize
3132
data object LibraryScreen : Screen {
3233
data class State(
3334
val isLoading: Boolean = false,
35+
val nickname: String = "",
36+
val email: String = "",
3437
val sideEffect: SideEffect? = null,
3538
val eventSink: (Event) -> Unit,
3639
) : CircuitUiState
@@ -51,6 +54,11 @@ internal fun Library(
5154
state: LibraryScreen.State,
5255
modifier: Modifier = Modifier,
5356
) {
57+
HandleLibrarySideEffects(
58+
state = state,
59+
eventSink = state.eventSink,
60+
)
61+
5462
Column(
5563
modifier = modifier.fillMaxSize(),
5664
horizontalAlignment = Alignment.CenterHorizontally,
@@ -68,21 +76,23 @@ internal fun LibraryContent(
6876
state: LibraryScreen.State,
6977
modifier: Modifier = Modifier,
7078
) {
71-
HandleLibrarySideEffects(
72-
state = state,
73-
eventSink = state.eventSink,
74-
)
75-
7679
Column(
7780
modifier = modifier.fillMaxSize(),
7881
horizontalAlignment = Alignment.CenterHorizontally,
7982
verticalArrangement = Arrangement.Center,
8083
) {
8184
Box(modifier = modifier.fillMaxSize()) {
82-
Text(
83-
text = "내 서재",
84-
modifier = Modifier.align(Alignment.Center),
85-
)
85+
Column(
86+
modifier = Modifier.fillMaxSize(),
87+
horizontalAlignment = Alignment.CenterHorizontally,
88+
verticalArrangement = Arrangement.Center,
89+
) {
90+
Text(text = "내 서재")
91+
Spacer(modifier = Modifier.height(16.dp))
92+
Text(text = state.nickname)
93+
Spacer(modifier = Modifier.height(16.dp))
94+
Text(text = state.email)
95+
}
8696
BooketButton(
8797
onClick = {
8898
state.eventSink(LibraryScreen.Event.OnLogoutButtonClick)
@@ -118,6 +128,8 @@ private fun LibraryPreview() {
118128
BooketTheme {
119129
Library(
120130
state = LibraryScreen.State(
131+
nickname = "홍길동",
132+
email = "[email protected]",
121133
eventSink = {},
122134
),
123135
)

0 commit comments

Comments
 (0)