From 94c48263331c2cdc07107d8b483371583c691c5f Mon Sep 17 00:00:00 2001 From: songpink Date: Sat, 7 Dec 2024 14:45:02 +0900 Subject: [PATCH 01/16] =?UTF-8?q?refactor:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/repository/AuthRepository.kt | 2 +- .../auth/repository/AuthRepositoryImpl.kt | 2 +- .../di/annotations/UseCaseQualifier.kt | 7 +++++ .../di/module/UseCaseDependencyModule.kt | 19 ++++++++++++ .../presentation/view/login/LoginViewModel.kt | 10 +++---- .../view/login/usecase/PostLoginUseCase.kt | 11 +++++++ .../login/usecase/PostLoginUseCaseImpl.kt | 30 +++++++++++++++++++ .../chongdae/repository/FakeAuthRepository.kt | 2 +- 8 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepository.kt b/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepository.kt index f0011b172..db6a706fa 100644 --- a/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepository.kt +++ b/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepository.kt @@ -5,7 +5,7 @@ import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result interface AuthRepository { - suspend fun saveLogin( + suspend fun postLogin( accessToken: String, fcmToken: String, ): Result diff --git a/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepositoryImpl.kt b/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepositoryImpl.kt index 5b47307aa..136fc500c 100644 --- a/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepositoryImpl.kt +++ b/android/app/src/main/java/com/zzang/chongdae/auth/repository/AuthRepositoryImpl.kt @@ -14,7 +14,7 @@ class AuthRepositoryImpl constructor( @AuthDataSourceQualifier private val authRemoteDataSource: AuthRemoteDataSource, ) : AuthRepository { - override suspend fun saveLogin( + override suspend fun postLogin( accessToken: String, fcmToken: String, ): Result { diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt new file mode 100644 index 000000000..1a9c0ea59 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -0,0 +1,7 @@ +package com.zzang.chongdae.di.annotations + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class PostLoginUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt new file mode 100644 index 000000000..3c89b6422 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -0,0 +1,19 @@ +package com.zzang.chongdae.di.module + +import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier +import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase +import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCaseImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@InstallIn(SingletonComponent::class) +@Module +abstract class UseCaseDependencyModule { + @Binds + @Singleton + @PostLoginUseCaseQualifier + abstract fun provideLoginUseCase(impl: PostLoginUseCaseImpl): PostLoginUseCase +} diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt index 22ab89501..78f54a53c 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt @@ -3,12 +3,12 @@ package com.zzang.chongdae.presentation.view.login import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.zzang.chongdae.auth.repository.AuthRepository import com.zzang.chongdae.common.datastore.UserPreferencesDataStore import com.zzang.chongdae.common.handler.Result -import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData +import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -18,7 +18,7 @@ import javax.inject.Inject class LoginViewModel @Inject constructor( - @AuthRepositoryQualifier private val authRepository: AuthRepository, + @PostLoginUseCaseQualifier private val postLoginUseCase: PostLoginUseCase, private val userPreferencesDataStore: UserPreferencesDataStore, ) : ViewModel() { private val _loginSuccessEvent: MutableSingleLiveData = MutableSingleLiveData() @@ -45,10 +45,8 @@ class LoginViewModel fcmToken: String, ) { viewModelScope.launch { - when (val result = authRepository.saveLogin(accessToken = accessToken, fcmToken = fcmToken)) { + when (val result = postLoginUseCase(accessToken = accessToken, fcmToken = fcmToken)) { is Result.Success -> { - userPreferencesDataStore.saveMember(result.data.memberId, result.data.nickName) - userPreferencesDataStore.saveFcmToken(fcmToken) _loginSuccessEvent.setValue(Unit) } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt new file mode 100644 index 000000000..a8b9d7a88 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt @@ -0,0 +1,11 @@ +package com.zzang.chongdae.presentation.view.login.usecase + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result + +interface PostLoginUseCase { + suspend operator fun invoke( + accessToken: String, + fcmToken: String, + ): Result +} diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt new file mode 100644 index 000000000..687f9f833 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt @@ -0,0 +1,30 @@ +package com.zzang.chongdae.presentation.view.login.usecase + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.datastore.UserPreferencesDataStore +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import javax.inject.Inject + +class PostLoginUseCaseImpl + @Inject + constructor( + @AuthRepositoryQualifier private val authRepository: AuthRepository, + private val userPreferencesDataStore: UserPreferencesDataStore, + ) : PostLoginUseCase { + override suspend fun invoke( + accessToken: String, + fcmToken: String, + ): Result { + return when (val result = authRepository.postLogin(accessToken, fcmToken)) { + is Result.Success -> { + userPreferencesDataStore.saveMember(result.data.memberId, result.data.nickName) + userPreferencesDataStore.saveFcmToken(fcmToken) + Result.Success(Unit) + } + + is Result.Error -> result + } + } + } diff --git a/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt b/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt index a7856da73..c492ee9f4 100644 --- a/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt +++ b/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt @@ -6,7 +6,7 @@ import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result class FakeAuthRepository : AuthRepository { - override suspend fun saveLogin( + override suspend fun postLogin( accessToken: String, fcmToken: String, ): Result { From c9d267dafaa0446f907007adf80a7383b78cd293 Mon Sep 17 00:00:00 2001 From: songpink Date: Sat, 7 Dec 2024 15:04:18 +0900 Subject: [PATCH 02/16] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=EC=9D=B4=20=EB=90=98=EC=96=B4=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20=EB=94=94=EB=B0=94=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EC=9D=B8=EC=A7=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EC=97=90=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chongdae/di/annotations/UseCaseQualifier.kt | 4 ++++ .../di/module/UseCaseDependencyModule.kt | 10 +++++++++- .../presentation/view/login/LoginViewModel.kt | 8 +++++--- .../usecase/CheckIfAlreadyLoggedInUseCase.kt | 5 +++++ .../usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt | 16 ++++++++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt index 1a9c0ea59..63d969326 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -2,6 +2,10 @@ package com.zzang.chongdae.di.annotations import javax.inject.Qualifier +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class CheckAlreadyLoggedInUseCaseQualifier + @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PostLoginUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index 3c89b6422..17bce6e0d 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -1,6 +1,9 @@ package com.zzang.chongdae.di.module +import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier +import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCase +import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCaseImpl import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCaseImpl import dagger.Binds @@ -12,8 +15,13 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) @Module abstract class UseCaseDependencyModule { + @Binds + @Singleton + @CheckAlreadyLoggedInUseCaseQualifier + abstract fun provideCheckIfAlreadyLoggedInUseCase(impl: CheckIfAlreadyLoggedInUseCaseImpl): CheckIfAlreadyLoggedInUseCase + @Binds @Singleton @PostLoginUseCaseQualifier - abstract fun provideLoginUseCase(impl: PostLoginUseCaseImpl): PostLoginUseCase + abstract fun providePostLoginUseCase(impl: PostLoginUseCaseImpl): PostLoginUseCase } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt index 78f54a53c..9a91b2170 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt @@ -5,12 +5,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.zzang.chongdae.common.datastore.UserPreferencesDataStore import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData +import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import javax.inject.Inject @@ -18,6 +19,7 @@ import javax.inject.Inject class LoginViewModel @Inject constructor( + @CheckAlreadyLoggedInUseCaseQualifier private val checkIfAlreadyLoggedInUseCase: CheckIfAlreadyLoggedInUseCase, @PostLoginUseCaseQualifier private val postLoginUseCase: PostLoginUseCase, private val userPreferencesDataStore: UserPreferencesDataStore, ) : ViewModel() { @@ -33,8 +35,8 @@ class LoginViewModel private fun makeAlreadyLoggedInEvent() { viewModelScope.launch { - val accessToken = userPreferencesDataStore.accessTokenFlow.first() - if (accessToken != null) { + val isAlreadyLoggedIn = checkIfAlreadyLoggedInUseCase() + if (isAlreadyLoggedIn) { _alreadyLoggedInEvent.setValue(Unit) } } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt new file mode 100644 index 000000000..6eb4a2e72 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt @@ -0,0 +1,5 @@ +package com.zzang.chongdae.presentation.view.login.usecase + +interface CheckIfAlreadyLoggedInUseCase { + suspend operator fun invoke(): Boolean +} diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt new file mode 100644 index 000000000..79ea9de20 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt @@ -0,0 +1,16 @@ +package com.zzang.chongdae.presentation.view.login.usecase + +import com.zzang.chongdae.common.datastore.UserPreferencesDataStore +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +class CheckIfAlreadyLoggedInUseCaseImpl + @Inject + constructor( + private val userPreferencesDataStore: UserPreferencesDataStore, + ) : CheckIfAlreadyLoggedInUseCase { + override suspend fun invoke(): Boolean { + val accessToken = userPreferencesDataStore.accessTokenFlow.first() + return accessToken != null + } + } From 7c8d59074fb44516e4b7cb1d85a310bf1df05edd Mon Sep 17 00:00:00 2001 From: songpink Date: Sat, 7 Dec 2024 15:15:45 +0900 Subject: [PATCH 03/16] =?UTF-8?q?refactor:=20=EB=84=A4=EC=9D=B4=EB=B0=8D?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zzang/chongdae/presentation/view/login/LoginActivity.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginActivity.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginActivity.kt index de2fcbf58..1c099cfdc 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginActivity.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginActivity.kt @@ -32,7 +32,7 @@ class LoginActivity : AppCompatActivity(), OnAuthClickListener { FirebaseAnalyticsManager(firebaseAnalytics) } - val callback: (OAuthToken?, Throwable?) -> Unit = { token, error -> + private val kakaoAuthResultHandler: (OAuthToken?, Throwable?) -> Unit = { token, error -> if (error != null) { Log.e("error", "카카오계정으로 로그인 실패", error) } else if (token != null) { @@ -84,7 +84,7 @@ class LoginActivity : AppCompatActivity(), OnAuthClickListener { if (UserApiClient.instance.isKakaoTalkLoginAvailable(this)) { loginWithKakaoTalk() } else { - UserApiClient.instance.loginWithKakaoAccount(this, callback = callback) + UserApiClient.instance.loginWithKakaoAccount(this, callback = kakaoAuthResultHandler) } } @@ -103,7 +103,7 @@ class LoginActivity : AppCompatActivity(), OnAuthClickListener { if (isKakaoTalkLoginCanceled(error)) { return } - UserApiClient.instance.loginWithKakaoAccount(this, callback = callback) + UserApiClient.instance.loginWithKakaoAccount(this, callback = kakaoAuthResultHandler) } private fun isKakaoTalkLoginCanceled(error: Throwable?): Boolean { From e56fc2b59183bdaf3c3ba638199431f72ab9ba49 Mon Sep 17 00:00:00 2001 From: songpink Date: Sat, 7 Dec 2024 15:30:22 +0900 Subject: [PATCH 04/16] =?UTF-8?q?style:=20=ED=95=84=EC=9A=94=20=EC=97=86?= =?UTF-8?q?=EC=96=B4=EC=A7=84=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/zzang/chongdae/presentation/view/login/LoginViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt index 9a91b2170..778809837 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt @@ -21,7 +21,6 @@ class LoginViewModel constructor( @CheckAlreadyLoggedInUseCaseQualifier private val checkIfAlreadyLoggedInUseCase: CheckIfAlreadyLoggedInUseCase, @PostLoginUseCaseQualifier private val postLoginUseCase: PostLoginUseCase, - private val userPreferencesDataStore: UserPreferencesDataStore, ) : ViewModel() { private val _loginSuccessEvent: MutableSingleLiveData = MutableSingleLiveData() val loginSuccessEvent: SingleLiveData get() = _loginSuccessEvent From dc20a78364ec78ce17deba149680b38b16efe2ec Mon Sep 17 00:00:00 2001 From: songpink Date: Sat, 7 Dec 2024 16:10:52 +0900 Subject: [PATCH 05/16] =?UTF-8?q?refactor:=20=EA=B3=B5=EB=AA=A8=EA=B8=80?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EC=9C=A0?= =?UTF-8?q?=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/annotations/UseCaseQualifier.kt | 4 + .../di/module/UseCaseDependencyModule.kt | 8 + .../view/write/OfferingWriteViewModel.kt | 608 +++++++++--------- .../view/write/usecase/PostOfferingUseCase.kt | 9 + .../write/usecase/PostOfferingUseCaseImpl.kt | 32 + 5 files changed, 352 insertions(+), 309 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt index 63d969326..ed15202f1 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -9,3 +9,7 @@ annotation class CheckAlreadyLoggedInUseCaseQualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PostLoginUseCaseQualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class PostOfferingUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index 17bce6e0d..679a8509e 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -2,10 +2,13 @@ package com.zzang.chongdae.di.module import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier +import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCaseImpl import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCaseImpl +import com.zzang.chongdae.presentation.view.write.usecase.PostOfferingUseCase +import com.zzang.chongdae.presentation.view.write.usecase.PostOfferingUseCaseImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -24,4 +27,9 @@ abstract class UseCaseDependencyModule { @Singleton @PostLoginUseCaseQualifier abstract fun providePostLoginUseCase(impl: PostLoginUseCaseImpl): PostLoginUseCase + + @Binds + @Singleton + @PostOfferingUseCaseQualifier + abstract fun providePostOfferingUseCase(impl: PostOfferingUseCaseImpl): PostOfferingUseCase } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt index 43173f7ff..92847788c 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt @@ -13,6 +13,7 @@ import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier +import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier import com.zzang.chongdae.domain.model.Count import com.zzang.chongdae.domain.model.DiscountPrice import com.zzang.chongdae.domain.model.OfferingWrite @@ -20,6 +21,7 @@ import com.zzang.chongdae.domain.model.Price import com.zzang.chongdae.domain.repository.OfferingRepository import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData +import com.zzang.chongdae.presentation.view.write.usecase.PostOfferingUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import okhttp3.MultipartBody @@ -29,402 +31,390 @@ import javax.inject.Inject @HiltViewModel class OfferingWriteViewModel - @Inject - constructor( - @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, - @AuthRepositoryQualifier private val authRepository: AuthRepository, - ) : ViewModel() { - val title: MutableLiveData = MutableLiveData("") +@Inject +constructor( + @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, +) : ViewModel() { + val title: MutableLiveData = MutableLiveData("") - val productUrl: MutableLiveData = MutableLiveData(null) + val productUrl: MutableLiveData = MutableLiveData(null) - val thumbnailUrl: MutableLiveData = MutableLiveData("") + val thumbnailUrl: MutableLiveData = MutableLiveData("") - val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } + val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } - val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") + val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") - val totalPrice: MutableLiveData = MutableLiveData("") + val totalPrice: MutableLiveData = MutableLiveData("") - val originPrice: MutableLiveData = MutableLiveData("") + val originPrice: MutableLiveData = MutableLiveData("") - val meetingAddress: MutableLiveData = MutableLiveData("") + val meetingAddress: MutableLiveData = MutableLiveData("") - val meetingAddressDetail: MutableLiveData = MutableLiveData("") + val meetingAddressDetail: MutableLiveData = MutableLiveData("") - val meetingDate: MutableLiveData = MutableLiveData("") + val meetingDate: MutableLiveData = MutableLiveData("") - private val meetingDateValue: MutableLiveData = MutableLiveData("") + private val meetingDateValue: MutableLiveData = MutableLiveData("") - val description: MutableLiveData = MutableLiveData("") + val description: MutableLiveData = MutableLiveData("") - val descriptionLength: LiveData - get() = description.map { it.length } + val descriptionLength: LiveData + get() = description.map { it.length } - private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled + private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled - private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val extractButtonEnabled: LiveData get() = _extractButtonEnabled + private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val extractButtonEnabled: LiveData get() = _extractButtonEnabled - private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) - val splitPrice: LiveData get() = _splitPrice + private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) + val splitPrice: LiveData get() = _splitPrice - private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) - val splitPriceValidity: LiveData - get() = _splitPrice.map { it >= 0 } + private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) + val splitPriceValidity: LiveData + get() = _splitPrice.map { it >= 0 } - val discountRateValidity: LiveData - get() = _discountRate.map { it >= 0 } + val discountRateValidity: LiveData + get() = _discountRate.map { it >= 0 } - val discountRate: LiveData get() = _discountRate + val discountRate: LiveData get() = _discountRate - private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() - val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent + private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() + val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent - private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() - val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent + private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() + val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent - private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() - val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent + private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() + val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent - private val _imageUploadEvent = MutableLiveData() - val imageUploadEvent: LiveData get() = _imageUploadEvent + private val _imageUploadEvent = MutableLiveData() + val imageUploadEvent: LiveData get() = _imageUploadEvent - private val _writeUIState = MutableLiveData(WriteUIState.Initial) - val writeUIState: LiveData get() = _writeUIState + private val _writeUIState = MutableLiveData(WriteUIState.Initial) + val writeUIState: LiveData get() = _writeUIState - val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } + val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } - private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) - val isSubmitLoading: LiveData get() = _isSubmitLoading + private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) + val isSubmitLoading: LiveData get() = _isSubmitLoading - init { - _essentialSubmitButtonEnabled.apply { - addSource(title) { updateSubmitButtonEnabled() } - addSource(totalCount) { updateSubmitButtonEnabled() } - addSource(totalPrice) { updateSubmitButtonEnabled() } - addSource(meetingAddress) { updateSubmitButtonEnabled() } - addSource(meetingDate) { updateSubmitButtonEnabled() } - } - - _splitPrice.apply { - addSource(totalCount) { safeUpdateSplitPrice() } - addSource(totalPrice) { safeUpdateSplitPrice() } - } - - _discountRate.apply { - addSource(_splitPrice) { safeUpdateDiscountRate() } - addSource(originPrice) { safeUpdateDiscountRate() } - } + init { + _essentialSubmitButtonEnabled.apply { + addSource(title) { updateSubmitButtonEnabled() } + addSource(totalCount) { updateSubmitButtonEnabled() } + addSource(totalPrice) { updateSubmitButtonEnabled() } + addSource(meetingAddress) { updateSubmitButtonEnabled() } + addSource(meetingDate) { updateSubmitButtonEnabled() } + } - _extractButtonEnabled.apply { - addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } - } + _splitPrice.apply { + addSource(totalCount) { safeUpdateSplitPrice() } + addSource(totalPrice) { safeUpdateSplitPrice() } } - private fun safeUpdateSplitPrice() { - runCatching { - updateSplitPrice() - }.onFailure { - _splitPrice.value = ERROR_INTEGER_FORMAT - } + _discountRate.apply { + addSource(_splitPrice) { safeUpdateDiscountRate() } + addSource(originPrice) { safeUpdateDiscountRate() } } - fun clearProductUrl() { - productUrl.value = null + _extractButtonEnabled.apply { + addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } } + } - fun onUploadPhotoClick() { - _imageUploadEvent.value = Unit + private fun safeUpdateSplitPrice() { + runCatching { + updateSplitPrice() + }.onFailure { + _splitPrice.value = ERROR_INTEGER_FORMAT } + } - fun uploadImageFile(multipartBody: MultipartBody.Part) { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = offeringRepository.saveProductImageS3(multipartBody)) { - is Result.Success -> { - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = result.data.imageUrl - } + fun clearProductUrl() { + productUrl.value = null + } - is Result.Error -> { - Log.e("error", "uploadImageFile: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> uploadImageFile(multipartBody) - is Result.Error -> return@launch - } - } + fun onUploadPhotoClick() { + _imageUploadEvent.value = Unit + } - else -> { - _writeUIState.value = - WriteUIState.Error( - R.string.all_error_image_upload, - "${result.error}", - ) + fun uploadImageFile(multipartBody: MultipartBody.Part) { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = offeringRepository.saveProductImageS3(multipartBody)) { + is Result.Success -> { + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = result.data.imageUrl + } + + is Result.Error -> { + Log.e("error", "uploadImageFile: ${result.error}") + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> uploadImageFile(multipartBody) + is Result.Error -> return@launch } } + + else -> { + _writeUIState.value = + WriteUIState.Error( + R.string.all_error_image_upload, + "${result.error}", + ) + } } } } } + } - fun postProductImageOg() { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = offeringRepository.saveProductImageOg(productUrl.value ?: "")) { - is Result.Success -> { - if (result.data.imageUrl.isBlank()) { - _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) - return@launch - } - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = HTTPS + result.data.imageUrl + fun postProductImageOg() { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = offeringRepository.saveProductImageOg(productUrl.value ?: "")) { + is Result.Success -> { + if (result.data.imageUrl.isBlank()) { + _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) + return@launch } + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = HTTPS + result.data.imageUrl + } - is Result.Error -> { - Log.e("error", "postProductImageOg: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> postProductImageOg() - is Result.Error -> return@launch - } + is Result.Error -> { + Log.e("error", "postProductImageOg: ${result.error}") + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> postProductImageOg() + is Result.Error -> return@launch } + } - else -> { - _writeUIState.value = - WriteUIState.Error( - R.string.error_invalid_product_url, - "${result.error}", - ) - } + else -> { + _writeUIState.value = + WriteUIState.Error( + R.string.error_invalid_product_url, + "${result.error}", + ) } } } } } + } - fun clearProductImage() { - thumbnailUrl.value = null - } + fun clearProductImage() { + thumbnailUrl.value = null + } - private fun safeUpdateDiscountRate() { - runCatching { - updateDiscountRate() - }.onFailure { - _discountRate.value = ERROR_FLOAT_FORMAT - } + private fun safeUpdateDiscountRate() { + runCatching { + updateDiscountRate() + }.onFailure { + _discountRate.value = ERROR_FLOAT_FORMAT } + } - private fun updateSubmitButtonEnabled() { - _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && + private fun updateSubmitButtonEnabled() { + _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && !totalCount.value.isNullOrBlank() && !totalPrice.value.isNullOrBlank() && !meetingAddress.value.isNullOrBlank() && !meetingDate.value.isNullOrBlank() - } - - private fun updateSplitPrice() { - val totalPrice = Price.fromString(totalPrice.value) - val totalCount = Count.fromString(totalCount.value) - _splitPrice.value = totalPrice.amount / totalCount.number - } + } - private fun updateDiscountRate() { - val originPrice = Price.fromString(originPrice.value) - val splitPrice = Price.fromInteger(_splitPrice.value) - val discountPriceValue = originPrice.amount - splitPrice.amount - val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) - _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 - } + private fun updateSplitPrice() { + val totalPrice = Price.fromString(totalPrice.value) + val totalCount = Count.fromString(totalCount.value) + _splitPrice.value = totalPrice.amount / totalCount.number + } - fun increaseTotalCount() { - val totalCount = Count.fromString(totalCount.value).increase() - this.totalCount.value = totalCount.number.toString() - } + private fun updateDiscountRate() { + val originPrice = Price.fromString(originPrice.value) + val splitPrice = Price.fromInteger(_splitPrice.value) + val discountPriceValue = originPrice.amount - splitPrice.amount + val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) + _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 + } - fun decreaseTotalCount() { - if (Count.fromString(totalCount.value).number < 0) { - this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() - return - } - val totalCount = Count.fromString(totalCount.value).decrease() - this.totalCount.value = totalCount.number.toString() - } + fun increaseTotalCount() { + val totalCount = Count.fromString(totalCount.value).increase() + this.totalCount.value = totalCount.number.toString() + } - fun makeMeetingDateChoiceEvent() { - _meetingDateChoiceEvent.setValue(true) + fun decreaseTotalCount() { + if (Count.fromString(totalCount.value).number < 0) { + this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() + return } + val totalCount = Count.fromString(totalCount.value).decrease() + this.totalCount.value = totalCount.number.toString() + } - fun updateMeetingDate(date: String) { - val dateTime = "$date" - val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) - val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) + fun makeMeetingDateChoiceEvent() { + _meetingDateChoiceEvent.setValue(true) + } - val parsedDateTime = inputFormat.parse(dateTime) - meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } - meetingDate.value = dateTime - } + fun updateMeetingDate(date: String) { + val dateTime = "$date" + val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) + val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) - fun postOffering() { - _isSubmitLoading.value = true - val title = title.value ?: return - val totalCount = totalCount.value ?: return - val totalPrice = totalPrice.value ?: return - val meetingAddress = meetingAddress.value ?: return - val meetingAddressDetail = meetingAddressDetail.value ?: return - val meetingDate = meetingDateValue.value ?: return - val description = description.value ?: return - - val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return - val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return - val meetingAddressDong = extractDong(meetingAddress) - - var originPriceNotBlank: Int? = 0 - runCatching { - originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) - }.onFailure { - makeOriginPriceInvalidEvent() - return - } - if (isOriginPriceCheaperThanSplitPriceEvent()) return - - viewModelScope.launch { - when ( - val result = - offeringRepository.saveOffering( - offeringWrite = - OfferingWrite( - title = title, - productUrl = productUrlOrNull(), - thumbnailUrl = thumbnailUrl.value, - totalCount = totalCountConverted, - totalPrice = totalPriceConverted, - originPrice = originPriceNotBlank, - meetingAddress = meetingAddress, - meetingAddressDong = meetingAddressDong, - meetingAddressDetail = meetingAddressDetail, - meetingDate = meetingDate, - description = description, - ), - ) - ) { - is Result.Success -> { - makeSubmitOfferingEvent() - _isSubmitLoading.value = false - } + val parsedDateTime = inputFormat.parse(dateTime) + meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } + meetingDate.value = dateTime + } - is Result.Error -> { - Log.e("error", "postOffering: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> postOffering() - is Result.Error -> return@launch - } - } + fun postOffering() { + _isSubmitLoading.value = true + val title = title.value ?: return + val totalCount = totalCount.value ?: return + val totalPrice = totalPrice.value ?: return + val meetingAddress = meetingAddress.value ?: return + val meetingAddressDetail = meetingAddressDetail.value ?: return + val meetingDate = meetingDateValue.value ?: return + val description = description.value ?: return + + val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return + val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return + val meetingAddressDong = extractDong(meetingAddress) + + var originPriceNotBlank: Int? = 0 + runCatching { + originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) + }.onFailure { + makeOriginPriceInvalidEvent() + return + } + if (isOriginPriceCheaperThanSplitPriceEvent()) return + + viewModelScope.launch { + when ( + val result = postOfferingUseCase( + OfferingWrite( + title = title, + productUrl = productUrlOrNull(), + thumbnailUrl = thumbnailUrl.value, + totalCount = totalCountConverted, + totalPrice = totalPriceConverted, + originPrice = originPriceNotBlank, + meetingAddress = meetingAddress, + meetingAddressDong = meetingAddressDong, + meetingAddressDetail = meetingAddressDetail, + meetingDate = meetingDate, + description = description, + ), + ) + ) { + is Result.Success -> { + makeSubmitOfferingEvent() + _isSubmitLoading.value = false + } - else -> { - _writeUIState.value = - WriteUIState.Error(R.string.write_error_writing, "${result.error}") - } - } - _isSubmitLoading.value = false - } + is Result.Error -> { + Log.e("error", "postOffering: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.write_error_writing, "${result.error}") + _isSubmitLoading.value = false } } } + } - private fun productUrlOrNull(): String? { - val productUrl = productUrl.value - if (productUrl == "") return null - return productUrl - } + private fun productUrlOrNull(): String? { + val productUrl = productUrl.value + if (productUrl == "") return null + return productUrl + } - private fun originPriceToPositiveIntOrNull(input: String?): Int? { - val originPriceInputTrim = input?.trim() - if (originPriceInputTrim.isNullOrBlank()) { - return null - } - if (originPriceInputTrim.toInt() < 0) { - throw NumberFormatException() - } - return originPriceInputTrim.toInt() + private fun originPriceToPositiveIntOrNull(input: String?): Int? { + val originPriceInputTrim = input?.trim() + if (originPriceInputTrim.isNullOrBlank()) { + return null } - - private fun extractDong(address: String): String? { - val regex = """\((.*?)\)""".toRegex() - val matchResult = regex.find(address) - val content = matchResult?.groups?.get(1)?.value - return content?.split(",")?.get(0)?.trim() + if (originPriceInputTrim.toInt() < 0) { + throw NumberFormatException() } + return originPriceInputTrim.toInt() + } - private fun makeTotalCountInvalidEvent(totalCount: String): Int? { - val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) - return null - } - return totalCountValue - } + private fun extractDong(address: String): String? { + val regex = """\((.*?)\)""".toRegex() + val matchResult = regex.find(address) + val content = matchResult?.groups?.get(1)?.value + return content?.split(",")?.get(0)?.trim() + } - private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { - val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalPriceConverted < 0) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) - return null - } - return totalPriceConverted + private fun makeTotalCountInvalidEvent(totalCount: String): Int? { + val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) + return null } + return totalCountValue + } - private fun makeOriginPriceInvalidEvent() { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) + private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { + val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalPriceConverted < 0) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) + return null } + return totalPriceConverted + } - private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { - if (originPrice.value.isNullOrBlank()) return false - val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT - if (discountRateValue <= 0f) { - _writeUIState.value = - WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) - return true - } - return false - } + private fun makeOriginPriceInvalidEvent() { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) + } - fun makeNavigateToOptionalEvent() { - _navigateToOptionalEvent.setValue(true) + private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { + if (originPrice.value.isNullOrBlank()) return false + val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT + if (discountRateValue <= 0f) { + _writeUIState.value = + WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) + return true } + return false + } - private fun makeSubmitOfferingEvent() { - _submitOfferingEvent.setValue(Unit) - } + fun makeNavigateToOptionalEvent() { + _navigateToOptionalEvent.setValue(true) + } - fun initOfferingWriteInputs() { - title.value = "" - productUrl.value = "" - thumbnailUrl.value = "" - totalCount.value = "$MINIMUM_TOTAL_COUNT" - totalPrice.value = "" - originPrice.value = "" - meetingAddress.value = "" - meetingAddressDetail.value = "" - meetingDate.value = "" - meetingDateValue.value = "" - description.value = "" - } + private fun makeSubmitOfferingEvent() { + _submitOfferingEvent.setValue(Unit) + } - companion object { - private const val ERROR_INTEGER_FORMAT = -1 - private const val ERROR_FLOAT_FORMAT = -1f - private const val MINIMUM_TOTAL_COUNT = 2 - private const val MAXIMUM_TOTAL_COUNT = 10_000 - private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" - private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" - private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" - const val HTTPS = "https:" - } + fun initOfferingWriteInputs() { + title.value = "" + productUrl.value = "" + thumbnailUrl.value = "" + totalCount.value = "$MINIMUM_TOTAL_COUNT" + totalPrice.value = "" + originPrice.value = "" + meetingAddress.value = "" + meetingAddressDetail.value = "" + meetingDate.value = "" + meetingDateValue.value = "" + description.value = "" + } + + companion object { + private const val ERROR_INTEGER_FORMAT = -1 + private const val ERROR_FLOAT_FORMAT = -1f + private const val MINIMUM_TOTAL_COUNT = 2 + private const val MAXIMUM_TOTAL_COUNT = 10_000 + private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" + private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" + private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" + const val HTTPS = "https:" } +} diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt new file mode 100644 index 000000000..373ef6930 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt @@ -0,0 +1,9 @@ +package com.zzang.chongdae.presentation.view.write.usecase + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.model.OfferingWrite + +interface PostOfferingUseCase { + suspend operator fun invoke(offeringWrite: OfferingWrite): Result +} \ No newline at end of file diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt new file mode 100644 index 000000000..a24318e36 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt @@ -0,0 +1,32 @@ +package com.zzang.chongdae.presentation.view.write.usecase + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier +import com.zzang.chongdae.domain.model.OfferingWrite +import com.zzang.chongdae.domain.repository.OfferingRepository +import javax.inject.Inject + +class PostOfferingUseCaseImpl @Inject constructor( + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, +) : PostOfferingUseCase { + override suspend fun invoke(offeringWrite: OfferingWrite): Result { + return when (val result = offeringRepository.saveOffering(offeringWrite)) { + is Result.Success -> Result.Success(Unit) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(offeringWrite) + is Result.Error -> result + } + } + else -> result + } + } + } + } +} \ No newline at end of file From 15778c46a068c18feecba11505946e120ab29581 Mon Sep 17 00:00:00 2001 From: songpink Date: Sat, 7 Dec 2024 16:24:46 +0900 Subject: [PATCH 06/16] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=EB=A5=BC=20=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chongdae/di/module/UseCaseDependencyModule.kt | 12 ++++++------ .../usecase/login}/CheckIfAlreadyLoggedInUseCase.kt | 2 +- .../login}/CheckIfAlreadyLoggedInUseCaseImpl.kt | 2 +- .../usecase/login}/PostLoginUseCase.kt | 2 +- .../usecase/login}/PostLoginUseCaseImpl.kt | 2 +- .../usecase/write}/PostOfferingUseCase.kt | 2 +- .../usecase/write}/PostOfferingUseCaseImpl.kt | 2 +- .../presentation/view/login/LoginViewModel.kt | 5 ++--- .../view/write/OfferingWriteViewModel.kt | 2 +- 9 files changed, 15 insertions(+), 16 deletions(-) rename android/app/src/main/java/com/zzang/chongdae/{presentation/view/login/usecase => domain/usecase/login}/CheckIfAlreadyLoggedInUseCase.kt (59%) rename android/app/src/main/java/com/zzang/chongdae/{presentation/view/login/usecase => domain/usecase/login}/CheckIfAlreadyLoggedInUseCaseImpl.kt (89%) rename android/app/src/main/java/com/zzang/chongdae/{presentation/view/login/usecase => domain/usecase/login}/PostLoginUseCase.kt (81%) rename android/app/src/main/java/com/zzang/chongdae/{presentation/view/login/usecase => domain/usecase/login}/PostLoginUseCaseImpl.kt (95%) rename android/app/src/main/java/com/zzang/chongdae/{presentation/view/write/usecase => domain/usecase/write}/PostOfferingUseCase.kt (82%) rename android/app/src/main/java/com/zzang/chongdae/{presentation/view/write/usecase => domain/usecase/write}/PostOfferingUseCaseImpl.kt (95%) diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index 679a8509e..d1d52ae43 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -3,12 +3,12 @@ package com.zzang.chongdae.di.module import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier -import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCase -import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCaseImpl -import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase -import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCaseImpl -import com.zzang.chongdae.presentation.view.write.usecase.PostOfferingUseCase -import com.zzang.chongdae.presentation.view.write.usecase.PostOfferingUseCaseImpl +import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase +import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCaseImpl +import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase +import com.zzang.chongdae.domain.usecase.login.PostLoginUseCaseImpl +import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase +import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCaseImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/CheckIfAlreadyLoggedInUseCase.kt similarity index 59% rename from android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt rename to android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/CheckIfAlreadyLoggedInUseCase.kt index 6eb4a2e72..8db4b301c 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCase.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/CheckIfAlreadyLoggedInUseCase.kt @@ -1,4 +1,4 @@ -package com.zzang.chongdae.presentation.view.login.usecase +package com.zzang.chongdae.domain.usecase.login interface CheckIfAlreadyLoggedInUseCase { suspend operator fun invoke(): Boolean diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/CheckIfAlreadyLoggedInUseCaseImpl.kt similarity index 89% rename from android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt rename to android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/CheckIfAlreadyLoggedInUseCaseImpl.kt index 79ea9de20..7fb4a3d21 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/CheckIfAlreadyLoggedInUseCaseImpl.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/CheckIfAlreadyLoggedInUseCaseImpl.kt @@ -1,4 +1,4 @@ -package com.zzang.chongdae.presentation.view.login.usecase +package com.zzang.chongdae.domain.usecase.login import com.zzang.chongdae.common.datastore.UserPreferencesDataStore import kotlinx.coroutines.flow.first diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCase.kt similarity index 81% rename from android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt rename to android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCase.kt index a8b9d7a88..2369692df 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCase.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCase.kt @@ -1,4 +1,4 @@ -package com.zzang.chongdae.presentation.view.login.usecase +package com.zzang.chongdae.domain.usecase.login import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseImpl.kt similarity index 95% rename from android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt rename to android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseImpl.kt index 687f9f833..35f98557e 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/usecase/PostLoginUseCaseImpl.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseImpl.kt @@ -1,4 +1,4 @@ -package com.zzang.chongdae.presentation.view.login.usecase +package com.zzang.chongdae.domain.usecase.login import com.zzang.chongdae.auth.repository.AuthRepository import com.zzang.chongdae.common.datastore.UserPreferencesDataStore diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt similarity index 82% rename from android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt rename to android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt index 373ef6930..28a24707d 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCase.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt @@ -1,4 +1,4 @@ -package com.zzang.chongdae.presentation.view.write.usecase +package com.zzang.chongdae.domain.usecase.write import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt similarity index 95% rename from android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt rename to android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt index a24318e36..a9585a01b 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/usecase/PostOfferingUseCaseImpl.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt @@ -1,4 +1,4 @@ -package com.zzang.chongdae.presentation.view.write.usecase +package com.zzang.chongdae.domain.usecase.write import com.zzang.chongdae.auth.repository.AuthRepository import com.zzang.chongdae.common.handler.DataError diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt index 778809837..5804cf425 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt @@ -3,14 +3,13 @@ package com.zzang.chongdae.presentation.view.login import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.zzang.chongdae.common.datastore.UserPreferencesDataStore import com.zzang.chongdae.common.handler.Result import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData -import com.zzang.chongdae.presentation.view.login.usecase.CheckIfAlreadyLoggedInUseCase -import com.zzang.chongdae.presentation.view.login.usecase.PostLoginUseCase +import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase +import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt index 92847788c..89ccfb4a8 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt @@ -21,7 +21,7 @@ import com.zzang.chongdae.domain.model.Price import com.zzang.chongdae.domain.repository.OfferingRepository import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData -import com.zzang.chongdae.presentation.view.write.usecase.PostOfferingUseCase +import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import okhttp3.MultipartBody From 118b2372adc3cd597aba0be1a3bb87e4a8e89e40 Mon Sep 17 00:00:00 2001 From: songpink Date: Sun, 15 Dec 2024 16:21:29 +0900 Subject: [PATCH 07/16] =?UTF-8?q?style:=20ktlint=20format=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/write/PostOfferingUseCase.kt | 2 +- .../usecase/write/PostOfferingUseCaseImpl.kt | 32 +- .../presentation/view/login/LoginViewModel.kt | 4 +- .../view/write/OfferingWriteViewModel.kt | 597 +++++++++--------- 4 files changed, 319 insertions(+), 316 deletions(-) diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt index 28a24707d..c602c0531 100644 --- a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCase.kt @@ -6,4 +6,4 @@ import com.zzang.chongdae.domain.model.OfferingWrite interface PostOfferingUseCase { suspend operator fun invoke(offeringWrite: OfferingWrite): Result -} \ No newline at end of file +} diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt index a9585a01b..664cacbe7 100644 --- a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostOfferingUseCaseImpl.kt @@ -9,24 +9,26 @@ import com.zzang.chongdae.domain.model.OfferingWrite import com.zzang.chongdae.domain.repository.OfferingRepository import javax.inject.Inject -class PostOfferingUseCaseImpl @Inject constructor( - @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, - @AuthRepositoryQualifier private val authRepository: AuthRepository, -) : PostOfferingUseCase { - override suspend fun invoke(offeringWrite: OfferingWrite): Result { - return when (val result = offeringRepository.saveOffering(offeringWrite)) { - is Result.Success -> Result.Success(Unit) - is Result.Error -> { - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> invoke(offeringWrite) - is Result.Error -> result +class PostOfferingUseCaseImpl + @Inject + constructor( + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, + ) : PostOfferingUseCase { + override suspend fun invoke(offeringWrite: OfferingWrite): Result { + return when (val result = offeringRepository.saveOffering(offeringWrite)) { + is Result.Success -> Result.Success(Unit) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(offeringWrite) + is Result.Error -> result + } } + else -> result } - else -> result } } } } -} \ No newline at end of file diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt index 5804cf425..15e1d5445 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/login/LoginViewModel.kt @@ -6,10 +6,10 @@ import androidx.lifecycle.viewModelScope import com.zzang.chongdae.common.handler.Result import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier -import com.zzang.chongdae.presentation.util.MutableSingleLiveData -import com.zzang.chongdae.presentation.util.SingleLiveData import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase +import com.zzang.chongdae.presentation.util.MutableSingleLiveData +import com.zzang.chongdae.presentation.util.SingleLiveData import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt index 89ccfb4a8..42cbfb610 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt @@ -19,9 +19,9 @@ import com.zzang.chongdae.domain.model.DiscountPrice import com.zzang.chongdae.domain.model.OfferingWrite import com.zzang.chongdae.domain.model.Price import com.zzang.chongdae.domain.repository.OfferingRepository +import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData -import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import okhttp3.MultipartBody @@ -31,390 +31,391 @@ import javax.inject.Inject @HiltViewModel class OfferingWriteViewModel -@Inject -constructor( - @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, - @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, - @AuthRepositoryQualifier private val authRepository: AuthRepository, -) : ViewModel() { - val title: MutableLiveData = MutableLiveData("") + @Inject + constructor( + @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, + ) : ViewModel() { + val title: MutableLiveData = MutableLiveData("") - val productUrl: MutableLiveData = MutableLiveData(null) + val productUrl: MutableLiveData = MutableLiveData(null) - val thumbnailUrl: MutableLiveData = MutableLiveData("") + val thumbnailUrl: MutableLiveData = MutableLiveData("") - val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } + val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } - val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") + val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") - val totalPrice: MutableLiveData = MutableLiveData("") + val totalPrice: MutableLiveData = MutableLiveData("") - val originPrice: MutableLiveData = MutableLiveData("") + val originPrice: MutableLiveData = MutableLiveData("") - val meetingAddress: MutableLiveData = MutableLiveData("") + val meetingAddress: MutableLiveData = MutableLiveData("") - val meetingAddressDetail: MutableLiveData = MutableLiveData("") + val meetingAddressDetail: MutableLiveData = MutableLiveData("") - val meetingDate: MutableLiveData = MutableLiveData("") + val meetingDate: MutableLiveData = MutableLiveData("") - private val meetingDateValue: MutableLiveData = MutableLiveData("") + private val meetingDateValue: MutableLiveData = MutableLiveData("") - val description: MutableLiveData = MutableLiveData("") + val description: MutableLiveData = MutableLiveData("") - val descriptionLength: LiveData - get() = description.map { it.length } + val descriptionLength: LiveData + get() = description.map { it.length } - private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled + private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled - private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val extractButtonEnabled: LiveData get() = _extractButtonEnabled + private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val extractButtonEnabled: LiveData get() = _extractButtonEnabled - private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) - val splitPrice: LiveData get() = _splitPrice + private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) + val splitPrice: LiveData get() = _splitPrice - private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) - val splitPriceValidity: LiveData - get() = _splitPrice.map { it >= 0 } + private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) + val splitPriceValidity: LiveData + get() = _splitPrice.map { it >= 0 } - val discountRateValidity: LiveData - get() = _discountRate.map { it >= 0 } + val discountRateValidity: LiveData + get() = _discountRate.map { it >= 0 } - val discountRate: LiveData get() = _discountRate + val discountRate: LiveData get() = _discountRate - private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() - val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent + private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() + val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent - private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() - val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent + private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() + val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent - private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() - val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent + private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() + val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent - private val _imageUploadEvent = MutableLiveData() - val imageUploadEvent: LiveData get() = _imageUploadEvent + private val _imageUploadEvent = MutableLiveData() + val imageUploadEvent: LiveData get() = _imageUploadEvent - private val _writeUIState = MutableLiveData(WriteUIState.Initial) - val writeUIState: LiveData get() = _writeUIState + private val _writeUIState = MutableLiveData(WriteUIState.Initial) + val writeUIState: LiveData get() = _writeUIState - val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } + val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } - private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) - val isSubmitLoading: LiveData get() = _isSubmitLoading + private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) + val isSubmitLoading: LiveData get() = _isSubmitLoading - init { - _essentialSubmitButtonEnabled.apply { - addSource(title) { updateSubmitButtonEnabled() } - addSource(totalCount) { updateSubmitButtonEnabled() } - addSource(totalPrice) { updateSubmitButtonEnabled() } - addSource(meetingAddress) { updateSubmitButtonEnabled() } - addSource(meetingDate) { updateSubmitButtonEnabled() } - } + init { + _essentialSubmitButtonEnabled.apply { + addSource(title) { updateSubmitButtonEnabled() } + addSource(totalCount) { updateSubmitButtonEnabled() } + addSource(totalPrice) { updateSubmitButtonEnabled() } + addSource(meetingAddress) { updateSubmitButtonEnabled() } + addSource(meetingDate) { updateSubmitButtonEnabled() } + } - _splitPrice.apply { - addSource(totalCount) { safeUpdateSplitPrice() } - addSource(totalPrice) { safeUpdateSplitPrice() } - } + _splitPrice.apply { + addSource(totalCount) { safeUpdateSplitPrice() } + addSource(totalPrice) { safeUpdateSplitPrice() } + } - _discountRate.apply { - addSource(_splitPrice) { safeUpdateDiscountRate() } - addSource(originPrice) { safeUpdateDiscountRate() } - } + _discountRate.apply { + addSource(_splitPrice) { safeUpdateDiscountRate() } + addSource(originPrice) { safeUpdateDiscountRate() } + } - _extractButtonEnabled.apply { - addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } + _extractButtonEnabled.apply { + addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } + } } - } - private fun safeUpdateSplitPrice() { - runCatching { - updateSplitPrice() - }.onFailure { - _splitPrice.value = ERROR_INTEGER_FORMAT + private fun safeUpdateSplitPrice() { + runCatching { + updateSplitPrice() + }.onFailure { + _splitPrice.value = ERROR_INTEGER_FORMAT + } } - } - fun clearProductUrl() { - productUrl.value = null - } + fun clearProductUrl() { + productUrl.value = null + } - fun onUploadPhotoClick() { - _imageUploadEvent.value = Unit - } + fun onUploadPhotoClick() { + _imageUploadEvent.value = Unit + } - fun uploadImageFile(multipartBody: MultipartBody.Part) { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = offeringRepository.saveProductImageS3(multipartBody)) { - is Result.Success -> { - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = result.data.imageUrl - } + fun uploadImageFile(multipartBody: MultipartBody.Part) { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = offeringRepository.saveProductImageS3(multipartBody)) { + is Result.Success -> { + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = result.data.imageUrl + } - is Result.Error -> { - Log.e("error", "uploadImageFile: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> uploadImageFile(multipartBody) - is Result.Error -> return@launch + is Result.Error -> { + Log.e("error", "uploadImageFile: ${result.error}") + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> uploadImageFile(multipartBody) + is Result.Error -> return@launch + } } - } - else -> { - _writeUIState.value = - WriteUIState.Error( - R.string.all_error_image_upload, - "${result.error}", - ) + else -> { + _writeUIState.value = + WriteUIState.Error( + R.string.all_error_image_upload, + "${result.error}", + ) + } } } } } } - } - fun postProductImageOg() { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = offeringRepository.saveProductImageOg(productUrl.value ?: "")) { - is Result.Success -> { - if (result.data.imageUrl.isBlank()) { - _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) - return@launch + fun postProductImageOg() { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = offeringRepository.saveProductImageOg(productUrl.value ?: "")) { + is Result.Success -> { + if (result.data.imageUrl.isBlank()) { + _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) + return@launch + } + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = HTTPS + result.data.imageUrl } - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = HTTPS + result.data.imageUrl - } - is Result.Error -> { - Log.e("error", "postProductImageOg: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> postProductImageOg() - is Result.Error -> return@launch + is Result.Error -> { + Log.e("error", "postProductImageOg: ${result.error}") + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> postProductImageOg() + is Result.Error -> return@launch + } } - } - else -> { - _writeUIState.value = - WriteUIState.Error( - R.string.error_invalid_product_url, - "${result.error}", - ) + else -> { + _writeUIState.value = + WriteUIState.Error( + R.string.error_invalid_product_url, + "${result.error}", + ) + } } } } } } - } - fun clearProductImage() { - thumbnailUrl.value = null - } + fun clearProductImage() { + thumbnailUrl.value = null + } - private fun safeUpdateDiscountRate() { - runCatching { - updateDiscountRate() - }.onFailure { - _discountRate.value = ERROR_FLOAT_FORMAT + private fun safeUpdateDiscountRate() { + runCatching { + updateDiscountRate() + }.onFailure { + _discountRate.value = ERROR_FLOAT_FORMAT + } } - } - private fun updateSubmitButtonEnabled() { - _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && + private fun updateSubmitButtonEnabled() { + _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && !totalCount.value.isNullOrBlank() && !totalPrice.value.isNullOrBlank() && !meetingAddress.value.isNullOrBlank() && !meetingDate.value.isNullOrBlank() - } - - private fun updateSplitPrice() { - val totalPrice = Price.fromString(totalPrice.value) - val totalCount = Count.fromString(totalCount.value) - _splitPrice.value = totalPrice.amount / totalCount.number - } + } - private fun updateDiscountRate() { - val originPrice = Price.fromString(originPrice.value) - val splitPrice = Price.fromInteger(_splitPrice.value) - val discountPriceValue = originPrice.amount - splitPrice.amount - val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) - _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 - } + private fun updateSplitPrice() { + val totalPrice = Price.fromString(totalPrice.value) + val totalCount = Count.fromString(totalCount.value) + _splitPrice.value = totalPrice.amount / totalCount.number + } - fun increaseTotalCount() { - val totalCount = Count.fromString(totalCount.value).increase() - this.totalCount.value = totalCount.number.toString() - } + private fun updateDiscountRate() { + val originPrice = Price.fromString(originPrice.value) + val splitPrice = Price.fromInteger(_splitPrice.value) + val discountPriceValue = originPrice.amount - splitPrice.amount + val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) + _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 + } - fun decreaseTotalCount() { - if (Count.fromString(totalCount.value).number < 0) { - this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() - return + fun increaseTotalCount() { + val totalCount = Count.fromString(totalCount.value).increase() + this.totalCount.value = totalCount.number.toString() } - val totalCount = Count.fromString(totalCount.value).decrease() - this.totalCount.value = totalCount.number.toString() - } - fun makeMeetingDateChoiceEvent() { - _meetingDateChoiceEvent.setValue(true) - } + fun decreaseTotalCount() { + if (Count.fromString(totalCount.value).number < 0) { + this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() + return + } + val totalCount = Count.fromString(totalCount.value).decrease() + this.totalCount.value = totalCount.number.toString() + } - fun updateMeetingDate(date: String) { - val dateTime = "$date" - val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) - val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) + fun makeMeetingDateChoiceEvent() { + _meetingDateChoiceEvent.setValue(true) + } - val parsedDateTime = inputFormat.parse(dateTime) - meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } - meetingDate.value = dateTime - } + fun updateMeetingDate(date: String) { + val dateTime = "$date" + val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) + val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) - fun postOffering() { - _isSubmitLoading.value = true - val title = title.value ?: return - val totalCount = totalCount.value ?: return - val totalPrice = totalPrice.value ?: return - val meetingAddress = meetingAddress.value ?: return - val meetingAddressDetail = meetingAddressDetail.value ?: return - val meetingDate = meetingDateValue.value ?: return - val description = description.value ?: return - - val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return - val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return - val meetingAddressDong = extractDong(meetingAddress) - - var originPriceNotBlank: Int? = 0 - runCatching { - originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) - }.onFailure { - makeOriginPriceInvalidEvent() - return + val parsedDateTime = inputFormat.parse(dateTime) + meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } + meetingDate.value = dateTime } - if (isOriginPriceCheaperThanSplitPriceEvent()) return - - viewModelScope.launch { - when ( - val result = postOfferingUseCase( - OfferingWrite( - title = title, - productUrl = productUrlOrNull(), - thumbnailUrl = thumbnailUrl.value, - totalCount = totalCountConverted, - totalPrice = totalPriceConverted, - originPrice = originPriceNotBlank, - meetingAddress = meetingAddress, - meetingAddressDong = meetingAddressDong, - meetingAddressDetail = meetingAddressDetail, - meetingDate = meetingDate, - description = description, - ), - ) - ) { - is Result.Success -> { - makeSubmitOfferingEvent() - _isSubmitLoading.value = false - } - is Result.Error -> { - Log.e("error", "postOffering: ${result.error}") - _writeUIState.value = - WriteUIState.Error(R.string.write_error_writing, "${result.error}") - _isSubmitLoading.value = false + fun postOffering() { + _isSubmitLoading.value = true + val title = title.value ?: return + val totalCount = totalCount.value ?: return + val totalPrice = totalPrice.value ?: return + val meetingAddress = meetingAddress.value ?: return + val meetingAddressDetail = meetingAddressDetail.value ?: return + val meetingDate = meetingDateValue.value ?: return + val description = description.value ?: return + + val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return + val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return + val meetingAddressDong = extractDong(meetingAddress) + + var originPriceNotBlank: Int? = 0 + runCatching { + originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) + }.onFailure { + makeOriginPriceInvalidEvent() + return + } + if (isOriginPriceCheaperThanSplitPriceEvent()) return + + viewModelScope.launch { + when ( + val result = + postOfferingUseCase( + OfferingWrite( + title = title, + productUrl = productUrlOrNull(), + thumbnailUrl = thumbnailUrl.value, + totalCount = totalCountConverted, + totalPrice = totalPriceConverted, + originPrice = originPriceNotBlank, + meetingAddress = meetingAddress, + meetingAddressDong = meetingAddressDong, + meetingAddressDetail = meetingAddressDetail, + meetingDate = meetingDate, + description = description, + ), + ) + ) { + is Result.Success -> { + makeSubmitOfferingEvent() + _isSubmitLoading.value = false + } + + is Result.Error -> { + Log.e("error", "postOffering: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.write_error_writing, "${result.error}") + _isSubmitLoading.value = false + } } } } - } - private fun productUrlOrNull(): String? { - val productUrl = productUrl.value - if (productUrl == "") return null - return productUrl - } - - private fun originPriceToPositiveIntOrNull(input: String?): Int? { - val originPriceInputTrim = input?.trim() - if (originPriceInputTrim.isNullOrBlank()) { - return null + private fun productUrlOrNull(): String? { + val productUrl = productUrl.value + if (productUrl == "") return null + return productUrl } - if (originPriceInputTrim.toInt() < 0) { - throw NumberFormatException() + + private fun originPriceToPositiveIntOrNull(input: String?): Int? { + val originPriceInputTrim = input?.trim() + if (originPriceInputTrim.isNullOrBlank()) { + return null + } + if (originPriceInputTrim.toInt() < 0) { + throw NumberFormatException() + } + return originPriceInputTrim.toInt() } - return originPriceInputTrim.toInt() - } - private fun extractDong(address: String): String? { - val regex = """\((.*?)\)""".toRegex() - val matchResult = regex.find(address) - val content = matchResult?.groups?.get(1)?.value - return content?.split(",")?.get(0)?.trim() - } + private fun extractDong(address: String): String? { + val regex = """\((.*?)\)""".toRegex() + val matchResult = regex.find(address) + val content = matchResult?.groups?.get(1)?.value + return content?.split(",")?.get(0)?.trim() + } - private fun makeTotalCountInvalidEvent(totalCount: String): Int? { - val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) - return null + private fun makeTotalCountInvalidEvent(totalCount: String): Int? { + val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) + return null + } + return totalCountValue } - return totalCountValue - } - private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { - val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalPriceConverted < 0) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) - return null + private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { + val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalPriceConverted < 0) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) + return null + } + return totalPriceConverted } - return totalPriceConverted - } - private fun makeOriginPriceInvalidEvent() { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) - } + private fun makeOriginPriceInvalidEvent() { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) + } - private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { - if (originPrice.value.isNullOrBlank()) return false - val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT - if (discountRateValue <= 0f) { - _writeUIState.value = - WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) - return true + private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { + if (originPrice.value.isNullOrBlank()) return false + val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT + if (discountRateValue <= 0f) { + _writeUIState.value = + WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) + return true + } + return false } - return false - } - fun makeNavigateToOptionalEvent() { - _navigateToOptionalEvent.setValue(true) - } + fun makeNavigateToOptionalEvent() { + _navigateToOptionalEvent.setValue(true) + } - private fun makeSubmitOfferingEvent() { - _submitOfferingEvent.setValue(Unit) - } + private fun makeSubmitOfferingEvent() { + _submitOfferingEvent.setValue(Unit) + } - fun initOfferingWriteInputs() { - title.value = "" - productUrl.value = "" - thumbnailUrl.value = "" - totalCount.value = "$MINIMUM_TOTAL_COUNT" - totalPrice.value = "" - originPrice.value = "" - meetingAddress.value = "" - meetingAddressDetail.value = "" - meetingDate.value = "" - meetingDateValue.value = "" - description.value = "" - } + fun initOfferingWriteInputs() { + title.value = "" + productUrl.value = "" + thumbnailUrl.value = "" + totalCount.value = "$MINIMUM_TOTAL_COUNT" + totalPrice.value = "" + originPrice.value = "" + meetingAddress.value = "" + meetingAddressDetail.value = "" + meetingDate.value = "" + meetingDateValue.value = "" + description.value = "" + } - companion object { - private const val ERROR_INTEGER_FORMAT = -1 - private const val ERROR_FLOAT_FORMAT = -1f - private const val MINIMUM_TOTAL_COUNT = 2 - private const val MAXIMUM_TOTAL_COUNT = 10_000 - private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" - private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" - private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" - const val HTTPS = "https:" + companion object { + private const val ERROR_INTEGER_FORMAT = -1 + private const val ERROR_FLOAT_FORMAT = -1f + private const val MINIMUM_TOTAL_COUNT = 2 + private const val MAXIMUM_TOTAL_COUNT = 10_000 + private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" + private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" + private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" + const val HTTPS = "https:" + } } -} From ad0ac120df8580110f43b45a7a93a0ae4162d38f Mon Sep 17 00:00:00 2001 From: songpink Date: Sun, 15 Dec 2024 18:25:25 +0900 Subject: [PATCH 08/16] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/annotations/UseCaseQualifier.kt | 4 ++ .../di/module/UseCaseDependencyModule.kt | 8 ++++ .../usecase/write/UploadImageFileUseCase.kt | 10 +++++ .../write/UploadImageFileUseCaseImpl.kt | 36 ++++++++++++++++++ .../view/write/OfferingWriteViewModel.kt | 38 ++++++++++--------- 5 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt index ed15202f1..405bd2360 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -13,3 +13,7 @@ annotation class PostLoginUseCaseQualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PostOfferingUseCaseQualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class UploadImageFileUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index d1d52ae43..c06643583 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -3,12 +3,15 @@ package com.zzang.chongdae.di.module import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier +import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCaseImpl import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase import com.zzang.chongdae.domain.usecase.login.PostLoginUseCaseImpl import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCaseImpl +import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase +import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCaseImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -32,4 +35,9 @@ abstract class UseCaseDependencyModule { @Singleton @PostOfferingUseCaseQualifier abstract fun providePostOfferingUseCase(impl: PostOfferingUseCaseImpl): PostOfferingUseCase + + @Binds + @Singleton + @UploadImageFileUseCaseQualifier + abstract fun provideUploadImageFileUseCase(impl: UploadImageFileUseCaseImpl): UploadImageFileUseCase } diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCase.kt new file mode 100644 index 000000000..5a3beec8e --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCase.kt @@ -0,0 +1,10 @@ +package com.zzang.chongdae.domain.usecase.write + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.model.ProductUrl +import okhttp3.MultipartBody + +interface UploadImageFileUseCase { + suspend operator fun invoke(multipartBody: MultipartBody.Part): Result +} diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCaseImpl.kt new file mode 100644 index 000000000..c1490e32b --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/UploadImageFileUseCaseImpl.kt @@ -0,0 +1,36 @@ +package com.zzang.chongdae.domain.usecase.write + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier +import com.zzang.chongdae.domain.model.ProductUrl +import com.zzang.chongdae.domain.repository.OfferingRepository +import okhttp3.MultipartBody +import javax.inject.Inject + +class UploadImageFileUseCaseImpl + @Inject + constructor( + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, + ) : UploadImageFileUseCase { + override suspend fun invoke(multipartBody: MultipartBody.Part): Result { + return when (val result = offeringRepository.saveProductImageS3(multipartBody)) { + is Result.Success -> Result.Success(result.data) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(multipartBody) + is Result.Error -> result + } + } + + else -> result + } + } + } + } + } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt index 42cbfb610..8fc752143 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt @@ -14,12 +14,14 @@ import com.zzang.chongdae.common.handler.Result import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier +import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier import com.zzang.chongdae.domain.model.Count import com.zzang.chongdae.domain.model.DiscountPrice import com.zzang.chongdae.domain.model.OfferingWrite import com.zzang.chongdae.domain.model.Price import com.zzang.chongdae.domain.repository.OfferingRepository import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase +import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData import dagger.hilt.android.lifecycle.HiltViewModel @@ -34,6 +36,7 @@ class OfferingWriteViewModel @Inject constructor( @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, + @UploadImageFileUseCaseQualifier val uploadImageFileUseCase: UploadImageFileUseCase, @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, @AuthRepositoryQualifier private val authRepository: AuthRepository, ) : ViewModel() { @@ -145,7 +148,7 @@ class OfferingWriteViewModel fun uploadImageFile(multipartBody: MultipartBody.Part) { viewModelScope.launch { _writeUIState.value = WriteUIState.Loading - when (val result = offeringRepository.saveProductImageS3(multipartBody)) { + when (val result = uploadImageFileUseCase.invoke(multipartBody)) { is Result.Success -> { _writeUIState.value = WriteUIState.Success(result.data.imageUrl) thumbnailUrl.value = result.data.imageUrl @@ -153,22 +156,23 @@ class OfferingWriteViewModel is Result.Error -> { Log.e("error", "uploadImageFile: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> uploadImageFile(multipartBody) - is Result.Error -> return@launch - } - } - - else -> { - _writeUIState.value = - WriteUIState.Error( - R.string.all_error_image_upload, - "${result.error}", - ) - } - } + _writeUIState.value = WriteUIState.Error(R.string.all_error_image_upload, "${result.error}") +// when (result.error) { +// DataError.Network.UNAUTHORIZED -> { +// when (authRepository.saveRefresh()) { +// is Result.Success -> uploadImageFile(multipartBody) +// is Result.Error -> return@launch +// } +// } +// +// else -> { +// _writeUIState.value = +// WriteUIState.Error( +// R.string.all_error_image_upload, +// "${result.error}", +// ) +// } +// } } } } From bd25819a64712bc4b63b9cab5e69d91afc3bf5d1 Mon Sep 17 00:00:00 2001 From: songpink Date: Sun, 15 Dec 2024 18:51:11 +0900 Subject: [PATCH 09/16] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=EC=97=90=20?= =?UTF-8?q?=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/annotations/UseCaseQualifier.kt | 4 + .../di/module/UseCaseDependencyModule.kt | 8 + .../write/PostProductImageOgUseCase.kt | 9 + .../write/PostProductImageOgUseCaseImpl.kt | 35 ++ .../view/write/OfferingWriteViewModel.kt | 593 +++++++++--------- 5 files changed, 336 insertions(+), 313 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt index 405bd2360..25047580f 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -17,3 +17,7 @@ annotation class PostOfferingUseCaseQualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class UploadImageFileUseCaseQualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class PostProductImageOgUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index c06643583..1e6d5ec80 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -3,6 +3,7 @@ package com.zzang.chongdae.di.module import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier +import com.zzang.chongdae.di.annotations.PostProductImageOgUseCaseQualifier import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCaseImpl @@ -10,6 +11,8 @@ import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase import com.zzang.chongdae.domain.usecase.login.PostLoginUseCaseImpl import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCaseImpl +import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCase +import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCaseImpl import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCaseImpl import dagger.Binds @@ -40,4 +43,9 @@ abstract class UseCaseDependencyModule { @Singleton @UploadImageFileUseCaseQualifier abstract fun provideUploadImageFileUseCase(impl: UploadImageFileUseCaseImpl): UploadImageFileUseCase + + @Binds + @Singleton + @PostProductImageOgUseCaseQualifier + abstract fun providePostProductImageOgUseCase(impl: PostProductImageOgUseCaseImpl): PostProductImageOgUseCase } diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt new file mode 100644 index 000000000..220a1d0f4 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt @@ -0,0 +1,9 @@ +package com.zzang.chongdae.domain.usecase.write + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.domain.model.ProductUrl +import com.zzang.chongdae.common.handler.Result + +interface PostProductImageOgUseCase { + suspend operator fun invoke(productUrl: String): Result +} \ No newline at end of file diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt new file mode 100644 index 000000000..841d4445d --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt @@ -0,0 +1,35 @@ +package com.zzang.chongdae.domain.usecase.write + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier +import com.zzang.chongdae.domain.model.ProductUrl +import com.zzang.chongdae.domain.repository.OfferingRepository +import javax.inject.Inject + +class PostProductImageOgUseCaseImpl +@Inject +constructor( + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, +) : PostProductImageOgUseCase { + override suspend fun invoke(productUrl: String): Result { + return when (val result = offeringRepository.saveProductImageOg(productUrl)) { + is Result.Success -> Result.Success(result.data) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(productUrl) + is Result.Error -> result + } + } + + else -> result + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt index 8fc752143..c6a7318e3 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt @@ -8,19 +8,16 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.map import androidx.lifecycle.viewModelScope import com.zzang.chongdae.R -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result -import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier -import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier +import com.zzang.chongdae.di.annotations.PostProductImageOgUseCaseQualifier import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier import com.zzang.chongdae.domain.model.Count import com.zzang.chongdae.domain.model.DiscountPrice import com.zzang.chongdae.domain.model.OfferingWrite import com.zzang.chongdae.domain.model.Price -import com.zzang.chongdae.domain.repository.OfferingRepository import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase +import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCase import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData @@ -33,393 +30,363 @@ import javax.inject.Inject @HiltViewModel class OfferingWriteViewModel - @Inject - constructor( - @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, - @UploadImageFileUseCaseQualifier val uploadImageFileUseCase: UploadImageFileUseCase, - @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, - @AuthRepositoryQualifier private val authRepository: AuthRepository, - ) : ViewModel() { - val title: MutableLiveData = MutableLiveData("") +@Inject +constructor( + @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, + @UploadImageFileUseCaseQualifier val uploadImageFileUseCase: UploadImageFileUseCase, + @PostProductImageOgUseCaseQualifier private val postProductImageOgUseCase: PostProductImageOgUseCase, +) : ViewModel() { + val title: MutableLiveData = MutableLiveData("") - val productUrl: MutableLiveData = MutableLiveData(null) + val productUrl: MutableLiveData = MutableLiveData(null) - val thumbnailUrl: MutableLiveData = MutableLiveData("") + val thumbnailUrl: MutableLiveData = MutableLiveData("") - val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } + val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } - val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") + val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") - val totalPrice: MutableLiveData = MutableLiveData("") + val totalPrice: MutableLiveData = MutableLiveData("") - val originPrice: MutableLiveData = MutableLiveData("") + val originPrice: MutableLiveData = MutableLiveData("") - val meetingAddress: MutableLiveData = MutableLiveData("") + val meetingAddress: MutableLiveData = MutableLiveData("") - val meetingAddressDetail: MutableLiveData = MutableLiveData("") + val meetingAddressDetail: MutableLiveData = MutableLiveData("") - val meetingDate: MutableLiveData = MutableLiveData("") + val meetingDate: MutableLiveData = MutableLiveData("") - private val meetingDateValue: MutableLiveData = MutableLiveData("") + private val meetingDateValue: MutableLiveData = MutableLiveData("") - val description: MutableLiveData = MutableLiveData("") + val description: MutableLiveData = MutableLiveData("") - val descriptionLength: LiveData - get() = description.map { it.length } + val descriptionLength: LiveData + get() = description.map { it.length } - private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled + private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled - private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val extractButtonEnabled: LiveData get() = _extractButtonEnabled + private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val extractButtonEnabled: LiveData get() = _extractButtonEnabled - private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) - val splitPrice: LiveData get() = _splitPrice + private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) + val splitPrice: LiveData get() = _splitPrice - private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) - val splitPriceValidity: LiveData - get() = _splitPrice.map { it >= 0 } + private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) + val splitPriceValidity: LiveData + get() = _splitPrice.map { it >= 0 } - val discountRateValidity: LiveData - get() = _discountRate.map { it >= 0 } + val discountRateValidity: LiveData + get() = _discountRate.map { it >= 0 } - val discountRate: LiveData get() = _discountRate + val discountRate: LiveData get() = _discountRate - private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() - val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent + private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() + val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent - private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() - val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent + private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() + val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent - private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() - val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent + private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() + val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent - private val _imageUploadEvent = MutableLiveData() - val imageUploadEvent: LiveData get() = _imageUploadEvent + private val _imageUploadEvent = MutableLiveData() + val imageUploadEvent: LiveData get() = _imageUploadEvent - private val _writeUIState = MutableLiveData(WriteUIState.Initial) - val writeUIState: LiveData get() = _writeUIState + private val _writeUIState = MutableLiveData(WriteUIState.Initial) + val writeUIState: LiveData get() = _writeUIState - val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } + val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } - private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) - val isSubmitLoading: LiveData get() = _isSubmitLoading + private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) + val isSubmitLoading: LiveData get() = _isSubmitLoading - init { - _essentialSubmitButtonEnabled.apply { - addSource(title) { updateSubmitButtonEnabled() } - addSource(totalCount) { updateSubmitButtonEnabled() } - addSource(totalPrice) { updateSubmitButtonEnabled() } - addSource(meetingAddress) { updateSubmitButtonEnabled() } - addSource(meetingDate) { updateSubmitButtonEnabled() } - } - - _splitPrice.apply { - addSource(totalCount) { safeUpdateSplitPrice() } - addSource(totalPrice) { safeUpdateSplitPrice() } - } - - _discountRate.apply { - addSource(_splitPrice) { safeUpdateDiscountRate() } - addSource(originPrice) { safeUpdateDiscountRate() } - } + init { + _essentialSubmitButtonEnabled.apply { + addSource(title) { updateSubmitButtonEnabled() } + addSource(totalCount) { updateSubmitButtonEnabled() } + addSource(totalPrice) { updateSubmitButtonEnabled() } + addSource(meetingAddress) { updateSubmitButtonEnabled() } + addSource(meetingDate) { updateSubmitButtonEnabled() } + } - _extractButtonEnabled.apply { - addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } - } + _splitPrice.apply { + addSource(totalCount) { safeUpdateSplitPrice() } + addSource(totalPrice) { safeUpdateSplitPrice() } } - private fun safeUpdateSplitPrice() { - runCatching { - updateSplitPrice() - }.onFailure { - _splitPrice.value = ERROR_INTEGER_FORMAT - } + _discountRate.apply { + addSource(_splitPrice) { safeUpdateDiscountRate() } + addSource(originPrice) { safeUpdateDiscountRate() } } - fun clearProductUrl() { - productUrl.value = null + _extractButtonEnabled.apply { + addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } } + } - fun onUploadPhotoClick() { - _imageUploadEvent.value = Unit + private fun safeUpdateSplitPrice() { + runCatching { + updateSplitPrice() + }.onFailure { + _splitPrice.value = ERROR_INTEGER_FORMAT } + } - fun uploadImageFile(multipartBody: MultipartBody.Part) { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = uploadImageFileUseCase.invoke(multipartBody)) { - is Result.Success -> { - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = result.data.imageUrl - } + fun clearProductUrl() { + productUrl.value = null + } - is Result.Error -> { - Log.e("error", "uploadImageFile: ${result.error}") - _writeUIState.value = WriteUIState.Error(R.string.all_error_image_upload, "${result.error}") -// when (result.error) { -// DataError.Network.UNAUTHORIZED -> { -// when (authRepository.saveRefresh()) { -// is Result.Success -> uploadImageFile(multipartBody) -// is Result.Error -> return@launch -// } -// } -// -// else -> { -// _writeUIState.value = -// WriteUIState.Error( -// R.string.all_error_image_upload, -// "${result.error}", -// ) -// } -// } - } + fun onUploadPhotoClick() { + _imageUploadEvent.value = Unit + } + + fun uploadImageFile(multipartBody: MultipartBody.Part) { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = uploadImageFileUseCase.invoke(multipartBody)) { + is Result.Success -> { + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = result.data.imageUrl + } + + is Result.Error -> { + Log.e("error", "uploadImageFile: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.all_error_image_upload, "${result.error}") } } } + } - fun postProductImageOg() { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = offeringRepository.saveProductImageOg(productUrl.value ?: "")) { - is Result.Success -> { - if (result.data.imageUrl.isBlank()) { - _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) - return@launch - } - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = HTTPS + result.data.imageUrl + fun postProductImageOg() { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = postProductImageOgUseCase.invoke(productUrl.value ?: "")) { + is Result.Success -> { + if (result.data.imageUrl.isBlank()) { + _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) + return@launch } + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = HTTPS + result.data.imageUrl + } - is Result.Error -> { - Log.e("error", "postProductImageOg: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> postProductImageOg() - is Result.Error -> return@launch - } - } - - else -> { - _writeUIState.value = - WriteUIState.Error( - R.string.error_invalid_product_url, - "${result.error}", - ) - } - } - } + is Result.Error -> { + Log.e("error", "postProductImageOg: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.error_invalid_product_url, "${result.error}") } } } + } - fun clearProductImage() { - thumbnailUrl.value = null - } + fun clearProductImage() { + thumbnailUrl.value = null + } - private fun safeUpdateDiscountRate() { - runCatching { - updateDiscountRate() - }.onFailure { - _discountRate.value = ERROR_FLOAT_FORMAT - } + private fun safeUpdateDiscountRate() { + runCatching { + updateDiscountRate() + }.onFailure { + _discountRate.value = ERROR_FLOAT_FORMAT } + } - private fun updateSubmitButtonEnabled() { - _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && + private fun updateSubmitButtonEnabled() { + _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && !totalCount.value.isNullOrBlank() && !totalPrice.value.isNullOrBlank() && !meetingAddress.value.isNullOrBlank() && !meetingDate.value.isNullOrBlank() - } + } - private fun updateSplitPrice() { - val totalPrice = Price.fromString(totalPrice.value) - val totalCount = Count.fromString(totalCount.value) - _splitPrice.value = totalPrice.amount / totalCount.number - } + private fun updateSplitPrice() { + val totalPrice = Price.fromString(totalPrice.value) + val totalCount = Count.fromString(totalCount.value) + _splitPrice.value = totalPrice.amount / totalCount.number + } - private fun updateDiscountRate() { - val originPrice = Price.fromString(originPrice.value) - val splitPrice = Price.fromInteger(_splitPrice.value) - val discountPriceValue = originPrice.amount - splitPrice.amount - val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) - _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 - } + private fun updateDiscountRate() { + val originPrice = Price.fromString(originPrice.value) + val splitPrice = Price.fromInteger(_splitPrice.value) + val discountPriceValue = originPrice.amount - splitPrice.amount + val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) + _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 + } - fun increaseTotalCount() { - val totalCount = Count.fromString(totalCount.value).increase() - this.totalCount.value = totalCount.number.toString() - } + fun increaseTotalCount() { + val totalCount = Count.fromString(totalCount.value).increase() + this.totalCount.value = totalCount.number.toString() + } - fun decreaseTotalCount() { - if (Count.fromString(totalCount.value).number < 0) { - this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() - return - } - val totalCount = Count.fromString(totalCount.value).decrease() - this.totalCount.value = totalCount.number.toString() + fun decreaseTotalCount() { + if (Count.fromString(totalCount.value).number < 0) { + this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() + return } + val totalCount = Count.fromString(totalCount.value).decrease() + this.totalCount.value = totalCount.number.toString() + } - fun makeMeetingDateChoiceEvent() { - _meetingDateChoiceEvent.setValue(true) - } + fun makeMeetingDateChoiceEvent() { + _meetingDateChoiceEvent.setValue(true) + } - fun updateMeetingDate(date: String) { - val dateTime = "$date" - val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) - val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) + fun updateMeetingDate(date: String) { + val dateTime = "$date" + val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) + val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) - val parsedDateTime = inputFormat.parse(dateTime) - meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } - meetingDate.value = dateTime - } + val parsedDateTime = inputFormat.parse(dateTime) + meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } + meetingDate.value = dateTime + } - fun postOffering() { - _isSubmitLoading.value = true - val title = title.value ?: return - val totalCount = totalCount.value ?: return - val totalPrice = totalPrice.value ?: return - val meetingAddress = meetingAddress.value ?: return - val meetingAddressDetail = meetingAddressDetail.value ?: return - val meetingDate = meetingDateValue.value ?: return - val description = description.value ?: return - - val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return - val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return - val meetingAddressDong = extractDong(meetingAddress) - - var originPriceNotBlank: Int? = 0 - runCatching { - originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) - }.onFailure { - makeOriginPriceInvalidEvent() - return - } - if (isOriginPriceCheaperThanSplitPriceEvent()) return - - viewModelScope.launch { - when ( - val result = - postOfferingUseCase( - OfferingWrite( - title = title, - productUrl = productUrlOrNull(), - thumbnailUrl = thumbnailUrl.value, - totalCount = totalCountConverted, - totalPrice = totalPriceConverted, - originPrice = originPriceNotBlank, - meetingAddress = meetingAddress, - meetingAddressDong = meetingAddressDong, - meetingAddressDetail = meetingAddressDetail, - meetingDate = meetingDate, - description = description, - ), - ) - ) { - is Result.Success -> { - makeSubmitOfferingEvent() - _isSubmitLoading.value = false - } + fun postOffering() { + _isSubmitLoading.value = true + val title = title.value ?: return + val totalCount = totalCount.value ?: return + val totalPrice = totalPrice.value ?: return + val meetingAddress = meetingAddress.value ?: return + val meetingAddressDetail = meetingAddressDetail.value ?: return + val meetingDate = meetingDateValue.value ?: return + val description = description.value ?: return + + val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return + val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return + val meetingAddressDong = extractDong(meetingAddress) + + var originPriceNotBlank: Int? = 0 + runCatching { + originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) + }.onFailure { + makeOriginPriceInvalidEvent() + return + } + if (isOriginPriceCheaperThanSplitPriceEvent()) return + + viewModelScope.launch { + when ( + val result = + postOfferingUseCase( + OfferingWrite( + title = title, + productUrl = productUrlOrNull(), + thumbnailUrl = thumbnailUrl.value, + totalCount = totalCountConverted, + totalPrice = totalPriceConverted, + originPrice = originPriceNotBlank, + meetingAddress = meetingAddress, + meetingAddressDong = meetingAddressDong, + meetingAddressDetail = meetingAddressDetail, + meetingDate = meetingDate, + description = description, + ), + ) + ) { + is Result.Success -> { + makeSubmitOfferingEvent() + _isSubmitLoading.value = false + } - is Result.Error -> { - Log.e("error", "postOffering: ${result.error}") - _writeUIState.value = - WriteUIState.Error(R.string.write_error_writing, "${result.error}") - _isSubmitLoading.value = false - } + is Result.Error -> { + Log.e("error", "postOffering: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.write_error_writing, "${result.error}") + _isSubmitLoading.value = false } } } + } - private fun productUrlOrNull(): String? { - val productUrl = productUrl.value - if (productUrl == "") return null - return productUrl - } + private fun productUrlOrNull(): String? { + val productUrl = productUrl.value + if (productUrl == "") return null + return productUrl + } - private fun originPriceToPositiveIntOrNull(input: String?): Int? { - val originPriceInputTrim = input?.trim() - if (originPriceInputTrim.isNullOrBlank()) { - return null - } - if (originPriceInputTrim.toInt() < 0) { - throw NumberFormatException() - } - return originPriceInputTrim.toInt() + private fun originPriceToPositiveIntOrNull(input: String?): Int? { + val originPriceInputTrim = input?.trim() + if (originPriceInputTrim.isNullOrBlank()) { + return null } - - private fun extractDong(address: String): String? { - val regex = """\((.*?)\)""".toRegex() - val matchResult = regex.find(address) - val content = matchResult?.groups?.get(1)?.value - return content?.split(",")?.get(0)?.trim() + if (originPriceInputTrim.toInt() < 0) { + throw NumberFormatException() } + return originPriceInputTrim.toInt() + } - private fun makeTotalCountInvalidEvent(totalCount: String): Int? { - val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) - return null - } - return totalCountValue - } + private fun extractDong(address: String): String? { + val regex = """\((.*?)\)""".toRegex() + val matchResult = regex.find(address) + val content = matchResult?.groups?.get(1)?.value + return content?.split(",")?.get(0)?.trim() + } - private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { - val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalPriceConverted < 0) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) - return null - } - return totalPriceConverted + private fun makeTotalCountInvalidEvent(totalCount: String): Int? { + val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) + return null } + return totalCountValue + } - private fun makeOriginPriceInvalidEvent() { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) + private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { + val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalPriceConverted < 0) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) + return null } + return totalPriceConverted + } - private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { - if (originPrice.value.isNullOrBlank()) return false - val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT - if (discountRateValue <= 0f) { - _writeUIState.value = - WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) - return true - } - return false - } + private fun makeOriginPriceInvalidEvent() { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) + } - fun makeNavigateToOptionalEvent() { - _navigateToOptionalEvent.setValue(true) + private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { + if (originPrice.value.isNullOrBlank()) return false + val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT + if (discountRateValue <= 0f) { + _writeUIState.value = + WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) + return true } + return false + } - private fun makeSubmitOfferingEvent() { - _submitOfferingEvent.setValue(Unit) - } + fun makeNavigateToOptionalEvent() { + _navigateToOptionalEvent.setValue(true) + } - fun initOfferingWriteInputs() { - title.value = "" - productUrl.value = "" - thumbnailUrl.value = "" - totalCount.value = "$MINIMUM_TOTAL_COUNT" - totalPrice.value = "" - originPrice.value = "" - meetingAddress.value = "" - meetingAddressDetail.value = "" - meetingDate.value = "" - meetingDateValue.value = "" - description.value = "" - } + private fun makeSubmitOfferingEvent() { + _submitOfferingEvent.setValue(Unit) + } - companion object { - private const val ERROR_INTEGER_FORMAT = -1 - private const val ERROR_FLOAT_FORMAT = -1f - private const val MINIMUM_TOTAL_COUNT = 2 - private const val MAXIMUM_TOTAL_COUNT = 10_000 - private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" - private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" - private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" - const val HTTPS = "https:" - } + fun initOfferingWriteInputs() { + title.value = "" + productUrl.value = "" + thumbnailUrl.value = "" + totalCount.value = "$MINIMUM_TOTAL_COUNT" + totalPrice.value = "" + originPrice.value = "" + meetingAddress.value = "" + meetingAddressDetail.value = "" + meetingDate.value = "" + meetingDateValue.value = "" + description.value = "" + } + + companion object { + private const val ERROR_INTEGER_FORMAT = -1 + private const val ERROR_FLOAT_FORMAT = -1f + private const val MINIMUM_TOTAL_COUNT = 2 + private const val MAXIMUM_TOTAL_COUNT = 10_000 + private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" + private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" + private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" + const val HTTPS = "https:" } +} From 8d3838e151ba6b67ca6b14e9b449b676b75ed696 Mon Sep 17 00:00:00 2001 From: songpink Date: Sun, 15 Dec 2024 20:25:02 +0900 Subject: [PATCH 10/16] =?UTF-8?q?refactor:=20=EA=B3=B5=EB=AA=A8=EA=B8=80?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EC=9C=A0?= =?UTF-8?q?=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/annotations/UseCaseQualifier.kt | 8 + .../di/module/UseCaseDependencyModule.kt | 16 + .../FetchOfferingDetailUseCase.kt | 9 + .../FetchOfferingDetailUseCaseImpl.kt | 35 ++ .../PostOfferingModifyUseCase.kt | 12 + .../PostOfferingModifyUseCaseImpl.kt | 41 ++ .../write/PostProductImageOgUseCase.kt | 4 +- .../write/PostProductImageOgUseCaseImpl.kt | 34 +- .../offeringmodify/OfferingModifyViewModel.kt | 98 +-- .../view/write/OfferingWriteViewModel.kt | 556 +++++++++--------- 10 files changed, 440 insertions(+), 373 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCaseImpl.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt index 25047580f..fb3578e34 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -21,3 +21,11 @@ annotation class UploadImageFileUseCaseQualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PostProductImageOgUseCaseQualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class FetchOfferingDetailUseCaseQualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class PostOfferingModifyUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index 1e6d5ec80..723ba1209 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -1,7 +1,9 @@ package com.zzang.chongdae.di.module import com.zzang.chongdae.di.annotations.CheckAlreadyLoggedInUseCaseQualifier +import com.zzang.chongdae.di.annotations.FetchOfferingDetailUseCaseQualifier import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier +import com.zzang.chongdae.di.annotations.PostOfferingModifyUseCaseQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier import com.zzang.chongdae.di.annotations.PostProductImageOgUseCaseQualifier import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier @@ -9,6 +11,10 @@ import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCaseImpl import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase import com.zzang.chongdae.domain.usecase.login.PostLoginUseCaseImpl +import com.zzang.chongdae.domain.usecase.offeringmodify.FetchOfferingDetailUseCase +import com.zzang.chongdae.domain.usecase.offeringmodify.FetchOfferingDetailUseCaseImpl +import com.zzang.chongdae.domain.usecase.offeringmodify.PostOfferingModifyUseCase +import com.zzang.chongdae.domain.usecase.offeringmodify.PostOfferingModifyUseCaseImpl import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCaseImpl import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCase @@ -48,4 +54,14 @@ abstract class UseCaseDependencyModule { @Singleton @PostProductImageOgUseCaseQualifier abstract fun providePostProductImageOgUseCase(impl: PostProductImageOgUseCaseImpl): PostProductImageOgUseCase + + @Binds + @Singleton + @FetchOfferingDetailUseCaseQualifier + abstract fun provideFetchOfferingDetailUseCase(impl: FetchOfferingDetailUseCaseImpl): FetchOfferingDetailUseCase + + @Binds + @Singleton + @PostOfferingModifyUseCaseQualifier + abstract fun providePostOfferingModifyUseCase(impl: PostOfferingModifyUseCaseImpl): PostOfferingModifyUseCase } diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCase.kt new file mode 100644 index 000000000..41a63a395 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCase.kt @@ -0,0 +1,9 @@ +package com.zzang.chongdae.domain.usecase.offeringmodify + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.model.OfferingDetail + +interface FetchOfferingDetailUseCase { + suspend operator fun invoke(offeringId: Long): Result +} diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCaseImpl.kt new file mode 100644 index 000000000..41189ab69 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/FetchOfferingDetailUseCaseImpl.kt @@ -0,0 +1,35 @@ +package com.zzang.chongdae.domain.usecase.offeringmodify + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.OfferingDetailRepositoryQualifier +import com.zzang.chongdae.domain.model.OfferingDetail +import com.zzang.chongdae.domain.repository.OfferingDetailRepository +import javax.inject.Inject + +class FetchOfferingDetailUseCaseImpl + @Inject + constructor( + @OfferingDetailRepositoryQualifier private val offeringDetailRepository: OfferingDetailRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, + ) : FetchOfferingDetailUseCase { + override suspend fun invoke(offeringId: Long): Result { + return when (val result = offeringDetailRepository.fetchOfferingDetail(offeringId)) { + is Result.Success -> Result.Success(result.data) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(offeringId) + is Result.Error -> result + } + } + + else -> result + } + } + } + } + } diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCase.kt new file mode 100644 index 000000000..dd8185b55 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCase.kt @@ -0,0 +1,12 @@ +package com.zzang.chongdae.domain.usecase.offeringmodify + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.model.OfferingModifyDomainRequest + +interface PostOfferingModifyUseCase { + suspend operator fun invoke( + offeringId: Long, + offeringModifyDomainRequest: OfferingModifyDomainRequest, + ): Result +} diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCaseImpl.kt new file mode 100644 index 000000000..fdf03a275 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/offeringmodify/PostOfferingModifyUseCaseImpl.kt @@ -0,0 +1,41 @@ +package com.zzang.chongdae.domain.usecase.offeringmodify + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier +import com.zzang.chongdae.domain.model.OfferingModifyDomainRequest +import com.zzang.chongdae.domain.repository.OfferingRepository +import javax.inject.Inject + +class PostOfferingModifyUseCaseImpl + @Inject + constructor( + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, + ) : PostOfferingModifyUseCase { + override suspend fun invoke( + offeringId: Long, + offeringModifyDomainRequest: OfferingModifyDomainRequest, + ): Result { + return when ( + val result = + offeringRepository.patchOffering(offeringId, offeringModifyDomainRequest) + ) { + is Result.Success -> Result.Success(Unit) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(offeringId, offeringModifyDomainRequest) + is Result.Error -> result + } + } + + else -> result + } + } + } + } + } diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt index 220a1d0f4..384d0d410 100644 --- a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCase.kt @@ -1,9 +1,9 @@ package com.zzang.chongdae.domain.usecase.write import com.zzang.chongdae.common.handler.DataError -import com.zzang.chongdae.domain.model.ProductUrl import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.model.ProductUrl interface PostProductImageOgUseCase { suspend operator fun invoke(productUrl: String): Result -} \ No newline at end of file +} diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt index 841d4445d..399a95f41 100644 --- a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/write/PostProductImageOgUseCaseImpl.kt @@ -10,26 +10,26 @@ import com.zzang.chongdae.domain.repository.OfferingRepository import javax.inject.Inject class PostProductImageOgUseCaseImpl -@Inject -constructor( - @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, - @AuthRepositoryQualifier private val authRepository: AuthRepository, -) : PostProductImageOgUseCase { - override suspend fun invoke(productUrl: String): Result { - return when (val result = offeringRepository.saveProductImageOg(productUrl)) { - is Result.Success -> Result.Success(result.data) - is Result.Error -> { - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> invoke(productUrl) - is Result.Error -> result + @Inject + constructor( + @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, + @AuthRepositoryQualifier private val authRepository: AuthRepository, + ) : PostProductImageOgUseCase { + override suspend fun invoke(productUrl: String): Result { + return when (val result = offeringRepository.saveProductImageOg(productUrl)) { + is Result.Success -> Result.Success(result.data) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke(productUrl) + is Result.Error -> result + } } - } - else -> result + else -> result + } } } } } -} \ No newline at end of file diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/offeringmodify/OfferingModifyViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/offeringmodify/OfferingModifyViewModel.kt index 62465d018..7b38d5091 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/offeringmodify/OfferingModifyViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/offeringmodify/OfferingModifyViewModel.kt @@ -8,19 +8,20 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.map import androidx.lifecycle.viewModelScope import com.zzang.chongdae.R -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result -import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier -import com.zzang.chongdae.di.annotations.OfferingDetailRepositoryQualifier -import com.zzang.chongdae.di.annotations.OfferingRepositoryQualifier +import com.zzang.chongdae.di.annotations.FetchOfferingDetailUseCaseQualifier +import com.zzang.chongdae.di.annotations.PostOfferingModifyUseCaseQualifier +import com.zzang.chongdae.di.annotations.PostProductImageOgUseCaseQualifier +import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier import com.zzang.chongdae.domain.model.Count import com.zzang.chongdae.domain.model.DiscountPrice import com.zzang.chongdae.domain.model.OfferingDetail import com.zzang.chongdae.domain.model.OfferingModifyDomainRequest import com.zzang.chongdae.domain.model.Price -import com.zzang.chongdae.domain.repository.OfferingDetailRepository -import com.zzang.chongdae.domain.repository.OfferingRepository +import com.zzang.chongdae.domain.usecase.offeringmodify.FetchOfferingDetailUseCase +import com.zzang.chongdae.domain.usecase.offeringmodify.PostOfferingModifyUseCase +import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCase +import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase import com.zzang.chongdae.presentation.util.MutableSingleLiveData import com.zzang.chongdae.presentation.util.SingleLiveData import dagger.hilt.android.lifecycle.HiltViewModel @@ -36,9 +37,10 @@ import javax.inject.Inject class OfferingModifyViewModel @Inject constructor( - @OfferingRepositoryQualifier private val offeringRepository: OfferingRepository, - @OfferingDetailRepositoryQualifier private val offeringDetailRepository: OfferingDetailRepository, - @AuthRepositoryQualifier private val authRepository: AuthRepository, + @UploadImageFileUseCaseQualifier private val uploadImageFileUseCase: UploadImageFileUseCase, + @PostProductImageOgUseCaseQualifier private val postProductImageOgUseCase: PostProductImageOgUseCase, + @FetchOfferingDetailUseCaseQualifier private val fetchOfferingDetailUseCase: FetchOfferingDetailUseCase, + @PostOfferingModifyUseCaseQualifier private val postOfferingModifyUseCase: PostOfferingModifyUseCase, ) : ViewModel() { private var offeringId: Long = DEFAULT_OFFERING_ID @@ -105,7 +107,6 @@ class OfferingModifyViewModel val isLoading: LiveData = _modifyUIState.map { it is ModifyUIState.Loading } init { - _essentialSubmitButtonEnabled.apply { addSource(title) { updateSubmitButtonEnabled() } addSource(totalCount) { updateSubmitButtonEnabled() } @@ -152,7 +153,7 @@ class OfferingModifyViewModel fun uploadImageFile(multipartBody: MultipartBody.Part) { viewModelScope.launch { _modifyUIState.value = ModifyUIState.Loading - when (val result = offeringRepository.saveProductImageS3(multipartBody)) { + when (val result = uploadImageFileUseCase.invoke(multipartBody)) { is Result.Success -> { _modifyUIState.value = ModifyUIState.Success(result.data.imageUrl) thumbnailUrl.value = result.data.imageUrl @@ -160,22 +161,8 @@ class OfferingModifyViewModel is Result.Error -> { Log.e("error", "uploadImageFile: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> uploadImageFile(multipartBody) - is Result.Error -> return@launch - } - } - - else -> { - _modifyUIState.value = - ModifyUIState.Error( - R.string.all_error_image_upload, - "${result.error}", - ) - } - } + _modifyUIState.value = + ModifyUIState.Error(R.string.all_error_image_upload, "${result.error}") } } } @@ -184,7 +171,7 @@ class OfferingModifyViewModel fun postProductImageOg() { viewModelScope.launch { _modifyUIState.value = ModifyUIState.Loading - when (val result = offeringRepository.saveProductImageOg(productUrl.value ?: "")) { + when (val result = postProductImageOgUseCase.invoke(productUrl.value ?: "")) { is Result.Success -> { if (result.data.imageUrl.isBlank()) { _modifyUIState.value = ModifyUIState.Empty(R.string.error_empty_product_url) @@ -196,22 +183,8 @@ class OfferingModifyViewModel is Result.Error -> { Log.e("error", "postProductImageOg: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> postProductImageOg() - is Result.Error -> return@launch - } - } - - else -> { - _modifyUIState.value = - ModifyUIState.Error( - R.string.error_invalid_product_url, - "${result.error}", - ) - } - } + _modifyUIState.value = + ModifyUIState.Error(R.string.error_invalid_product_url, "${result.error}") } } } @@ -290,24 +263,12 @@ class OfferingModifyViewModel fun fetchOfferingDetail() { viewModelScope.launch { - when (val result = offeringDetailRepository.fetchOfferingDetail(offeringId)) { + when (val result = fetchOfferingDetailUseCase(offeringId)) { is Result.Success -> { initExistingOffering(result.data) } - is Result.Error -> - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> fetchOfferingDetail() - is Result.Error -> return@launch - } - } - - else -> { - Log.e("error", "loadOffering Error: ${result.error.name}") - } - } + is Result.Error -> Log.e("error", "loadOffering Error: ${result.error.name}") } } } @@ -350,7 +311,7 @@ class OfferingModifyViewModel viewModelScope.launch { when ( val result = - offeringRepository.patchOffering( + postOfferingModifyUseCase( offeringId = offeringId, offeringModifyDomainRequest = OfferingModifyDomainRequest( @@ -374,22 +335,7 @@ class OfferingModifyViewModel is Result.Error -> { Log.e("error", "postOffering: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> postOfferingModify() - is Result.Error -> return@launch - } - } - - else -> { - _modifyUIState.value = - ModifyUIState.Error( - R.string.modify_error_modifing, - "${result.error}", - ) - } - } + _modifyUIState.value = ModifyUIState.Error(R.string.modify_error_modifing, "${result.error}") } } } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt index c6a7318e3..c7e2b2ff9 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModel.kt @@ -30,363 +30,363 @@ import javax.inject.Inject @HiltViewModel class OfferingWriteViewModel -@Inject -constructor( - @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, - @UploadImageFileUseCaseQualifier val uploadImageFileUseCase: UploadImageFileUseCase, - @PostProductImageOgUseCaseQualifier private val postProductImageOgUseCase: PostProductImageOgUseCase, -) : ViewModel() { - val title: MutableLiveData = MutableLiveData("") + @Inject + constructor( + @PostOfferingUseCaseQualifier private val postOfferingUseCase: PostOfferingUseCase, + @UploadImageFileUseCaseQualifier val uploadImageFileUseCase: UploadImageFileUseCase, + @PostProductImageOgUseCaseQualifier private val postProductImageOgUseCase: PostProductImageOgUseCase, + ) : ViewModel() { + val title: MutableLiveData = MutableLiveData("") - val productUrl: MutableLiveData = MutableLiveData(null) + val productUrl: MutableLiveData = MutableLiveData(null) - val thumbnailUrl: MutableLiveData = MutableLiveData("") + val thumbnailUrl: MutableLiveData = MutableLiveData("") - val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } + val deleteImageVisible: LiveData = thumbnailUrl.map { !it.isNullOrBlank() } - val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") + val totalCount: MutableLiveData = MutableLiveData("$MINIMUM_TOTAL_COUNT") - val totalPrice: MutableLiveData = MutableLiveData("") + val totalPrice: MutableLiveData = MutableLiveData("") - val originPrice: MutableLiveData = MutableLiveData("") + val originPrice: MutableLiveData = MutableLiveData("") - val meetingAddress: MutableLiveData = MutableLiveData("") + val meetingAddress: MutableLiveData = MutableLiveData("") - val meetingAddressDetail: MutableLiveData = MutableLiveData("") + val meetingAddressDetail: MutableLiveData = MutableLiveData("") - val meetingDate: MutableLiveData = MutableLiveData("") + val meetingDate: MutableLiveData = MutableLiveData("") - private val meetingDateValue: MutableLiveData = MutableLiveData("") + private val meetingDateValue: MutableLiveData = MutableLiveData("") - val description: MutableLiveData = MutableLiveData("") + val description: MutableLiveData = MutableLiveData("") - val descriptionLength: LiveData - get() = description.map { it.length } + val descriptionLength: LiveData + get() = description.map { it.length } - private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled + private val _essentialSubmitButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val essentialSubmitButtonEnabled: LiveData get() = _essentialSubmitButtonEnabled - private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) - val extractButtonEnabled: LiveData get() = _extractButtonEnabled + private val _extractButtonEnabled: MediatorLiveData = MediatorLiveData(false) + val extractButtonEnabled: LiveData get() = _extractButtonEnabled - private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) - val splitPrice: LiveData get() = _splitPrice + private val _splitPrice: MediatorLiveData = MediatorLiveData(ERROR_INTEGER_FORMAT) + val splitPrice: LiveData get() = _splitPrice - private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) - val splitPriceValidity: LiveData - get() = _splitPrice.map { it >= 0 } + private val _discountRate: MediatorLiveData = MediatorLiveData(ERROR_FLOAT_FORMAT) + val splitPriceValidity: LiveData + get() = _splitPrice.map { it >= 0 } - val discountRateValidity: LiveData - get() = _discountRate.map { it >= 0 } + val discountRateValidity: LiveData + get() = _discountRate.map { it >= 0 } - val discountRate: LiveData get() = _discountRate + val discountRate: LiveData get() = _discountRate - private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() - val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent + private val _meetingDateChoiceEvent: MutableSingleLiveData = MutableSingleLiveData() + val meetingDateChoiceEvent: SingleLiveData get() = _meetingDateChoiceEvent - private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() - val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent + private val _navigateToOptionalEvent: MutableSingleLiveData = MutableSingleLiveData() + val navigateToOptionalEvent: SingleLiveData get() = _navigateToOptionalEvent - private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() - val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent + private val _submitOfferingEvent: MutableSingleLiveData = MutableSingleLiveData() + val submitOfferingEvent: SingleLiveData get() = _submitOfferingEvent - private val _imageUploadEvent = MutableLiveData() - val imageUploadEvent: LiveData get() = _imageUploadEvent + private val _imageUploadEvent = MutableLiveData() + val imageUploadEvent: LiveData get() = _imageUploadEvent - private val _writeUIState = MutableLiveData(WriteUIState.Initial) - val writeUIState: LiveData get() = _writeUIState + private val _writeUIState = MutableLiveData(WriteUIState.Initial) + val writeUIState: LiveData get() = _writeUIState - val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } + val isImageUpLoading: LiveData = _writeUIState.map { it is WriteUIState.Loading } - private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) - val isSubmitLoading: LiveData get() = _isSubmitLoading + private val _isSubmitLoading: MutableLiveData = MutableLiveData(false) + val isSubmitLoading: LiveData get() = _isSubmitLoading - init { - _essentialSubmitButtonEnabled.apply { - addSource(title) { updateSubmitButtonEnabled() } - addSource(totalCount) { updateSubmitButtonEnabled() } - addSource(totalPrice) { updateSubmitButtonEnabled() } - addSource(meetingAddress) { updateSubmitButtonEnabled() } - addSource(meetingDate) { updateSubmitButtonEnabled() } - } + init { + _essentialSubmitButtonEnabled.apply { + addSource(title) { updateSubmitButtonEnabled() } + addSource(totalCount) { updateSubmitButtonEnabled() } + addSource(totalPrice) { updateSubmitButtonEnabled() } + addSource(meetingAddress) { updateSubmitButtonEnabled() } + addSource(meetingDate) { updateSubmitButtonEnabled() } + } - _splitPrice.apply { - addSource(totalCount) { safeUpdateSplitPrice() } - addSource(totalPrice) { safeUpdateSplitPrice() } - } + _splitPrice.apply { + addSource(totalCount) { safeUpdateSplitPrice() } + addSource(totalPrice) { safeUpdateSplitPrice() } + } - _discountRate.apply { - addSource(_splitPrice) { safeUpdateDiscountRate() } - addSource(originPrice) { safeUpdateDiscountRate() } - } + _discountRate.apply { + addSource(_splitPrice) { safeUpdateDiscountRate() } + addSource(originPrice) { safeUpdateDiscountRate() } + } - _extractButtonEnabled.apply { - addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } + _extractButtonEnabled.apply { + addSource(productUrl) { value = !productUrl.value.isNullOrBlank() } + } } - } - private fun safeUpdateSplitPrice() { - runCatching { - updateSplitPrice() - }.onFailure { - _splitPrice.value = ERROR_INTEGER_FORMAT + private fun safeUpdateSplitPrice() { + runCatching { + updateSplitPrice() + }.onFailure { + _splitPrice.value = ERROR_INTEGER_FORMAT + } } - } - fun clearProductUrl() { - productUrl.value = null - } + fun clearProductUrl() { + productUrl.value = null + } - fun onUploadPhotoClick() { - _imageUploadEvent.value = Unit - } + fun onUploadPhotoClick() { + _imageUploadEvent.value = Unit + } - fun uploadImageFile(multipartBody: MultipartBody.Part) { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = uploadImageFileUseCase.invoke(multipartBody)) { - is Result.Success -> { - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = result.data.imageUrl - } + fun uploadImageFile(multipartBody: MultipartBody.Part) { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = uploadImageFileUseCase.invoke(multipartBody)) { + is Result.Success -> { + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = result.data.imageUrl + } - is Result.Error -> { - Log.e("error", "uploadImageFile: ${result.error}") - _writeUIState.value = - WriteUIState.Error(R.string.all_error_image_upload, "${result.error}") + is Result.Error -> { + Log.e("error", "uploadImageFile: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.all_error_image_upload, "${result.error}") + } } } } - } - fun postProductImageOg() { - viewModelScope.launch { - _writeUIState.value = WriteUIState.Loading - when (val result = postProductImageOgUseCase.invoke(productUrl.value ?: "")) { - is Result.Success -> { - if (result.data.imageUrl.isBlank()) { - _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) - return@launch + fun postProductImageOg() { + viewModelScope.launch { + _writeUIState.value = WriteUIState.Loading + when (val result = postProductImageOgUseCase.invoke(productUrl.value ?: "")) { + is Result.Success -> { + if (result.data.imageUrl.isBlank()) { + _writeUIState.value = WriteUIState.Empty(R.string.error_empty_product_url) + return@launch + } + _writeUIState.value = WriteUIState.Success(result.data.imageUrl) + thumbnailUrl.value = HTTPS + result.data.imageUrl } - _writeUIState.value = WriteUIState.Success(result.data.imageUrl) - thumbnailUrl.value = HTTPS + result.data.imageUrl - } - is Result.Error -> { - Log.e("error", "postProductImageOg: ${result.error}") - _writeUIState.value = - WriteUIState.Error(R.string.error_invalid_product_url, "${result.error}") + is Result.Error -> { + Log.e("error", "postProductImageOg: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.error_invalid_product_url, "${result.error}") + } } } } - } - fun clearProductImage() { - thumbnailUrl.value = null - } + fun clearProductImage() { + thumbnailUrl.value = null + } - private fun safeUpdateDiscountRate() { - runCatching { - updateDiscountRate() - }.onFailure { - _discountRate.value = ERROR_FLOAT_FORMAT + private fun safeUpdateDiscountRate() { + runCatching { + updateDiscountRate() + }.onFailure { + _discountRate.value = ERROR_FLOAT_FORMAT + } } - } - private fun updateSubmitButtonEnabled() { - _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && + private fun updateSubmitButtonEnabled() { + _essentialSubmitButtonEnabled.value = !title.value.isNullOrBlank() && !totalCount.value.isNullOrBlank() && !totalPrice.value.isNullOrBlank() && !meetingAddress.value.isNullOrBlank() && !meetingDate.value.isNullOrBlank() - } - - private fun updateSplitPrice() { - val totalPrice = Price.fromString(totalPrice.value) - val totalCount = Count.fromString(totalCount.value) - _splitPrice.value = totalPrice.amount / totalCount.number - } + } - private fun updateDiscountRate() { - val originPrice = Price.fromString(originPrice.value) - val splitPrice = Price.fromInteger(_splitPrice.value) - val discountPriceValue = originPrice.amount - splitPrice.amount - val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) - _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 - } + private fun updateSplitPrice() { + val totalPrice = Price.fromString(totalPrice.value) + val totalCount = Count.fromString(totalCount.value) + _splitPrice.value = totalPrice.amount / totalCount.number + } - fun increaseTotalCount() { - val totalCount = Count.fromString(totalCount.value).increase() - this.totalCount.value = totalCount.number.toString() - } + private fun updateDiscountRate() { + val originPrice = Price.fromString(originPrice.value) + val splitPrice = Price.fromInteger(_splitPrice.value) + val discountPriceValue = originPrice.amount - splitPrice.amount + val discountPrice = DiscountPrice.fromFloat(discountPriceValue.toFloat()) + _discountRate.value = (discountPrice.amount / originPrice.amount) * 100 + } - fun decreaseTotalCount() { - if (Count.fromString(totalCount.value).number < 0) { - this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() - return + fun increaseTotalCount() { + val totalCount = Count.fromString(totalCount.value).increase() + this.totalCount.value = totalCount.number.toString() } - val totalCount = Count.fromString(totalCount.value).decrease() - this.totalCount.value = totalCount.number.toString() - } - fun makeMeetingDateChoiceEvent() { - _meetingDateChoiceEvent.setValue(true) - } + fun decreaseTotalCount() { + if (Count.fromString(totalCount.value).number < 0) { + this.totalCount.value = MINIMUM_TOTAL_COUNT.toString() + return + } + val totalCount = Count.fromString(totalCount.value).decrease() + this.totalCount.value = totalCount.number.toString() + } - fun updateMeetingDate(date: String) { - val dateTime = "$date" - val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) - val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) + fun makeMeetingDateChoiceEvent() { + _meetingDateChoiceEvent.setValue(true) + } - val parsedDateTime = inputFormat.parse(dateTime) - meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } - meetingDate.value = dateTime - } + fun updateMeetingDate(date: String) { + val dateTime = "$date" + val inputFormat = SimpleDateFormat(INPUT_DATE_FORMAT, Locale.KOREAN) + val outputFormat = SimpleDateFormat(OUTPUT_DATE_TIME_FORMAT, Locale.getDefault()) - fun postOffering() { - _isSubmitLoading.value = true - val title = title.value ?: return - val totalCount = totalCount.value ?: return - val totalPrice = totalPrice.value ?: return - val meetingAddress = meetingAddress.value ?: return - val meetingAddressDetail = meetingAddressDetail.value ?: return - val meetingDate = meetingDateValue.value ?: return - val description = description.value ?: return - - val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return - val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return - val meetingAddressDong = extractDong(meetingAddress) - - var originPriceNotBlank: Int? = 0 - runCatching { - originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) - }.onFailure { - makeOriginPriceInvalidEvent() - return + val parsedDateTime = inputFormat.parse(dateTime) + meetingDateValue.value = parsedDateTime?.let { outputFormat.format(it) } + meetingDate.value = dateTime } - if (isOriginPriceCheaperThanSplitPriceEvent()) return - - viewModelScope.launch { - when ( - val result = - postOfferingUseCase( - OfferingWrite( - title = title, - productUrl = productUrlOrNull(), - thumbnailUrl = thumbnailUrl.value, - totalCount = totalCountConverted, - totalPrice = totalPriceConverted, - originPrice = originPriceNotBlank, - meetingAddress = meetingAddress, - meetingAddressDong = meetingAddressDong, - meetingAddressDetail = meetingAddressDetail, - meetingDate = meetingDate, - description = description, - ), - ) - ) { - is Result.Success -> { - makeSubmitOfferingEvent() - _isSubmitLoading.value = false - } - is Result.Error -> { - Log.e("error", "postOffering: ${result.error}") - _writeUIState.value = - WriteUIState.Error(R.string.write_error_writing, "${result.error}") - _isSubmitLoading.value = false + fun postOffering() { + _isSubmitLoading.value = true + val title = title.value ?: return + val totalCount = totalCount.value ?: return + val totalPrice = totalPrice.value ?: return + val meetingAddress = meetingAddress.value ?: return + val meetingAddressDetail = meetingAddressDetail.value ?: return + val meetingDate = meetingDateValue.value ?: return + val description = description.value ?: return + + val totalCountConverted = makeTotalCountInvalidEvent(totalCount) ?: return + val totalPriceConverted = makeTotalPriceInvalidEvent(totalPrice) ?: return + val meetingAddressDong = extractDong(meetingAddress) + + var originPriceNotBlank: Int? = 0 + runCatching { + originPriceNotBlank = originPriceToPositiveIntOrNull(originPrice.value) + }.onFailure { + makeOriginPriceInvalidEvent() + return + } + if (isOriginPriceCheaperThanSplitPriceEvent()) return + + viewModelScope.launch { + when ( + val result = + postOfferingUseCase( + OfferingWrite( + title = title, + productUrl = productUrlOrNull(), + thumbnailUrl = thumbnailUrl.value, + totalCount = totalCountConverted, + totalPrice = totalPriceConverted, + originPrice = originPriceNotBlank, + meetingAddress = meetingAddress, + meetingAddressDong = meetingAddressDong, + meetingAddressDetail = meetingAddressDetail, + meetingDate = meetingDate, + description = description, + ), + ) + ) { + is Result.Success -> { + makeSubmitOfferingEvent() + _isSubmitLoading.value = false + } + + is Result.Error -> { + Log.e("error", "postOffering: ${result.error}") + _writeUIState.value = + WriteUIState.Error(R.string.write_error_writing, "${result.error}") + _isSubmitLoading.value = false + } } } } - } - - private fun productUrlOrNull(): String? { - val productUrl = productUrl.value - if (productUrl == "") return null - return productUrl - } - private fun originPriceToPositiveIntOrNull(input: String?): Int? { - val originPriceInputTrim = input?.trim() - if (originPriceInputTrim.isNullOrBlank()) { - return null + private fun productUrlOrNull(): String? { + val productUrl = productUrl.value + if (productUrl == "") return null + return productUrl } - if (originPriceInputTrim.toInt() < 0) { - throw NumberFormatException() + + private fun originPriceToPositiveIntOrNull(input: String?): Int? { + val originPriceInputTrim = input?.trim() + if (originPriceInputTrim.isNullOrBlank()) { + return null + } + if (originPriceInputTrim.toInt() < 0) { + throw NumberFormatException() + } + return originPriceInputTrim.toInt() } - return originPriceInputTrim.toInt() - } - private fun extractDong(address: String): String? { - val regex = """\((.*?)\)""".toRegex() - val matchResult = regex.find(address) - val content = matchResult?.groups?.get(1)?.value - return content?.split(",")?.get(0)?.trim() - } + private fun extractDong(address: String): String? { + val regex = """\((.*?)\)""".toRegex() + val matchResult = regex.find(address) + val content = matchResult?.groups?.get(1)?.value + return content?.split(",")?.get(0)?.trim() + } - private fun makeTotalCountInvalidEvent(totalCount: String): Int? { - val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) - return null + private fun makeTotalCountInvalidEvent(totalCount: String): Int? { + val totalCountValue = totalCount.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalCountValue < MINIMUM_TOTAL_COUNT || totalCountValue > MAXIMUM_TOTAL_COUNT) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_count) + return null + } + return totalCountValue } - return totalCountValue - } - private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { - val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT - if (totalPriceConverted < 0) { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) - return null + private fun makeTotalPriceInvalidEvent(totalPrice: String): Int? { + val totalPriceConverted = totalPrice.trim().toIntOrNull() ?: ERROR_INTEGER_FORMAT + if (totalPriceConverted < 0) { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_total_price) + return null + } + return totalPriceConverted } - return totalPriceConverted - } - private fun makeOriginPriceInvalidEvent() { - _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) - } + private fun makeOriginPriceInvalidEvent() { + _writeUIState.value = WriteUIState.InvalidInput(R.string.write_invalid_origin_price) + } - private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { - if (originPrice.value.isNullOrBlank()) return false - val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT - if (discountRateValue <= 0f) { - _writeUIState.value = - WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) - return true + private fun isOriginPriceCheaperThanSplitPriceEvent(): Boolean { + if (originPrice.value.isNullOrBlank()) return false + val discountRateValue = discountRate.value ?: ERROR_FLOAT_FORMAT + if (discountRateValue <= 0f) { + _writeUIState.value = + WriteUIState.InvalidInput(R.string.write_origin_price_cheaper_than_total_price) + return true + } + return false } - return false - } - fun makeNavigateToOptionalEvent() { - _navigateToOptionalEvent.setValue(true) - } + fun makeNavigateToOptionalEvent() { + _navigateToOptionalEvent.setValue(true) + } - private fun makeSubmitOfferingEvent() { - _submitOfferingEvent.setValue(Unit) - } + private fun makeSubmitOfferingEvent() { + _submitOfferingEvent.setValue(Unit) + } - fun initOfferingWriteInputs() { - title.value = "" - productUrl.value = "" - thumbnailUrl.value = "" - totalCount.value = "$MINIMUM_TOTAL_COUNT" - totalPrice.value = "" - originPrice.value = "" - meetingAddress.value = "" - meetingAddressDetail.value = "" - meetingDate.value = "" - meetingDateValue.value = "" - description.value = "" - } + fun initOfferingWriteInputs() { + title.value = "" + productUrl.value = "" + thumbnailUrl.value = "" + totalCount.value = "$MINIMUM_TOTAL_COUNT" + totalPrice.value = "" + originPrice.value = "" + meetingAddress.value = "" + meetingAddressDetail.value = "" + meetingDate.value = "" + meetingDateValue.value = "" + description.value = "" + } - companion object { - private const val ERROR_INTEGER_FORMAT = -1 - private const val ERROR_FLOAT_FORMAT = -1f - private const val MINIMUM_TOTAL_COUNT = 2 - private const val MAXIMUM_TOTAL_COUNT = 10_000 - private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" - private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" - private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" - const val HTTPS = "https:" + companion object { + private const val ERROR_INTEGER_FORMAT = -1 + private const val ERROR_FLOAT_FORMAT = -1f + private const val MINIMUM_TOTAL_COUNT = 2 + private const val MAXIMUM_TOTAL_COUNT = 10_000 + private const val INPUT_DATE_TIME_FORMAT = "yyyy년 M월 d일 a h시 m분" + private const val INPUT_DATE_FORMAT = "yyyy년 M월 d일" + private const val OUTPUT_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss" + const val HTTPS = "https:" + } } -} From 9fcc0d1fa42210f98aa192a3e2d34e81fc8c7026 Mon Sep 17 00:00:00 2001 From: songpink Date: Sun, 15 Dec 2024 21:23:52 +0900 Subject: [PATCH 11/16] =?UTF-8?q?refactor:=20=EC=B1=84=ED=8C=85=EB=B0=A9?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EC=97=90=20=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/annotations/UseCaseQualifier.kt | 4 +++ .../di/module/UseCaseDependencyModule.kt | 8 +++++ .../comment/UpdateCommentRoomsUseCase.kt | 9 +++++ .../comment/UpdateCommentRoomsUseCaseImpl.kt | 35 +++++++++++++++++++ .../view/comment/CommentRoomsViewModel.kt | 30 ++++------------ 5 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCase.kt create mode 100644 android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImpl.kt diff --git a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt index fb3578e34..7641682b9 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/annotations/UseCaseQualifier.kt @@ -29,3 +29,7 @@ annotation class FetchOfferingDetailUseCaseQualifier @Qualifier @Retention(AnnotationRetention.BINARY) annotation class PostOfferingModifyUseCaseQualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class UpdateCommentRoomsUseCaseQualifier diff --git a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt index 723ba1209..0ed070259 100644 --- a/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt +++ b/android/app/src/main/java/com/zzang/chongdae/di/module/UseCaseDependencyModule.kt @@ -6,7 +6,10 @@ import com.zzang.chongdae.di.annotations.PostLoginUseCaseQualifier import com.zzang.chongdae.di.annotations.PostOfferingModifyUseCaseQualifier import com.zzang.chongdae.di.annotations.PostOfferingUseCaseQualifier import com.zzang.chongdae.di.annotations.PostProductImageOgUseCaseQualifier +import com.zzang.chongdae.di.annotations.UpdateCommentRoomsUseCaseQualifier import com.zzang.chongdae.di.annotations.UploadImageFileUseCaseQualifier +import com.zzang.chongdae.domain.usecase.comment.UpdateCommentRoomsUseCase +import com.zzang.chongdae.domain.usecase.comment.UpdateCommentRoomsUseCaseImpl import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCase import com.zzang.chongdae.domain.usecase.login.CheckIfAlreadyLoggedInUseCaseImpl import com.zzang.chongdae.domain.usecase.login.PostLoginUseCase @@ -64,4 +67,9 @@ abstract class UseCaseDependencyModule { @Singleton @PostOfferingModifyUseCaseQualifier abstract fun providePostOfferingModifyUseCase(impl: PostOfferingModifyUseCaseImpl): PostOfferingModifyUseCase + + @Binds + @Singleton + @UpdateCommentRoomsUseCaseQualifier + abstract fun provideUpdateCommentRoomsUseCase(impl: UpdateCommentRoomsUseCaseImpl): UpdateCommentRoomsUseCase } diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCase.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCase.kt new file mode 100644 index 000000000..b0a06c1cc --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCase.kt @@ -0,0 +1,9 @@ +package com.zzang.chongdae.domain.usecase.comment + +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.model.CommentRoom + +interface UpdateCommentRoomsUseCase { + suspend operator fun invoke(): Result, DataError.Network> +} diff --git a/android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImpl.kt b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImpl.kt new file mode 100644 index 000000000..248cdf264 --- /dev/null +++ b/android/app/src/main/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImpl.kt @@ -0,0 +1,35 @@ +package com.zzang.chongdae.domain.usecase.comment + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.DataError +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier +import com.zzang.chongdae.di.annotations.CommentRoomsRepositoryQualifier +import com.zzang.chongdae.domain.model.CommentRoom +import com.zzang.chongdae.domain.repository.CommentRoomsRepository +import javax.inject.Inject + +class UpdateCommentRoomsUseCaseImpl + @Inject + constructor( + @AuthRepositoryQualifier private val authRepository: AuthRepository, + @CommentRoomsRepositoryQualifier private val commentRoomsRepository: CommentRoomsRepository, + ) : UpdateCommentRoomsUseCase { + override suspend fun invoke(): Result, DataError.Network> { + return when (val result = commentRoomsRepository.fetchCommentRooms()) { + is Result.Success -> Result.Success(result.data) + is Result.Error -> { + when (result.error) { + DataError.Network.UNAUTHORIZED -> { + when (authRepository.saveRefresh()) { + is Result.Success -> invoke() + is Result.Error -> result + } + } + + else -> result + } + } + } + } + } diff --git a/android/app/src/main/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModel.kt b/android/app/src/main/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModel.kt index e67720024..a333a2350 100644 --- a/android/app/src/main/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModel.kt +++ b/android/app/src/main/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModel.kt @@ -6,13 +6,10 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.map import androidx.lifecycle.viewModelScope -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.common.handler.DataError import com.zzang.chongdae.common.handler.Result -import com.zzang.chongdae.di.annotations.AuthRepositoryQualifier -import com.zzang.chongdae.di.annotations.CommentRoomsRepositoryQualifier +import com.zzang.chongdae.di.annotations.UpdateCommentRoomsUseCaseQualifier import com.zzang.chongdae.domain.model.CommentRoom -import com.zzang.chongdae.domain.repository.CommentRoomsRepository +import com.zzang.chongdae.domain.usecase.comment.UpdateCommentRoomsUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import javax.inject.Inject @@ -21,34 +18,19 @@ import javax.inject.Inject class CommentRoomsViewModel @Inject constructor( - @AuthRepositoryQualifier private val authRepository: AuthRepository, - @CommentRoomsRepositoryQualifier private val commentRoomsRepository: CommentRoomsRepository, + @UpdateCommentRoomsUseCaseQualifier private val updateCommentRoomsUseCase: UpdateCommentRoomsUseCase, ) : ViewModel() { private val _commentRooms: MutableLiveData> = MutableLiveData() val commentRooms: LiveData> get() = _commentRooms val isCommentRoomsEmpty: LiveData - get() = - commentRooms.map { - it.isEmpty() - } + get() = commentRooms.map { it.isEmpty() } fun updateCommentRooms() { viewModelScope.launch { - when (val result = commentRoomsRepository.fetchCommentRooms()) { - is Result.Error -> { - Log.e("error", "updateCommentRooms: ${result.error}") - when (result.error) { - DataError.Network.UNAUTHORIZED -> { - when (authRepository.saveRefresh()) { - is Result.Success -> updateCommentRooms() - is Result.Error -> return@launch - } - } - else -> {} - } - } + when (val result = updateCommentRoomsUseCase()) { is Result.Success -> _commentRooms.value = result.data + is Result.Error -> Log.e("error", "updateCommentRooms: ${result.error}") } } } From b47a75679f2954cc4e895e50b979556b699304a2 Mon Sep 17 00:00:00 2001 From: songpink Date: Mon, 16 Dec 2024 15:45:10 +0900 Subject: [PATCH 12/16] =?UTF-8?q?test:=20=EB=8C=93=EA=B8=80=EB=B0=A9=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=EC=9D=84=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EB=8A=94=20=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UpdateCommentRoomsUseCaseImplTest.kt | 48 ++++ .../usecase/login/PostLoginUseCaseTest.kt | 37 +++ .../view/comment/CommentRoomsViewModelTest.kt | 94 +++---- .../view/write/OfferingWriteViewModelTest.kt | 262 +++++++++--------- .../chongdae/repository/FakeAuthRepository.kt | 4 +- .../repository/FakeCommentRoomsRepository.kt | 9 +- 6 files changed, 273 insertions(+), 181 deletions(-) create mode 100644 android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImplTest.kt create mode 100644 android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt diff --git a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImplTest.kt b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImplTest.kt new file mode 100644 index 000000000..d6759fd35 --- /dev/null +++ b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImplTest.kt @@ -0,0 +1,48 @@ +package com.zzang.chongdae.domain.usecase.comment + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.domain.repository.CommentRoomsRepository +import com.zzang.chongdae.repository.FakeAuthRepository +import com.zzang.chongdae.repository.FakeCommentRoomsRepository +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class UpdateCommentRoomsUseCaseTest { + private lateinit var authRepository: AuthRepository + private lateinit var commentRoomsRepository: CommentRoomsRepository + private lateinit var updateCommentRoomsUseCase: UpdateCommentRoomsUseCase + + @BeforeEach + fun setUp() { + authRepository = FakeAuthRepository() + commentRoomsRepository = FakeCommentRoomsRepository() + updateCommentRoomsUseCase = UpdateCommentRoomsUseCaseImpl(authRepository, commentRoomsRepository) + } + + @Test + fun `댓글방 목록을 불러오는 데 성공한다`() = runTest { + // given + (commentRoomsRepository as FakeCommentRoomsRepository).isAccessTokenValid = true + + // when + val result = updateCommentRoomsUseCase() + + // then + assertThat(result).isInstanceOf(Result.Success::class.java) + } + + @Test + fun `AccessToken이 만료되면 토큰을 갱신하고 재시도해 댓글방 목록을 불러온다`() = runTest { + // given + (commentRoomsRepository as FakeCommentRoomsRepository).isAccessTokenValid = false + + // when + val result = updateCommentRoomsUseCase() + + // then + assertThat(result).isInstanceOf(Result.Success::class.java) + } +} \ No newline at end of file diff --git a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt new file mode 100644 index 000000000..4e4f50d84 --- /dev/null +++ b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt @@ -0,0 +1,37 @@ +//package com.zzang.chongdae.domain.usecase.login +// +//import com.zzang.chongdae.auth.repository.AuthRepository +//import com.zzang.chongdae.common.datastore.UserPreferencesDataStore +//import com.zzang.chongdae.repository.FakeAuthRepository +//import com.zzang.chongdae.repository.FakeDataStore +//import com.zzang.chongdae.util.CoroutinesTestExtension +//import kotlinx.coroutines.ExperimentalCoroutinesApi +//import org.junit.jupiter.api.BeforeEach +//import org.junit.jupiter.api.Test +//import org.junit.jupiter.api.extension.ExtendWith +// +//@ExperimentalCoroutinesApi +//@ExtendWith(CoroutinesTestExtension::class) +//class PostLoginUseCaseTest { +// private lateinit var authRepository: AuthRepository +// private lateinit var userPreferenceDataStore: UserPreferencesDataStore +// private lateinit var postLoginUseCase: PostLoginUseCase +// +// @BeforeEach +// fun setUp() { +// authRepository = FakeAuthRepository() +// userPreferenceDataStore = UserPreferencesDataStore(FakeDataStore()) +// postLoginUseCase = PostLoginUseCaseImpl(authRepository, userPreferenceDataStore) +// } +// +// @Test +// fun `임시 이름`() { +// // given +// val a = postLoginUseCase("FakeAccessToken", "FakeRefreshToken") +// +// // when +// +// // then +// +// } +//} \ No newline at end of file diff --git a/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt b/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt index 24d450c49..5182b16b5 100644 --- a/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt @@ -1,47 +1,47 @@ -package com.zzang.chongdae.presentation.view.comment - -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.domain.repository.CommentRoomsRepository -import com.zzang.chongdae.repository.FakeAuthRepository -import com.zzang.chongdae.repository.FakeCommentRoomsRepository -import com.zzang.chongdae.util.CoroutinesTestExtension -import com.zzang.chongdae.util.InstantTaskExecutorExtension -import com.zzang.chongdae.util.getOrAwaitValue -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import java.time.LocalDateTime - -@ExperimentalCoroutinesApi -@ExtendWith(CoroutinesTestExtension::class) -@ExtendWith(InstantTaskExecutorExtension::class) -class CommentRoomsViewModelTest { - private lateinit var viewModel: CommentRoomsViewModel - private lateinit var authRepository: AuthRepository - private lateinit var commentRoomsRepository: CommentRoomsRepository - - @BeforeEach - fun setUp() { - authRepository = FakeAuthRepository() - commentRoomsRepository = FakeCommentRoomsRepository() - viewModel = CommentRoomsViewModel(authRepository, commentRoomsRepository) - } - - @DisplayName("댓글방 목록을 확인할 수 있어야 한다") - @Test - fun loadCommentRooms() { - // when - viewModel.updateCommentRooms() - - // then - val actual = viewModel.commentRooms.getOrAwaitValue() - assertThat(actual[0].id).isEqualTo(1) - assertThat(actual[0].title).isEqualTo("title") - assertThat(actual[0].latestComment).isEqualTo("latestComment") - assertThat(actual[0].latestCommentTime).isEqualTo(LocalDateTime.of(1, 1, 1, 0, 0)) - assertThat(actual[0].isProposer).isEqualTo(true) - } -} +//package com.zzang.chongdae.presentation.view.comment +// +//import com.zzang.chongdae.auth.repository.AuthRepository +//import com.zzang.chongdae.domain.repository.CommentRoomsRepository +//import com.zzang.chongdae.repository.FakeAuthRepository +//import com.zzang.chongdae.repository.FakeCommentRoomsRepository +//import com.zzang.chongdae.util.CoroutinesTestExtension +//import com.zzang.chongdae.util.InstantTaskExecutorExtension +//import com.zzang.chongdae.util.getOrAwaitValue +//import kotlinx.coroutines.ExperimentalCoroutinesApi +//import org.assertj.core.api.Assertions.assertThat +//import org.junit.jupiter.api.BeforeEach +//import org.junit.jupiter.api.DisplayName +//import org.junit.jupiter.api.Test +//import org.junit.jupiter.api.extension.ExtendWith +//import java.time.LocalDateTime +// +//@ExperimentalCoroutinesApi +//@ExtendWith(CoroutinesTestExtension::class) +//@ExtendWith(InstantTaskExecutorExtension::class) +//class CommentRoomsViewModelTest { +// private lateinit var viewModel: CommentRoomsViewModel +// private lateinit var authRepository: AuthRepository +// private lateinit var commentRoomsRepository: CommentRoomsRepository +// +// @BeforeEach +// fun setUp() { +// authRepository = FakeAuthRepository() +// commentRoomsRepository = FakeCommentRoomsRepository() +// viewModel = CommentRoomsViewModel(authRepository, commentRoomsRepository) +// } +// +// @DisplayName("댓글방 목록을 확인할 수 있어야 한다") +// @Test +// fun loadCommentRooms() { +// // when +// viewModel.updateCommentRooms() +// +// // then +// val actual = viewModel.commentRooms.getOrAwaitValue() +// assertThat(actual[0].id).isEqualTo(1) +// assertThat(actual[0].title).isEqualTo("title") +// assertThat(actual[0].latestComment).isEqualTo("latestComment") +// assertThat(actual[0].latestCommentTime).isEqualTo(LocalDateTime.of(1, 1, 1, 0, 0)) +// assertThat(actual[0].isProposer).isEqualTo(true) +// } +//} diff --git a/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt b/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt index df2466dfa..4b63a8ddf 100644 --- a/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt @@ -1,131 +1,131 @@ -package com.zzang.chongdae.presentation.view.write - -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.domain.repository.OfferingRepository -import com.zzang.chongdae.presentation.view.write.OfferingWriteViewModel.Companion.HTTPS -import com.zzang.chongdae.repository.FakeAuthRepository -import com.zzang.chongdae.repository.FakeOfferingRepository -import com.zzang.chongdae.util.CoroutinesTestExtension -import com.zzang.chongdae.util.InstantTaskExecutorExtension -import com.zzang.chongdae.util.TestFixture.martiPartBody -import com.zzang.chongdae.util.TestFixture.productUrl -import com.zzang.chongdae.util.getOrAwaitValue -import kotlinx.coroutines.ExperimentalCoroutinesApi -import org.assertj.core.api.Assertions.assertThat -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.DisplayName -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith - -@ExperimentalCoroutinesApi -@ExtendWith(CoroutinesTestExtension::class) -@ExtendWith(InstantTaskExecutorExtension::class) -class OfferingWriteViewModelTest { - private lateinit var viewModel: OfferingWriteViewModel - private lateinit var offeringRepository: OfferingRepository - private lateinit var authRepository: AuthRepository - - @BeforeEach - fun setUp() { - offeringRepository = FakeOfferingRepository() - authRepository = FakeAuthRepository() - viewModel = OfferingWriteViewModel(offeringRepository, authRepository) - } - - @DisplayName("상품 url을 통해 og 이미지 정보를 가져온다") - @Test - fun saveProductImageOg() { - // given - // when - viewModel.postProductImageOg() - - // then - val result = viewModel.thumbnailUrl.getOrAwaitValue() - assertThat(result).isEqualTo(HTTPS + productUrl.imageUrl) - } - - @DisplayName("상품 이미지를 S3에 저장한다") - @Test - fun saveProductImageS3() { - // given - // when - viewModel.uploadImageFile(martiPartBody) - - // then - val result = viewModel.thumbnailUrl.getOrAwaitValue() - assertThat(result).isEqualTo(productUrl.imageUrl) - } - - @DisplayName("업로드한 상품 이미지를 지울 수 있다") - @Test - fun deleteProductImage() { - // given - viewModel.uploadImageFile(martiPartBody) - - // when - viewModel.clearProductImage() - - // then - val result = viewModel.thumbnailUrl.getOrAwaitValue() - assertThat(result).isEqualTo(null) - } - - @DisplayName("총원과 총 가격을 입력받았을 때 엔빵 가격을 계산할 수 있어야 한다") - @Test - fun calculateSplitPrice() { - // when - viewModel.totalCount.value = "3" - viewModel.totalPrice.value = "3000" - - // then - val result = viewModel.splitPrice.getOrAwaitValue() - assertThat(result).isEqualTo(1000) - } - - @DisplayName("총원과 총 가격, 낱개 가격을 입력받았을 때 할인율을 계산할 수 있어야 한다") - @Test - fun calculateDiscountRate() { - // when - viewModel.totalCount.value = "3" - viewModel.totalPrice.value = "3000" - viewModel.originPrice.value = "2000" - - // then - val result = viewModel.discountRate.getOrAwaitValue() - assertThat(result).isEqualTo(50f) - } - - @DisplayName("필수 항목이 모두 입력되어야만 제출 버튼이 활성화 되어야 한다") - @Test - fun enabledSubmitButton() { - // when - viewModel.apply { - title.value = "테스트 제목" - totalCount.value = "2" - totalPrice.value = "1000" - meetingAddress.value = "테스트 장소" - meetingDate.value = "테스트 시간" - } - - // then - val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() - assertThat(result).isTrue() - } - - @DisplayName("필수 항목이 모두 입력되지 않으면 제출 버튼이 비활성화 되어야 한다") - @Test - fun disabledSubmitButton() { - // when - viewModel.apply { - title.value = "테스트 제목" - totalCount.value = "\n" - totalPrice.value = "" - meetingAddress.value = " " - meetingDate.value = "테스트 시간" - } - - // then - val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() - assertThat(result).isFalse() - } -} +//package com.zzang.chongdae.presentation.view.write +// +//import com.zzang.chongdae.auth.repository.AuthRepository +//import com.zzang.chongdae.domain.repository.OfferingRepository +//import com.zzang.chongdae.presentation.view.write.OfferingWriteViewModel.Companion.HTTPS +//import com.zzang.chongdae.repository.FakeAuthRepository +//import com.zzang.chongdae.repository.FakeOfferingRepository +//import com.zzang.chongdae.util.CoroutinesTestExtension +//import com.zzang.chongdae.util.InstantTaskExecutorExtension +//import com.zzang.chongdae.util.TestFixture.martiPartBody +//import com.zzang.chongdae.util.TestFixture.productUrl +//import com.zzang.chongdae.util.getOrAwaitValue +//import kotlinx.coroutines.ExperimentalCoroutinesApi +//import org.assertj.core.api.Assertions.assertThat +//import org.junit.jupiter.api.BeforeEach +//import org.junit.jupiter.api.DisplayName +//import org.junit.jupiter.api.Test +//import org.junit.jupiter.api.extension.ExtendWith +// +//@ExperimentalCoroutinesApi +//@ExtendWith(CoroutinesTestExtension::class) +//@ExtendWith(InstantTaskExecutorExtension::class) +//class OfferingWriteViewModelTest { +// private lateinit var viewModel: OfferingWriteViewModel +// private lateinit var offeringRepository: OfferingRepository +// private lateinit var authRepository: AuthRepository +// +// @BeforeEach +// fun setUp() { +// offeringRepository = FakeOfferingRepository() +// authRepository = FakeAuthRepository() +// viewModel = OfferingWriteViewModel(offeringRepository, authRepository) +// } +// +// @DisplayName("상품 url을 통해 og 이미지 정보를 가져온다") +// @Test +// fun saveProductImageOg() { +// // given +// // when +// viewModel.postProductImageOg() +// +// // then +// val result = viewModel.thumbnailUrl.getOrAwaitValue() +// assertThat(result).isEqualTo(HTTPS + productUrl.imageUrl) +// } +// +// @DisplayName("상품 이미지를 S3에 저장한다") +// @Test +// fun saveProductImageS3() { +// // given +// // when +// viewModel.uploadImageFile(martiPartBody) +// +// // then +// val result = viewModel.thumbnailUrl.getOrAwaitValue() +// assertThat(result).isEqualTo(productUrl.imageUrl) +// } +// +// @DisplayName("업로드한 상품 이미지를 지울 수 있다") +// @Test +// fun deleteProductImage() { +// // given +// viewModel.uploadImageFile(martiPartBody) +// +// // when +// viewModel.clearProductImage() +// +// // then +// val result = viewModel.thumbnailUrl.getOrAwaitValue() +// assertThat(result).isEqualTo(null) +// } +// +// @DisplayName("총원과 총 가격을 입력받았을 때 엔빵 가격을 계산할 수 있어야 한다") +// @Test +// fun calculateSplitPrice() { +// // when +// viewModel.totalCount.value = "3" +// viewModel.totalPrice.value = "3000" +// +// // then +// val result = viewModel.splitPrice.getOrAwaitValue() +// assertThat(result).isEqualTo(1000) +// } +// +// @DisplayName("총원과 총 가격, 낱개 가격을 입력받았을 때 할인율을 계산할 수 있어야 한다") +// @Test +// fun calculateDiscountRate() { +// // when +// viewModel.totalCount.value = "3" +// viewModel.totalPrice.value = "3000" +// viewModel.originPrice.value = "2000" +// +// // then +// val result = viewModel.discountRate.getOrAwaitValue() +// assertThat(result).isEqualTo(50f) +// } +// +// @DisplayName("필수 항목이 모두 입력되어야만 제출 버튼이 활성화 되어야 한다") +// @Test +// fun enabledSubmitButton() { +// // when +// viewModel.apply { +// title.value = "테스트 제목" +// totalCount.value = "2" +// totalPrice.value = "1000" +// meetingAddress.value = "테스트 장소" +// meetingDate.value = "테스트 시간" +// } +// +// // then +// val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() +// assertThat(result).isTrue() +// } +// +// @DisplayName("필수 항목이 모두 입력되지 않으면 제출 버튼이 비활성화 되어야 한다") +// @Test +// fun disabledSubmitButton() { +// // when +// viewModel.apply { +// title.value = "테스트 제목" +// totalCount.value = "\n" +// totalPrice.value = "" +// meetingAddress.value = " " +// meetingDate.value = "테스트 시간" +// } +// +// // then +// val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() +// assertThat(result).isFalse() +// } +//} diff --git a/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt b/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt index c492ee9f4..1b19cc8f6 100644 --- a/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt +++ b/android/app/src/test/java/com/zzang/chongdae/repository/FakeAuthRepository.kt @@ -10,10 +10,10 @@ class FakeAuthRepository : AuthRepository { accessToken: String, fcmToken: String, ): Result { - TODO("Not yet implemented") + return Result.Success(Member(0, "dummy")) } override suspend fun saveRefresh(): Result { - TODO("Not yet implemented") + return Result.Success(Unit) } } diff --git a/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt b/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt index 1dfb014de..17c974aa7 100644 --- a/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt +++ b/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt @@ -7,7 +7,14 @@ import com.zzang.chongdae.domain.repository.CommentRoomsRepository import com.zzang.chongdae.util.TestFixture class FakeCommentRoomsRepository : CommentRoomsRepository { + var isAccessTokenValid = true override suspend fun fetchCommentRooms(): Result, DataError.Network> { - return Result.Success(TestFixture.COMMENT_ROOMS_STUB) + return when (isAccessTokenValid) { + true -> Result.Success(TestFixture.COMMENT_ROOMS_STUB) + false -> { + isAccessTokenValid = true + Result.Error("AccessToken 만료됨", DataError.Network.UNAUTHORIZED) + } + } } } From 0c6e0d45eecdd4ce2f39b4fab7f40cf4e768ce39 Mon Sep 17 00:00:00 2001 From: songpink Date: Mon, 16 Dec 2024 16:13:40 +0900 Subject: [PATCH 13/16] =?UTF-8?q?test:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A6=88=EC=BC=80=EC=9D=B4=EC=8A=A4=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...st.kt => UpdateCommentRoomsUseCaseTest.kt} | 0 .../usecase/login/PostLoginUseCaseTest.kt | 75 ++++++++++--------- 2 files changed, 38 insertions(+), 37 deletions(-) rename android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/{UpdateCommentRoomsUseCaseImplTest.kt => UpdateCommentRoomsUseCaseTest.kt} (100%) diff --git a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImplTest.kt b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseTest.kt similarity index 100% rename from android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseImplTest.kt rename to android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseTest.kt diff --git a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt index 4e4f50d84..2f8eebe2b 100644 --- a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt @@ -1,37 +1,38 @@ -//package com.zzang.chongdae.domain.usecase.login -// -//import com.zzang.chongdae.auth.repository.AuthRepository -//import com.zzang.chongdae.common.datastore.UserPreferencesDataStore -//import com.zzang.chongdae.repository.FakeAuthRepository -//import com.zzang.chongdae.repository.FakeDataStore -//import com.zzang.chongdae.util.CoroutinesTestExtension -//import kotlinx.coroutines.ExperimentalCoroutinesApi -//import org.junit.jupiter.api.BeforeEach -//import org.junit.jupiter.api.Test -//import org.junit.jupiter.api.extension.ExtendWith -// -//@ExperimentalCoroutinesApi -//@ExtendWith(CoroutinesTestExtension::class) -//class PostLoginUseCaseTest { -// private lateinit var authRepository: AuthRepository -// private lateinit var userPreferenceDataStore: UserPreferencesDataStore -// private lateinit var postLoginUseCase: PostLoginUseCase -// -// @BeforeEach -// fun setUp() { -// authRepository = FakeAuthRepository() -// userPreferenceDataStore = UserPreferencesDataStore(FakeDataStore()) -// postLoginUseCase = PostLoginUseCaseImpl(authRepository, userPreferenceDataStore) -// } -// -// @Test -// fun `임시 이름`() { -// // given -// val a = postLoginUseCase("FakeAccessToken", "FakeRefreshToken") -// -// // when -// -// // then -// -// } -//} \ No newline at end of file +package com.zzang.chongdae.domain.usecase.login + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.common.datastore.UserPreferencesDataStore +import com.zzang.chongdae.common.handler.Result +import com.zzang.chongdae.repository.FakeAuthRepository +import com.zzang.chongdae.repository.FakeDataStore +import com.zzang.chongdae.util.CoroutinesTestExtension +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +class PostLoginUseCaseTest { + private lateinit var authRepository: AuthRepository + private lateinit var userPreferenceDataStore: UserPreferencesDataStore + private lateinit var postLoginUseCase: PostLoginUseCase + + @BeforeEach + fun setUp() { + authRepository = FakeAuthRepository() + userPreferenceDataStore = UserPreferencesDataStore(FakeDataStore()) + postLoginUseCase = PostLoginUseCaseImpl(authRepository, userPreferenceDataStore) + } + + @Test + fun `로그인에 성공한다`() = runTest { + // given + + // when + val result = postLoginUseCase("FakeAccessToken", "FakeRefreshToken") + + // then + assertThat(result).isInstanceOf(Result.Success::class.java) + } +} \ No newline at end of file From 10001daf7c8b944af03ef3000bf3ceb631403a99 Mon Sep 17 00:00:00 2001 From: songpink Date: Mon, 16 Dec 2024 16:41:52 +0900 Subject: [PATCH 14/16] =?UTF-8?q?test:=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=A0=81=EC=9A=A9=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EA=B3=B5=EB=AA=A8=EA=B8=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/write/OfferingWriteViewModelTest.kt | 272 +++++++++--------- 1 file changed, 141 insertions(+), 131 deletions(-) diff --git a/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt b/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt index 4b63a8ddf..6516a45b4 100644 --- a/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt @@ -1,131 +1,141 @@ -//package com.zzang.chongdae.presentation.view.write -// -//import com.zzang.chongdae.auth.repository.AuthRepository -//import com.zzang.chongdae.domain.repository.OfferingRepository -//import com.zzang.chongdae.presentation.view.write.OfferingWriteViewModel.Companion.HTTPS -//import com.zzang.chongdae.repository.FakeAuthRepository -//import com.zzang.chongdae.repository.FakeOfferingRepository -//import com.zzang.chongdae.util.CoroutinesTestExtension -//import com.zzang.chongdae.util.InstantTaskExecutorExtension -//import com.zzang.chongdae.util.TestFixture.martiPartBody -//import com.zzang.chongdae.util.TestFixture.productUrl -//import com.zzang.chongdae.util.getOrAwaitValue -//import kotlinx.coroutines.ExperimentalCoroutinesApi -//import org.assertj.core.api.Assertions.assertThat -//import org.junit.jupiter.api.BeforeEach -//import org.junit.jupiter.api.DisplayName -//import org.junit.jupiter.api.Test -//import org.junit.jupiter.api.extension.ExtendWith -// -//@ExperimentalCoroutinesApi -//@ExtendWith(CoroutinesTestExtension::class) -//@ExtendWith(InstantTaskExecutorExtension::class) -//class OfferingWriteViewModelTest { -// private lateinit var viewModel: OfferingWriteViewModel -// private lateinit var offeringRepository: OfferingRepository -// private lateinit var authRepository: AuthRepository -// -// @BeforeEach -// fun setUp() { -// offeringRepository = FakeOfferingRepository() -// authRepository = FakeAuthRepository() -// viewModel = OfferingWriteViewModel(offeringRepository, authRepository) -// } -// -// @DisplayName("상품 url을 통해 og 이미지 정보를 가져온다") -// @Test -// fun saveProductImageOg() { -// // given -// // when -// viewModel.postProductImageOg() -// -// // then -// val result = viewModel.thumbnailUrl.getOrAwaitValue() -// assertThat(result).isEqualTo(HTTPS + productUrl.imageUrl) -// } -// -// @DisplayName("상품 이미지를 S3에 저장한다") -// @Test -// fun saveProductImageS3() { -// // given -// // when -// viewModel.uploadImageFile(martiPartBody) -// -// // then -// val result = viewModel.thumbnailUrl.getOrAwaitValue() -// assertThat(result).isEqualTo(productUrl.imageUrl) -// } -// -// @DisplayName("업로드한 상품 이미지를 지울 수 있다") -// @Test -// fun deleteProductImage() { -// // given -// viewModel.uploadImageFile(martiPartBody) -// -// // when -// viewModel.clearProductImage() -// -// // then -// val result = viewModel.thumbnailUrl.getOrAwaitValue() -// assertThat(result).isEqualTo(null) -// } -// -// @DisplayName("총원과 총 가격을 입력받았을 때 엔빵 가격을 계산할 수 있어야 한다") -// @Test -// fun calculateSplitPrice() { -// // when -// viewModel.totalCount.value = "3" -// viewModel.totalPrice.value = "3000" -// -// // then -// val result = viewModel.splitPrice.getOrAwaitValue() -// assertThat(result).isEqualTo(1000) -// } -// -// @DisplayName("총원과 총 가격, 낱개 가격을 입력받았을 때 할인율을 계산할 수 있어야 한다") -// @Test -// fun calculateDiscountRate() { -// // when -// viewModel.totalCount.value = "3" -// viewModel.totalPrice.value = "3000" -// viewModel.originPrice.value = "2000" -// -// // then -// val result = viewModel.discountRate.getOrAwaitValue() -// assertThat(result).isEqualTo(50f) -// } -// -// @DisplayName("필수 항목이 모두 입력되어야만 제출 버튼이 활성화 되어야 한다") -// @Test -// fun enabledSubmitButton() { -// // when -// viewModel.apply { -// title.value = "테스트 제목" -// totalCount.value = "2" -// totalPrice.value = "1000" -// meetingAddress.value = "테스트 장소" -// meetingDate.value = "테스트 시간" -// } -// -// // then -// val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() -// assertThat(result).isTrue() -// } -// -// @DisplayName("필수 항목이 모두 입력되지 않으면 제출 버튼이 비활성화 되어야 한다") -// @Test -// fun disabledSubmitButton() { -// // when -// viewModel.apply { -// title.value = "테스트 제목" -// totalCount.value = "\n" -// totalPrice.value = "" -// meetingAddress.value = " " -// meetingDate.value = "테스트 시간" -// } -// -// // then -// val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() -// assertThat(result).isFalse() -// } -//} +package com.zzang.chongdae.presentation.view.write + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.domain.repository.OfferingRepository +import com.zzang.chongdae.domain.usecase.offeringmodify.PostOfferingModifyUseCase +import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase +import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCaseImpl +import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCase +import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCaseImpl +import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase +import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCaseImpl +import com.zzang.chongdae.presentation.view.write.OfferingWriteViewModel.Companion.HTTPS +import com.zzang.chongdae.repository.FakeAuthRepository +import com.zzang.chongdae.repository.FakeOfferingRepository +import com.zzang.chongdae.util.CoroutinesTestExtension +import com.zzang.chongdae.util.InstantTaskExecutorExtension +import com.zzang.chongdae.util.TestFixture.martiPartBody +import com.zzang.chongdae.util.TestFixture.productUrl +import com.zzang.chongdae.util.getOrAwaitValue +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExperimentalCoroutinesApi +@ExtendWith(CoroutinesTestExtension::class) +@ExtendWith(InstantTaskExecutorExtension::class) +class OfferingWriteViewModelTest { + private lateinit var viewModel: OfferingWriteViewModel + + @BeforeEach + fun setUp() { + val fakeOfferingRepository = FakeOfferingRepository() + val fakeAuthRepository = FakeAuthRepository() + + val postOfferingUseCase = PostOfferingUseCaseImpl(fakeOfferingRepository, fakeAuthRepository) + val uploadImageFileUseCase = UploadImageFileUseCaseImpl(fakeOfferingRepository, fakeAuthRepository) + val postProductImageOgUseCase = PostProductImageOgUseCaseImpl(fakeOfferingRepository, fakeAuthRepository) + + viewModel = OfferingWriteViewModel(postOfferingUseCase, uploadImageFileUseCase, postProductImageOgUseCase) + } + + @DisplayName("상품 url을 통해 og 이미지 정보를 가져온다") + @Test + fun saveProductImageOg() { + // given + // when + viewModel.postProductImageOg() + + // then + val result = viewModel.thumbnailUrl.getOrAwaitValue() + assertThat(result).isEqualTo(HTTPS + productUrl.imageUrl) + } + + @DisplayName("상품 이미지를 S3에 저장한다") + @Test + fun saveProductImageS3() { + // given + // when + viewModel.uploadImageFile(martiPartBody) + + // then + val result = viewModel.thumbnailUrl.getOrAwaitValue() + assertThat(result).isEqualTo(productUrl.imageUrl) + } + + @DisplayName("업로드한 상품 이미지를 지울 수 있다") + @Test + fun deleteProductImage() { + // given + viewModel.uploadImageFile(martiPartBody) + + // when + viewModel.clearProductImage() + + // then + val result = viewModel.thumbnailUrl.getOrAwaitValue() + assertThat(result).isEqualTo(null) + } + + @DisplayName("총원과 총 가격을 입력받았을 때 엔빵 가격을 계산할 수 있어야 한다") + @Test + fun calculateSplitPrice() { + // when + viewModel.totalCount.value = "3" + viewModel.totalPrice.value = "3000" + + // then + val result = viewModel.splitPrice.getOrAwaitValue() + assertThat(result).isEqualTo(1000) + } + + @DisplayName("총원과 총 가격, 낱개 가격을 입력받았을 때 할인율을 계산할 수 있어야 한다") + @Test + fun calculateDiscountRate() { + // when + viewModel.totalCount.value = "3" + viewModel.totalPrice.value = "3000" + viewModel.originPrice.value = "2000" + + // then + val result = viewModel.discountRate.getOrAwaitValue() + assertThat(result).isEqualTo(50f) + } + + @DisplayName("필수 항목이 모두 입력되어야만 제출 버튼이 활성화 되어야 한다") + @Test + fun enabledSubmitButton() { + // when + viewModel.apply { + title.value = "테스트 제목" + totalCount.value = "2" + totalPrice.value = "1000" + meetingAddress.value = "테스트 장소" + meetingDate.value = "테스트 시간" + } + + // then + val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() + assertThat(result).isTrue() + } + + @DisplayName("필수 항목이 모두 입력되지 않으면 제출 버튼이 비활성화 되어야 한다") + @Test + fun disabledSubmitButton() { + // when + viewModel.apply { + title.value = "테스트 제목" + totalCount.value = "\n" + totalPrice.value = "" + meetingAddress.value = " " + meetingDate.value = "테스트 시간" + } + + // then + val result = viewModel.essentialSubmitButtonEnabled.getOrAwaitValue() + assertThat(result).isFalse() + } +} From b288858346614656e2ac030a9a21a0e87798e8c8 Mon Sep 17 00:00:00 2001 From: songpink Date: Mon, 16 Dec 2024 16:46:19 +0900 Subject: [PATCH 15/16] =?UTF-8?q?test:=20=EC=9C=A0=EC=A6=88=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=A0=81=EC=9A=A9=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EB=8C=93=EA=B8=80=EB=B0=A9=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/comment/CommentRoomsViewModelTest.kt | 96 ++++++++++--------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt b/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt index 5182b16b5..976fe2d53 100644 --- a/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt @@ -1,47 +1,49 @@ -//package com.zzang.chongdae.presentation.view.comment -// -//import com.zzang.chongdae.auth.repository.AuthRepository -//import com.zzang.chongdae.domain.repository.CommentRoomsRepository -//import com.zzang.chongdae.repository.FakeAuthRepository -//import com.zzang.chongdae.repository.FakeCommentRoomsRepository -//import com.zzang.chongdae.util.CoroutinesTestExtension -//import com.zzang.chongdae.util.InstantTaskExecutorExtension -//import com.zzang.chongdae.util.getOrAwaitValue -//import kotlinx.coroutines.ExperimentalCoroutinesApi -//import org.assertj.core.api.Assertions.assertThat -//import org.junit.jupiter.api.BeforeEach -//import org.junit.jupiter.api.DisplayName -//import org.junit.jupiter.api.Test -//import org.junit.jupiter.api.extension.ExtendWith -//import java.time.LocalDateTime -// -//@ExperimentalCoroutinesApi -//@ExtendWith(CoroutinesTestExtension::class) -//@ExtendWith(InstantTaskExecutorExtension::class) -//class CommentRoomsViewModelTest { -// private lateinit var viewModel: CommentRoomsViewModel -// private lateinit var authRepository: AuthRepository -// private lateinit var commentRoomsRepository: CommentRoomsRepository -// -// @BeforeEach -// fun setUp() { -// authRepository = FakeAuthRepository() -// commentRoomsRepository = FakeCommentRoomsRepository() -// viewModel = CommentRoomsViewModel(authRepository, commentRoomsRepository) -// } -// -// @DisplayName("댓글방 목록을 확인할 수 있어야 한다") -// @Test -// fun loadCommentRooms() { -// // when -// viewModel.updateCommentRooms() -// -// // then -// val actual = viewModel.commentRooms.getOrAwaitValue() -// assertThat(actual[0].id).isEqualTo(1) -// assertThat(actual[0].title).isEqualTo("title") -// assertThat(actual[0].latestComment).isEqualTo("latestComment") -// assertThat(actual[0].latestCommentTime).isEqualTo(LocalDateTime.of(1, 1, 1, 0, 0)) -// assertThat(actual[0].isProposer).isEqualTo(true) -// } -//} +package com.zzang.chongdae.presentation.view.comment + +import com.zzang.chongdae.auth.repository.AuthRepository +import com.zzang.chongdae.domain.repository.CommentRoomsRepository +import com.zzang.chongdae.domain.usecase.comment.UpdateCommentRoomsUseCaseImpl +import com.zzang.chongdae.repository.FakeAuthRepository +import com.zzang.chongdae.repository.FakeCommentRoomsRepository +import com.zzang.chongdae.util.CoroutinesTestExtension +import com.zzang.chongdae.util.InstantTaskExecutorExtension +import com.zzang.chongdae.util.getOrAwaitValue +import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import java.time.LocalDateTime + +@ExperimentalCoroutinesApi +@ExtendWith(CoroutinesTestExtension::class) +@ExtendWith(InstantTaskExecutorExtension::class) +class CommentRoomsViewModelTest { + private lateinit var viewModel: CommentRoomsViewModel + + @BeforeEach + fun setUp() { + val fakeAuthRepository = FakeAuthRepository() + val fakeCommentRoomsRepository = FakeCommentRoomsRepository() + + val updateCommentRoomsUseCase = UpdateCommentRoomsUseCaseImpl(fakeAuthRepository, fakeCommentRoomsRepository) + + viewModel = CommentRoomsViewModel(updateCommentRoomsUseCase) + } + + @DisplayName("댓글방 목록을 확인할 수 있어야 한다") + @Test + fun loadCommentRooms() { + // when + viewModel.updateCommentRooms() + + // then + val actual = viewModel.commentRooms.getOrAwaitValue() + assertThat(actual[0].id).isEqualTo(1) + assertThat(actual[0].title).isEqualTo("title") + assertThat(actual[0].latestComment).isEqualTo("latestComment") + assertThat(actual[0].latestCommentTime).isEqualTo(LocalDateTime.of(1, 1, 1, 0, 0)) + assertThat(actual[0].isProposer).isEqualTo(true) + } +} From caa01c24c7b0b04f671695ca565484f7dfb66104 Mon Sep 17 00:00:00 2001 From: songpink Date: Mon, 16 Dec 2024 16:46:56 +0900 Subject: [PATCH 16/16] =?UTF-8?q?style:=20ktlint=20format=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/UpdateCommentRoomsUseCaseTest.kt | 40 ++++++++++--------- .../usecase/login/PostLoginUseCaseTest.kt | 20 +++++----- .../view/comment/CommentRoomsViewModelTest.kt | 2 - .../view/write/OfferingWriteViewModelTest.kt | 6 --- .../repository/FakeCommentRoomsRepository.kt | 1 + 5 files changed, 31 insertions(+), 38 deletions(-) diff --git a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseTest.kt b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseTest.kt index d6759fd35..4e40ef6cd 100644 --- a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/comment/UpdateCommentRoomsUseCaseTest.kt @@ -23,26 +23,28 @@ class UpdateCommentRoomsUseCaseTest { } @Test - fun `댓글방 목록을 불러오는 데 성공한다`() = runTest { - // given - (commentRoomsRepository as FakeCommentRoomsRepository).isAccessTokenValid = true + fun `댓글방 목록을 불러오는 데 성공한다`() = + runTest { + // given + (commentRoomsRepository as FakeCommentRoomsRepository).isAccessTokenValid = true - // when - val result = updateCommentRoomsUseCase() + // when + val result = updateCommentRoomsUseCase() - // then - assertThat(result).isInstanceOf(Result.Success::class.java) - } + // then + assertThat(result).isInstanceOf(Result.Success::class.java) + } @Test - fun `AccessToken이 만료되면 토큰을 갱신하고 재시도해 댓글방 목록을 불러온다`() = runTest { - // given - (commentRoomsRepository as FakeCommentRoomsRepository).isAccessTokenValid = false - - // when - val result = updateCommentRoomsUseCase() - - // then - assertThat(result).isInstanceOf(Result.Success::class.java) - } -} \ No newline at end of file + fun `AccessToken이 만료되면 토큰을 갱신하고 재시도해 댓글방 목록을 불러온다`() = + runTest { + // given + (commentRoomsRepository as FakeCommentRoomsRepository).isAccessTokenValid = false + + // when + val result = updateCommentRoomsUseCase() + + // then + assertThat(result).isInstanceOf(Result.Success::class.java) + } +} diff --git a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt index 2f8eebe2b..d90f182aa 100644 --- a/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/domain/usecase/login/PostLoginUseCaseTest.kt @@ -5,13 +5,10 @@ import com.zzang.chongdae.common.datastore.UserPreferencesDataStore import com.zzang.chongdae.common.handler.Result import com.zzang.chongdae.repository.FakeAuthRepository import com.zzang.chongdae.repository.FakeDataStore -import com.zzang.chongdae.util.CoroutinesTestExtension -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith class PostLoginUseCaseTest { private lateinit var authRepository: AuthRepository @@ -26,13 +23,14 @@ class PostLoginUseCaseTest { } @Test - fun `로그인에 성공한다`() = runTest { - // given + fun `로그인에 성공한다`() = + runTest { + // given - // when - val result = postLoginUseCase("FakeAccessToken", "FakeRefreshToken") + // when + val result = postLoginUseCase("FakeAccessToken", "FakeRefreshToken") - // then - assertThat(result).isInstanceOf(Result.Success::class.java) - } -} \ No newline at end of file + // then + assertThat(result).isInstanceOf(Result.Success::class.java) + } +} diff --git a/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt b/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt index 976fe2d53..a9c64e321 100644 --- a/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/presentation/view/comment/CommentRoomsViewModelTest.kt @@ -1,7 +1,5 @@ package com.zzang.chongdae.presentation.view.comment -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.domain.repository.CommentRoomsRepository import com.zzang.chongdae.domain.usecase.comment.UpdateCommentRoomsUseCaseImpl import com.zzang.chongdae.repository.FakeAuthRepository import com.zzang.chongdae.repository.FakeCommentRoomsRepository diff --git a/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt b/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt index 6516a45b4..21a3a3eb6 100644 --- a/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt +++ b/android/app/src/test/java/com/zzang/chongdae/presentation/view/write/OfferingWriteViewModelTest.kt @@ -1,13 +1,7 @@ package com.zzang.chongdae.presentation.view.write -import com.zzang.chongdae.auth.repository.AuthRepository -import com.zzang.chongdae.domain.repository.OfferingRepository -import com.zzang.chongdae.domain.usecase.offeringmodify.PostOfferingModifyUseCase -import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCase import com.zzang.chongdae.domain.usecase.write.PostOfferingUseCaseImpl -import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCase import com.zzang.chongdae.domain.usecase.write.PostProductImageOgUseCaseImpl -import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCase import com.zzang.chongdae.domain.usecase.write.UploadImageFileUseCaseImpl import com.zzang.chongdae.presentation.view.write.OfferingWriteViewModel.Companion.HTTPS import com.zzang.chongdae.repository.FakeAuthRepository diff --git a/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt b/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt index 17c974aa7..147da867f 100644 --- a/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt +++ b/android/app/src/test/java/com/zzang/chongdae/repository/FakeCommentRoomsRepository.kt @@ -8,6 +8,7 @@ import com.zzang.chongdae.util.TestFixture class FakeCommentRoomsRepository : CommentRoomsRepository { var isAccessTokenValid = true + override suspend fun fetchCommentRooms(): Result, DataError.Network> { return when (isAccessTokenValid) { true -> Result.Success(TestFixture.COMMENT_ROOMS_STUB)