From 935d201bd5c6361605254ccb02af92425a66fc5f Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 11:41:30 +0900 Subject: [PATCH 01/16] =?UTF-8?q?[feat/#10]=20api=205=EA=B0=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20service=EC=99=80=20model=EC=9D=84=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => datasource}/TokenLocalDataSource.kt | 2 +- .../TokenLocalDataSourceImpl.kt | 3 ++- .../model/request/UserInfoUpdateRequestDto.kt | 10 +++++++++ ...obbyResponseDto.kt => HobbyResponseDto.kt} | 2 +- .../and/data/remote/service/AuthService.kt | 5 +++-- .../and/data/remote/service/UserService.kt | 22 ++++++++++++++++--- .../repositoryimpl/TokenRepositoryImpl.kt | 2 +- .../org/sopt/and/di/TokenDataStoreModule.kt | 4 ++-- .../ui/main/screen/MainViewModel.kt | 12 +++++----- 9 files changed, 45 insertions(+), 17 deletions(-) rename app/src/main/java/org/sopt/and/data/local/{ => datasource}/TokenLocalDataSource.kt (64%) rename app/src/main/java/org/sopt/and/data/local/{ => datasourceimpl}/TokenLocalDataSourceImpl.kt (83%) create mode 100644 app/src/main/java/org/sopt/and/data/remote/model/request/UserInfoUpdateRequestDto.kt rename app/src/main/java/org/sopt/and/data/remote/model/response/{GetMyHobbyResponseDto.kt => HobbyResponseDto.kt} (84%) diff --git a/app/src/main/java/org/sopt/and/data/local/TokenLocalDataSource.kt b/app/src/main/java/org/sopt/and/data/local/datasource/TokenLocalDataSource.kt similarity index 64% rename from app/src/main/java/org/sopt/and/data/local/TokenLocalDataSource.kt rename to app/src/main/java/org/sopt/and/data/local/datasource/TokenLocalDataSource.kt index cbe23b9..e6303bb 100644 --- a/app/src/main/java/org/sopt/and/data/local/TokenLocalDataSource.kt +++ b/app/src/main/java/org/sopt/and/data/local/datasource/TokenLocalDataSource.kt @@ -1,4 +1,4 @@ -package org.sopt.and.data.local +package org.sopt.and.data.local.datasource interface TokenLocalDataSource { var token: String diff --git a/app/src/main/java/org/sopt/and/data/local/TokenLocalDataSourceImpl.kt b/app/src/main/java/org/sopt/and/data/local/datasourceimpl/TokenLocalDataSourceImpl.kt similarity index 83% rename from app/src/main/java/org/sopt/and/data/local/TokenLocalDataSourceImpl.kt rename to app/src/main/java/org/sopt/and/data/local/datasourceimpl/TokenLocalDataSourceImpl.kt index 32858b1..b211fcc 100644 --- a/app/src/main/java/org/sopt/and/data/local/TokenLocalDataSourceImpl.kt +++ b/app/src/main/java/org/sopt/and/data/local/datasourceimpl/TokenLocalDataSourceImpl.kt @@ -1,6 +1,7 @@ -package org.sopt.and.data.local +package org.sopt.and.data.local.datasourceimpl import android.content.SharedPreferences +import org.sopt.and.data.local.datasource.TokenLocalDataSource import javax.inject.Inject class TokenLocalDataSourceImpl @Inject constructor( diff --git a/app/src/main/java/org/sopt/and/data/remote/model/request/UserInfoUpdateRequestDto.kt b/app/src/main/java/org/sopt/and/data/remote/model/request/UserInfoUpdateRequestDto.kt new file mode 100644 index 0000000..934535d --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/remote/model/request/UserInfoUpdateRequestDto.kt @@ -0,0 +1,10 @@ +package org.sopt.and.data.remote.model.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class UserInfoUpdateRequestDto ( + @SerialName("hobby") val hobby: String?, + @SerialName("password") val password: String? +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/model/response/GetMyHobbyResponseDto.kt b/app/src/main/java/org/sopt/and/data/remote/model/response/HobbyResponseDto.kt similarity index 84% rename from app/src/main/java/org/sopt/and/data/remote/model/response/GetMyHobbyResponseDto.kt rename to app/src/main/java/org/sopt/and/data/remote/model/response/HobbyResponseDto.kt index e1f2d5b..bbf0e2b 100644 --- a/app/src/main/java/org/sopt/and/data/remote/model/response/GetMyHobbyResponseDto.kt +++ b/app/src/main/java/org/sopt/and/data/remote/model/response/HobbyResponseDto.kt @@ -4,6 +4,6 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class GetMyHobbyResponseDto( +data class HobbyResponseDto( @SerialName("hobby") val hobby: String ) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt b/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt index 960af66..0348f0d 100644 --- a/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt +++ b/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt @@ -1,5 +1,6 @@ package org.sopt.and.data.remote.service +import org.sopt.and.data.remote.model.base.ApiResponse import org.sopt.and.data.remote.model.request.LoginRequestDto import org.sopt.and.data.remote.model.request.UserRegistrationRequestDto import org.sopt.and.data.remote.model.response.LoginResponseDto @@ -12,10 +13,10 @@ interface AuthService { @POST("user") fun registerUser( @Body userRegistrationRequestDto: UserRegistrationRequestDto - ): Call + ): ApiResponse @POST("login") fun login( @Body loginRequestDto: LoginRequestDto - ): Call + ): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt b/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt index 5653f60..0ae9d5e 100644 --- a/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt +++ b/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt @@ -1,13 +1,29 @@ package org.sopt.and.data.remote.service -import org.sopt.and.data.remote.model.response.GetMyHobbyResponseDto -import retrofit2.Call +import org.sopt.and.data.remote.model.base.ApiResponse +import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto +import org.sopt.and.data.remote.model.response.HobbyResponseDto +import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header +import retrofit2.http.PUT +import retrofit2.http.Path interface UserService { @GET("user/my-hobby") fun getMyHobby( @Header("token") token: String - ): Call + ): ApiResponse + + @GET("user/{no}/hobby") + fun getOthersHobby( + @Header("token") token: String, + @Path("no") userNo: Int + ): ApiResponse + + @PUT("user") + fun updateUserInfo( + @Header("token") token: String, + @Body userInfoUpdateRequestDto: UserInfoUpdateRequestDto + ): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/repositoryimpl/TokenRepositoryImpl.kt b/app/src/main/java/org/sopt/and/data/repositoryimpl/TokenRepositoryImpl.kt index eab9008..fd59e75 100644 --- a/app/src/main/java/org/sopt/and/data/repositoryimpl/TokenRepositoryImpl.kt +++ b/app/src/main/java/org/sopt/and/data/repositoryimpl/TokenRepositoryImpl.kt @@ -1,6 +1,6 @@ package org.sopt.and.data.repositoryimpl -import org.sopt.and.data.local.TokenLocalDataSource +import org.sopt.and.data.local.datasource.TokenLocalDataSource import org.sopt.and.domain.repository.TokenRepository import javax.inject.Inject diff --git a/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt b/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt index 3eb6c9a..d5310b1 100644 --- a/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt +++ b/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt @@ -5,8 +5,8 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import org.sopt.and.data.local.TokenLocalDataSource -import org.sopt.and.data.local.TokenLocalDataSourceImpl +import org.sopt.and.data.local.datasource.TokenLocalDataSource +import org.sopt.and.data.local.datasourceimpl.TokenLocalDataSourceImpl import javax.inject.Singleton @Module diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt index 214b2f5..60d24e2 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt @@ -6,8 +6,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import org.sopt.and.data.local.TokenLocalDataSource -import org.sopt.and.data.remote.model.response.GetMyHobbyResponseDto +import org.sopt.and.data.local.datasource.TokenLocalDataSource +import org.sopt.and.data.remote.model.response.HobbyResponseDto import org.sopt.and.di.ServicePool import retrofit2.Call import retrofit2.Callback @@ -26,10 +26,10 @@ class MainViewModel @Inject constructor( fun getUserHobby() { viewModelScope.launch { userService.getMyHobby(token = tokenLocalDataSource.token).enqueue(object : - Callback { + Callback { override fun onResponse( - call: Call, - response: Response + call: Call, + response: Response ) { if (response.isSuccessful) { _userHobbyState.value = @@ -40,7 +40,7 @@ class MainViewModel @Inject constructor( } override fun onFailure( - call: Call, + call: Call, t: Throwable ) { _userHobbyState.value = UserHobbyState.Failure(t.message.toString()) From f48e44e4a628cf3280367d5d202821e77568b602 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 13:16:26 +0900 Subject: [PATCH 02/16] =?UTF-8?q?[feat/#10]=20datasource=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/datasource/AuthRemoteDataSource.kt | 12 +++++++++ .../remote/datasource/UserRemoteDataSource.kt | 11 ++++++++ .../AuthRemoteDataSourceImpl.kt | 20 +++++++++++++++ .../UserRemoteDataSourceImpl.kt | 25 +++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 app/src/main/java/org/sopt/and/data/remote/datasource/AuthRemoteDataSource.kt create mode 100644 app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt create mode 100644 app/src/main/java/org/sopt/and/data/remote/datasourceimpl/AuthRemoteDataSourceImpl.kt create mode 100644 app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt diff --git a/app/src/main/java/org/sopt/and/data/remote/datasource/AuthRemoteDataSource.kt b/app/src/main/java/org/sopt/and/data/remote/datasource/AuthRemoteDataSource.kt new file mode 100644 index 0000000..5f68b97 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/remote/datasource/AuthRemoteDataSource.kt @@ -0,0 +1,12 @@ +package org.sopt.and.data.remote.datasource + +import org.sopt.and.data.remote.model.base.ApiResponse +import org.sopt.and.data.remote.model.request.LoginRequestDto +import org.sopt.and.data.remote.model.request.UserRegistrationRequestDto +import org.sopt.and.data.remote.model.response.LoginResponseDto +import org.sopt.and.data.remote.model.response.UserRegistrationResponseDto + +interface AuthRemoteDataSource { + suspend fun registerUser(userRegistrationRequestDto: UserRegistrationRequestDto): ApiResponse + suspend fun login(loginRequestDto: LoginRequestDto): ApiResponse +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt b/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt new file mode 100644 index 0000000..69f7e41 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt @@ -0,0 +1,11 @@ +package org.sopt.and.data.remote.datasource + +import org.sopt.and.data.remote.model.base.ApiResponse +import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto +import org.sopt.and.data.remote.model.response.HobbyResponseDto + +interface UserRemoteDataSource { + suspend fun getMyHobby(token: String): ApiResponse + suspend fun getOthersHobby(token: String, userNo: Int): ApiResponse + suspend fun updateUserInfo(token: String, userInfoUpdateRequestDto: UserInfoUpdateRequestDto): ApiResponse +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/AuthRemoteDataSourceImpl.kt b/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/AuthRemoteDataSourceImpl.kt new file mode 100644 index 0000000..3b9d4c6 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/AuthRemoteDataSourceImpl.kt @@ -0,0 +1,20 @@ +package org.sopt.and.data.remote.datasourceimpl + +import org.sopt.and.data.remote.datasource.AuthRemoteDataSource +import org.sopt.and.data.remote.model.base.ApiResponse +import org.sopt.and.data.remote.model.request.LoginRequestDto +import org.sopt.and.data.remote.model.request.UserRegistrationRequestDto +import org.sopt.and.data.remote.model.response.LoginResponseDto +import org.sopt.and.data.remote.model.response.UserRegistrationResponseDto +import org.sopt.and.data.remote.service.AuthService +import javax.inject.Inject + +class AuthRemoteDataSourceImpl @Inject constructor( + private val authService: AuthService +) : AuthRemoteDataSource { + override suspend fun registerUser(userRegistrationRequestDto: UserRegistrationRequestDto): ApiResponse = + authService.registerUser(userRegistrationRequestDto = userRegistrationRequestDto) + + override suspend fun login(loginRequestDto: LoginRequestDto): ApiResponse = + authService.login(loginRequestDto = loginRequestDto) +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt b/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt new file mode 100644 index 0000000..8dbeb99 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt @@ -0,0 +1,25 @@ +package org.sopt.and.data.remote.datasourceimpl + +import org.sopt.and.data.remote.datasource.UserRemoteDataSource +import org.sopt.and.data.remote.model.base.ApiResponse +import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto +import org.sopt.and.data.remote.model.response.HobbyResponseDto +import org.sopt.and.data.remote.service.UserService +import javax.inject.Inject + +class UserRemoteDataSourceImpl @Inject constructor( + private val userService: UserService +): UserRemoteDataSource { + override suspend fun getMyHobby(token: String): ApiResponse = + userService.getMyHobby(token = token) + + + override suspend fun getOthersHobby(token: String, userNo: Int): ApiResponse = + userService.getOthersHobby(token = token, userNo = userNo) + + override suspend fun updateUserInfo( + token: String, + userInfoUpdateRequestDto: UserInfoUpdateRequestDto + ): ApiResponse = + userService.updateUserInfo(token = token, userInfoUpdateRequestDto = userInfoUpdateRequestDto) +} \ No newline at end of file From daf06f552fbfca2a083da358190be928278c77b4 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 15:29:45 +0900 Subject: [PATCH 03/16] =?UTF-8?q?[feat/#10]=20repository=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/and/data/mapper/todata/UserMapper.kt | 23 +++++++++++ .../mapper/todomain/HobbyResponseDtoMapper.kt | 8 ++++ .../mapper/todomain/LoginResponseDtoMapper.kt | 8 ++++ .../UserRegistrationResponseDtoMapper.kt | 8 ++++ .../and/data/remote/util/handleApiResponse.kt | 8 ++++ .../data/repositoryimpl/AuthRepositoryImpl.kt | 32 ++++++++++++++++ .../data/repositoryimpl/UserRepositoryImpl.kt | 38 +++++++++++++++++++ app/src/main/java/org/sopt/and/domain/User.kt | 7 ---- .../java/org/sopt/and/domain/model/Hobby.kt | 5 +++ .../java/org/sopt/and/domain/model/Token.kt | 5 +++ .../java/org/sopt/and/domain/model/User.kt | 7 ++++ .../java/org/sopt/and/domain/model/UserNo.kt | 5 +++ .../and/domain/repository/AuthRepository.kt | 10 +++++ .../and/domain/repository/UserRepository.kt | 9 +++++ 14 files changed, 166 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/org/sopt/and/data/mapper/todata/UserMapper.kt create mode 100644 app/src/main/java/org/sopt/and/data/mapper/todomain/HobbyResponseDtoMapper.kt create mode 100644 app/src/main/java/org/sopt/and/data/mapper/todomain/LoginResponseDtoMapper.kt create mode 100644 app/src/main/java/org/sopt/and/data/mapper/todomain/UserRegistrationResponseDtoMapper.kt create mode 100644 app/src/main/java/org/sopt/and/data/remote/util/handleApiResponse.kt create mode 100644 app/src/main/java/org/sopt/and/data/repositoryimpl/AuthRepositoryImpl.kt create mode 100644 app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt delete mode 100644 app/src/main/java/org/sopt/and/domain/User.kt create mode 100644 app/src/main/java/org/sopt/and/domain/model/Hobby.kt create mode 100644 app/src/main/java/org/sopt/and/domain/model/Token.kt create mode 100644 app/src/main/java/org/sopt/and/domain/model/User.kt create mode 100644 app/src/main/java/org/sopt/and/domain/model/UserNo.kt create mode 100644 app/src/main/java/org/sopt/and/domain/repository/AuthRepository.kt create mode 100644 app/src/main/java/org/sopt/and/domain/repository/UserRepository.kt diff --git a/app/src/main/java/org/sopt/and/data/mapper/todata/UserMapper.kt b/app/src/main/java/org/sopt/and/data/mapper/todata/UserMapper.kt new file mode 100644 index 0000000..2fa8df9 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/mapper/todata/UserMapper.kt @@ -0,0 +1,23 @@ +package org.sopt.and.data.mapper.todata + +import org.sopt.and.data.remote.model.request.LoginRequestDto +import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto +import org.sopt.and.data.remote.model.request.UserRegistrationRequestDto +import org.sopt.and.domain.model.User + +fun User.toUserRegistrationRequestDto(): UserRegistrationRequestDto = UserRegistrationRequestDto( + username = this.username, + password = this.password, + hobby = this.hobby +) + +fun User.toLoginRequestDto(): LoginRequestDto = LoginRequestDto( + username = this.username, + password = this.password +) + + +fun User.toUserInfoUpdateRequestDto(): UserInfoUpdateRequestDto = UserInfoUpdateRequestDto( + hobby = this.hobby, + password = this.password +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/mapper/todomain/HobbyResponseDtoMapper.kt b/app/src/main/java/org/sopt/and/data/mapper/todomain/HobbyResponseDtoMapper.kt new file mode 100644 index 0000000..7614ae6 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/mapper/todomain/HobbyResponseDtoMapper.kt @@ -0,0 +1,8 @@ +package org.sopt.and.data.mapper.todomain + +import org.sopt.and.data.remote.model.response.HobbyResponseDto +import org.sopt.and.domain.model.Hobby + +fun HobbyResponseDto.toDomain(): Hobby = Hobby( + hobby = this.hobby +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/mapper/todomain/LoginResponseDtoMapper.kt b/app/src/main/java/org/sopt/and/data/mapper/todomain/LoginResponseDtoMapper.kt new file mode 100644 index 0000000..2179712 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/mapper/todomain/LoginResponseDtoMapper.kt @@ -0,0 +1,8 @@ +package org.sopt.and.data.mapper.todomain + +import org.sopt.and.data.remote.model.response.LoginResponseDto +import org.sopt.and.domain.model.Token + +fun LoginResponseDto.toDomain(): Token = Token( + token = this.token +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/mapper/todomain/UserRegistrationResponseDtoMapper.kt b/app/src/main/java/org/sopt/and/data/mapper/todomain/UserRegistrationResponseDtoMapper.kt new file mode 100644 index 0000000..3fb8702 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/mapper/todomain/UserRegistrationResponseDtoMapper.kt @@ -0,0 +1,8 @@ +package org.sopt.and.data.mapper.todomain + +import org.sopt.and.data.remote.model.response.UserRegistrationResponseDto +import org.sopt.and.domain.model.UserNo + +fun UserRegistrationResponseDto.toDomain(): UserNo = UserNo( + no = this.no +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/util/handleApiResponse.kt b/app/src/main/java/org/sopt/and/data/remote/util/handleApiResponse.kt new file mode 100644 index 0000000..c9d5f2a --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/remote/util/handleApiResponse.kt @@ -0,0 +1,8 @@ +package org.sopt.and.data.remote.util + +import org.sopt.and.data.remote.model.base.ApiResponse + +fun ApiResponse.handleApiResponse(): Result { + return if (this.result != null) Result.success(this.result) + else Result.failure(Exception("No data Available")) +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/repositoryimpl/AuthRepositoryImpl.kt b/app/src/main/java/org/sopt/and/data/repositoryimpl/AuthRepositoryImpl.kt new file mode 100644 index 0000000..a69a964 --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/repositoryimpl/AuthRepositoryImpl.kt @@ -0,0 +1,32 @@ +package org.sopt.and.data.repositoryimpl + +import org.sopt.and.data.mapper.todata.toUserRegistrationRequestDto +import org.sopt.and.data.mapper.todomain.toDomain +import org.sopt.and.data.remote.datasource.AuthRemoteDataSource +import org.sopt.and.data.remote.model.request.LoginRequestDto +import org.sopt.and.data.remote.util.handleApiResponse +import org.sopt.and.domain.model.Token +import org.sopt.and.domain.model.User +import org.sopt.and.domain.model.UserNo +import org.sopt.and.domain.repository.AuthRepository +import javax.inject.Inject + +class AuthRepositoryImpl @Inject constructor( + private val authRemoteDataSource: AuthRemoteDataSource +): AuthRepository { + override suspend fun registerUser(user: User): Result { + return runCatching { + authRemoteDataSource.registerUser(userRegistrationRequestDto = user.toUserRegistrationRequestDto()).handleApiResponse().getOrThrow().toDomain() + } + } + + override suspend fun login(username: String, password: String): Result { + return runCatching { + authRemoteDataSource.login(loginRequestDto = LoginRequestDto( + username = username, + password = password + )).handleApiResponse().getOrThrow().toDomain() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt b/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt new file mode 100644 index 0000000..fb9001f --- /dev/null +++ b/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt @@ -0,0 +1,38 @@ +package org.sopt.and.data.repositoryimpl + +import org.sopt.and.data.mapper.todomain.toDomain +import org.sopt.and.data.remote.datasource.UserRemoteDataSource +import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto +import org.sopt.and.data.remote.util.handleApiResponse +import org.sopt.and.domain.model.Hobby +import org.sopt.and.domain.repository.UserRepository +import javax.inject.Inject + +class UserRepositoryImpl @Inject constructor( + private val userRemoteDataSource: UserRemoteDataSource +): UserRepository { + override suspend fun getMyHobby(token: String): Result { + return runCatching { + userRemoteDataSource.getMyHobby(token = token).handleApiResponse().getOrThrow().toDomain() + } + } + + override suspend fun getOthersHobby(token: String, userNo: Int): Result { + return runCatching { + userRemoteDataSource.getOthersHobby(token = token, userNo = userNo).handleApiResponse().getOrThrow().toDomain() + } + } + + override suspend fun updateUserInfo( + token: String, + password: String?, + hobby: String? + ): Result { + return runCatching { + userRemoteDataSource.updateUserInfo(token = token, userInfoUpdateRequestDto = UserInfoUpdateRequestDto( + password = password, + hobby = hobby + )).handleApiResponse().getOrThrow() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/User.kt b/app/src/main/java/org/sopt/and/domain/User.kt deleted file mode 100644 index 67756cb..0000000 --- a/app/src/main/java/org/sopt/and/domain/User.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.sopt.and.domain - -data class User( - val name: String, - val password: String, - val hobby: String -) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/model/Hobby.kt b/app/src/main/java/org/sopt/and/domain/model/Hobby.kt new file mode 100644 index 0000000..cff60c9 --- /dev/null +++ b/app/src/main/java/org/sopt/and/domain/model/Hobby.kt @@ -0,0 +1,5 @@ +package org.sopt.and.domain.model + +data class Hobby( + val hobby: String +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/model/Token.kt b/app/src/main/java/org/sopt/and/domain/model/Token.kt new file mode 100644 index 0000000..947b584 --- /dev/null +++ b/app/src/main/java/org/sopt/and/domain/model/Token.kt @@ -0,0 +1,5 @@ +package org.sopt.and.domain.model + +data class Token( + val token: String +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/model/User.kt b/app/src/main/java/org/sopt/and/domain/model/User.kt new file mode 100644 index 0000000..6d0efe2 --- /dev/null +++ b/app/src/main/java/org/sopt/and/domain/model/User.kt @@ -0,0 +1,7 @@ +package org.sopt.and.domain.model + +data class User( + val username: String, + val password: String, + val hobby: String, +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/model/UserNo.kt b/app/src/main/java/org/sopt/and/domain/model/UserNo.kt new file mode 100644 index 0000000..8bad4cf --- /dev/null +++ b/app/src/main/java/org/sopt/and/domain/model/UserNo.kt @@ -0,0 +1,5 @@ +package org.sopt.and.domain.model + +data class UserNo( + val no: Int +) \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/repository/AuthRepository.kt b/app/src/main/java/org/sopt/and/domain/repository/AuthRepository.kt new file mode 100644 index 0000000..d71c32b --- /dev/null +++ b/app/src/main/java/org/sopt/and/domain/repository/AuthRepository.kt @@ -0,0 +1,10 @@ +package org.sopt.and.domain.repository + +import org.sopt.and.domain.model.Token +import org.sopt.and.domain.model.User +import org.sopt.and.domain.model.UserNo + +interface AuthRepository { + suspend fun registerUser(user: User): Result + suspend fun login(username: String, password: String): Result +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/domain/repository/UserRepository.kt b/app/src/main/java/org/sopt/and/domain/repository/UserRepository.kt new file mode 100644 index 0000000..a69788d --- /dev/null +++ b/app/src/main/java/org/sopt/and/domain/repository/UserRepository.kt @@ -0,0 +1,9 @@ +package org.sopt.and.domain.repository + +import org.sopt.and.domain.model.Hobby + +interface UserRepository { + suspend fun getMyHobby(token: String): Result + suspend fun getOthersHobby(token: String, userNo: Int): Result + suspend fun updateUserInfo(token: String, password: String?, hobby: String?): Result +} \ No newline at end of file From 22edaeb773cd3ba5e50296bdf4c1b84798a6edc4 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 20:02:24 +0900 Subject: [PATCH 04/16] =?UTF-8?q?[feat/#10]=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EC=A3=BC=EC=9E=85=EC=9D=84=20=EC=9C=84=ED=95=9C=20module?= =?UTF-8?q?=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/sopt/and/di/ApiFactory.kt | 38 ------------- .../java/org/sopt/and/di/DataSourceModule.kt | 37 ++++++++++++ .../java/org/sopt/and/di/NetworkModule.kt | 56 +++++++++++++++++++ .../java/org/sopt/and/di/RepositoryModule.kt | 16 ++++++ .../java/org/sopt/and/di/ServiceModule.kt | 24 ++++++++ .../org/sopt/and/di/TokenDataStoreModule.kt | 20 ------- 6 files changed, 133 insertions(+), 58 deletions(-) delete mode 100644 app/src/main/java/org/sopt/and/di/ApiFactory.kt create mode 100644 app/src/main/java/org/sopt/and/di/DataSourceModule.kt create mode 100644 app/src/main/java/org/sopt/and/di/NetworkModule.kt create mode 100644 app/src/main/java/org/sopt/and/di/ServiceModule.kt delete mode 100644 app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt diff --git a/app/src/main/java/org/sopt/and/di/ApiFactory.kt b/app/src/main/java/org/sopt/and/di/ApiFactory.kt deleted file mode 100644 index 67532d3..0000000 --- a/app/src/main/java/org/sopt/and/di/ApiFactory.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.sopt.and.di - -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import org.sopt.and.BuildConfig -import org.sopt.and.data.remote.service.AuthService -import org.sopt.and.data.remote.service.UserService -import retrofit2.Retrofit - -object ApiFactory { - private const val BASE_URL: String = BuildConfig.BASE_URL - - private val loggingInterceptor = HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - } - - private val client = OkHttpClient.Builder() - .addInterceptor(loggingInterceptor) - .build() - - val retrofit: Retrofit by lazy { - Retrofit.Builder() - .baseUrl(BASE_URL) - .client(client) - .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) - .build() - } - - inline fun create(): T = retrofit.create(T::class.java) -} - -object ServicePool { - val authService = ApiFactory.create() - val userService = ApiFactory.create() -} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/di/DataSourceModule.kt b/app/src/main/java/org/sopt/and/di/DataSourceModule.kt new file mode 100644 index 0000000..7512eb7 --- /dev/null +++ b/app/src/main/java/org/sopt/and/di/DataSourceModule.kt @@ -0,0 +1,37 @@ +package org.sopt.and.di + +import android.content.SharedPreferences +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import org.sopt.and.data.local.datasource.TokenLocalDataSource +import org.sopt.and.data.local.datasourceimpl.TokenLocalDataSourceImpl +import org.sopt.and.data.remote.datasource.AuthRemoteDataSource +import org.sopt.and.data.remote.datasource.UserRemoteDataSource +import org.sopt.and.data.remote.datasourceimpl.AuthRemoteDataSourceImpl +import org.sopt.and.data.remote.datasourceimpl.UserRemoteDataSourceImpl +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class DataSourceModule { + @Binds + @Singleton + fun bindTokenLocalDataSource(sharedPreferences: SharedPreferences): TokenLocalDataSource { + return TokenLocalDataSourceImpl(sharedPreferences) + } + + @Binds + @Singleton + abstract fun bindAuthRemoteDataSource( + authRemoteDataSourceImpl: AuthRemoteDataSourceImpl + ): AuthRemoteDataSource + + @Binds + @Singleton + abstract fun bindUserRemoteDataSource( + userRemoteDataSourceImpl: UserRemoteDataSourceImpl + ): UserRemoteDataSource +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/di/NetworkModule.kt b/app/src/main/java/org/sopt/and/di/NetworkModule.kt new file mode 100644 index 0000000..5d258f3 --- /dev/null +++ b/app/src/main/java/org/sopt/and/di/NetworkModule.kt @@ -0,0 +1,56 @@ +package org.sopt.and.di + +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.sopt.and.BuildConfig +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object NetworkModule { + @Provides + @Singleton + fun providesJson(): Json = + Json { + isLenient = true + prettyPrint = true + explicitNulls = false + ignoreUnknownKeys = true + } + + @Provides + @Singleton + fun provideLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + } + + @Provides + @Singleton + fun providesOkHttpClient( + loggingInterceptor: HttpLoggingInterceptor, + ): OkHttpClient = + OkHttpClient.Builder().apply { + addInterceptor(loggingInterceptor) + }.build() + + @Provides + @Singleton + fun provideRetrofit( + okHttpClient: OkHttpClient, + json: Json + ): Retrofit { + return Retrofit.Builder() + .baseUrl(BuildConfig.BASE_URL) + .client(okHttpClient) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/di/RepositoryModule.kt b/app/src/main/java/org/sopt/and/di/RepositoryModule.kt index 5994e15..ae4f975 100644 --- a/app/src/main/java/org/sopt/and/di/RepositoryModule.kt +++ b/app/src/main/java/org/sopt/and/di/RepositoryModule.kt @@ -4,8 +4,12 @@ import dagger.Binds import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import org.sopt.and.data.repositoryimpl.AuthRepositoryImpl import org.sopt.and.domain.repository.TokenRepository import org.sopt.and.data.repositoryimpl.TokenRepositoryImpl +import org.sopt.and.data.repositoryimpl.UserRepositoryImpl +import org.sopt.and.domain.repository.AuthRepository +import org.sopt.and.domain.repository.UserRepository import javax.inject.Singleton @Module @@ -16,4 +20,16 @@ abstract class RepositoryModule { abstract fun bindTokenRepository( tokenRepositoryImpl: TokenRepositoryImpl ): TokenRepository + + @Binds + @Singleton + abstract fun bindAuthRepository( + authRepositoryImpl: AuthRepositoryImpl + ): AuthRepository + + @Binds + @Singleton + abstract fun bindUserRepository( + userRepositoryImpl: UserRepositoryImpl + ): UserRepository } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/di/ServiceModule.kt b/app/src/main/java/org/sopt/and/di/ServiceModule.kt new file mode 100644 index 0000000..4f74b4d --- /dev/null +++ b/app/src/main/java/org/sopt/and/di/ServiceModule.kt @@ -0,0 +1,24 @@ +package org.sopt.and.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import org.sopt.and.data.remote.service.AuthService +import org.sopt.and.data.remote.service.UserService +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ServiceModule { + @Provides + @Singleton + fun provideAuthService(retrofit: Retrofit): AuthService = + retrofit.create(AuthService::class.java) + + @Provides + @Singleton + fun provideUserService(retrofit: Retrofit): UserService = + retrofit.create(UserService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt b/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt deleted file mode 100644 index d5310b1..0000000 --- a/app/src/main/java/org/sopt/and/di/TokenDataStoreModule.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.sopt.and.di - -import android.content.SharedPreferences -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import org.sopt.and.data.local.datasource.TokenLocalDataSource -import org.sopt.and.data.local.datasourceimpl.TokenLocalDataSourceImpl -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object TokenLocalDataSourceModule { - @Provides - @Singleton - fun provideTokenLocalDataSource(sharedPreferences: SharedPreferences): TokenLocalDataSource { - return TokenLocalDataSourceImpl(sharedPreferences) - } -} \ No newline at end of file From 663c08dd94b461dc9fd85cd579318c7976a0d437 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 20:09:10 +0900 Subject: [PATCH 05/16] =?UTF-8?q?[feat/#10]=20SignIn,=20SignUp=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=B0=EC=9D=98=20=EC=84=9C=EB=B2=84=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=EC=9D=84=20Coroutine=20=EB=B0=A9=EC=8B=9D=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/auth/screen/AuthState.kt | 11 +- .../ui/auth/screen/AuthViewModel.kt | 102 ++++++------------ .../ui/auth/screen/SignInScreen.kt | 46 ++++---- .../ui/auth/screen/SignUpScreen.kt | 48 ++++----- 4 files changed, 85 insertions(+), 122 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthState.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthState.kt index 07882d8..719c434 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthState.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthState.kt @@ -1,17 +1,18 @@ package org.sopt.and.presentation.ui.auth.screen -import org.sopt.and.data.remote.model.response.UserRegistrationResponseDto +import org.sopt.and.domain.model.Token +import org.sopt.and.domain.model.UserNo sealed class SignInState { data object Idle : SignInState() - data object EmailEmpty : SignInState() - data object PasswordEmpty : SignInState() - data object Success : SignInState() + data object Loading : SignInState() + data class Success(val result: Token) : SignInState() data class Failure(val errorMessage: String) : SignInState() } sealed class SignUpState { data object Idle : SignUpState() - data class Success(val response: UserRegistrationResponseDto?) : SignUpState() + data object Loading : SignUpState() + data class Success(val result: UserNo) : SignUpState() data class Failure(val errorMessage: String) : SignUpState() } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt index ff9096a..14bd85e 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt @@ -1,106 +1,64 @@ package org.sopt.and.presentation.ui.auth.screen import android.util.Log -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import org.sopt.and.data.remote.model.request.LoginRequestDto -import org.sopt.and.data.remote.model.request.UserRegistrationRequestDto -import org.sopt.and.data.remote.model.response.LoginResponseDto -import org.sopt.and.data.remote.model.response.UserRegistrationResponseDto +import org.sopt.and.domain.model.User +import org.sopt.and.domain.repository.AuthRepository import org.sopt.and.domain.repository.TokenRepository -import org.sopt.and.di.ServicePool -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response import javax.inject.Inject @HiltViewModel class AuthViewModel @Inject constructor( private val tokenRepository: TokenRepository, + private val authRepository: AuthRepository, ) : ViewModel() { - private val authService by lazy { ServicePool.authService } - private val _signInState = MutableStateFlow(SignInState.Idle) val signInState: StateFlow = _signInState - private val _signInUserName = MutableLiveData("") - val signInUserName: LiveData = _signInUserName - - private val _signInPassword = MutableLiveData("") - val signInPassword: LiveData = _signInPassword + private val _token = MutableStateFlow("") + val token: StateFlow = _token private val _signUpState = MutableStateFlow(SignUpState.Idle) val signUpState: StateFlow = _signUpState - private val _signUpUserName = MutableLiveData("") - val signUpUserName: LiveData = _signUpUserName - - private val _signUpPassword = MutableLiveData("") - val signUpPassword: LiveData = _signUpPassword - - private val _signUpHobby = MutableLiveData("") - val signUpHobby: LiveData = _signUpHobby - - - - fun validateSignIn(inputEmail: String, inputPassword: String) { + fun validateSignIn(username: String, password: String) { + _signInState.value = SignInState.Loading viewModelScope.launch { - authService.login(LoginRequestDto(inputEmail, inputPassword)).enqueue(object : - Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - _signInState.value = SignInState.Success - tokenRepository.setToken(token = response.body()!!.token) - Log.d("token", tokenRepository.getToken()) - } else { - _signInState.value = SignInState.Failure(response.message()) - } - } - - override fun onFailure( - call: Call, - t: Throwable - ) { - _signInState.value = SignInState.Failure(t.message.toString()) + val result = authRepository.login(username = username, password = password) + _signInState.value = result.fold( + onSuccess = { token -> + tokenRepository.setToken(token.token) + Log.d("token","토큰 저장 완료! 저장값은 ${token.token} 입니다.") + SignInState.Success(token) + }, + onFailure = { + SignInState.Failure(it.localizedMessage ?: "에러 발생") } - } ) } } - fun validateSignUp(email: String, password: String, hobby: String) { + fun validateSignUp(username: String, password: String, hobby: String) { + _signUpState.value = SignUpState.Loading viewModelScope.launch { - authService.registerUser(UserRegistrationRequestDto(email, password, hobby)) - .enqueue(object : - Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - _signUpState.value = SignUpState.Success(response.body()) - } else { - _signUpState.value = SignUpState.Failure(response.message()) - } - } - - override fun onFailure( - call: Call, - t: Throwable - ) { - _signUpState.value = SignUpState.Failure(t.message.toString()) - } + val result = authRepository.registerUser(user = User( + username = username, + password = password, + hobby = hobby + )) + _signUpState.value = result.fold( + onSuccess = { + SignUpState.Success(it) + }, + onFailure = { + SignUpState.Failure(it.localizedMessage ?: "에러 발생") } - ) + ) } } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt index 2302604..cfbdd02 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt @@ -49,36 +49,24 @@ fun SignInRoute( navigateToSignUp: () -> Unit, navigateToMain: () -> Unit, ) { - val context = LocalContext.current val signInState by authViewModel.signInState.collectAsState() - val onSignInClick: (String, String) -> Unit = { email, password -> - authViewModel.validateSignIn(email, password) - when (signInState) { - is SignInState.Success -> { - showToast(context = context, message = "로그인에 성공했습니다.") - navigateToMain() - } - - is SignInState.Failure -> { - showToast(context = context, message = "로그인에 실패했습니다.") - } - - else -> {} - } - } - SignInScreen( + signInState = signInState, onSignUpClick = navigateToSignUp, - onSignInClick = onSignInClick, + onSignInClick = { email, password -> authViewModel.validateSignIn(email, password) }, + navigateToMain = navigateToMain ) } @Composable fun SignInScreen( + signInState: SignInState, onSignUpClick: () -> Unit, - onSignInClick: (String, String) -> Unit + onSignInClick: (String, String) -> Unit, + navigateToMain: () -> Unit, ) { + val context = LocalContext.current var inputEmail by remember { mutableStateOf("") } var inputPassword by remember { mutableStateOf("") } @@ -139,8 +127,7 @@ fun SignInScreen( Button( onClick = { onSignInClick(inputEmail, inputPassword) }, - modifier = Modifier - .fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), colors = ButtonDefaults.buttonColors(Color(0xFF0F42C7)) ) { Text( @@ -227,6 +214,23 @@ fun SignInScreen( } Spacer(modifier = Modifier.weight(1f)) + + when(signInState) { + is SignInState.Success -> { + showToast( + context = context, + message = "로그인에 성공했습니다." + ) + navigateToMain() + } + is SignInState.Failure -> { + showToast( + context = context, + message = "아이디와 비밃번호를 다시 확인해주세요." + ) + } + else -> {} + } } } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt index 0828948..5146693 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt @@ -47,39 +47,24 @@ fun SignUpRoute( navigateToSignIn: () -> Unit, navigateToBack: () -> Unit, ) { - val context = LocalContext.current val signUpState by authViewModel.signUpState.collectAsState() - val onSignUpClick: (String, String, String) -> Unit = { username, password, hobby -> - authViewModel.validateSignUp(username, password, hobby) - when (signUpState) { - is SignUpState.Success -> { - showToast( - context = context, - message = "회원가입에 성공했습니다. 회원번호는 ${(signUpState as SignUpState.Success).response?.result?.no}입니다." - ) - navigateToSignIn() - } - - is SignUpState.Failure -> { - showToast(context = context, message = "회원가입에 실패하였습니다.") - } - - else -> {} - } - } - SignUpScreen( - onSignUpClick = onSignUpClick, + signUpState = signUpState, + onSignUpClick = { username, password, hobby -> authViewModel.validateSignUp(username, password, hobby)}, + navigateToSignIn = navigateToSignIn, onCancelClick = navigateToBack, ) } @Composable fun SignUpScreen( + signUpState: SignUpState, onSignUpClick: (String, String, String) -> Unit, + navigateToSignIn: () -> Unit, onCancelClick: () -> Unit, ) { + val context = LocalContext.current var inputEmail by remember { mutableStateOf("") } var inputPassword by remember { mutableStateOf("") } var inputHobby by remember { mutableStateOf("") } @@ -307,9 +292,7 @@ fun SignUpScreen( modifier = Modifier .fillMaxWidth() .background(color = Color(0xFF0F42C7)) - .clickable( - onClick = { onSignUpClick(inputEmail, inputPassword, inputHobby) } - ), + .clickable(onClick = { onSignUpClick(inputEmail, inputPassword, inputHobby) }), contentAlignment = Alignment.Center ) { Text( @@ -318,6 +301,23 @@ fun SignUpScreen( color = Color.White ) } + + when(signUpState) { + is SignUpState.Success -> { + showToast( + context = context, + message = "회원가입에 성공했습니다. 유저번호는 ${signUpState.result.no}입니다." + ) + navigateToSignIn() + } + is SignUpState.Failure -> { + showToast( + context = context, + message = "회원가입에 실패했습니다. 형식을 다시 확인해주세요." + ) + } + else -> {} + } } } From d1e65496248c78af49a26e468ba1d773441b5ece Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 23:00:02 +0900 Subject: [PATCH 06/16] =?UTF-8?q?[feat/#10]=20=EC=84=9C=EB=B2=84=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=20=EB=B0=A9=EC=8B=9D=EC=9D=84=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=98=EA=B3=A0,=20=EA=B2=80=EC=83=89=ED=95=98=EA=B8=B0?= =?UTF-8?q?=EC=99=80=20=ED=9A=8C=EC=9B=90=EC=A0=95=EB=B3=B4=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/auth/screen/AuthViewModel.kt | 22 ++- .../ui/auth/screen/SignInScreen.kt | 14 +- .../ui/auth/screen/SignUpScreen.kt | 12 +- .../ui/main/navigation/MainNavGraph.kt | 22 +-- .../ui/main/navigation/MainNavigator.kt | 4 + .../presentation/ui/main/screen/HomeScreen.kt | 8 + .../presentation/ui/main/screen/MainScreen.kt | 35 +---- .../ui/main/screen/MainViewModel.kt | 84 ++++++---- .../ui/main/screen/MyPageScreen.kt | 40 ++++- .../ui/main/screen/SearchScreen.kt | 130 +++++++++++++++- .../ui/main/screen/SettingScreen.kt | 145 ++++++++++++++++++ .../ui/main/screen/UserHobbyState.kt | 5 +- .../ui/main/screen/UserInfoUpdateState.kt | 10 ++ 13 files changed, 448 insertions(+), 83 deletions(-) create mode 100644 app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt create mode 100644 app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt index 14bd85e..773afbd 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt @@ -33,7 +33,7 @@ class AuthViewModel @Inject constructor( _signInState.value = result.fold( onSuccess = { token -> tokenRepository.setToken(token.token) - Log.d("token","토큰 저장 완료! 저장값은 ${token.token} 입니다.") + Log.d("token", "토큰 저장 완료! 저장값은 ${token.token} 입니다.") SignInState.Success(token) }, onFailure = { @@ -46,11 +46,13 @@ class AuthViewModel @Inject constructor( fun validateSignUp(username: String, password: String, hobby: String) { _signUpState.value = SignUpState.Loading viewModelScope.launch { - val result = authRepository.registerUser(user = User( - username = username, - password = password, - hobby = hobby - )) + val result = authRepository.registerUser( + user = User( + username = username, + password = password, + hobby = hobby + ) + ) _signUpState.value = result.fold( onSuccess = { SignUpState.Success(it) @@ -61,4 +63,12 @@ class AuthViewModel @Inject constructor( ) } } + + fun resetSignInState() { + _signInState.value = SignInState.Idle + } + + fun resetSignUpState() { + _signUpState.value = SignUpState.Idle + } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt index cfbdd02..a35ba2b 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt @@ -37,7 +37,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import org.sopt.and.R -import org.sopt.and.presentation.ui.auth.component.AuthTextField +import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.ui.auth.component.SocialPlatformIconRow import org.sopt.and.presentation.ui.auth.component.SocialPlatformList import org.sopt.and.presentation.util.showToast @@ -53,6 +53,7 @@ fun SignInRoute( SignInScreen( signInState = signInState, + resetSignInState = { authViewModel.resetSignInState() }, onSignUpClick = navigateToSignUp, onSignInClick = { email, password -> authViewModel.validateSignIn(email, password) }, navigateToMain = navigateToMain @@ -62,6 +63,7 @@ fun SignInRoute( @Composable fun SignInScreen( signInState: SignInState, + resetSignInState: () -> Unit, onSignUpClick: () -> Unit, onSignInClick: (String, String) -> Unit, navigateToMain: () -> Unit, @@ -102,7 +104,7 @@ fun SignInScreen( Spacer(Modifier.height(40.dp)) - AuthTextField( + WavveTextField( value = inputEmail, onValueChange = { newValue -> inputEmail = newValue }, modifier = Modifier @@ -112,7 +114,7 @@ fun SignInScreen( hint = "이메일 주소 또는 아이디" ) Spacer(Modifier.height(4.dp)) - AuthTextField( + WavveTextField( value = inputPassword, onValueChange = { newValue -> inputPassword = newValue }, modifier = Modifier @@ -215,20 +217,24 @@ fun SignInScreen( Spacer(modifier = Modifier.weight(1f)) - when(signInState) { + when (signInState) { is SignInState.Success -> { showToast( context = context, message = "로그인에 성공했습니다." ) + resetSignInState() navigateToMain() } + is SignInState.Failure -> { showToast( context = context, message = "아이디와 비밃번호를 다시 확인해주세요." ) + resetSignInState() } + else -> {} } } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt index 5146693..cebe5b7 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignUpScreen.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import org.sopt.and.R -import org.sopt.and.presentation.ui.auth.component.AuthTextField +import org.sopt.and.presentation.ui.common.WavveTextField import org.sopt.and.presentation.ui.auth.component.SocialPlatformIconRow import org.sopt.and.presentation.ui.auth.component.SocialPlatformList import org.sopt.and.presentation.util.showToast @@ -51,6 +51,7 @@ fun SignUpRoute( SignUpScreen( signUpState = signUpState, + resetSignUpState = { authViewModel.resetSignUpState() }, onSignUpClick = { username, password, hobby -> authViewModel.validateSignUp(username, password, hobby)}, navigateToSignIn = navigateToSignIn, onCancelClick = navigateToBack, @@ -60,6 +61,7 @@ fun SignUpRoute( @Composable fun SignUpScreen( signUpState: SignUpState, + resetSignUpState: () -> Unit, onSignUpClick: (String, String, String) -> Unit, navigateToSignIn: () -> Unit, onCancelClick: () -> Unit, @@ -131,7 +133,7 @@ fun SignUpScreen( Spacer(modifier = Modifier.height(18.dp)) - AuthTextField( + WavveTextField( value = inputEmail, onValueChange = { newValue -> inputEmail = newValue }, modifier = Modifier @@ -163,7 +165,7 @@ fun SignUpScreen( Spacer(modifier = Modifier.height(10.dp)) - AuthTextField( + WavveTextField( value = inputPassword, onValueChange = { newValue -> inputPassword = newValue }, modifier = Modifier @@ -194,7 +196,7 @@ fun SignUpScreen( ) } - AuthTextField( + WavveTextField( value = inputHobby, onValueChange = { newValue -> inputHobby = newValue }, modifier = Modifier @@ -309,12 +311,14 @@ fun SignUpScreen( message = "회원가입에 성공했습니다. 유저번호는 ${signUpState.result.no}입니다." ) navigateToSignIn() + resetSignUpState() } is SignUpState.Failure -> { showToast( context = context, message = "회원가입에 실패했습니다. 형식을 다시 확인해주세요." ) + resetSignUpState() } else -> {} } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt index cca39d2..febcc6c 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt @@ -4,10 +4,11 @@ import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController import androidx.navigation.compose.composable import androidx.navigation.navigation -import org.sopt.and.presentation.ui.main.screen.HomeScreen +import org.sopt.and.presentation.ui.main.screen.HomeRoute import org.sopt.and.presentation.ui.main.screen.MainRoute -import org.sopt.and.presentation.ui.main.screen.MyPageScreen -import org.sopt.and.presentation.ui.main.screen.SearchScreen +import org.sopt.and.presentation.ui.main.screen.MyPageRoute +import org.sopt.and.presentation.ui.main.screen.SearchRoute +import org.sopt.and.presentation.ui.main.screen.SettingRoute import org.sopt.and.presentation.ui.navigation.WavveRoute fun NavGraphBuilder.mainNavGraph( @@ -19,19 +20,22 @@ fun NavGraphBuilder.mainNavGraph( ) { composable(route = WavveRoute.MAIN) { MainRoute( - navigateToHome = { navController.navigateToHome() }, - navigateToSearch = { navController.navigateToSearch() }, - navigateToMy = { navController.navigateToMy() } + navigateToSetting = { navController.navigateToSetting() } ) } composable(route = WavveRoute.HOME) { - HomeScreen() + HomeRoute() } composable(route = WavveRoute.SEARCH) { - SearchScreen() + SearchRoute() } composable(route = WavveRoute.MY) { - MyPageScreen(userHobby = "") + MyPageRoute( + navigateToSetting = { navController.navigateToSetting() } + ) + } + composable(route = WavveRoute.SETTING) { + SettingRoute() } } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavigator.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavigator.kt index f4b2028..4336c03 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavigator.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavigator.kt @@ -17,4 +17,8 @@ fun NavController.navigateToSearch() { fun NavController.navigateToMy() { navigate(WavveRoute.MY) +} + +fun NavController.navigateToSetting() { + navigate(WavveRoute.SETTING) } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HomeScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HomeScreen.kt index 2d8b56e..265bf72 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HomeScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HomeScreen.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.delay import org.sopt.and.R import org.sopt.and.presentation.ui.main.component.CategoryItem @@ -41,6 +42,13 @@ import org.sopt.and.presentation.ui.main.component.editorRecommendationImages import org.sopt.and.presentation.ui.main.component.todayTop20Images import org.sopt.and.ui.theme.ANDANDROIDTheme +@Composable +fun HomeRoute( + mainViewModel: MainViewModel = hiltViewModel() +) { + HomeScreen() +} + @Composable fun HomeScreen() { LazyColumn( diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainScreen.kt index 7bd8cfd..dd74556 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainScreen.kt @@ -35,24 +35,16 @@ import org.sopt.and.ui.theme.ANDANDROIDTheme @Composable fun MainRoute( mainViewModel: MainViewModel = hiltViewModel(), - navigateToHome: () -> Unit, - navigateToSearch: () -> Unit, - navigateToMy: () -> Unit, + navigateToSetting: () -> Unit ) { - val userHobbyState by mainViewModel.userHobbyState.collectAsState() - - LaunchedEffect(Unit) { - mainViewModel.getUserHobby() - } - MainScreen( - userHobbyState = userHobbyState, + navigateToSetting = navigateToSetting ) } @Composable fun MainScreen( - userHobbyState: UserHobbyState, + navigateToSetting: ()-> Unit ) { var selectedTab by remember { mutableStateOf(MainTabList.HOME) } val onTabSelected: (MainTabList) -> Unit = { tab -> @@ -144,22 +136,11 @@ fun MainScreen( .padding(innerPadding) ) { when (selectedTab) { - MainTabList.HOME -> HomeScreen() - MainTabList.SEARCH -> SearchScreen() - MainTabList.MY -> { - when (userHobbyState) { - is UserHobbyState.Success -> { - val userHobby = userHobbyState.hobby - MyPageScreen(userHobby = userHobby) - } - - is UserHobbyState.Failure -> { - val errorMessage = userHobbyState.errorMessage - } - - else -> {} - } - } + MainTabList.HOME -> HomeRoute() + MainTabList.SEARCH -> SearchRoute() + MainTabList.MY -> MyPageRoute( + navigateToSetting = navigateToSetting + ) } } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt index 60d24e2..77c6148 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt @@ -6,47 +6,79 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import org.sopt.and.data.local.datasource.TokenLocalDataSource -import org.sopt.and.data.remote.model.response.HobbyResponseDto -import org.sopt.and.di.ServicePool -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import org.sopt.and.domain.repository.TokenRepository +import org.sopt.and.domain.repository.UserRepository import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( - private val tokenLocalDataSource: TokenLocalDataSource + private val tokenRepository: TokenRepository, + private val userRepository: UserRepository ) : ViewModel() { - private val userService by lazy { ServicePool.userService } + private val token = tokenRepository.getToken() private val _userHobbyState = MutableStateFlow(UserHobbyState.Idle) val userHobbyState: StateFlow = _userHobbyState + private val _hobbySearchState = MutableStateFlow(HobbySearchState.Idle) + val hobbySearchState: StateFlow = _hobbySearchState + + private val _userInfoUpdateState = MutableStateFlow(UserInfoUpdateState.Idle) + val userInfoUpdateState: StateFlow = _userInfoUpdateState + fun getUserHobby() { + _userHobbyState.value = UserHobbyState.Loading + viewModelScope.launch { + val result = userRepository.getMyHobby(token = token) + _userHobbyState.value = result.fold( + onSuccess = { + UserHobbyState.Success(it) + }, + onFailure = { + UserHobbyState.Failure(it.localizedMessage ?: "에러 발생") + } + ) + } + } + + fun getOthersHobby(userNo: String) { + _hobbySearchState.value = HobbySearchState.Loading viewModelScope.launch { - userService.getMyHobby(token = tokenLocalDataSource.token).enqueue(object : - Callback { - override fun onResponse( - call: Call, - response: Response - ) { - if (response.isSuccessful) { - _userHobbyState.value = - UserHobbyState.Success(response.body()?.result!!.hobby) - } else { - _userHobbyState.value = UserHobbyState.Failure(response.message()) - } + val result = userRepository.getOthersHobby( + token = token, + userNo = userNo.toInt() + ) + _hobbySearchState.value = result.fold( + onSuccess = { + HobbySearchState.Success(it) + }, + onFailure = { + HobbySearchState.Failure(it.localizedMessage ?: "에러 발생") } + ) + } + } - override fun onFailure( - call: Call, - t: Throwable - ) { - _userHobbyState.value = UserHobbyState.Failure(t.message.toString()) + fun updateUserInfo(password: String?, hobby: String?) { + _userInfoUpdateState.value = UserInfoUpdateState.Loading + viewModelScope.launch { + val result = userRepository.updateUserInfo( + token = token, + password = password, + hobby = hobby + ) + _userInfoUpdateState.value = result.fold( + onSuccess = { + UserInfoUpdateState.Success + }, + onFailure = { + UserInfoUpdateState.Failure(it.localizedMessage ?: "에러발생") } - } ) } } + + fun resetUserInfoUpdateState() { + _userInfoUpdateState.value = UserInfoUpdateState.Idle + } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MyPageScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MyPageScreen.kt index 4ae120b..e72fddb 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MyPageScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MyPageScreen.kt @@ -2,6 +2,7 @@ package org.sopt.and.presentation.ui.main.screen import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -15,6 +16,12 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -24,10 +31,40 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import org.sopt.and.R @Composable -fun MyPageScreen(userHobby: String) { +fun MyPageRoute( + mainViewModel: MainViewModel = hiltViewModel(), + navigateToSetting: () -> Unit +) { + val userHobbyState by mainViewModel.userHobbyState.collectAsState() + var userHobby by remember { mutableStateOf("") } + + LaunchedEffect(Unit) { + mainViewModel.getUserHobby() + } + + when (userHobbyState) { + is UserHobbyState.Success -> { + userHobby = (userHobbyState as UserHobbyState.Success).result.hobby + } + + else -> {} + } + + MyPageScreen( + userHobby = userHobby, + navigateToSetting = navigateToSetting + ) +} + +@Composable +fun MyPageScreen( + userHobby: String, + navigateToSetting: () -> Unit, +) { Column( modifier = Modifier .fillMaxWidth() @@ -67,6 +104,7 @@ fun MyPageScreen(userHobby: String) { modifier = Modifier .padding(8.dp) .size(28.dp) + .clickable(onClick = navigateToSetting) ) } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt index ce42cae..a7a4285 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt @@ -1,27 +1,147 @@ package org.sopt.and.presentation.ui.main.screen import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import org.sopt.and.presentation.ui.common.WavveTextField +import org.sopt.and.ui.theme.ANDANDROIDTheme @Composable -fun SearchScreen() { +fun SearchRoute( + mainViewModel: MainViewModel = hiltViewModel() +) { + val hobbySearchState by mainViewModel.hobbySearchState.collectAsState() + + SearchScreen( + hobbySearchState = hobbySearchState, + onSearchClick = { userNo -> mainViewModel.getOthersHobby(userNo) } + ) +} + +@Composable +fun SearchScreen( + hobbySearchState: HobbySearchState, + onSearchClick: (String) -> Unit +) { + var inputSearch by remember { mutableStateOf("") } + val isInputAvailable = inputSearch.toIntOrNull() != null + Column( modifier = Modifier .fillMaxSize() - .background(color = Color(0xFF161616)), - verticalArrangement = Arrangement.Center, + .background(color = Color(0xFF121212)) + .padding(30.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( text = "검색화면", + modifier = Modifier.padding(bottom = 80.dp), + fontSize = 20.sp, color = Color(0xFFCCCCCC) ) + Row( + modifier = Modifier.padding(bottom = 100.dp), + verticalAlignment = Alignment.CenterVertically + ) { + WavveTextField( + value = inputSearch, + onValueChange = { newValue -> inputSearch = newValue }, + modifier = Modifier + .weight(1f) + .clip(shape = RoundedCornerShape(8.dp)) + .background(color = Color(0XFF3B5999)), + hint = "검색할 userNo을 입력하세요" + ) + Spacer(modifier = Modifier.width(8.dp)) + Box( + modifier = Modifier + .wrapContentWidth() + .clip(shape = RoundedCornerShape(8.dp)) + .background(color = if (isInputAvailable) Color.Green else Color.Red) + .clickable( + enabled = isInputAvailable, + onClick = { onSearchClick(inputSearch) } + ), + contentAlignment = Alignment.Center, + content = { + Text( + text = "검색하기", + modifier = Modifier.padding(horizontal = 10.dp, vertical = 16.dp), + fontSize = 14.sp + ) + } + ) + } + Box( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .clip(shape = RoundedCornerShape(12.dp)) + .background(color = Color.White), + contentAlignment = Alignment.Center, + content = { + when (hobbySearchState) { + is HobbySearchState.Success -> { + Text( + text = "해당 유저의 취미는\n" + + "${hobbySearchState.result.hobby}입니다.", + modifier = Modifier.padding(12.dp) + ) + } + + is HobbySearchState.Failure -> { + Text( + text = "해당 유저의 취미를\n" + + "불러 오는데 실패했습니다.", + modifier = Modifier.padding(12.dp) + ) + } + + else -> { + Text( + text = "검색어를 입력하세요.", + modifier = Modifier.padding(12.dp) + ) + } + } + + } + ) + } +} + +@Preview(showBackground = true) +@Composable +fun ShowSearchScreen() { + ANDANDROIDTheme { + SearchScreen( + hobbySearchState = HobbySearchState.Idle, + onSearchClick = {} + ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt new file mode 100644 index 0000000..6eeb013 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt @@ -0,0 +1,145 @@ +package org.sopt.and.presentation.ui.main.screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel +import org.sopt.and.presentation.ui.common.WavveTextField +import org.sopt.and.presentation.util.showToast +import org.sopt.and.ui.theme.ANDANDROIDTheme + +@Composable +fun SettingRoute( + mainViewModel: MainViewModel = hiltViewModel() +) { + val userInfoUpdateState by mainViewModel.userInfoUpdateState.collectAsState() + + SettingScreen( + userInfoUpdateState = userInfoUpdateState, + resetUserInfoUpdateState = { mainViewModel.resetUserInfoUpdateState() }, + onUserInfoChangeClick = { password, hobby -> mainViewModel.updateUserInfo(password, hobby) } + ) +} + +@Composable +fun SettingScreen( + userInfoUpdateState: UserInfoUpdateState, + resetUserInfoUpdateState: () -> Unit, + onUserInfoChangeClick: (String?, String?) -> Unit +) { + val context = LocalContext.current + var inputPassword by remember { mutableStateOf("") } + var inputHobby by remember { mutableStateOf("") } + var isButtonEnabled = inputPassword.isNotBlank() || inputHobby.isNotBlank() + + Column( + modifier = Modifier + .fillMaxSize() + .background(color = Color(0xFF161616)) + .padding(20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "회원정보 변경", + modifier = Modifier.padding(bottom = 80.dp), + fontSize = 20.sp, + color = Color(0xFFCCCCCC) + ) + WavveTextField( + value = inputPassword, + onValueChange = { newValue -> inputPassword = newValue }, + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(6.dp)) + .background(color = Color(0xFF262626)), + hint = "변경할 비밀번호 (8자 이하)" + ) + Spacer(Modifier.height(8.dp)) + WavveTextField( + value = inputHobby, + onValueChange = { newValue -> inputHobby = newValue }, + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(6.dp)) + .background(color = Color(0xFF262626)), + hint = "변경할 취미 (8자 이하)" + ) + Spacer(Modifier.height(24.dp)) + Box( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .clip(shape = RoundedCornerShape(20.dp)) + .background(color = Color(0xFF1352F9)) + .clickable( + enabled = isButtonEnabled, + onClick = { + if (inputPassword.isBlank()) onUserInfoChangeClick(null, inputHobby) + else if (inputHobby.isBlank()) onUserInfoChangeClick(inputPassword, null) + else onUserInfoChangeClick(inputPassword, inputHobby) } + ), + contentAlignment = Alignment.Center, + content = { + Text( + text = "변경하기", + modifier = Modifier.padding(vertical = 12.dp), + color = Color.White + ) + } + ) + when (userInfoUpdateState) { + is UserInfoUpdateState.Success -> { + showToast( + context = context, + message = "회원정보 변경에 성공했습니다." + ) + resetUserInfoUpdateState() + } + + is UserInfoUpdateState.Failure -> { + showToast( + context = context, + message = "회원정보 변경에 실패했습니다." + ) + resetUserInfoUpdateState() + } + + else -> {} + } + } +} + +@Preview(showBackground = true) +@Composable +fun ShowSettingScreen() { + ANDANDROIDTheme { + SettingScreen( + userInfoUpdateState = UserInfoUpdateState.Idle, + resetUserInfoUpdateState = {}, + onUserInfoChangeClick = { _, _ -> } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserHobbyState.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserHobbyState.kt index fa33bab..469b079 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserHobbyState.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserHobbyState.kt @@ -1,7 +1,10 @@ package org.sopt.and.presentation.ui.main.screen +import org.sopt.and.domain.model.Hobby + sealed class UserHobbyState { data object Idle: UserHobbyState() - data class Success(val hobby: String): UserHobbyState() + data object Loading: UserHobbyState() + data class Success(val result: Hobby): UserHobbyState() data class Failure(val errorMessage: String): UserHobbyState() } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt new file mode 100644 index 0000000..4bf1da3 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt @@ -0,0 +1,10 @@ +package org.sopt.and.presentation.ui.main.screen + +import org.sopt.and.domain.model.Hobby + +sealed class UserInfoUpdateState { + data object Idle: UserInfoUpdateState() + data object Loading: UserInfoUpdateState() + data object Success: UserInfoUpdateState() + data class Failure(val errorMessage: String): UserInfoUpdateState() +} \ No newline at end of file From 1b876352b0a9f177975b3dd87c0b64df5a9f44ab Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 23:00:22 +0900 Subject: [PATCH 07/16] =?UTF-8?q?[feat/#10]=20SettingScreen=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=ED=8A=B8=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/sopt/and/presentation/ui/navigation/WavveRoute.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/navigation/WavveRoute.kt b/app/src/main/java/org/sopt/and/presentation/ui/navigation/WavveRoute.kt index 79572bf..591104f 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/navigation/WavveRoute.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/navigation/WavveRoute.kt @@ -12,4 +12,5 @@ object WavveRoute { const val HOME = "home" const val SEARCH = "search" const val MY = "my" + const val SETTING = "setting" } \ No newline at end of file From 48f7acb1160027299d113358a398fff76aafe045 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 23:00:34 +0900 Subject: [PATCH 08/16] =?UTF-8?q?[feat/#10]=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/AuthTextField.kt => common/WavveTextField.kt} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename app/src/main/java/org/sopt/and/presentation/ui/{auth/component/AuthTextField.kt => common/WavveTextField.kt} (98%) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/component/AuthTextField.kt b/app/src/main/java/org/sopt/and/presentation/ui/common/WavveTextField.kt similarity index 98% rename from app/src/main/java/org/sopt/and/presentation/ui/auth/component/AuthTextField.kt rename to app/src/main/java/org/sopt/and/presentation/ui/common/WavveTextField.kt index d4071ce..40be75b 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/component/AuthTextField.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/common/WavveTextField.kt @@ -1,4 +1,4 @@ -package org.sopt.and.presentation.ui.auth.component +package org.sopt.and.presentation.ui.common import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable -fun AuthTextField( +fun WavveTextField( value: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, From 6c61aaeaa4e44397dad1e339a43b992bf98523b1 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 23:01:01 +0900 Subject: [PATCH 09/16] =?UTF-8?q?[feat/#10]=20=EC=B7=A8=EB=AF=B8=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=ED=86=B5=EC=8B=A0=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EC=9E=85=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/main/screen/HobbySearchState.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/src/main/java/org/sopt/and/presentation/ui/main/screen/HobbySearchState.kt diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HobbySearchState.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HobbySearchState.kt new file mode 100644 index 0000000..ca26711 --- /dev/null +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/HobbySearchState.kt @@ -0,0 +1,10 @@ +package org.sopt.and.presentation.ui.main.screen + +import org.sopt.and.domain.model.Hobby + +sealed class HobbySearchState { + data object Idle: HobbySearchState() + data object Loading: HobbySearchState() + data class Success(val result: Hobby): HobbySearchState() + data class Failure(val errorMessage: String): HobbySearchState() +} \ No newline at end of file From 797bbdabd705d41a392a90392601728c0962b687 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Mon, 2 Dec 2024 23:01:24 +0900 Subject: [PATCH 10/16] =?UTF-8?q?[chore/#10]=20=EC=84=A0=EC=96=B8=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EB=A5=BC=20=EC=88=98=EC=A0=95=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/sopt/and/di/DataSourceModule.kt | 6 +++--- .../java/org/sopt/and/di/NetworkModule.kt | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/org/sopt/and/di/DataSourceModule.kt b/app/src/main/java/org/sopt/and/di/DataSourceModule.kt index 7512eb7..32d58d0 100644 --- a/app/src/main/java/org/sopt/and/di/DataSourceModule.kt +++ b/app/src/main/java/org/sopt/and/di/DataSourceModule.kt @@ -19,9 +19,9 @@ import javax.inject.Singleton abstract class DataSourceModule { @Binds @Singleton - fun bindTokenLocalDataSource(sharedPreferences: SharedPreferences): TokenLocalDataSource { - return TokenLocalDataSourceImpl(sharedPreferences) - } + abstract fun bindTokenLocalDataSource( + tokenLocalDataSourceImpl: TokenLocalDataSourceImpl + ): TokenLocalDataSource @Binds @Singleton diff --git a/app/src/main/java/org/sopt/and/di/NetworkModule.kt b/app/src/main/java/org/sopt/and/di/NetworkModule.kt index 5d258f3..e312113 100644 --- a/app/src/main/java/org/sopt/and/di/NetworkModule.kt +++ b/app/src/main/java/org/sopt/and/di/NetworkModule.kt @@ -16,16 +16,6 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object NetworkModule { - @Provides - @Singleton - fun providesJson(): Json = - Json { - isLenient = true - prettyPrint = true - explicitNulls = false - ignoreUnknownKeys = true - } - @Provides @Singleton fun provideLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor().apply { @@ -41,6 +31,16 @@ object NetworkModule { addInterceptor(loggingInterceptor) }.build() + @Provides + @Singleton + fun providesJson(): Json = + Json { + isLenient = true + prettyPrint = true + explicitNulls = false + ignoreUnknownKeys = true + } + @Provides @Singleton fun provideRetrofit( From 66a5a9014480931b1a3954d83562adcf1fc4fa05 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 4 Dec 2024 11:32:01 +0900 Subject: [PATCH 11/16] =?UTF-8?q?[chore/#10]=20Loading=20State=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=ED=95=98=EB=8A=94=20=EC=9C=84=EC=B9=98?= =?UTF-8?q?=EB=A5=BC=20=EB=B3=80=EA=B2=BD=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../and/presentation/ui/auth/screen/AuthViewModel.kt | 5 ++--- .../and/presentation/ui/main/screen/MainViewModel.kt | 9 +++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt index 773afbd..dd592fb 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/AuthViewModel.kt @@ -27,13 +27,12 @@ class AuthViewModel @Inject constructor( val signUpState: StateFlow = _signUpState fun validateSignIn(username: String, password: String) { - _signInState.value = SignInState.Loading viewModelScope.launch { + _signInState.value = SignInState.Loading val result = authRepository.login(username = username, password = password) _signInState.value = result.fold( onSuccess = { token -> tokenRepository.setToken(token.token) - Log.d("token", "토큰 저장 완료! 저장값은 ${token.token} 입니다.") SignInState.Success(token) }, onFailure = { @@ -44,8 +43,8 @@ class AuthViewModel @Inject constructor( } fun validateSignUp(username: String, password: String, hobby: String) { - _signUpState.value = SignUpState.Loading viewModelScope.launch { + _signUpState.value = SignUpState.Loading val result = authRepository.registerUser( user = User( username = username, diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt index 77c6148..8f2cf10 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/MainViewModel.kt @@ -23,12 +23,13 @@ class MainViewModel @Inject constructor( private val _hobbySearchState = MutableStateFlow(HobbySearchState.Idle) val hobbySearchState: StateFlow = _hobbySearchState - private val _userInfoUpdateState = MutableStateFlow(UserInfoUpdateState.Idle) + private val _userInfoUpdateState = + MutableStateFlow(UserInfoUpdateState.Idle) val userInfoUpdateState: StateFlow = _userInfoUpdateState fun getUserHobby() { - _userHobbyState.value = UserHobbyState.Loading viewModelScope.launch { + _userHobbyState.value = UserHobbyState.Loading val result = userRepository.getMyHobby(token = token) _userHobbyState.value = result.fold( onSuccess = { @@ -42,8 +43,8 @@ class MainViewModel @Inject constructor( } fun getOthersHobby(userNo: String) { - _hobbySearchState.value = HobbySearchState.Loading viewModelScope.launch { + _hobbySearchState.value = HobbySearchState.Loading val result = userRepository.getOthersHobby( token = token, userNo = userNo.toInt() @@ -60,8 +61,8 @@ class MainViewModel @Inject constructor( } fun updateUserInfo(password: String?, hobby: String?) { - _userInfoUpdateState.value = UserInfoUpdateState.Loading viewModelScope.launch { + _userInfoUpdateState.value = UserInfoUpdateState.Loading val result = userRepository.updateUserInfo( token = token, password = password, From 4720867bb793280537bd7df97f4b1ba274c034b2 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 4 Dec 2024 11:32:28 +0900 Subject: [PATCH 12/16] =?UTF-8?q?[chore/#10]=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20UI=EB=A5=BC=20=EA=B0=9C=EC=84=A0=ED=95=A9?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/main/screen/SearchScreen.kt | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt index a7a4285..4c87d7f 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt @@ -4,14 +4,12 @@ import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -55,7 +53,7 @@ fun SearchScreen( modifier = Modifier .fillMaxSize() .background(color = Color(0xFF121212)) - .padding(30.dp), + .padding(20.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( @@ -64,39 +62,36 @@ fun SearchScreen( fontSize = 20.sp, color = Color(0xFFCCCCCC) ) - Row( - modifier = Modifier.padding(bottom = 100.dp), - verticalAlignment = Alignment.CenterVertically - ) { - WavveTextField( - value = inputSearch, - onValueChange = { newValue -> inputSearch = newValue }, - modifier = Modifier - .weight(1f) - .clip(shape = RoundedCornerShape(8.dp)) - .background(color = Color(0XFF3B5999)), - hint = "검색할 userNo을 입력하세요" - ) - Spacer(modifier = Modifier.width(8.dp)) - Box( - modifier = Modifier - .wrapContentWidth() - .clip(shape = RoundedCornerShape(8.dp)) - .background(color = if (isInputAvailable) Color.Green else Color.Red) - .clickable( - enabled = isInputAvailable, - onClick = { onSearchClick(inputSearch) } - ), - contentAlignment = Alignment.Center, - content = { - Text( - text = "검색하기", - modifier = Modifier.padding(horizontal = 10.dp, vertical = 16.dp), - fontSize = 14.sp - ) - } - ) - } + WavveTextField( + value = inputSearch, + onValueChange = { newValue -> inputSearch = newValue }, + modifier = Modifier + .fillMaxWidth() + .clip(shape = RoundedCornerShape(6.dp)) + .background(color = Color(0xFF262626)), + hint = "검색할 번호 입력" + ) + Spacer(Modifier.height(24.dp)) + Box( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .clip(shape = RoundedCornerShape(20.dp)) + .background(color = Color(0xFF1352F9)) + .clickable( + enabled = isInputAvailable, + onClick = { onSearchClick(inputSearch) } + ), + contentAlignment = Alignment.Center, + content = { + Text( + text = "검색하기", + modifier = Modifier.padding(vertical = 12.dp), + color = Color.White + ) + } + ) + Spacer(Modifier.height(100.dp)) Box( modifier = Modifier .fillMaxWidth() From dc24357e33c221072fbecad0db6ad37800eab6e3 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 4 Dec 2024 11:32:59 +0900 Subject: [PATCH 13/16] =?UTF-8?q?[chore/#10]=20=ED=9A=8C=EC=9B=90=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=B3=80=EA=B2=BD=20=ED=99=94=EB=A9=B4=20UI?= =?UTF-8?q?=EB=A5=BC=20=EA=B0=9C=EC=84=A0=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sopt/and/presentation/ui/main/screen/SettingScreen.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt index 6eeb013..2bd287d 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt @@ -93,13 +93,14 @@ fun SettingScreen( .fillMaxWidth() .wrapContentHeight() .clip(shape = RoundedCornerShape(20.dp)) - .background(color = Color(0xFF1352F9)) + .background(color = if(isButtonEnabled) Color(0xFF1352F9) else Color.Gray) .clickable( enabled = isButtonEnabled, onClick = { if (inputPassword.isBlank()) onUserInfoChangeClick(null, inputHobby) else if (inputHobby.isBlank()) onUserInfoChangeClick(inputPassword, null) - else onUserInfoChangeClick(inputPassword, inputHobby) } + else onUserInfoChangeClick(inputPassword, inputHobby) + } ), contentAlignment = Alignment.Center, content = { From 0d1916197059a3805f2ac1d81d611f8f3208ec93 Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 4 Dec 2024 11:34:13 +0900 Subject: [PATCH 14/16] =?UTF-8?q?[chore/#10]=20=EB=B3=80=EC=88=98=EB=AA=85?= =?UTF-8?q?=EC=9D=84=20email=20->=20username=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt index a35ba2b..6985d96 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/auth/screen/SignInScreen.kt @@ -55,7 +55,7 @@ fun SignInRoute( signInState = signInState, resetSignInState = { authViewModel.resetSignInState() }, onSignUpClick = navigateToSignUp, - onSignInClick = { email, password -> authViewModel.validateSignIn(email, password) }, + onSignInClick = { username, password -> authViewModel.validateSignIn(username, password) }, navigateToMain = navigateToMain ) } @@ -230,7 +230,7 @@ fun SignInScreen( is SignInState.Failure -> { showToast( context = context, - message = "아이디와 비밃번호를 다시 확인해주세요." + message = "아이디와 비밀번호를 다시 확인해주세요." ) resetSignInState() } From 189f67413c2497a3de0caa784e08d4d30b05b7aa Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 4 Dec 2024 11:35:18 +0900 Subject: [PATCH 15/16] =?UTF-8?q?[feat/#10]=20response=20body=EA=B0=80=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20Response=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=98=EC=97=AC=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=ED=86=B5=EC=8B=A0=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A5=BC=20=EA=B2=80=EC=82=AC=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/datasource/UserRemoteDataSource.kt | 3 ++- .../datasourceimpl/UserRemoteDataSourceImpl.kt | 3 ++- .../and/data/remote/service/AuthService.kt | 5 ++--- .../and/data/remote/service/UserService.kt | 9 +++++---- .../data/repositoryimpl/UserRepositoryImpl.kt | 18 +++++++++++++----- .../ui/main/screen/UserInfoUpdateState.kt | 10 ++++------ 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt b/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt index 69f7e41..124011e 100644 --- a/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt +++ b/app/src/main/java/org/sopt/and/data/remote/datasource/UserRemoteDataSource.kt @@ -3,9 +3,10 @@ package org.sopt.and.data.remote.datasource import org.sopt.and.data.remote.model.base.ApiResponse import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto import org.sopt.and.data.remote.model.response.HobbyResponseDto +import retrofit2.Response interface UserRemoteDataSource { suspend fun getMyHobby(token: String): ApiResponse suspend fun getOthersHobby(token: String, userNo: Int): ApiResponse - suspend fun updateUserInfo(token: String, userInfoUpdateRequestDto: UserInfoUpdateRequestDto): ApiResponse + suspend fun updateUserInfo(token: String, userInfoUpdateRequestDto: UserInfoUpdateRequestDto): Response } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt b/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt index 8dbeb99..518c330 100644 --- a/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt +++ b/app/src/main/java/org/sopt/and/data/remote/datasourceimpl/UserRemoteDataSourceImpl.kt @@ -5,6 +5,7 @@ import org.sopt.and.data.remote.model.base.ApiResponse import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto import org.sopt.and.data.remote.model.response.HobbyResponseDto import org.sopt.and.data.remote.service.UserService +import retrofit2.Response import javax.inject.Inject class UserRemoteDataSourceImpl @Inject constructor( @@ -20,6 +21,6 @@ class UserRemoteDataSourceImpl @Inject constructor( override suspend fun updateUserInfo( token: String, userInfoUpdateRequestDto: UserInfoUpdateRequestDto - ): ApiResponse = + ): Response = userService.updateUserInfo(token = token, userInfoUpdateRequestDto = userInfoUpdateRequestDto) } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt b/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt index 0348f0d..8b2c459 100644 --- a/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt +++ b/app/src/main/java/org/sopt/and/data/remote/service/AuthService.kt @@ -5,18 +5,17 @@ import org.sopt.and.data.remote.model.request.LoginRequestDto import org.sopt.and.data.remote.model.request.UserRegistrationRequestDto import org.sopt.and.data.remote.model.response.LoginResponseDto import org.sopt.and.data.remote.model.response.UserRegistrationResponseDto -import retrofit2.Call import retrofit2.http.Body import retrofit2.http.POST interface AuthService { @POST("user") - fun registerUser( + suspend fun registerUser( @Body userRegistrationRequestDto: UserRegistrationRequestDto ): ApiResponse @POST("login") - fun login( + suspend fun login( @Body loginRequestDto: LoginRequestDto ): ApiResponse } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt b/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt index 0ae9d5e..2e05891 100644 --- a/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt +++ b/app/src/main/java/org/sopt/and/data/remote/service/UserService.kt @@ -3,6 +3,7 @@ package org.sopt.and.data.remote.service import org.sopt.and.data.remote.model.base.ApiResponse import org.sopt.and.data.remote.model.request.UserInfoUpdateRequestDto import org.sopt.and.data.remote.model.response.HobbyResponseDto +import retrofit2.Response import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.Header @@ -11,19 +12,19 @@ import retrofit2.http.Path interface UserService { @GET("user/my-hobby") - fun getMyHobby( + suspend fun getMyHobby( @Header("token") token: String ): ApiResponse @GET("user/{no}/hobby") - fun getOthersHobby( + suspend fun getOthersHobby( @Header("token") token: String, @Path("no") userNo: Int ): ApiResponse @PUT("user") - fun updateUserInfo( + suspend fun updateUserInfo( @Header("token") token: String, @Body userInfoUpdateRequestDto: UserInfoUpdateRequestDto - ): ApiResponse + ): Response } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt b/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt index fb9001f..b02efd8 100644 --- a/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt +++ b/app/src/main/java/org/sopt/and/data/repositoryimpl/UserRepositoryImpl.kt @@ -10,7 +10,7 @@ import javax.inject.Inject class UserRepositoryImpl @Inject constructor( private val userRemoteDataSource: UserRemoteDataSource -): UserRepository { +) : UserRepository { override suspend fun getMyHobby(token: String): Result { return runCatching { userRemoteDataSource.getMyHobby(token = token).handleApiResponse().getOrThrow().toDomain() @@ -29,10 +29,18 @@ class UserRepositoryImpl @Inject constructor( hobby: String? ): Result { return runCatching { - userRemoteDataSource.updateUserInfo(token = token, userInfoUpdateRequestDto = UserInfoUpdateRequestDto( - password = password, - hobby = hobby - )).handleApiResponse().getOrThrow() + val response = userRemoteDataSource.updateUserInfo( + token = token, + userInfoUpdateRequestDto = UserInfoUpdateRequestDto( + password = password, + hobby = hobby + ) + ) + if(response.isSuccessful) { + Result.success(Unit) + } else { + Result.failure(Exception("Error : ${response.code()} ${response.message()}")) + } } } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt index 4bf1da3..9ef94f2 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/UserInfoUpdateState.kt @@ -1,10 +1,8 @@ package org.sopt.and.presentation.ui.main.screen -import org.sopt.and.domain.model.Hobby - sealed class UserInfoUpdateState { - data object Idle: UserInfoUpdateState() - data object Loading: UserInfoUpdateState() - data object Success: UserInfoUpdateState() - data class Failure(val errorMessage: String): UserInfoUpdateState() + data object Idle : UserInfoUpdateState() + data object Loading : UserInfoUpdateState() + data object Success : UserInfoUpdateState() + data class Failure(val errorMessage: String) : UserInfoUpdateState() } \ No newline at end of file From e298d38db2861405d1f15e5f6f671694940bdaea Mon Sep 17 00:00:00 2001 From: SYAAINN Date: Wed, 4 Dec 2024 11:40:47 +0900 Subject: [PATCH 16/16] =?UTF-8?q?[chore/#10]=20UI=EB=A5=BC=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/ui/main/navigation/MainNavGraph.kt | 4 +++- .../and/presentation/ui/main/screen/SearchScreen.kt | 2 +- .../presentation/ui/main/screen/SettingScreen.kt | 13 +++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt index febcc6c..474ff8b 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/navigation/MainNavGraph.kt @@ -35,7 +35,9 @@ fun NavGraphBuilder.mainNavGraph( ) } composable(route = WavveRoute.SETTING) { - SettingRoute() + SettingRoute( + navigateToMyPage = { navController.navigateToMy() } + ) } } } \ No newline at end of file diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt index 4c87d7f..09708e1 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SearchScreen.kt @@ -77,7 +77,7 @@ fun SearchScreen( .fillMaxWidth() .wrapContentHeight() .clip(shape = RoundedCornerShape(20.dp)) - .background(color = Color(0xFF1352F9)) + .background(color = if(isInputAvailable) Color(0xFF1352F9) else Color.Gray) .clickable( enabled = isInputAvailable, onClick = { onSearchClick(inputSearch) } diff --git a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt index 2bd287d..3e7fe2c 100644 --- a/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt +++ b/app/src/main/java/org/sopt/and/presentation/ui/main/screen/SettingScreen.kt @@ -33,14 +33,16 @@ import org.sopt.and.ui.theme.ANDANDROIDTheme @Composable fun SettingRoute( - mainViewModel: MainViewModel = hiltViewModel() + mainViewModel: MainViewModel = hiltViewModel(), + navigateToMyPage: () -> Unit ) { val userInfoUpdateState by mainViewModel.userInfoUpdateState.collectAsState() SettingScreen( userInfoUpdateState = userInfoUpdateState, resetUserInfoUpdateState = { mainViewModel.resetUserInfoUpdateState() }, - onUserInfoChangeClick = { password, hobby -> mainViewModel.updateUserInfo(password, hobby) } + onUserInfoChangeClick = { password, hobby -> mainViewModel.updateUserInfo(password, hobby) }, + navigateToMyPage = navigateToMyPage ) } @@ -48,7 +50,8 @@ fun SettingRoute( fun SettingScreen( userInfoUpdateState: UserInfoUpdateState, resetUserInfoUpdateState: () -> Unit, - onUserInfoChangeClick: (String?, String?) -> Unit + onUserInfoChangeClick: (String?, String?) -> Unit, + navigateToMyPage: () -> Unit ) { val context = LocalContext.current var inputPassword by remember { mutableStateOf("") } @@ -118,6 +121,7 @@ fun SettingScreen( message = "회원정보 변경에 성공했습니다." ) resetUserInfoUpdateState() + navigateToMyPage() } is UserInfoUpdateState.Failure -> { @@ -140,7 +144,8 @@ fun ShowSettingScreen() { SettingScreen( userInfoUpdateState = UserInfoUpdateState.Idle, resetUserInfoUpdateState = {}, - onUserInfoChangeClick = { _, _ -> } + onUserInfoChangeClick = { _, _ -> }, + navigateToMyPage = {} ) } } \ No newline at end of file