diff --git a/app/build.gradle b/app/build.gradle index c33f6c9..d4e24b0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,24 +65,24 @@ dependencies { implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - androidTestImplementation platform('androidx.compose:compose-bom:2024.05.00') + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + androidTestImplementation platform('androidx.compose:compose-bom:2024.06.00') androidTestImplementation 'androidx.compose.ui:ui-test-junit4' debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' - implementation 'androidx.compose.runtime:runtime-livedata:1.6.7' + implementation 'androidx.compose.runtime:runtime-livedata:1.6.8' implementation 'androidx.navigation:navigation-compose:2.7.7' implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.1' implementation 'com.brandongogetap:stickyheaders:0.6.2' implementation 'com.squareup.retrofit2:retrofit:2.11.0' - implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3' + implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.0' implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0' implementation(platform 'com.squareup.okhttp3:okhttp-bom:4.10.0') implementation 'com.squareup.okhttp3:okhttp' implementation 'com.squareup.okhttp3:logging-interceptor' implementation "io.coil-kt:coil-compose:2.6.0" - implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.8.1' + implementation 'androidx.lifecycle:lifecycle-runtime-compose:2.8.2' implementation "com.google.dagger:hilt-android:$hilt_version" kapt "com.google.dagger:hilt-compiler:$hilt_version" diff --git a/app/src/main/java/com/sopt/now/compose/data/User.kt b/app/src/main/java/com/sopt/now/compose/data/User.kt deleted file mode 100644 index 3fc3582..0000000 --- a/app/src/main/java/com/sopt/now/compose/data/User.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.sopt.now.compose.data - -import java.io.Serializable - -data class User( - val id: String, - val password: String, - val nickname: String, - val phone: String, -) : Serializable \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/UserLocalDataSource.kt b/app/src/main/java/com/sopt/now/compose/data/UserLocalDataSource.kt deleted file mode 100644 index 362d722..0000000 --- a/app/src/main/java/com/sopt/now/compose/data/UserLocalDataSource.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.sopt.now.compose.data - -import android.content.SharedPreferences -import androidx.core.content.edit - -class UserLocalDataSource(private val preferences: SharedPreferences) { - fun setUserLoggedIn(loggedIn: Boolean) { - preferences.edit().putBoolean("isLoggedIn", loggedIn).commit() - } - - fun isUserLoggedIn(): Boolean { - return preferences.getBoolean("isLoggedIn", false) - } - - fun logoutUser() { - preferences.edit().clear().apply() - } - - fun saveUserData(user: User) { - preferences.edit { - putString(KEY_USER_ID, user.id) - putString(KEY_USER_PW, user.password) - putString(KEY_NICKNAME, user.nickname) - putString(KEY_PHONE, user.phone) - } - } - - - fun updateUserPassword(newPassword: String) { - preferences.edit().putString(KEY_USER_PW, newPassword).apply() - } - - fun setMemberId(memberId: String) { - preferences.edit().putString(KEY_MEMBER_ID, memberId).apply() - } - - fun getMemberId(): String? { - return preferences.getString(KEY_MEMBER_ID, null) - } - - companion object { - private const val KEY_USER_ID = "UserID" - private const val KEY_USER_PW = "Password" - private const val KEY_NICKNAME = "Nickname" - private const val KEY_PHONE = "Phone" - private const val KEY_MEMBER_ID = "MemberID" - } -} diff --git a/app/src/main/java/com/sopt/now/compose/data/UserRemoteDataSource.kt b/app/src/main/java/com/sopt/now/compose/data/UserRemoteDataSource.kt deleted file mode 100644 index d836e76..0000000 --- a/app/src/main/java/com/sopt/now/compose/data/UserRemoteDataSource.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.sopt.now.compose.data - -import com.sopt.now.compose.network.reponse.ResponseDto -import com.sopt.now.compose.network.reponse.ResponseInfoDto -import com.sopt.now.compose.network.service.AuthService -import com.sopt.now.compose.network.request.RequestChangePasswordDto -import com.sopt.now.compose.network.request.RequestLoginDto -import com.sopt.now.compose.network.request.RequestSignUpDto -import retrofit2.Response - -class UserRemoteDataSource(private val authService: AuthService) { - suspend fun signUp(request: RequestSignUpDto): Response { - return authService.signUp(request) - } - - suspend fun login(request: RequestLoginDto): Response { - return authService.login(request) - } - - suspend fun getUserInfo(): Response { - return authService.userInfo() - } - - suspend fun changePassword(request: RequestChangePasswordDto): Response { - return authService.changePassword(request) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/UserRepository.kt b/app/src/main/java/com/sopt/now/compose/data/UserRepository.kt deleted file mode 100644 index 859f6ae..0000000 --- a/app/src/main/java/com/sopt/now/compose/data/UserRepository.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.sopt.now.compose.data - -import com.sopt.now.compose.network.reponse.ResponseDto -import com.sopt.now.compose.network.reponse.ResponseInfoDto -import com.sopt.now.compose.network.request.RequestChangePasswordDto -import com.sopt.now.compose.network.request.RequestLoginDto -import com.sopt.now.compose.network.request.RequestSignUpDto -import retrofit2.Response - -interface UserRepository { - fun setUserLoggedIn(loggedIn: Boolean) - fun isUserLoggedIn(): Boolean - fun logoutUser() - fun saveUserData(user: User) - fun updateUserPassword(newPassword: String) - fun setMemberId(memberId: String) - suspend fun signUp(request: RequestSignUpDto): Response - suspend fun login(request: RequestLoginDto): Response - suspend fun getUserInfo(): Response - suspend fun changePassword(request: RequestChangePasswordDto): Response -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/UserRepositoryImpl.kt b/app/src/main/java/com/sopt/now/compose/data/UserRepositoryImpl.kt deleted file mode 100644 index 02827cf..0000000 --- a/app/src/main/java/com/sopt/now/compose/data/UserRepositoryImpl.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.sopt.now.compose.data - -import com.sopt.now.compose.network.reponse.ResponseDto -import com.sopt.now.compose.network.reponse.ResponseInfoDto -import com.sopt.now.compose.network.request.RequestChangePasswordDto -import com.sopt.now.compose.network.request.RequestLoginDto -import com.sopt.now.compose.network.request.RequestSignUpDto -import retrofit2.Response -import javax.inject.Inject - -class UserRepositoryImpl @Inject constructor( - private val localDataSource: UserLocalDataSource, - private val remoteDataSource: UserRemoteDataSource -) : UserRepository { - override fun setUserLoggedIn(loggedIn: Boolean) { - localDataSource.setUserLoggedIn(loggedIn) - } - - override fun isUserLoggedIn(): Boolean { - return localDataSource.isUserLoggedIn() - } - - override fun logoutUser() { - localDataSource.logoutUser() - } - - override fun saveUserData(user: User) { - localDataSource.saveUserData(user) - } - - override fun updateUserPassword(newPassword: String) { - localDataSource.updateUserPassword(newPassword) - } - - override fun setMemberId(memberId: String) { - localDataSource.setMemberId(memberId) - } - - override suspend fun signUp(request: RequestSignUpDto): Response { - return remoteDataSource.signUp(request) - } - - override suspend fun login(request: RequestLoginDto): Response { - return remoteDataSource.login(request) - } - - override suspend fun getUserInfo(): Response { - return remoteDataSource.getUserInfo() - } - - override suspend fun changePassword(request: RequestChangePasswordDto): Response { - return remoteDataSource.changePassword(request) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/local/UserDataStore.kt b/app/src/main/java/com/sopt/now/compose/data/local/UserDataStore.kt new file mode 100644 index 0000000..d4caa46 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/local/UserDataStore.kt @@ -0,0 +1,11 @@ +package com.sopt.now.compose.data.local + +interface UserDataStore { + var userId: String + var id: String + var password: String + var nickname: String + var phoneNumber: String + var isLoggedIn: Boolean + fun clearInfo() +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/local/UserDataStoreImpl.kt b/app/src/main/java/com/sopt/now/compose/data/local/UserDataStoreImpl.kt new file mode 100644 index 0000000..1f17cc7 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/local/UserDataStoreImpl.kt @@ -0,0 +1,47 @@ +package com.sopt.now.compose.data.local + +import android.content.SharedPreferences +import androidx.core.content.edit +import javax.inject.Inject + +class UserDataStoreImpl @Inject constructor( + private val dataStore: SharedPreferences, +) : UserDataStore { + override var userId: String + get() = dataStore.getString(USER_ID, "").orEmpty() + set(value) = dataStore.edit { putString(USER_ID, value) } + + override var id: String + get() = dataStore.getString(ID, "").orEmpty() + set(value) = dataStore.edit { putString(ID, value) } + + override var password: String + get() = dataStore.getString(PASSWORD, "").orEmpty() + set(value) = dataStore.edit { putString(PASSWORD, value) } + + override var nickname: String + get() = dataStore.getString(NICKNAME, "").orEmpty() + set(value) = dataStore.edit { putString(NICKNAME, value) } + + override var phoneNumber: String + get() = dataStore.getString(PHONE_NUMBER, "").orEmpty() + set(value) = dataStore.edit { putString(PHONE_NUMBER, value) } + + override var isLoggedIn: Boolean + get() = dataStore.getBoolean(IS_LOGGED_IN, false) + set(value) = dataStore.edit { putBoolean(IS_LOGGED_IN, value) } + + + override fun clearInfo() { + dataStore.edit().clear().commit() + } + + companion object { + private const val USER_ID = "userId" + private const val ID = "id" + private const val PASSWORD = "password" + private const val NICKNAME = "nickname" + private const val PHONE_NUMBER = "phoneNumber" + private const val IS_LOGGED_IN = "isLoggedIn" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/datasource/AuthDataSource.kt b/app/src/main/java/com/sopt/now/compose/data/remote/datasource/AuthDataSource.kt new file mode 100644 index 0000000..33cd0a0 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/datasource/AuthDataSource.kt @@ -0,0 +1,15 @@ +package com.sopt.now.compose.data.remote.datasource + +import com.sopt.now.compose.data.remote.response.BaseResponseWithoutDataDto +import com.sopt.now.compose.domain.entity.request.RequestChangePasswordEntity +import com.sopt.now.compose.domain.entity.request.RequestSignInEntity +import com.sopt.now.compose.domain.entity.request.RequestUserEntity +import retrofit2.Response + +interface AuthDataSource { + suspend fun postSignUp(user: RequestUserEntity): Response + + suspend fun postSignIn(user: RequestSignInEntity): Response + + suspend fun postChangePassword(user: RequestChangePasswordEntity): Response +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/datasource/MypageDataSource.kt b/app/src/main/java/com/sopt/now/compose/data/remote/datasource/MypageDataSource.kt new file mode 100644 index 0000000..0d158f5 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/datasource/MypageDataSource.kt @@ -0,0 +1,8 @@ +package com.sopt.now.compose.data.remote.datasource + +import com.sopt.now.compose.data.remote.response.BaseResponseDto +import com.sopt.now.compose.data.remote.response.ResponseUserDto + +interface MypageDataSource { + suspend fun getUserInfo(): BaseResponseDto +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/datasourceImpl/AuthDataSourceImpl.kt b/app/src/main/java/com/sopt/now/compose/data/remote/datasourceImpl/AuthDataSourceImpl.kt new file mode 100644 index 0000000..35fdccd --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/datasourceImpl/AuthDataSourceImpl.kt @@ -0,0 +1,24 @@ +package com.sopt.now.compose.data.remote.datasourceImpl + +import com.sopt.now.compose.data.remote.datasource.AuthDataSource +import com.sopt.now.compose.data.remote.response.BaseResponseWithoutDataDto +import com.sopt.now.compose.data.remote.service.AuthService +import com.sopt.now.compose.domain.entity.request.RequestChangePasswordEntity +import com.sopt.now.compose.domain.entity.request.RequestSignInEntity +import com.sopt.now.compose.domain.entity.request.RequestUserEntity +import retrofit2.Response +import javax.inject.Inject + +class AuthDataSourceImpl @Inject constructor( + private val authService: AuthService +) : AuthDataSource { + override suspend fun postSignUp(user: RequestUserEntity): Response = + authService.postSignUp(user) + + override suspend fun postSignIn(user: RequestSignInEntity): Response = + authService.postSignIn(user) + + override suspend fun postChangePassword(user: RequestChangePasswordEntity): Response = + authService.postChangePassword(user) + +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/datasourceImpl/MypageDataSourceImpl.kt b/app/src/main/java/com/sopt/now/compose/data/remote/datasourceImpl/MypageDataSourceImpl.kt new file mode 100644 index 0000000..58615ea --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/datasourceImpl/MypageDataSourceImpl.kt @@ -0,0 +1,13 @@ +package com.sopt.now.compose.data.remote.datasourceImpl + +import com.sopt.now.compose.data.remote.datasource.MypageDataSource +import com.sopt.now.compose.data.remote.response.BaseResponseDto +import com.sopt.now.compose.data.remote.response.ResponseUserDto +import com.sopt.now.compose.data.remote.service.MypageService +import javax.inject.Inject + +class MypageDataSourceImpl @Inject constructor( + private val maypageService: MypageService, +) : MypageDataSource { + override suspend fun getUserInfo(): BaseResponseDto = maypageService.getUserInfo() +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/repositoryImpl/AuthRepositoryImpl.kt b/app/src/main/java/com/sopt/now/compose/data/remote/repositoryImpl/AuthRepositoryImpl.kt new file mode 100644 index 0000000..3bad9a6 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/repositoryImpl/AuthRepositoryImpl.kt @@ -0,0 +1,79 @@ +package com.sopt.now.compose.data.remote.repositoryImpl + +import com.sopt.now.compose.data.local.UserDataStore +import com.sopt.now.compose.data.remote.datasource.AuthDataSource +import com.sopt.now.compose.domain.entity.request.RequestChangePasswordEntity +import com.sopt.now.compose.domain.entity.request.RequestSignInEntity +import com.sopt.now.compose.domain.entity.request.RequestUserEntity +import com.sopt.now.compose.domain.repository.AuthRepository +import com.sopt.now.compose.domain.entity.Result +import javax.inject.Inject + +class AuthRepositoryImpl @Inject constructor( + private val authDataSource: AuthDataSource, + private val userDataStore: UserDataStore +) : AuthRepository { + override suspend fun signup(user: RequestUserEntity): Result { + return runCatching { + authDataSource.postSignUp(user) + }.fold( + onSuccess = { Result.Success(Unit) }, + onFailure = { Result.Error(it) } + ) + } + + override suspend fun login(user: RequestSignInEntity): Result { + return runCatching { + authDataSource.postSignIn(user) + }.fold( + onSuccess = { Result.Success(Unit) }, + onFailure = { Result.Error(it) } + ) + } + + + override suspend fun changePassword(user: RequestChangePasswordEntity): Result { + return runCatching { + authDataSource.postChangePassword(user) + }.fold( + onSuccess = { Result.Success(Unit) }, + onFailure = { Result.Error(it) } + ) + } + + override fun getId() = userDataStore.id + override fun getPassword() = userDataStore.password + override fun getNickname() = userDataStore.nickname + override fun getPhoneNumber() = userDataStore.phoneNumber + override fun setId(id: String) { + userDataStore.id = id + } + + override fun setPassword(password: String) { + userDataStore.password = password + } + + override fun setNickname(nickname: String) { + userDataStore.nickname = nickname + } + + override fun setPhoneNumber(phoneNumber: String) { + userDataStore.phoneNumber = phoneNumber + } + + override fun setUserId(userId: String) { + userDataStore.userId = userId + } + + override fun setUserLoggedIn(loggedIn: Boolean) { + userDataStore.isLoggedIn = loggedIn + } + + override fun clearInfo() { + userDataStore.clearInfo() + } + + companion object { + const val HEADER = "location" + } +} diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/repositoryImpl/MypageRepositoryImpl.kt b/app/src/main/java/com/sopt/now/compose/data/remote/repositoryImpl/MypageRepositoryImpl.kt new file mode 100644 index 0000000..a8537e1 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/repositoryImpl/MypageRepositoryImpl.kt @@ -0,0 +1,16 @@ +package com.sopt.now.compose.data.remote.repositoryImpl + +import com.sopt.now.compose.data.remote.datasource.MypageDataSource +import com.sopt.now.compose.data.remote.response.toResponseUserInfo +import com.sopt.now.compose.domain.entity.response.ResponseUserInfo +import com.sopt.now.compose.domain.repository.MypageRepository +import javax.inject.Inject + +class MypageRepositoryImpl @Inject constructor( + private val mypageDataSource: MypageDataSource +) : MypageRepository { + override suspend fun getUserInfo(): Result = + runCatching { + mypageDataSource.getUserInfo().data.toResponseUserInfo() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/response/BaseResponseDto.kt b/app/src/main/java/com/sopt/now/compose/data/remote/response/BaseResponseDto.kt new file mode 100644 index 0000000..227746f --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/response/BaseResponseDto.kt @@ -0,0 +1,14 @@ +package com.sopt.now.compose.data.remote.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BaseResponseDto( + @SerialName("code") + val code: Int, + @SerialName("message") + val message: String, + @SerialName("data") + val data: T, +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseDto.kt b/app/src/main/java/com/sopt/now/compose/data/remote/response/BaseResponseWithoutDataDto.kt similarity index 68% rename from app/src/main/java/com/sopt/now/compose/network/reponse/ResponseDto.kt rename to app/src/main/java/com/sopt/now/compose/data/remote/response/BaseResponseWithoutDataDto.kt index 9501305..60b6a2b 100644 --- a/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseDto.kt +++ b/app/src/main/java/com/sopt/now/compose/data/remote/response/BaseResponseWithoutDataDto.kt @@ -1,10 +1,10 @@ -package com.sopt.now.compose.network.reponse +package com.sopt.now.compose.data.remote.response import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class ResponseDto( +data class BaseResponseWithoutDataDto( @SerialName("code") val code: Int, @SerialName("message") diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/response/ResponseUserDto.kt b/app/src/main/java/com/sopt/now/compose/data/remote/response/ResponseUserDto.kt new file mode 100644 index 0000000..dfed399 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/response/ResponseUserDto.kt @@ -0,0 +1,17 @@ +package com.sopt.now.compose.data.remote.response + +import com.sopt.now.compose.domain.entity.response.ResponseUserInfo +import kotlinx.serialization.SerialName + +data class ResponseUserDto( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("nickname") + val nickname: String, + @SerialName("phone") + val phone: String, +) + +fun ResponseUserDto.toResponseUserInfo() = ResponseUserInfo( + authenticationId, nickname, phone +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/service/AuthService.kt b/app/src/main/java/com/sopt/now/compose/data/remote/service/AuthService.kt new file mode 100644 index 0000000..97ca72d --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/service/AuthService.kt @@ -0,0 +1,27 @@ +package com.sopt.now.compose.data.remote.service + +import com.sopt.now.compose.data.remote.response.BaseResponseWithoutDataDto +import com.sopt.now.compose.domain.entity.request.RequestChangePasswordEntity +import com.sopt.now.compose.domain.entity.request.RequestSignInEntity +import com.sopt.now.compose.domain.entity.request.RequestUserEntity +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.PATCH +import retrofit2.http.POST + +interface AuthService { + @POST("member/join") + suspend fun postSignUp( + @Body user: RequestUserEntity, + ): Response + + @POST("member/login") + suspend fun postSignIn( + @Body user: RequestSignInEntity, + ): Response + + @PATCH("member/password") + suspend fun postChangePassword( + @Body request: RequestChangePasswordEntity, + ): Response +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/data/remote/service/MypageService.kt b/app/src/main/java/com/sopt/now/compose/data/remote/service/MypageService.kt new file mode 100644 index 0000000..3914f92 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/data/remote/service/MypageService.kt @@ -0,0 +1,10 @@ +package com.sopt.now.compose.data.remote.service + +import com.sopt.now.compose.data.remote.response.BaseResponseDto +import com.sopt.now.compose.data.remote.response.ResponseUserDto +import retrofit2.http.GET + +interface MypageService { + @GET("member/info") + suspend fun getUserInfo():BaseResponseDto +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/AppModule.kt b/app/src/main/java/com/sopt/now/compose/di/AppModule.kt deleted file mode 100644 index 2151679..0000000 --- a/app/src/main/java/com/sopt/now/compose/di/AppModule.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.sopt.now.compose.di - -import android.content.Context -import android.content.SharedPreferences -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory -import com.sopt.now.compose.BuildConfig -import com.sopt.now.compose.data.UserLocalDataSource -import com.sopt.now.compose.data.UserRemoteDataSource -import com.sopt.now.compose.data.UserRepository -import com.sopt.now.compose.network.HeaderInterceptor -import com.sopt.now.compose.network.service.AuthService -import com.sopt.now.compose.network.service.FollowerService -import com.sopt.now.compose.network.service.ServicePool -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -object AppModule { - - @Provides - @Singleton - fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences = - context.getSharedPreferences("SaveLogin", Context.MODE_PRIVATE) - - @Provides - @Singleton - fun provideHeaderInterceptor( - sharedPreferences: SharedPreferences - ): HeaderInterceptor = HeaderInterceptor(sharedPreferences) - - - @Provides - @Singleton - fun provideOkHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient { - return OkHttpClient.Builder() - .addInterceptor(headerInterceptor) - .addInterceptor(HttpLoggingInterceptor().apply { - level = HttpLoggingInterceptor.Level.BODY - }) - .build() - } - - @Provides - @Singleton - fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { - return Retrofit.Builder() - .baseUrl(BuildConfig.AUTH_BASE_URL) - .client(okHttpClient) - .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) - .build() - } - - @Provides - @Singleton - fun provideAuthService(retrofit: Retrofit): AuthService = retrofit.create(AuthService::class.java) - - @Provides - @Singleton - fun provideFollowerService(retrofit: Retrofit): FollowerService = retrofit.create(FollowerService::class.java) - - @Provides - @Singleton - fun provideServicePool( - authService: AuthService, - followerService: FollowerService - ): ServicePool = ServicePool(authService, followerService) - - @Provides - @Singleton - fun provideUserLocalDataSource(sharedPreferences: SharedPreferences): UserLocalDataSource = - UserLocalDataSource(sharedPreferences) - - @Provides - @Singleton - fun provideUserRemoteDataSource(servicePool: ServicePool): UserRemoteDataSource = - UserRemoteDataSource(servicePool.authService) -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/DataSourceModule.kt b/app/src/main/java/com/sopt/now/compose/di/DataSourceModule.kt new file mode 100644 index 0000000..cc33f2f --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/DataSourceModule.kt @@ -0,0 +1,23 @@ +package com.sopt.now.compose.di + +import com.sopt.now.compose.data.remote.datasource.AuthDataSource +import com.sopt.now.compose.data.remote.datasource.MypageDataSource +import com.sopt.now.compose.data.remote.datasourceImpl.AuthDataSourceImpl +import com.sopt.now.compose.data.remote.datasourceImpl.MypageDataSourceImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class DataSourceModule { + @Binds + @Singleton + abstract fun bindAuthDataSource(dataSourceImpl: AuthDataSourceImpl): AuthDataSource + + @Binds + @Singleton + abstract fun bindMypageDataSource(dataSourceImpl: MypageDataSourceImpl): MypageDataSource +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/DataStoreModule.kt b/app/src/main/java/com/sopt/now/compose/di/DataStoreModule.kt new file mode 100644 index 0000000..eecfb88 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/DataStoreModule.kt @@ -0,0 +1,17 @@ +package com.sopt.now.compose.di + +import com.sopt.now.compose.data.local.UserDataStore +import com.sopt.now.compose.data.local.UserDataStoreImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +abstract class DataStoreModule { + @Binds + @Singleton + abstract fun bindDataStore(dataStoreImpl: UserDataStoreImpl): UserDataStore +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/HeaderInterceptor.kt b/app/src/main/java/com/sopt/now/compose/di/HeaderInterceptor.kt new file mode 100644 index 0000000..9ae4269 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/HeaderInterceptor.kt @@ -0,0 +1,23 @@ +package com.sopt.now.compose.di + +import com.sopt.now.compose.data.local.UserDataStore +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import javax.inject.Inject + +class HeaderInterceptor @Inject constructor( + private val dataStore: UserDataStore, +) : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + val newRequest = originalRequest.newBuilder().newAuthBuilder().build() + return chain.proceed(newRequest) + } + + private fun Request.Builder.newAuthBuilder() = this.addHeader(NAME, dataStore.userId) + + companion object { + private const val NAME = "memberId" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/NetworkModule.kt b/app/src/main/java/com/sopt/now/compose/di/NetworkModule.kt new file mode 100644 index 0000000..0fa751f --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/NetworkModule.kt @@ -0,0 +1,48 @@ +package com.sopt.now.compose.di + +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import com.sopt.now.compose.BuildConfig +import com.sopt.now.compose.di.qualifier.HEADER +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.serialization.json.Json +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +object NetworkModule { + @Provides + @Singleton + @HEADER + fun provideHeaderInterceptor(headerInterceptor: HeaderInterceptor): Interceptor = + headerInterceptor + + @Provides + @Singleton + fun provideOkHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient { + return OkHttpClient.Builder() + .addInterceptor(headerInterceptor) + .addInterceptor(HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + }) + .build() + } + + + @Provides + @Singleton + fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit { + return Retrofit.Builder() + .baseUrl(BuildConfig.AUTH_BASE_URL) + .client(okHttpClient) + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/RepositoryModule.kt b/app/src/main/java/com/sopt/now/compose/di/RepositoryModule.kt index 1854b51..8cb8310 100644 --- a/app/src/main/java/com/sopt/now/compose/di/RepositoryModule.kt +++ b/app/src/main/java/com/sopt/now/compose/di/RepositoryModule.kt @@ -1,7 +1,9 @@ package com.sopt.now.compose.di -import com.sopt.now.compose.data.UserRepository -import com.sopt.now.compose.data.UserRepositoryImpl +import com.sopt.now.compose.data.remote.repositoryImpl.AuthRepositoryImpl +import com.sopt.now.compose.data.remote.repositoryImpl.MypageRepositoryImpl +import com.sopt.now.compose.domain.repository.AuthRepository +import com.sopt.now.compose.domain.repository.MypageRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -14,7 +16,9 @@ abstract class RepositoryModule { @Binds @Singleton - abstract fun bindUserRepository( - impl: UserRepositoryImpl - ): UserRepository + abstract fun bindAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository + + @Binds + @Singleton + abstract fun bindMypageRepository(mypageRepository: MypageRepositoryImpl): MypageRepository } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/ServiceModule.kt b/app/src/main/java/com/sopt/now/compose/di/ServiceModule.kt new file mode 100644 index 0000000..571c05f --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/ServiceModule.kt @@ -0,0 +1,19 @@ +package com.sopt.now.compose.di + +import com.sopt.now.compose.data.remote.service.AuthService +import dagger.Module +import dagger.Provides +import com.sopt.now.compose.di.qualifier.AUTH +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import retrofit2.Retrofit +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ServiceModule { + @Provides + @Singleton + fun provideAuthService(@AUTH retrofit: Retrofit): AuthService = + retrofit.create(AuthService::class.java) +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/ShardPreferenceModule.kt b/app/src/main/java/com/sopt/now/compose/di/ShardPreferenceModule.kt new file mode 100644 index 0000000..a45acb8 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/ShardPreferenceModule.kt @@ -0,0 +1,19 @@ +package com.sopt.now.compose.di + +import android.content.Context +import android.content.SharedPreferences +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ShardPreferenceModule { + @Provides + @Singleton + fun provideUserSharedPreferences(@ApplicationContext context: Context): SharedPreferences = + context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE) +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/di/qualifier/RetrofitQualifier.kt b/app/src/main/java/com/sopt/now/compose/di/qualifier/RetrofitQualifier.kt new file mode 100644 index 0000000..f79f90a --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/di/qualifier/RetrofitQualifier.kt @@ -0,0 +1,11 @@ +package com.sopt.now.compose.di.qualifier + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class AUTH + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class HEADER \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/domain/entity/Result.kt b/app/src/main/java/com/sopt/now/compose/domain/entity/Result.kt new file mode 100644 index 0000000..625bb1b --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/domain/entity/Result.kt @@ -0,0 +1,6 @@ +package com.sopt.now.compose.domain.entity + +sealed class Result { + data class Success(val data: T) : Result() + data class Error(val exception: Throwable) : Result() +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/request/RequestChangePasswordDto.kt b/app/src/main/java/com/sopt/now/compose/domain/entity/request/RequesetChangePasswordEntity.kt similarity index 77% rename from app/src/main/java/com/sopt/now/compose/network/request/RequestChangePasswordDto.kt rename to app/src/main/java/com/sopt/now/compose/domain/entity/request/RequesetChangePasswordEntity.kt index 69d2743..3f0d340 100644 --- a/app/src/main/java/com/sopt/now/compose/network/request/RequestChangePasswordDto.kt +++ b/app/src/main/java/com/sopt/now/compose/domain/entity/request/RequesetChangePasswordEntity.kt @@ -1,10 +1,10 @@ -package com.sopt.now.compose.network.request +package com.sopt.now.compose.domain.entity.request import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class RequestChangePasswordDto( +data class RequestChangePasswordEntity( @SerialName("previousPassword") val previousPassword: String, @SerialName("newPassword") diff --git a/app/src/main/java/com/sopt/now/compose/network/request/RequestLoginDto.kt b/app/src/main/java/com/sopt/now/compose/domain/entity/request/RequestSignInEntity.kt similarity index 72% rename from app/src/main/java/com/sopt/now/compose/network/request/RequestLoginDto.kt rename to app/src/main/java/com/sopt/now/compose/domain/entity/request/RequestSignInEntity.kt index 490a99b..a5baa97 100644 --- a/app/src/main/java/com/sopt/now/compose/network/request/RequestLoginDto.kt +++ b/app/src/main/java/com/sopt/now/compose/domain/entity/request/RequestSignInEntity.kt @@ -1,10 +1,10 @@ -package com.sopt.now.compose.network.request +package com.sopt.now.compose.domain.entity.request import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class RequestLoginDto( +data class RequestSignInEntity( @SerialName("authenticationId") val authenticationId: String, @SerialName("password") diff --git a/app/src/main/java/com/sopt/now/compose/network/request/RequestSignUpDto.kt b/app/src/main/java/com/sopt/now/compose/domain/entity/request/RequestUserEntity.kt similarity index 80% rename from app/src/main/java/com/sopt/now/compose/network/request/RequestSignUpDto.kt rename to app/src/main/java/com/sopt/now/compose/domain/entity/request/RequestUserEntity.kt index a480e06..b12db31 100644 --- a/app/src/main/java/com/sopt/now/compose/network/request/RequestSignUpDto.kt +++ b/app/src/main/java/com/sopt/now/compose/domain/entity/request/RequestUserEntity.kt @@ -1,10 +1,10 @@ -package com.sopt.now.compose.network.request +package com.sopt.now.compose.domain.entity.request import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class RequestSignUpDto( +data class RequestUserEntity( @SerialName("authenticationId") val authenticationId: String, @SerialName("password") diff --git a/app/src/main/java/com/sopt/now/compose/domain/entity/response/ResponseUserInfo.kt b/app/src/main/java/com/sopt/now/compose/domain/entity/response/ResponseUserInfo.kt new file mode 100644 index 0000000..6b880ae --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/domain/entity/response/ResponseUserInfo.kt @@ -0,0 +1,14 @@ +package com.sopt.now.compose.domain.entity.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class ResponseUserInfo( + @SerialName("authenticationId") + val authenticationId: String, + @SerialName("nickname") + val nickname: String, + @SerialName("phone") + val phone: String, +) diff --git a/app/src/main/java/com/sopt/now/compose/domain/repository/AuthRepository.kt b/app/src/main/java/com/sopt/now/compose/domain/repository/AuthRepository.kt new file mode 100644 index 0000000..01f9e9a --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/domain/repository/AuthRepository.kt @@ -0,0 +1,23 @@ +package com.sopt.now.compose.domain.repository + +import com.sopt.now.compose.domain.entity.request.RequestChangePasswordEntity +import com.sopt.now.compose.domain.entity.request.RequestSignInEntity +import com.sopt.now.compose.domain.entity.request.RequestUserEntity +import com.sopt.now.compose.domain.entity.Result + +interface AuthRepository { + suspend fun signup(user: RequestUserEntity): Result + suspend fun login(user: RequestSignInEntity): Result + suspend fun changePassword(user: RequestChangePasswordEntity): Result + fun getId(): String + fun getPassword(): String + fun getNickname(): String + fun getPhoneNumber(): String + fun setId(id: String) + fun setPassword(password: String) + fun setNickname(nickname: String) + fun setPhoneNumber(phoneNumber: String) + fun setUserId(userId: String) + fun setUserLoggedIn(loggedIn: Boolean) + fun clearInfo() +} diff --git a/app/src/main/java/com/sopt/now/compose/domain/repository/MypageRepository.kt b/app/src/main/java/com/sopt/now/compose/domain/repository/MypageRepository.kt new file mode 100644 index 0000000..9c90d2c --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/domain/repository/MypageRepository.kt @@ -0,0 +1,7 @@ +package com.sopt.now.compose.domain.repository + +import com.sopt.now.compose.domain.entity.response.ResponseUserInfo + +interface MypageRepository { + suspend fun getUserInfo(): Result +} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/model/User.kt b/app/src/main/java/com/sopt/now/compose/model/User.kt new file mode 100644 index 0000000..b1250b3 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/model/User.kt @@ -0,0 +1,8 @@ +package com.sopt.now.compose.model + +data class User( + val id: String = "", + val pw: String = "", + val nickname: String = "", + val phoneNumber: String = "", +) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/ApiFactory.kt b/app/src/main/java/com/sopt/now/compose/network/ApiFactory.kt deleted file mode 100644 index 6a6901d..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/ApiFactory.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.sopt.now.compose.network - -import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory -import com.sopt.now.compose.BuildConfig -import com.sopt.now.compose.SoptApp -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ApiFactory @Inject constructor( - val retrofit: Retrofit -) { - inline fun create(): T = retrofit.create(T::class.java) -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/FollowerApiFactory.kt b/app/src/main/java/com/sopt/now/compose/network/FollowerApiFactory.kt deleted file mode 100644 index 3375d70..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/FollowerApiFactory.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.sopt.now.compose.network - -import retrofit2.Retrofit -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class FollowerApiFactory @Inject constructor( - val retrofit: Retrofit -) { - inline fun create(): T = retrofit.create(T::class.java) -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/HeaderInterceptor.kt b/app/src/main/java/com/sopt/now/compose/network/HeaderInterceptor.kt deleted file mode 100644 index 6e796aa..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/HeaderInterceptor.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.sopt.now.compose.network - -import android.content.SharedPreferences -import okhttp3.Interceptor -import okhttp3.Response -import javax.inject.Inject - -class HeaderInterceptor @Inject constructor( - private val sharedPreferences: SharedPreferences -) : - Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val originalRequest = chain.request() - val memberId = sharedPreferences.getString("MemberID", "default_member_id") ?: "default_member_id" - val newRequest = originalRequest.newBuilder() - .addHeader("memberId", memberId) - .build() - return chain.proceed(newRequest) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseFollowerDto.kt b/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseFollowerDto.kt deleted file mode 100644 index daaba7b..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseFollowerDto.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.sopt.now.compose.network.reponse - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class ResponseFollowerDto( - @SerialName("page") - val page: Int, - @SerialName("per_page") - val perPage: Int, - @SerialName("total") - val total: Int, - @SerialName("total_pages") - val totalPages: Int, - @SerialName("data") - val `data`: List, - @SerialName("support") - val support: Support, -) { - @Serializable - data class Data( - @SerialName("id") - val id: Int, - @SerialName("email") - val email: String, - @SerialName("first_name") - val firstName: String, - @SerialName("last_name") - val lastName: String, - @SerialName("avatar") - val avatar: String, - ) - - @Serializable - data class Support( - @SerialName("url") - val url: String, - @SerialName("text") - val text: String, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseInfoDto.kt b/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseInfoDto.kt deleted file mode 100644 index bbd6331..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/reponse/ResponseInfoDto.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.sopt.now.compose.network.reponse - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class ResponseInfoDto ( - @SerialName("code") - val code: Int, - @SerialName("message") - val message: String, - @SerialName("data") - val data: UserInfo -) - -@Serializable -data class UserInfo( - @SerialName("authenticationId") - val authenticationId: String, - @SerialName("nickname") - val nickname: String, - @SerialName("phone") - val phone: String -) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/service/AuthService.kt b/app/src/main/java/com/sopt/now/compose/network/service/AuthService.kt deleted file mode 100644 index 21e5f20..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/service/AuthService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.sopt.now.compose.network.service - -import com.sopt.now.compose.network.reponse.ResponseDto -import com.sopt.now.compose.network.reponse.ResponseInfoDto -import com.sopt.now.compose.network.request.RequestChangePasswordDto -import com.sopt.now.compose.network.request.RequestLoginDto -import com.sopt.now.compose.network.request.RequestSignUpDto -import retrofit2.Response -import retrofit2.http.Body -import retrofit2.http.GET -import retrofit2.http.PATCH -import retrofit2.http.POST - -interface AuthService { - @POST("member/join") - suspend fun signUp( - @Body request: RequestSignUpDto, - ): Response - - @POST("member/login") - suspend fun login( - @Body request: RequestLoginDto, - ): Response - - @PATCH("member/password") - suspend fun changePassword( - @Body request: RequestChangePasswordDto, - ): Response - - @GET("member/info") - suspend fun userInfo(): Response -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/service/FollowerService.kt b/app/src/main/java/com/sopt/now/compose/network/service/FollowerService.kt deleted file mode 100644 index 7f0b5ba..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/service/FollowerService.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.sopt.now.compose.network.service - -import com.sopt.now.compose.network.reponse.ResponseFollowerDto -import retrofit2.Call -import retrofit2.http.GET -import retrofit2.http.Query - -interface FollowerService { - @GET("users") - fun getFollowers( - @Query("page") page: Int, - ): Call -} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/network/service/ServicePool.kt b/app/src/main/java/com/sopt/now/compose/network/service/ServicePool.kt deleted file mode 100644 index bcd9541..0000000 --- a/app/src/main/java/com/sopt/now/compose/network/service/ServicePool.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.sopt.now.compose.network.service - -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ServicePool @Inject constructor( - val authService: AuthService, - val followerService: FollowerService -) \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/changePassword/.DS_Store b/app/src/main/java/com/sopt/now/compose/ui/changePassword/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/app/src/main/java/com/sopt/now/compose/ui/changePassword/.DS_Store differ diff --git a/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordScreen.kt index 9d79469..df4ab82 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordScreen.kt @@ -1,159 +1,157 @@ -package com.sopt.now.compose.ui.changePassword - -import androidx.compose.foundation.background -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.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarDuration -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -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.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavController -import com.sopt.now.compose.R -import com.sopt.now.compose.network.request.RequestChangePasswordDto -import com.sopt.now.compose.util.noRippleClickable - -@Composable -fun ChangePasswordScreen(navController: NavController) { - val viewModel: ChangePasswordViewModel = hiltViewModel() - val snackbarHostState = remember { SnackbarHostState() } - - var prePassword by remember { mutableStateOf("") } - var newPassword by remember { mutableStateOf("") } - var verifyPassword by remember { mutableStateOf("") } - - val authState by viewModel.changePasswordStatus.observeAsState() - - LaunchedEffect(authState) { - authState?.let { state -> - snackbarHostState.showSnackbar( - message = state.message, - duration = SnackbarDuration.Short - ) - if (state.isSuccess) { - navController.navigate("login") { - popUpTo("changePassword") { inclusive = true } - } - } - } - } - - Scaffold( - modifier = Modifier.fillMaxSize(), - bottomBar = {} - ) { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = 20.dp, vertical = 20.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - stringResource(R.string.ch_pw), - fontSize = 30.sp, - ) - - Spacer(Modifier.height(20.dp)) - OutlinedTextField( - value = prePassword, - onValueChange = { prePassword = it }, - label = { Text(stringResource(R.string.pre_pw)) }, - singleLine = true, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, - keyboardType = KeyboardType.Password - ), - visualTransformation = PasswordVisualTransformation(), - modifier = Modifier.fillMaxWidth() - ) - - Spacer(Modifier.height(16.dp)) - OutlinedTextField( - value = newPassword, - onValueChange = { newPassword = it }, - label = { Text(stringResource(R.string.new_pw)) }, - singleLine = true, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, - keyboardType = KeyboardType.Password - ), - visualTransformation = PasswordVisualTransformation(), - modifier = Modifier.fillMaxWidth() - ) - - Spacer(Modifier.height(16.dp)) - OutlinedTextField( - value = verifyPassword, - onValueChange = { verifyPassword = it }, - label = { Text(stringResource(R.string.verify_pw)) }, - singleLine = true, - keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, - keyboardType = KeyboardType.Password - ), - visualTransformation = PasswordVisualTransformation(), - modifier = Modifier.fillMaxWidth() - ) - - Spacer(modifier = Modifier.weight(1f)) - NoRippleButton( - text = stringResource(R.string.btn_ch_pw), - onClick = { - viewModel.changePassword(RequestChangePasswordDto(prePassword, newPassword, verifyPassword)) - }, - modifier = Modifier.fillMaxWidth() - ) - } - } -} - -@Composable -fun NoRippleButton( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Box( - contentAlignment = Alignment.Center, - modifier = modifier - .fillMaxWidth() - .background( - color = MaterialTheme.colorScheme.primary, - shape = RoundedCornerShape(30.dp) - ) - .noRippleClickable(onClick = onClick) - .padding(vertical = 12.dp, horizontal = 16.dp) - ) { - Text( - text = text, - fontSize = 14.sp, - color = MaterialTheme.colorScheme.onPrimary - ) - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.changePassword +// +//import androidx.compose.foundation.background +//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.shape.RoundedCornerShape +//import androidx.compose.foundation.text.KeyboardOptions +//import androidx.compose.material3.MaterialTheme +//import androidx.compose.material3.OutlinedTextField +//import androidx.compose.material3.Scaffold +//import androidx.compose.material3.SnackbarDuration +//import androidx.compose.material3.SnackbarHostState +//import androidx.compose.material3.Text +//import androidx.compose.runtime.Composable +//import androidx.compose.runtime.LaunchedEffect +//import androidx.compose.runtime.getValue +//import androidx.compose.runtime.livedata.observeAsState +//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.res.stringResource +//import androidx.compose.ui.text.input.ImeAction +//import androidx.compose.ui.text.input.KeyboardType +//import androidx.compose.ui.text.input.PasswordVisualTransformation +//import androidx.compose.ui.unit.dp +//import androidx.compose.ui.unit.sp +//import androidx.hilt.navigation.compose.hiltViewModel +//import androidx.navigation.NavController +//import com.sopt.now.compose.R +//import com.sopt.now.compose.util.noRippleClickable +// +//@Composable +//fun ChangePasswordScreen(navController: NavController) { +// val viewModel: ChangePasswordViewModel = hiltViewModel() +// val snackbarHostState = remember { SnackbarHostState() } +// +// var prePassword by remember { mutableStateOf("") } +// var newPassword by remember { mutableStateOf("") } +// var verifyPassword by remember { mutableStateOf("") } +// +// val authState by viewModel.changePasswordStatus.observeAsState() +// +// LaunchedEffect(authState) { +// authState?.let { state -> +// snackbarHostState.showSnackbar( +// message = state.message, +// duration = SnackbarDuration.Short +// ) +// if (state.isSuccess) { +// navController.navigate("login") { +// popUpTo("changePassword") { inclusive = true } +// } +// } +// } +// } +// +// Scaffold( +// modifier = Modifier.fillMaxSize(), +// bottomBar = {} +// ) { innerPadding -> +// Column( +// modifier = Modifier +// .fillMaxSize() +// .padding(innerPadding) +// .padding(horizontal = 20.dp, vertical = 20.dp), +// horizontalAlignment = Alignment.CenterHorizontally +// ) { +// Text( +// stringResource(R.string.ch_pw), +// fontSize = 30.sp, +// ) +// +// Spacer(Modifier.height(20.dp)) +// OutlinedTextField( +// value = prePassword, +// onValueChange = { prePassword = it }, +// label = { Text(stringResource(R.string.pre_pw)) }, +// singleLine = true, +// keyboardOptions = KeyboardOptions( +// imeAction = ImeAction.Next, +// keyboardType = KeyboardType.Password +// ), +// visualTransformation = PasswordVisualTransformation(), +// modifier = Modifier.fillMaxWidth() +// ) +// +// Spacer(Modifier.height(16.dp)) +// OutlinedTextField( +// value = newPassword, +// onValueChange = { newPassword = it }, +// label = { Text(stringResource(R.string.new_pw)) }, +// singleLine = true, +// keyboardOptions = KeyboardOptions( +// imeAction = ImeAction.Next, +// keyboardType = KeyboardType.Password +// ), +// visualTransformation = PasswordVisualTransformation(), +// modifier = Modifier.fillMaxWidth() +// ) +// +// Spacer(Modifier.height(16.dp)) +// OutlinedTextField( +// value = verifyPassword, +// onValueChange = { verifyPassword = it }, +// label = { Text(stringResource(R.string.verify_pw)) }, +// singleLine = true, +// keyboardOptions = KeyboardOptions( +// imeAction = ImeAction.Next, +// keyboardType = KeyboardType.Password +// ), +// visualTransformation = PasswordVisualTransformation(), +// modifier = Modifier.fillMaxWidth() +// ) +// +// Spacer(modifier = Modifier.weight(1f)) +// NoRippleButton( +// text = stringResource(R.string.btn_ch_pw), +// onClick = { +// viewModel.changePassword(RequestChangePasswordDto(prePassword, newPassword, verifyPassword)) +// }, +// modifier = Modifier.fillMaxWidth() +// ) +// } +// } +//} +// +//@Composable +//fun NoRippleButton( +// text: String, +// onClick: () -> Unit, +// modifier: Modifier = Modifier, +//) { +// Box( +// contentAlignment = Alignment.Center, +// modifier = modifier +// .fillMaxWidth() +// .background( +// color = MaterialTheme.colorScheme.primary, +// shape = RoundedCornerShape(30.dp) +// ) +// .noRippleClickable(onClick = onClick) +// .padding(vertical = 12.dp, horizontal = 16.dp) +// ) { +// Text( +// text = text, +// fontSize = 14.sp, +// color = MaterialTheme.colorScheme.onPrimary +// ) +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordViewModel.kt index ea784f4..86289fd 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordViewModel.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/changePassword/ChangePasswordViewModel.kt @@ -1,68 +1,70 @@ -package com.sopt.now.compose.ui.changePassword - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.sopt.now.compose.data.UserRepository -import com.sopt.now.compose.network.reponse.ResponseDto -import com.sopt.now.compose.network.request.RequestChangePasswordDto -import com.sopt.now.compose.ui.AuthState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import retrofit2.Response -import javax.inject.Inject - -@HiltViewModel -class ChangePasswordViewModel @Inject constructor ( - private val userRepository: UserRepository, -) : ViewModel() { - private val _changePasswordStatus = MutableLiveData() - val changePasswordStatus: LiveData = _changePasswordStatus - - fun changePassword(request: RequestChangePasswordDto) { - viewModelScope.launch { - runCatching { - userRepository.changePassword(request) - }.onSuccess { response -> - handleSuccess(response, request) - }.onFailure { - _changePasswordStatus.value = AuthState( - isSuccess = false, - message = "서버 에러" - ) - } - } - } - - private fun handleSuccess( - response: Response, - request: RequestChangePasswordDto, - ) { - if (response.isSuccessful) { - successResponse(response, request) - } else { - failResponse(response) - } - } - - private fun successResponse( - response: Response, - request: RequestChangePasswordDto, - ) { - val memberId = response.headers()["location"] ?: "unknown" - userRepository.updateUserPassword(request.newPassword) - _changePasswordStatus.value = AuthState( - isSuccess = true, - message = "비밀번호 변경 성공 유저의 ID는 $memberId 입니다." - ) - } - - private fun failResponse(response: Response) { - val error = response.message() - _changePasswordStatus.value = AuthState( - isSuccess = false, - message = "비밀번호 변경 실패 $error" - ) - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.changePassword +// +//import androidx.lifecycle.LiveData +//import androidx.lifecycle.MutableLiveData +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.sopt.now.compose.data.remote.response.BaseResponseWithoutDataDto +//import com.sopt.now.compose.domain.entity.request.RequestChangePasswordEntity +//import com.sopt.now.compose.domain.repository.AuthRepository +//import com.sopt.now.compose.network.request.RequestChangePasswordDto +//import com.sopt.now.compose.ui.AuthState +//import dagger.hilt.android.lifecycle.HiltViewModel +//import kotlinx.coroutines.launch +//import retrofit2.Response +//import javax.inject.Inject +// +//@HiltViewModel +//class ChangePasswordViewModel @Inject constructor( +// private val authRepository: AuthRepository, +//) : ViewModel() { +// private val _changePasswordStatus = MutableLiveData() +// val changePasswordStatus: LiveData = _changePasswordStatus +// +// fun changePassword(request: RequestChangePasswordDto) { +// viewModelScope.launch { +// runCatching { +// val requestEntity = RequestChangePasswordEntity( +// previousPassword = request.previousPassword, +// newPassword = request.newPassword, +// newPasswordVerification = request.newPasswordVerification +// ) +// authRepository.changePassword(requestEntity) +// }.onSuccess { response -> +// handleSuccess(response) +// }.onFailure { +// _changePasswordStatus.value = AuthState( +// isSuccess = false, +// message = "서버 에러" +// ) +// } +// } +// } +// +// private fun handleSuccess(response: Result>) { +// response.onSuccess { +// if (it.isSuccessful) { +// val memberId = it.headers()["location"] ?: "unknown" +// _changePasswordStatus.value = AuthState( +// isSuccess = true, +// message = "비밀번호 변경 성공 유저의 ID는 $memberId 입니다." +// ) +// } else { +// failResponse(it) +// } +// }.onFailure { +// _changePasswordStatus.value = AuthState( +// isSuccess = false, +// message = "비밀번호 변경 실패 ${it.message}" +// ) +// } +// } +// +// private fun failResponse(response: Response) { +// val error = response.message() +// _changePasswordStatus.value = AuthState( +// isSuccess = false, +// message = "비밀번호 변경 실패 $error" +// ) +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerScreen.kt index 192c913..80bbbd6 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerScreen.kt @@ -1,56 +1,56 @@ -package com.sopt.now.compose.ui.follower - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Card -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel -import coil.compose.AsyncImage -import com.sopt.now.compose.network.reponse.ResponseFollowerDto - -@Composable -fun FollowerScreen() { - val viewModel: FollowerViewModel = viewModel() - val followers by viewModel.followers.collectAsStateWithLifecycle() - - LazyColumn { - items(followers) { follower -> - FollowerItem(follower) - } - } -} - -@Composable -fun FollowerItem(follower: ResponseFollowerDto.Data) { - Card( - modifier = Modifier - .padding(8.dp) - .fillMaxSize() - ) { - Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = follower.avatar, - contentDescription = "Profile Picture", - modifier = Modifier - .size(150.dp) - .padding(8.dp) - ) - Column { - Text("${follower.firstName} ${follower.lastName}", color = Color.Black) - Text(follower.email, color = Color.Gray) - } - } - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.follower +// +//import androidx.compose.foundation.layout.Column +//import androidx.compose.foundation.layout.Row +//import androidx.compose.foundation.layout.fillMaxSize +//import androidx.compose.foundation.layout.padding +//import androidx.compose.foundation.layout.size +//import androidx.compose.foundation.lazy.LazyColumn +//import androidx.compose.foundation.lazy.items +//import androidx.compose.material3.Card +//import androidx.compose.material3.Text +//import androidx.compose.runtime.Composable +//import androidx.compose.runtime.getValue +//import androidx.compose.ui.Alignment +//import androidx.compose.ui.Modifier +//import androidx.compose.ui.graphics.Color +//import androidx.compose.ui.unit.dp +//import androidx.lifecycle.compose.collectAsStateWithLifecycle +//import androidx.lifecycle.viewmodel.compose.viewModel +//import coil.compose.AsyncImage +//import com.sopt.now.compose.network.reponse.ResponseFollowerDto +// +//@Composable +//fun FollowerScreen() { +// val viewModel: FollowerViewModel = viewModel() +// val followers by viewModel.followers.collectAsStateWithLifecycle() +// +// LazyColumn { +// items(followers) { follower -> +// FollowerItem(follower) +// } +// } +//} +// +//@Composable +//fun FollowerItem(follower: ResponseFollowerDto.Data) { +// Card( +// modifier = Modifier +// .padding(8.dp) +// .fillMaxSize() +// ) { +// Row(verticalAlignment = Alignment.CenterVertically) { +// AsyncImage( +// model = follower.avatar, +// contentDescription = "Profile Picture", +// modifier = Modifier +// .size(150.dp) +// .padding(8.dp) +// ) +// Column { +// Text("${follower.firstName} ${follower.lastName}", color = Color.Black) +// Text(follower.email, color = Color.Gray) +// } +// } +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerViewModel.kt index 6069db3..a7080cd 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerViewModel.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/follower/FollowerViewModel.kt @@ -1,45 +1,45 @@ -package com.sopt.now.compose.ui.follower - -import android.util.Log -import androidx.lifecycle.ViewModel -import com.sopt.now.compose.network.reponse.ResponseFollowerDto -import com.sopt.now.compose.network.service.ServicePool -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import javax.inject.Inject - -@HiltViewModel -class FollowerViewModel @Inject constructor( - private val servicePool: ServicePool -) : ViewModel() { - private val _followers = MutableStateFlow>(emptyList()) - val followers: StateFlow> = _followers - - init { - loadFollowers() - } - - private fun loadFollowers() { - servicePool.followerService.getFollowers(2).enqueue(object : Callback { - override fun onResponse( - call: Call, - response: Response, - ) { - if (response.isSuccessful) { - _followers.value = response.body()?.data.orEmpty() - } else { - Log.e("FollowerViewModel", "Failed to fetch followers") - } - } - - override fun onFailure(call: Call, t: Throwable) { - Log.e("FollowerViewModel", "Error fetching followers", t) - } - - }) - } -} +//package com.sopt.now.compose.ui.follower +// +//import android.util.Log +//import androidx.lifecycle.ViewModel +//import com.sopt.now.compose.network.reponse.ResponseFollowerDto +//import com.sopt.now.compose.network.service.ServicePool +//import dagger.hilt.android.lifecycle.HiltViewModel +//import kotlinx.coroutines.flow.MutableStateFlow +//import kotlinx.coroutines.flow.StateFlow +//import retrofit2.Call +//import retrofit2.Callback +//import retrofit2.Response +//import javax.inject.Inject +// +//@HiltViewModel +//class FollowerViewModel @Inject constructor( +// private val servicePool: ServicePool +//) : ViewModel() { +// private val _followers = MutableStateFlow>(emptyList()) +// val followers: StateFlow> = _followers +// +// init { +// loadFollowers() +// } +// +// private fun loadFollowers() { +// servicePool.followerService.getFollowers(2).enqueue(object : Callback { +// override fun onResponse( +// call: Call, +// response: Response, +// ) { +// if (response.isSuccessful) { +// _followers.value = response.body()?.data.orEmpty() +// } else { +// Log.e("FollowerViewModel", "Failed to fetch followers") +// } +// } +// +// override fun onFailure(call: Call, t: Throwable) { +// Log.e("FollowerViewModel", "Error fetching followers", t) +// } +// +// }) +// } +//} diff --git a/app/src/main/java/com/sopt/now/compose/ui/login/LoginScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/login/LoginScreen.kt index db98f82..8514ef1 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/login/LoginScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/login/LoginScreen.kt @@ -1,162 +1,162 @@ -package com.sopt.now.compose.ui.login - -import androidx.compose.foundation.background -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.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarDuration -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -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.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController -import com.sopt.now.compose.R -import com.sopt.now.compose.network.request.RequestLoginDto -import com.sopt.now.compose.util.noRippleClickable - -@Composable -fun LoginScreen(navController: NavController) { - val viewModel: LoginViewModel = hiltViewModel() - val snackbarHostState = remember { SnackbarHostState() } - - var inputId by remember { mutableStateOf("") } - var inputPw by remember { mutableStateOf("") } - - val authState by viewModel.loginStatus.observeAsState() - - LaunchedEffect(authState) { - authState?.let { state -> - if (state.isSuccess) { - snackbarHostState.showSnackbar( - message = "로그인 성공! 홈 화면으로 이동합니다.", - duration = SnackbarDuration.Short - ) - navController.navigate("home") { - popUpTo("login") { inclusive = true } - } - } else { - snackbarHostState.showSnackbar( - message = state.message, - duration = SnackbarDuration.Short - ) - } - } - } - - Scaffold( - modifier = Modifier.fillMaxSize(), - snackbarHost = { SnackbarHost(hostState = snackbarHostState) } - ) { innerPadding -> - Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = 20.dp, vertical = 20.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = stringResource(R.string.app_name), - fontSize = 30.sp, - ) - - Spacer(modifier = Modifier.height(30.dp)) - OutlinedTextField( - value = inputId, - onValueChange = { inputId = it }, - label = { Text(stringResource(R.string.id)) }, - singleLine = true, - keyboardOptions = KeyboardOptions.Default.copy( - imeAction = ImeAction.Next - ), - modifier = Modifier.fillMaxWidth() - ) - - Spacer(modifier = Modifier.height(20.dp)) - OutlinedTextField( - value = inputPw, - onValueChange = { inputPw = it }, - label = { Text(stringResource(R.string.pw)) }, - singleLine = true, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions.Default.copy( - imeAction = ImeAction.Done, - keyboardType = KeyboardType.Password - ), - modifier = Modifier.fillMaxWidth() - ) - - TextButton( - onClick = { navController.navigate("signup") }, - modifier = Modifier.align(Alignment.Start) - ) { - Text( - stringResource(R.string.signup_kr), - color = Color.DarkGray - ) - } - - Spacer(modifier = Modifier.weight(1f)) - CustomBtn( - text = stringResource(R.string.btn_login), - onClick = { - viewModel.login(RequestLoginDto(inputId, inputPw)) - }, - modifier = Modifier - .fillMaxWidth() - .padding(bottom = 10.dp) - ) - - } - } -} - -@Composable -fun CustomBtn( - text: String, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Box( - contentAlignment = Alignment.Center, - modifier = modifier - .fillMaxWidth() - .background( - MaterialTheme.colorScheme.primary, - shape = RoundedCornerShape(30.dp) - ) - .noRippleClickable(onClick = onClick) - .padding(vertical = 12.dp, horizontal = 16.dp) - ) { - Text( - text = text, - fontSize = 14.sp, - color = MaterialTheme.colorScheme.onPrimary - ) - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.login +// +//import androidx.compose.foundation.background +//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.shape.RoundedCornerShape +//import androidx.compose.foundation.text.KeyboardOptions +//import androidx.compose.material3.MaterialTheme +//import androidx.compose.material3.OutlinedTextField +//import androidx.compose.material3.Scaffold +//import androidx.compose.material3.SnackbarDuration +//import androidx.compose.material3.SnackbarHost +//import androidx.compose.material3.SnackbarHostState +//import androidx.compose.material3.Text +//import androidx.compose.material3.TextButton +//import androidx.compose.runtime.Composable +//import androidx.compose.runtime.LaunchedEffect +//import androidx.compose.runtime.getValue +//import androidx.compose.runtime.livedata.observeAsState +//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.graphics.Color +//import androidx.compose.ui.res.stringResource +//import androidx.compose.ui.text.input.ImeAction +//import androidx.compose.ui.text.input.KeyboardType +//import androidx.compose.ui.text.input.PasswordVisualTransformation +//import androidx.compose.ui.unit.dp +//import androidx.compose.ui.unit.sp +//import androidx.hilt.navigation.compose.hiltViewModel +//import androidx.navigation.NavController +//import com.sopt.now.compose.R +//import com.sopt.now.compose.network.request.RequestLoginDto +//import com.sopt.now.compose.util.noRippleClickable +// +//@Composable +//fun LoginScreen(navController: NavController) { +// val viewModel: LoginViewModel = hiltViewModel() +// val snackbarHostState = remember { SnackbarHostState() } +// +// var inputId by remember { mutableStateOf("") } +// var inputPw by remember { mutableStateOf("") } +// +// val authState by viewModel.loginStatus.observeAsState() +// +// LaunchedEffect(authState) { +// authState?.let { state -> +// if (state.isSuccess) { +// snackbarHostState.showSnackbar( +// message = "로그인 성공! 홈 화면으로 이동합니다.", +// duration = SnackbarDuration.Short +// ) +// navController.navigate("home") { +// popUpTo("login") { inclusive = true } +// } +// } else { +// snackbarHostState.showSnackbar( +// message = state.message, +// duration = SnackbarDuration.Short +// ) +// } +// } +// } +// +// Scaffold( +// modifier = Modifier.fillMaxSize(), +// snackbarHost = { SnackbarHost(hostState = snackbarHostState) } +// ) { innerPadding -> +// Column( +// modifier = Modifier +// .fillMaxSize() +// .padding(innerPadding) +// .padding(horizontal = 20.dp, vertical = 20.dp), +// horizontalAlignment = Alignment.CenterHorizontally +// ) { +// Text( +// text = stringResource(R.string.app_name), +// fontSize = 30.sp, +// ) +// +// Spacer(modifier = Modifier.height(30.dp)) +// OutlinedTextField( +// value = inputId, +// onValueChange = { inputId = it }, +// label = { Text(stringResource(R.string.id)) }, +// singleLine = true, +// keyboardOptions = KeyboardOptions.Default.copy( +// imeAction = ImeAction.Next +// ), +// modifier = Modifier.fillMaxWidth() +// ) +// +// Spacer(modifier = Modifier.height(20.dp)) +// OutlinedTextField( +// value = inputPw, +// onValueChange = { inputPw = it }, +// label = { Text(stringResource(R.string.pw)) }, +// singleLine = true, +// visualTransformation = PasswordVisualTransformation(), +// keyboardOptions = KeyboardOptions.Default.copy( +// imeAction = ImeAction.Done, +// keyboardType = KeyboardType.Password +// ), +// modifier = Modifier.fillMaxWidth() +// ) +// +// TextButton( +// onClick = { navController.navigate("signup") }, +// modifier = Modifier.align(Alignment.Start) +// ) { +// Text( +// stringResource(R.string.signup_kr), +// color = Color.DarkGray +// ) +// } +// +// Spacer(modifier = Modifier.weight(1f)) +// CustomBtn( +// text = stringResource(R.string.btn_login), +// onClick = { +// viewModel.login(RequestLoginDto(inputId, inputPw)) +// }, +// modifier = Modifier +// .fillMaxWidth() +// .padding(bottom = 10.dp) +// ) +// +// } +// } +//} +// +//@Composable +//fun CustomBtn( +// text: String, +// onClick: () -> Unit, +// modifier: Modifier = Modifier, +//) { +// Box( +// contentAlignment = Alignment.Center, +// modifier = modifier +// .fillMaxWidth() +// .background( +// MaterialTheme.colorScheme.primary, +// shape = RoundedCornerShape(30.dp) +// ) +// .noRippleClickable(onClick = onClick) +// .padding(vertical = 12.dp, horizontal = 16.dp) +// ) { +// Text( +// text = text, +// fontSize = 14.sp, +// color = MaterialTheme.colorScheme.onPrimary +// ) +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/login/LoginViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/login/LoginViewModel.kt index 6e39270..df73acb 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/login/LoginViewModel.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/login/LoginViewModel.kt @@ -1,65 +1,67 @@ -package com.sopt.now.compose.ui.login - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.sopt.now.compose.data.UserRepository -import com.sopt.now.compose.network.service.ServicePool -import com.sopt.now.compose.network.reponse.ResponseDto -import com.sopt.now.compose.network.request.RequestLoginDto -import com.sopt.now.compose.ui.AuthState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import retrofit2.Response -import javax.inject.Inject - -@HiltViewModel -class LoginViewModel @Inject constructor( - private val userRepository: UserRepository, -) : ViewModel() { - private val _loginStatus = MutableLiveData() - val loginStatus: LiveData = _loginStatus - - fun login(request: RequestLoginDto) { - viewModelScope.launch { - runCatching { - userRepository.login(request) - }.onSuccess { response -> - handleSuccess(response) - }.onFailure { - _loginStatus.value = AuthState( - isSuccess = false, - message = "서버 에러" - ) - } - } - } - - private fun handleSuccess(response: Response) { - if (response.isSuccessful) { - successResponse(response) - } else { - failResponse(response) - } - } - - private fun successResponse(response: Response) { - val memberId = response.headers()["location"] ?: "unknown" - userRepository.setUserLoggedIn(true) - userRepository.setMemberId(memberId) - - _loginStatus.value = AuthState( - isSuccess = true, - message = "로그인 성공 유저의 ID는 $memberId 입니다." - ) - } - - private fun failResponse(response: Response) { - val error = response.message() - _loginStatus.value = AuthState( - isSuccess = false, - message = "로그인 실패 $error" - ) - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.login +// +//import androidx.lifecycle.LiveData +//import androidx.lifecycle.MutableLiveData +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.sopt.now.compose.data.remote.response.BaseResponseWithoutDataDto +//import com.sopt.now.compose.domain.entity.request.RequestSignInEntity +//import com.sopt.now.compose.domain.repository.AuthRepository +//import com.sopt.now.compose.ui.AuthState +//import dagger.hilt.android.lifecycle.HiltViewModel +//import kotlinx.coroutines.launch +//import retrofit2.Response +//import javax.inject.Inject +// +//@HiltViewModel +//class LoginViewModel @Inject constructor( +// private val authRepository: AuthRepository, +//) : ViewModel() { +// private val _loginStatus = MutableLiveData() +// val loginStatus: LiveData = _loginStatus +// +// fun login(request: RequestLoginDto) { +// viewModelScope.launch { +// runCatching { +// authRepository.login( +// RequestSignInEntity( +// authenticationId = request.authenticationId, +// password = request.password +// ) +// ) +// }.onSuccess { result -> +// result.onSuccess { response -> +// handleSuccess(response) +// } +// }.onFailure { +// _loginStatus.value = AuthState( +// isSuccess = false, +// message = "서버 에러" +// ) +// } +// } +// } +// +// private fun handleSuccess(response: Response) { +// if (response.isSuccessful) { +// val memberId = response.headers()["location"] ?: "unknown" +// authRepository.setUserLoggedIn(true) +// authRepository.setUserId(memberId) +// +// _loginStatus.value = AuthState( +// isSuccess = true, +// message = "로그인 성공 유저의 ID는 $memberId 입니다." +// ) +// } else { +// failResponse(response) +// } +// } +// +// private fun failResponse(response: Response) { +// val error = response.message() +// _loginStatus.value = AuthState( +// isSuccess = false, +// message = "로그인 실패 $error" +// ) +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/main/MainActivity.kt b/app/src/main/java/com/sopt/now/compose/ui/main/MainActivity.kt index 9739ace..16b2f37 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/main/MainActivity.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/main/MainActivity.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController -import com.sopt.now.compose.data.UserRepository +import com.sopt.now.compose.domain.repository.AuthRepository import com.sopt.now.compose.ui.navigation.BottomNavigationItem import com.sopt.now.compose.ui.navigation.BottomNavigationBar import com.sopt.now.compose.ui.navigation.NavGraph @@ -29,7 +29,7 @@ import javax.inject.Inject @AndroidEntryPoint class MainActivity : ComponentActivity() { @Inject - lateinit var userRepository: UserRepository + lateinit var userRepository: AuthRepository override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -47,7 +47,7 @@ class MainActivity : ComponentActivity() { @Composable fun MainContent( navController: NavHostController, - userRepository: UserRepository, + userRepository: AuthRepository, ) { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route diff --git a/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageScreen.kt index 8c35835..abf3609 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageScreen.kt @@ -1,181 +1,181 @@ -package com.sopt.now.compose.ui.mypage - -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Person -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarDuration -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.constraintlayout.compose.ConstraintLayout -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavController -import com.sopt.now.compose.R - -@Composable -fun MypageScreen(navController: NavController) { - val viewModel: MypageViewModel = hiltViewModel() - val authState by viewModel.userInfoStatus.observeAsState() - val userState by viewModel.userLiveData.observeAsState() - val successLogout by viewModel.successLogout.observeAsState() - val snackbarHostState = remember { SnackbarHostState() } - - LaunchedEffect(authState) { - authState?.let { state -> - snackbarHostState.showSnackbar( - message = state.message, - duration = SnackbarDuration.Short - ) - } - } - - LaunchedEffect(Unit) { - viewModel.fetchUserInfo() - } - - LaunchedEffect(successLogout) { - successLogout?.let { success -> - if (success) { - navController.navigate("login") { - popUpTo("mypage") { inclusive = true } - } - } else { - snackbarHostState.showSnackbar( - message = "로그아웃 실패", - duration = SnackbarDuration.Short - ) - } - } - } - - Scaffold( - modifier = Modifier.fillMaxSize() - ) { padding -> - ConstraintLayout( - modifier = Modifier - .fillMaxSize() - .padding(padding) - .padding(horizontal = 16.dp), - ) { - val (userImg, userNickname, follower, userPhone, userId, chPw, logout) = createRefs() - - userState?.let { user -> - Image( - modifier = Modifier - .constrainAs(userImg) { - top.linkTo(parent.top, margin = 70.dp) - end.linkTo(parent.end) - start.linkTo(parent.start) - } - .size(130.dp), - imageVector = Icons.Default.Person, - contentDescription = stringResource(R.string.img) - ) - - Text( - modifier = Modifier.constrainAs(userNickname) { - top.linkTo(userImg.bottom) - start.linkTo(userImg.start) - end.linkTo(userImg.end) - }, - text = user.nickname, - fontSize = 30.sp, - fontWeight = FontWeight.Bold - ) - - TextButton( - modifier = Modifier.constrainAs(follower) { - top.linkTo(userNickname.top) - start.linkTo(userNickname.end, margin = 8.dp) - bottom.linkTo(userNickname.bottom) - }, - onClick = { - navController.navigate("follower") - { - popUpTo("mypage") { inclusive = true } - } - }, - ) { - Text( - text = stringResource(R.string.btn_follower), - fontSize = 20.sp, - color = Color.DarkGray - ) - } - - - Text( - modifier = Modifier.constrainAs(userId) { - top.linkTo(userNickname.bottom, margin = 5.dp) - start.linkTo(userNickname.start) - end.linkTo(userNickname.end) - }, - text = user.authenticationId, - fontSize = 20.sp - ) - - Text( - modifier = Modifier.constrainAs(userPhone) { - top.linkTo(userId.bottom, margin = 5.dp) - bottom.linkTo(logout.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - }, - text = user.phone, - fontSize = 20.sp, - ) - - TextButton( - modifier = Modifier.constrainAs(chPw) { - top.linkTo(userPhone.bottom, margin = 10.dp) - start.linkTo(parent.start) - end.linkTo(parent.end) - }, - onClick = { - navController.navigate("changePassword") { - popUpTo("mypage") { inclusive = true } - } - }, - ) { - Text( - text = stringResource(R.string.ch_pw), - fontSize = 20.sp, - color = Color.DarkGray - ) - } - - TextButton( - modifier = Modifier.constrainAs(logout) { - top.linkTo(chPw.bottom, margin = 10.dp) - start.linkTo(parent.start) - end.linkTo(parent.end) - }, - onClick = { - viewModel.logout() - }, - ) { - Text( - text = stringResource(R.string.btn_logout), - fontSize = 20.sp, - color = Color.DarkGray - ) - } - } - } - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.mypage +// +//import androidx.compose.foundation.Image +//import androidx.compose.foundation.layout.fillMaxSize +//import androidx.compose.foundation.layout.padding +//import androidx.compose.foundation.layout.size +//import androidx.compose.material.icons.Icons +//import androidx.compose.material.icons.filled.Person +//import androidx.compose.material3.Scaffold +//import androidx.compose.material3.SnackbarDuration +//import androidx.compose.material3.SnackbarHostState +//import androidx.compose.material3.Text +//import androidx.compose.material3.TextButton +//import androidx.compose.runtime.Composable +//import androidx.compose.runtime.LaunchedEffect +//import androidx.compose.runtime.getValue +//import androidx.compose.runtime.livedata.observeAsState +//import androidx.compose.runtime.remember +//import androidx.compose.ui.Modifier +//import androidx.compose.ui.graphics.Color +//import androidx.compose.ui.res.stringResource +//import androidx.compose.ui.text.font.FontWeight +//import androidx.compose.ui.unit.dp +//import androidx.compose.ui.unit.sp +//import androidx.constraintlayout.compose.ConstraintLayout +//import androidx.hilt.navigation.compose.hiltViewModel +//import androidx.navigation.NavController +//import com.sopt.now.compose.R +// +//@Composable +//fun MypageScreen(navController: NavController) { +// val viewModel: MypageViewModel = hiltViewModel() +// val authState by viewModel.userInfoStatus.observeAsState() +// val userState by viewModel.userLiveData.observeAsState() +// val successLogout by viewModel.successLogout.observeAsState() +// val snackbarHostState = remember { SnackbarHostState() } +// +// LaunchedEffect(authState) { +// authState?.let { state -> +// snackbarHostState.showSnackbar( +// message = state.message, +// duration = SnackbarDuration.Short +// ) +// } +// } +// +// LaunchedEffect(Unit) { +// viewModel.fetchUserInfo() +// } +// +// LaunchedEffect(successLogout) { +// successLogout?.let { success -> +// if (success) { +// navController.navigate("login") { +// popUpTo("mypage") { inclusive = true } +// } +// } else { +// snackbarHostState.showSnackbar( +// message = "로그아웃 실패", +// duration = SnackbarDuration.Short +// ) +// } +// } +// } +// +// Scaffold( +// modifier = Modifier.fillMaxSize() +// ) { padding -> +// ConstraintLayout( +// modifier = Modifier +// .fillMaxSize() +// .padding(padding) +// .padding(horizontal = 16.dp), +// ) { +// val (userImg, userNickname, follower, userPhone, userId, chPw, logout) = createRefs() +// +// userState?.let { user -> +// Image( +// modifier = Modifier +// .constrainAs(userImg) { +// top.linkTo(parent.top, margin = 70.dp) +// end.linkTo(parent.end) +// start.linkTo(parent.start) +// } +// .size(130.dp), +// imageVector = Icons.Default.Person, +// contentDescription = stringResource(R.string.img) +// ) +// +// Text( +// modifier = Modifier.constrainAs(userNickname) { +// top.linkTo(userImg.bottom) +// start.linkTo(userImg.start) +// end.linkTo(userImg.end) +// }, +// text = user.nickname, +// fontSize = 30.sp, +// fontWeight = FontWeight.Bold +// ) +// +// TextButton( +// modifier = Modifier.constrainAs(follower) { +// top.linkTo(userNickname.top) +// start.linkTo(userNickname.end, margin = 8.dp) +// bottom.linkTo(userNickname.bottom) +// }, +// onClick = { +// navController.navigate("follower") +// { +// popUpTo("mypage") { inclusive = true } +// } +// }, +// ) { +// Text( +// text = stringResource(R.string.btn_follower), +// fontSize = 20.sp, +// color = Color.DarkGray +// ) +// } +// +// +// Text( +// modifier = Modifier.constrainAs(userId) { +// top.linkTo(userNickname.bottom, margin = 5.dp) +// start.linkTo(userNickname.start) +// end.linkTo(userNickname.end) +// }, +// text = user.authenticationId, +// fontSize = 20.sp +// ) +// +// Text( +// modifier = Modifier.constrainAs(userPhone) { +// top.linkTo(userId.bottom, margin = 5.dp) +// bottom.linkTo(logout.top) +// start.linkTo(parent.start) +// end.linkTo(parent.end) +// }, +// text = user.phone, +// fontSize = 20.sp, +// ) +// +// TextButton( +// modifier = Modifier.constrainAs(chPw) { +// top.linkTo(userPhone.bottom, margin = 10.dp) +// start.linkTo(parent.start) +// end.linkTo(parent.end) +// }, +// onClick = { +// navController.navigate("changePassword") { +// popUpTo("mypage") { inclusive = true } +// } +// }, +// ) { +// Text( +// text = stringResource(R.string.ch_pw), +// fontSize = 20.sp, +// color = Color.DarkGray +// ) +// } +// +// TextButton( +// modifier = Modifier.constrainAs(logout) { +// top.linkTo(chPw.bottom, margin = 10.dp) +// start.linkTo(parent.start) +// end.linkTo(parent.end) +// }, +// onClick = { +// viewModel.logout() +// }, +// ) { +// Text( +// text = stringResource(R.string.btn_logout), +// fontSize = 20.sp, +// color = Color.DarkGray +// ) +// } +// } +// } +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageViewModel.kt index 1b835cb..a44e8b4 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageViewModel.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/mypage/MypageViewModel.kt @@ -1,70 +1,69 @@ -package com.sopt.now.compose.ui.mypage - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.sopt.now.compose.data.UserRepository -import com.sopt.now.compose.network.reponse.ResponseInfoDto -import com.sopt.now.compose.network.reponse.UserInfo -import com.sopt.now.compose.network.service.ServicePool -import com.sopt.now.compose.ui.AuthState -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.launch -import retrofit2.Response -import javax.inject.Inject - -@HiltViewModel -class MypageViewModel @Inject constructor( - private val userRepository: UserRepository, -) : ViewModel(){ - private val _userInfoStatus = MutableLiveData() - private val _userLiveData = MutableLiveData() - private val _successLogout = MutableLiveData(null) - - val userInfoStatus: LiveData = _userInfoStatus - val userLiveData: LiveData = _userLiveData - val successLogout: LiveData = _successLogout - - fun fetchUserInfo() { - viewModelScope.launch { - runCatching { - userRepository.getUserInfo() - }.onSuccess { response -> - handleSuccess(response) - }.onFailure { - _userInfoStatus.value = AuthState ( - isSuccess = false, - message = "서버 에러" - ) - - } - } - } - - private fun handleSuccess(response: Response) { - if (response.isSuccessful) { - successResponse(response) - } else { - failResponse() - } - } - - private fun successResponse(response: Response) { - response.body()?.data?.let { userInfo -> - _userLiveData.postValue(userInfo) - _userInfoStatus.postValue(AuthState(isSuccess = true, message = "회원 정보 조회 성공")) - } ?: run { - _userInfoStatus.postValue(AuthState(isSuccess = false, message = "회원 정보 없음")) - } - } - - private fun failResponse() { - _userInfoStatus.value = AuthState(isSuccess = false, message = "회원 정보 조회 실패") - } - - fun logout() { - userRepository.logoutUser() - _successLogout.postValue(true) - } -} \ No newline at end of file +//package com.sopt.now.compose.ui.mypage +// +//import androidx.lifecycle.LiveData +//import androidx.lifecycle.MutableLiveData +//import androidx.lifecycle.ViewModel +//import androidx.lifecycle.viewModelScope +//import com.sopt.now.compose.domain.repository.AuthRepository +//import com.sopt.now.compose.domain.repository.MypageRepository +//import com.sopt.now.compose.ui.AuthState +//import dagger.hilt.android.lifecycle.HiltViewModel +//import kotlinx.coroutines.launch +//import retrofit2.Response +//import javax.inject.Inject +// +//@HiltViewModel +//class MypageViewModel @Inject constructor( +// private val mypageRepository: MypageRepository, +// private val authRepository: AuthRepository, +//) : ViewModel() { +// private val _userInfoStatus = MutableLiveData() +// private val _userLiveData = MutableLiveData() +// private val _successLogout = MutableLiveData(null) +// +// val userInfoStatus: LiveData = _userInfoStatus +// val userLiveData: LiveData = _userLiveData +// val successLogout: LiveData = _successLogout +// +// fun fetchUserInfo() { +// viewModelScope.launch { +// runCatching { +// mypageRepository.getUserInfo() +// }.onSuccess { response -> +// handleSuccess(response) +// }.onFailure { +// _userInfoStatus.value = AuthState( +// isSuccess = false, +// message = "서버 에러" +// ) +// +// } +// } +// } +// +// private fun handleSuccess(response: String) { +// if (response.isSuccessful) { +// successResponse(response) +// } else { +// failResponse() +// } +// } +// +// private fun successResponse(response: Response) { +// response.body()?.data?.let { userInfo -> +// _userLiveData.postValue(userInfo) +// _userInfoStatus.postValue(AuthState(isSuccess = true, message = "회원 정보 조회 성공")) +// } ?: run { +// _userInfoStatus.postValue(AuthState(isSuccess = false, message = "회원 정보 없음")) +// } +// } +// +// private fun failResponse() { +// _userInfoStatus.value = AuthState(isSuccess = false, message = "회원 정보 조회 실패") +// } +// +// fun logout() { +// authRepository.clearInfo() +// _successLogout.postValue(true) +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraph.kt b/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraph.kt index 2f929fe..43cbdf9 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraph.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraph.kt @@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost -import com.sopt.now.compose.data.UserRepository @Composable fun NavGraph( diff --git a/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraphBuilder.kt b/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraphBuilder.kt index 098a077..f9f02a7 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraphBuilder.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/navigation/NavGraphBuilder.kt @@ -9,6 +9,7 @@ import com.sopt.now.compose.ui.home.HomeScreen import com.sopt.now.compose.ui.login.LoginScreen import com.sopt.now.compose.ui.mypage.MypageScreen import com.sopt.now.compose.ui.search.SearchScreen +import com.sopt.now.compose.ui.signup.SignupRoute import com.sopt.now.compose.ui.signup.SignupScreen fun NavGraphBuilder.addNavGraph( @@ -17,7 +18,7 @@ fun NavGraphBuilder.addNavGraph( composable("home") { HomeScreen() } composable("search") { SearchScreen() } composable("login") { LoginScreen(navController) } - composable("signup") { SignupScreen(navController) } + composable("signup") { SignupRoute(navController) } composable("mypage") { MypageScreen(navController) } composable("changePassword") { ChangePasswordScreen(navController) } composable("follower") { FollowerScreen() } diff --git a/app/src/main/java/com/sopt/now/compose/ui/signup/SignUpState.kt b/app/src/main/java/com/sopt/now/compose/ui/signup/SignUpState.kt new file mode 100644 index 0000000..2bf604a --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/ui/signup/SignUpState.kt @@ -0,0 +1,7 @@ +package com.sopt.now.compose.ui.signup +data class SignUpState( + val id: String = "", + val password: String = "", + val nickname: String = "", + val phone: String = "" +) diff --git a/app/src/main/java/com/sopt/now/compose/ui/signup/SignupScreen.kt b/app/src/main/java/com/sopt/now/compose/ui/signup/SignupScreen.kt index b21c39e..2ef3962 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/signup/SignupScreen.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/signup/SignupScreen.kt @@ -21,9 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState -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.res.stringResource @@ -33,39 +31,70 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.sopt.now.compose.R -import com.sopt.now.compose.SoptApp -import com.sopt.now.compose.network.request.RequestSignUpDto import com.sopt.now.compose.util.noRippleClickable +import kotlinx.coroutines.flow.collectLatest @Composable -fun SignupScreen(navController: NavController) { - val viewModel: SignupViewModel = hiltViewModel() +fun SignupRoute(navController: NavController, viewModel: SignupViewModel = hiltViewModel()) { val snackbarHostState = remember { SnackbarHostState() } - - var userId by remember { mutableStateOf("") } - var password by remember { mutableStateOf("") } - var nickname by remember { mutableStateOf("") } - var phone by remember { mutableStateOf("") } - - val authState by viewModel.signupStatus.observeAsState() + val signUpState by viewModel.signUpState.collectAsStateWithLifecycle() + val authState by viewModel.signupStatus.observeAsState(null) LaunchedEffect(authState) { authState?.let { state -> - snackbarHostState.showSnackbar( - message = state.message, - duration = SnackbarDuration.Short - ) - if (state.isSuccess) { - navController.navigate("login") { - popUpTo("signup") { inclusive = true } + if (state.message.isNotEmpty()) { + snackbarHostState.showSnackbar( + message = state.message, + duration = SnackbarDuration.Short + ) + } + } + } + + LaunchedEffect(viewModel.sideEffect) { + viewModel.sideEffect.collectLatest { sideEffect -> + when (sideEffect) { + is SignupSideEffect.ShowError -> { + snackbarHostState.showSnackbar( + message = sideEffect.message, + duration = SnackbarDuration.Short + ) + } + + SignupSideEffect.NavigateToMain -> { + navController.navigate("login") { + popUpTo("signup") { inclusive = true } + } } } } } + SignupScreen( + signUpState = signUpState, + onIdChange = { viewModel.updateSignUpState(id = it) }, + onPasswordChange = { viewModel.updateSignUpState(password = it) }, + onNicknameChange = { viewModel.updateSignUpState(nickname = it) }, + onPhoneChange = { viewModel.updateSignUpState(phone = it) }, + onSignUpClick = { viewModel.signUp() }, + snackbarHostState = snackbarHostState + ) +} + +@Composable +fun SignupScreen( + signUpState: SignUpState, + onIdChange: (String) -> Unit, + onPasswordChange: (String) -> Unit, + onNicknameChange: (String) -> Unit, + onPhoneChange: (String) -> Unit, + onSignUpClick: () -> Unit, + snackbarHostState: SnackbarHostState +) { + Scaffold( modifier = Modifier.fillMaxSize(), snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, @@ -85,8 +114,8 @@ fun SignupScreen(navController: NavController) { Spacer(Modifier.height(20.dp)) OutlinedTextField( - value = userId, - onValueChange = { userId = it }, + value = signUpState.id, + onValueChange = onIdChange, label = { Text(stringResource(R.string.id)) }, singleLine = true, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), @@ -95,8 +124,8 @@ fun SignupScreen(navController: NavController) { Spacer(Modifier.height(16.dp)) OutlinedTextField( - value = password, - onValueChange = { password = it }, + value = signUpState.password, + onValueChange = onPasswordChange, label = { Text(stringResource(R.string.pw)) }, singleLine = true, keyboardOptions = KeyboardOptions( @@ -109,8 +138,8 @@ fun SignupScreen(navController: NavController) { Spacer(Modifier.height(16.dp)) OutlinedTextField( - value = nickname, - onValueChange = { nickname = it }, + value = signUpState.nickname, + onValueChange = onNicknameChange, label = { Text(stringResource(R.string.nickname)) }, singleLine = true, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), @@ -119,8 +148,8 @@ fun SignupScreen(navController: NavController) { Spacer(Modifier.height(16.dp)) OutlinedTextField( - value = phone, - onValueChange = { phone = it.uppercase() }, + value = signUpState.phone, + onValueChange = onPhoneChange, label = { Text(stringResource(R.string.phone)) }, singleLine = true, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), @@ -130,9 +159,7 @@ fun SignupScreen(navController: NavController) { Spacer(modifier = Modifier.weight(1f)) CustomBtn( text = stringResource(R.string.signup_kr), - onClick = { - viewModel.signUp(RequestSignUpDto(userId, password, nickname, phone)) - }, + onClick = onSignUpClick, modifier = Modifier.fillMaxWidth() ) } diff --git a/app/src/main/java/com/sopt/now/compose/ui/signup/SignupSideEffect.kt b/app/src/main/java/com/sopt/now/compose/ui/signup/SignupSideEffect.kt new file mode 100644 index 0000000..4ee7a25 --- /dev/null +++ b/app/src/main/java/com/sopt/now/compose/ui/signup/SignupSideEffect.kt @@ -0,0 +1,6 @@ +package com.sopt.now.compose.ui.signup + +sealed class SignupSideEffect { + data class ShowError(val message: String) : SignupSideEffect() + data object NavigateToMain : SignupSideEffect() + } \ No newline at end of file diff --git a/app/src/main/java/com/sopt/now/compose/ui/signup/SignupViewModel.kt b/app/src/main/java/com/sopt/now/compose/ui/signup/SignupViewModel.kt index 88281d3..f8d5c4c 100644 --- a/app/src/main/java/com/sopt/now/compose/ui/signup/SignupViewModel.kt +++ b/app/src/main/java/com/sopt/now/compose/ui/signup/SignupViewModel.kt @@ -4,39 +4,70 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.sopt.now.compose.data.User -import com.sopt.now.compose.data.UserRepository -import com.sopt.now.compose.network.request.RequestSignUpDto -import com.sopt.now.compose.network.reponse.ResponseDto +import com.sopt.now.compose.data.remote.response.BaseResponseWithoutDataDto +import com.sopt.now.compose.domain.entity.request.RequestUserEntity +import com.sopt.now.compose.domain.repository.AuthRepository import com.sopt.now.compose.ui.AuthState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import retrofit2.Response import javax.inject.Inject @HiltViewModel class SignupViewModel @Inject constructor( - private val userRepository: UserRepository, + private val authRepository: AuthRepository, ) : ViewModel() { + + private val _signUpState = MutableStateFlow(SignUpState()) + val signUpState: StateFlow = _signUpState private val _signupStatus = MutableLiveData() val signupStatus: LiveData = _signupStatus - fun signUp(request: RequestSignUpDto) { + private val _sideEffect = MutableSharedFlow() + val sideEffect: SharedFlow = _sideEffect + + fun updateSignUpState(id: String? = null, password: String? = null, nickname: String? = null, phone: String? = null) { + val currentState = _signUpState.value + _signUpState.value = currentState.copy( + id = id ?: currentState.id, + password = password ?: currentState.password, + nickname = nickname ?: currentState.nickname, + phone = phone ?: currentState.phone + ) + } + + fun signUp() { + val currentState = _signUpState.value + viewModelScope.launch { runCatching { - userRepository.signUp(request) - }.onSuccess { response -> - handleSuccess(response, request) - }.onFailure { - _signupStatus.value = AuthState( - isSuccess = false, - message = "서버 에러" + val userEntity = RequestUserEntity( + authenticationId = currentState.id, + password = currentState.password, + nickname = currentState.nickname, + phone = currentState.phone + ) + authRepository.signup(userEntity) + }.onSuccess { result -> + result.fold( + onSuccess = { response -> + handleSuccess(response, currentState) + }, + onFailure = { + _sideEffect.emit(SignupSideEffect.ShowError("서버 에러")) + } ) + }.onFailure { + _sideEffect.emit(SignupSideEffect.ShowError("서버 에러")) } } } - private fun handleSuccess(response: Response, request: RequestSignUpDto) { + private fun handleSuccess(response: Response, request: SignUpState) { if (response.isSuccessful) { successResponse(response, request) } else { @@ -44,17 +75,23 @@ class SignupViewModel @Inject constructor( } } - private fun successResponse(response: Response, request: RequestSignUpDto) { + private fun successResponse(response: Response, request: SignUpState) { val memberId = response.headers()["location"] ?: "unknown" - val user = User(request.authenticationId, request.password, request.nickname, request.phone) - userRepository.saveUserData(user) + authRepository.setUserId(memberId) + authRepository.setId(request.id) + authRepository.setPassword(request.password) + authRepository.setNickname(request.nickname) + authRepository.setPhoneNumber(request.phone) _signupStatus.value = AuthState( isSuccess = true, message = "회원가입 성공 유저의 ID는 $memberId 입니다." ) + viewModelScope.launch { + _sideEffect.emit(SignupSideEffect.NavigateToMain) + } } - private fun failResponse(response: Response) { + private fun failResponse(response: Response) { val error = response.message() _signupStatus.value = AuthState( isSuccess = false,