diff --git a/app/build.gradle b/app/build.gradle
index 96f1576..5bb15d6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,6 +2,9 @@ plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization'
+ id 'com.google.dagger.hilt.android'
+ id 'kotlin-android'
+ id 'kotlin-kapt'
}
Properties properties = new Properties()
@@ -54,8 +57,8 @@ android {
}
dependencies {
- implementation 'androidx.core:core-ktx:1.13.0'
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
+ implementation 'androidx.core:core-ktx:1.13.1'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.0'
implementation 'androidx.activity:activity-compose:1.9.0'
implementation platform('androidx.compose:compose-bom:2024.05.00')
implementation 'androidx.compose.ui:ui'
@@ -73,10 +76,10 @@ dependencies {
implementation 'androidx.compose.material:material:1.6.7'
// Lifecycle Viewmodel
- implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0'
// Fragment && Activity
- implementation 'androidx.fragment:fragment-ktx:1.6.2'
+ implementation 'androidx.fragment:fragment-ktx:1.7.1'
implementation 'androidx.activity:activity-ktx:1.9.0'
// Retrofit
@@ -91,4 +94,13 @@ dependencies {
// Coil
implementation 'io.coil-kt:coil-compose:2.6.0'
+
+ // Hilt
+ implementation 'com.google.dagger:hilt-android:2.51'
+ kapt 'com.google.dagger:hilt-compiler:2.51.1'
+ implementation 'androidx.hilt:hilt-navigation-compose:1.2.0'
+}
+
+kapt {
+ correctErrorTypes true
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f73f094..a846894 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,8 +5,8 @@
createBaseRetrofit(): T = baseRetrofit.create()
- inline fun createFollowerRetrofit(): T = followerRetrofit.create()
-}
-
-object ServicePool {
- val authService = ApiFactory.createBaseRetrofit()
- val followerService = ApiFactory.createFollowerRetrofit()
+ @Provides
+ @Singleton
+ fun provideFollowerService(followerRetrofit: Retrofit): FollowerService {
+ return followerRetrofit.create(FollowerService::class.java)
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sopt/now/compose/repository/FollowerRepository.kt b/app/src/main/java/com/sopt/now/compose/repository/FollowerRepository.kt
new file mode 100644
index 0000000..91a0139
--- /dev/null
+++ b/app/src/main/java/com/sopt/now/compose/repository/FollowerRepository.kt
@@ -0,0 +1,22 @@
+package com.sopt.now.compose.repository
+
+import com.sopt.now.compose.data.model.ResponseUserDto
+import com.sopt.now.compose.data.network.FollowerService
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import retrofit2.Response
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class FollowerRepository @Inject constructor(
+ private val followerService: FollowerService,
+) {
+ suspend fun getUserList(page: Int): Result> {
+ return withContext(Dispatchers.IO) {
+ runCatching {
+ followerService.getUserList(page).execute()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/sopt/now/compose/ui/SoptApp.kt b/app/src/main/java/com/sopt/now/compose/ui/SoptApp.kt
deleted file mode 100644
index aedf704..0000000
--- a/app/src/main/java/com/sopt/now/compose/ui/SoptApp.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.sopt.now.compose.ui
-
-import androidx.compose.runtime.Composable
-import com.sopt.now.compose.ui.base.SoptAppNavHost
-
-@Composable
-fun SoptApp() {
- SoptAppNavHost()
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/sopt/now/compose/ui/base/SoptAppNavHost.kt b/app/src/main/java/com/sopt/now/compose/ui/base/SoptAppNavHost.kt
index 58ebb65..51d231e 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/base/SoptAppNavHost.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/base/SoptAppNavHost.kt
@@ -13,7 +13,7 @@ import com.sopt.now.compose.ui.signUp.SignUpScreen
fun SoptAppNavHost() {
val navController: NavHostController = rememberNavController()
- NavHost(navController = navController, startDestination = "sign_in") {
+ NavHost(navController = navController, startDestination = "main") {
composable("sign_in") {
SignInScreen(
onNavigateToHome = navController,
diff --git a/app/src/main/java/com/sopt/now/compose/ui/home/HomeScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/home/HomeScreen.kt
index ec787af..dc0dded 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/home/HomeScreen.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/home/HomeScreen.kt
@@ -8,11 +8,12 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
-import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.hilt.navigation.compose.hiltViewModel
import com.sopt.now.compose.R
+
@Composable
-fun HomeScreen(homeViewModel: HomeViewModel = viewModel()) {
+fun HomeScreen(homeViewModel: HomeViewModel = hiltViewModel()) {
val followerState by homeViewModel.followerState.collectAsState()
LazyColumn(modifier = Modifier.fillMaxSize()) {
diff --git a/app/src/main/java/com/sopt/now/compose/ui/home/HomeViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/home/HomeViewModel.kt
index a79e0bb..8bd5773 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/home/HomeViewModel.kt
@@ -1,59 +1,66 @@
package com.sopt.now.compose.ui.home
-import android.util.Log
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
import com.sopt.now.compose.data.model.Profile
-import com.sopt.now.compose.data.model.ResponseUserDto
import com.sopt.now.compose.data.model.UserDataDto
-import com.sopt.now.compose.data.module.ServicePool
+import com.sopt.now.compose.repository.FollowerRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import retrofit2.Call
-import retrofit2.Callback
-import retrofit2.Response
+import kotlinx.coroutines.launch
+import javax.inject.Inject
-class HomeViewModel : ViewModel() {
- private val followerService by lazy { ServicePool.followerService }
+@HiltViewModel
+class HomeViewModel @Inject constructor(
+ private val followerRepository: FollowerRepository,
+) : ViewModel() {
private val _followerState = MutableStateFlow>(emptyList())
val followerState = _followerState.asStateFlow()
- val friendList = mutableListOf()
+ private var _eventNetworkError = MutableLiveData(false)
+
+ private var _isNetworkErrorShown = MutableLiveData(false)
+
+ private val friendList = mutableListOf()
init {
fetchFollowerList()
}
private fun fetchFollowerList() {
- followerService.getUserList(page = 0).enqueue(object : Callback {
- override fun onResponse(
- call: Call,
- response: Response,
- ) {
- if (response.isSuccessful) {
- val data = response.body()?.data
- if (data != null) {
+ viewModelScope.launch {
+ followerRepository.getUserList(0)
+ .onSuccess { response ->
+ response.body()?.data?.let { data ->
_followerState.value = data
mapFollowersToFriendList(data)
+ _eventNetworkError.value = false
+ _isNetworkErrorShown.value = false
+ } ?: run {
+ _eventNetworkError.value = true
}
}
- }
+ .onFailure { exception ->
+ _eventNetworkError.value = true
+ }
+ }
+ }
- override fun onFailure(call: Call, t: Throwable) {
- Log.e("HomeError", "${t.message}")
- }
- })
+ fun onNetworkErrorShown() {
+ _isNetworkErrorShown.value = true
}
- fun mapFollowersToFriendList(followers: List) {
- for (follower in followers) {
- friendList.add(
- Profile(
- profileImage = follower.avatar,
- name = "${follower.firstName} ${follower.lastName}",
- description = follower.email
- )
+ private fun mapFollowersToFriendList(followers: List) {
+ friendList.clear()
+ friendList.addAll(followers.map { follower ->
+ Profile(
+ profileImage = follower.avatar,
+ name = "${follower.firstName} ${follower.lastName}",
+ description = follower.email
)
- }
+ })
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/sopt/now/compose/ui/myPage/MyPageViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/myPage/MyPageViewModel.kt
index 4a773bc..a1b077c 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/myPage/MyPageViewModel.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/myPage/MyPageViewModel.kt
@@ -3,16 +3,10 @@ package com.sopt.now.compose.ui.myPage
import androidx.lifecycle.ViewModel
import com.sopt.now.compose.data.model.ResponseInfoDto
import com.sopt.now.compose.data.model.UserInfoDto
-import com.sopt.now.compose.data.module.ServicePool
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.update
-import retrofit2.Call
-import retrofit2.Callback
-import retrofit2.Response
class MyPageViewModel : ViewModel() {
- private val authService by lazy { ServicePool.authService }
private val _infoState =
MutableStateFlow(
@@ -23,36 +17,4 @@ class MyPageViewModel : ViewModel() {
)
)
val infoState = _infoState.asStateFlow()
-
- init {
- fetchInfo()
- }
-
- private fun fetchInfo() {
- authService.memberInfo().enqueue(object : Callback {
- override fun onResponse(
- call: Call,
- response: Response,
- ) {
- if (response.isSuccessful) {
- val data = response.body()
- if (data != null) {
- _infoState.update {
- data
- }
- }
- }
- }
-
- override fun onFailure(call: Call, t: Throwable) {
- _infoState.update {
- ResponseInfoDto(
- code = -1,
- message = "${t.message}",
- data = UserInfoDto(authenticationId = "", nickname = "", phone = "")
- )
- }
- }
- })
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInScreen.kt
index 1fd0b28..bce922c 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInScreen.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInScreen.kt
@@ -82,20 +82,7 @@ fun SignInScreen(
SoptOutlinedButton(
text = R.string.btn_sign_in,
onClick = {
- if (isSignInButtonEnabled) {
- signInViewModel.signIn(
- RequestSignInDto(
- authenticationId = id,
- password = password,
- )
- )
- } else {
- Toast.makeText(
- context,
- R.string.sign_up_fail,
- Toast.LENGTH_SHORT
- ).show()
- }
+
},
enabled = true
)
diff --git a/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInViewModel.kt
index 42f9c72..a39d5b6 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInViewModel.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/signIn/SignInViewModel.kt
@@ -1,19 +1,11 @@
package com.sopt.now.compose.ui.signIn
import androidx.lifecycle.ViewModel
-import com.sopt.now.compose.data.model.RequestSignInDto
-import com.sopt.now.compose.data.model.ResponseSignInDto
import com.sopt.now.compose.data.model.SignInState
-import com.sopt.now.compose.data.module.ServicePool
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.update
-import retrofit2.Call
-import retrofit2.Callback
-import retrofit2.Response
class SignInViewModel : ViewModel() {
- private val authService by lazy { ServicePool.authService }
private val _signInState = MutableStateFlow(SignInState(isSuccess = false, message = ""))
val signInState = _signInState.asStateFlow()
@@ -31,39 +23,4 @@ class SignInViewModel : ViewModel() {
fun updatePassword(newPassword: String) {
_password.value = newPassword
}
-
-
- fun signIn(request: RequestSignInDto) {
- authService.signIn(request).enqueue(
- object : Callback {
- override fun onResponse(
- call: Call,
- response: Response,
- ) {
- if (response.isSuccessful) {
- val userId = response.headers()["location"]
-
- _signInState.update {
- SignInState(
- isSuccess = true,
- message = "유저 아이디는 $userId"
- )
- }
- } else {
- val error = response.code()
- _signInState.update {
- SignInState(
- isSuccess = false,
- message = "로그인 실패 : $error"
- )
- }
- }
- }
-
- override fun onFailure(call: Call, t: Throwable) {
- _signInState.update { SignInState(isSuccess = false, message = "서버 에러") }
- }
- }
- )
- }
}
diff --git a/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpScreen.kt
index 5596261..8a2c19e 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpScreen.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpScreen.kt
@@ -29,7 +29,6 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import com.sopt.now.compose.R
-import com.sopt.now.compose.data.model.RequestSignUpDto
import com.sopt.now.compose.ui.base.SoptInputTextField
import com.sopt.now.compose.ui.base.SoptOutlinedButton
import com.sopt.now.compose.ui.base.SoptPasswordTextField
@@ -59,6 +58,10 @@ fun SignUpScreen(
onNavigateToSignIn.navigate(context.getString(R.string.route_sign_in))
}
+ is SignUpSideEffect.Loading -> {
+
+ }
+
is SignUpSideEffect.Error -> {
Toast.makeText(context, event.message, Toast.LENGTH_SHORT).show()
}
@@ -83,16 +86,6 @@ fun SignUpScreen(
}
}
- val isSignUpButtonEnabled by remember(id, password, nickname, phoneNumber) {
- mutableStateOf(
- id.length in MIN_LENGTH_LOGIN..MAX_LENGTH_LOGIN
- && password.length in MIN_LENGTH_PASSWORD..MAX_LENGTH_PASSWORD
- && nickname.isNotEmpty() && !nickname.contains(
- " "
- ) && phoneNumber.matches(Regex("^010-\\d{4}-\\d{4}\$"))
- )
- }
-
Text(
text = stringResource(id = R.string.sign_up_title),
fontWeight = FontWeight.Bold,
@@ -120,22 +113,7 @@ fun SignUpScreen(
}
}
SoptOutlinedButton(text = R.string.btn_sign_up, onClick = {
- if (isSignUpButtonEnabled) {
- signUpViewModel.signUp(
- RequestSignUpDto(
- authenticationId = id,
- password = password,
- nickname = nickname,
- phone = phoneNumber
- )
- )
- } else {
- Toast.makeText(
- context,
- R.string.sign_up_fail_message,
- Toast.LENGTH_SHORT
- ).show()
- }
+
}, enabled = true)
}
}
diff --git a/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpSideEffect.kt b/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpSideEffect.kt
index 6d2bbd4..47176ad 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpSideEffect.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpSideEffect.kt
@@ -2,5 +2,6 @@ package com.sopt.now.compose.ui.signUp
sealed class SignUpSideEffect {
data class Success(val message: String) : SignUpSideEffect()
+ object Loading : SignUpSideEffect()
data class Error(val message: String) : SignUpSideEffect()
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpViewModel.kt
index f8c0020..dad7fbc 100644
--- a/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpViewModel.kt
+++ b/app/src/main/java/com/sopt/now/compose/ui/signUp/SignUpViewModel.kt
@@ -1,19 +1,11 @@
package com.sopt.now.compose.ui.signUp
import androidx.lifecycle.ViewModel
-import com.sopt.now.compose.data.model.RequestSignUpDto
-import com.sopt.now.compose.data.model.ResponseSignUpDto
-import com.sopt.now.compose.data.module.ServicePool
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import retrofit2.Call
-import retrofit2.Callback
-import retrofit2.Response
class SignUpViewModel : ViewModel() {
- private val authService by lazy { ServicePool.authService }
-
private val _signUpEvent = MutableStateFlow(null)
val signUpEvent = _signUpEvent.asStateFlow()
@@ -45,30 +37,6 @@ class SignUpViewModel : ViewModel() {
_phoneNumber.value = phoneNumber
}
-
- fun signUp(request: RequestSignUpDto) {
- authService.signUp(request).enqueue(
- object : Callback {
- override fun onResponse(
- call: Call,
- response: Response,
- ) {
- if (response.isSuccessful) {
- val userId = response.headers()["location"]
- _signUpEvent.value = SignUpSideEffect.Success("가입된 유저 아이디는 $userId")
- } else {
- val error = response.code()
- _signUpEvent.value = SignUpSideEffect.Error("회원가입 실패 : $error")
- }
- }
-
- override fun onFailure(call: Call, t: Throwable) {
- _signUpEvent.value = SignUpSideEffect.Error("서버 에러: ${t.message}")
- }
- },
- )
- }
-
companion object {
const val MIN_LENGTH_LOGIN = 6
const val MAX_LENGTH_LOGIN = 10
diff --git a/build.gradle b/build.gradle
index 4c76592..dbcc2e3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,4 +4,5 @@ plugins {
id 'com.android.library' version '8.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.9.0' apply false
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.20" apply false
+ id 'com.google.dagger.hilt.android' version '2.51' apply false
}
\ No newline at end of file