From 1aa3b068c2ac495c9922d3f53062649387eb8f63 Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Sat, 12 Jul 2025 22:24:49 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[IDLE-000]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20Result=20=EB=82=A8=EC=9A=A9=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../care.android.feature-binding.gradle.kts | 3 +- .../care.android.feature-compose.gradle.kts | 3 +- .../main/java/com/idle/common/ResultUtil.kt | 13 ++ .../data/repository/AuthRepositoryImpl.kt | 178 ++++++++---------- .../data/repository/ChatRepositoryImpl.kt | 154 +++++++-------- .../data/repository/ConfigRepositoryImpl.kt | 10 +- .../repository/JobPostingRepositoryImpl.kt | 78 ++++---- .../repository/NotificationRepositoryImpl.kt | 11 +- .../data/repository/ProfileRepositoryImpl.kt | 107 ++++++----- .../data/repository/TokenRepositoryImpl.kt | 4 +- .../datasource/UserInfoDataSource.kt | 8 +- .../idle/domain/repositorry/AuthRepository.kt | 31 ++- .../idle/domain/repositorry/ChatRepository.kt | 19 +- .../domain/repositorry/ConfigRepository.kt | 2 +- .../repositorry/JobPostingRepository.kt | 39 ++-- .../repositorry/NotificationRepository.kt | 6 +- .../domain/repositorry/ProfileRepository.kt | 34 ++-- .../domain/repositorry/TokenRepository.kt | 4 +- .../usecase/chat/GetChatRoomsUseCase.kt | 42 ++--- .../GetJobPostingsInProgressUseCase.kt | 16 +- .../profile/GetMyCenterProfileUseCase.kt | 8 +- .../profile/GetMyWorkerProfileUseCase.kt | 8 +- .../profile/RegisterCenterProfileUseCase.kt | 52 +++-- .../profile/UpdateCenterProfileUseCase.kt | 44 +++-- .../profile/UpdateWorkerProfileUseCase.kt | 52 +++-- .../authenticator/CareAuthenticator.kt | 14 +- .../com/idle/network/source/AuthDataSource.kt | 52 ++--- .../com/idle/network/source/ChatDataSource.kt | 103 +++++----- .../network/source/JobPostingDataSource.kt | 85 ++++----- .../network/source/NotificationDataSource.kt | 24 +-- .../idle/network/source/ProfileDataSource.kt | 58 +++--- .../idle/network/util/ResponseExtensions.kt | 30 +++ .../java/com/idle/network/util/safeApiCall.kt | 45 ----- .../inquiry/ApplicantInquiryViewModel.kt | 12 +- .../chatting/CenterChattingViewModel.kt | 49 +++-- .../idle/center/home/CenterHomeViewModel.kt | 17 +- .../center/jobposting/JobPostingViewModel.kt | 105 ++++++----- .../idle/pending/CenterPendingViewModel.kt | 17 +- .../center/profile/CenterProfileViewModel.kt | 21 ++- .../register/RegisterCenterInfoViewModel.kt | 21 ++- .../RegisterCenterInfoCompleteViewModel.kt | 13 +- .../ChattingDetailViewModel.kt | 83 ++++---- .../center/CenterJobPostingDetailViewModel.kt | 86 +++++---- .../worker/WorkerJobPostingDetailViewModel.kt | 51 +++-- .../notification/NotificationViewModel.kt | 9 +- .../setting/center/CenterSettingViewModel.kt | 9 +- .../setting/worker/WorkerSettingViewModel.kt | 9 +- .../signin/center/CenterSignInViewModel.kt | 48 +++-- .../newpassword/NewPasswordViewModel.kt | 29 +-- .../signup/center/CenterSignUpViewModel.kt | 93 ++++----- .../signup/worker/WorkerSignUpViewModel.kt | 55 +++--- .../idle/withdrawal/WithdrawalViewModel.kt | 61 +++--- .../chatting/WorkerChattingViewModel.kt | 38 ++-- .../idle/worker/home/WorkerHomeViewModel.kt | 66 ++++--- .../job/posting/WorkerJobPostingViewModel.kt | 56 +++--- .../worker/profile/WorkerProfileViewModel.kt | 29 +-- gradle/libs.versions.toml | 2 +- .../com/idle/presentation/MainViewModel.kt | 27 ++- 58 files changed, 1247 insertions(+), 1096 deletions(-) create mode 100644 core/common/src/main/java/com/idle/common/ResultUtil.kt create mode 100644 core/network/src/main/java/com/idle/network/util/ResponseExtensions.kt delete mode 100644 core/network/src/main/java/com/idle/network/util/safeApiCall.kt diff --git a/build-logic/src/main/java/com/idle/app/care.android.feature-binding.gradle.kts b/build-logic/src/main/java/com/idle/app/care.android.feature-binding.gradle.kts index fda52d3e..543b199a 100644 --- a/build-logic/src/main/java/com/idle/app/care.android.feature-binding.gradle.kts +++ b/build-logic/src/main/java/com/idle/app/care.android.feature-binding.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":core:designresource")) implementation(project(":core:navigation")) implementation(project(":core:analytics")) + implementation(project(":core:common")) implementation(project(":core:common-ui:binding")) val libs = project.extensions.libs @@ -31,4 +32,4 @@ dependencies { implementation(libs.findLibrary("androidx.lifecycle.viewModel").get()) implementation(libs.findLibrary("androidx.lifecycle.runtime").get()) implementation(libs.findLibrary("material").get()) -} \ No newline at end of file +} diff --git a/build-logic/src/main/java/com/idle/app/care.android.feature-compose.gradle.kts b/build-logic/src/main/java/com/idle/app/care.android.feature-compose.gradle.kts index 9b917339..bfc1bf49 100644 --- a/build-logic/src/main/java/com/idle/app/care.android.feature-compose.gradle.kts +++ b/build-logic/src/main/java/com/idle/app/care.android.feature-compose.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(project(":core:designresource")) implementation(project(":core:navigation")) implementation(project(":core:analytics")) + implementation(project(":core:common")) implementation(project(":core:common-ui:binding")) implementation(project(":core:common-ui:compose")) @@ -30,4 +31,4 @@ dependencies { implementation(libs.findLibrary("androidx-navigation-fragment").get()) implementation(libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) implementation(libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) -} \ No newline at end of file +} diff --git a/core/common/src/main/java/com/idle/common/ResultUtil.kt b/core/common/src/main/java/com/idle/common/ResultUtil.kt new file mode 100644 index 00000000..d115174b --- /dev/null +++ b/core/common/src/main/java/com/idle/common/ResultUtil.kt @@ -0,0 +1,13 @@ +package com.idle.common + +import kotlin.coroutines.cancellation.CancellationException + +suspend inline fun T.suspendRunCatching(crossinline block: suspend T.() -> R): Result { + return try { + Result.success(block()) + } catch (e: CancellationException) { + throw e + } catch (t: Throwable) { + Result.failure(t) + } +} diff --git a/core/data/src/main/java/com/idle/data/repository/AuthRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/AuthRepositoryImpl.kt index cd7e002b..30dead76 100644 --- a/core/data/src/main/java/com/idle/data/repository/AuthRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/AuthRepositoryImpl.kt @@ -5,8 +5,8 @@ import com.idle.datastore.datasource.UserInfoDataSource import com.idle.domain.model.auth.BusinessRegistrationInfo import com.idle.domain.model.auth.UserType import com.idle.domain.repositorry.AuthRepository -import com.idle.domain.repositorry.TokenRepository import com.idle.domain.repositorry.ProfileRepository +import com.idle.domain.repositorry.TokenRepository import com.idle.network.model.auth.ConfirmAuthCodeRequest import com.idle.network.model.auth.GenerateNewPasswordRequest import com.idle.network.model.auth.SendPhoneRequest @@ -29,18 +29,21 @@ class AuthRepositoryImpl @Inject constructor( private val userInfoDataSource: UserInfoDataSource, private val tokenRepository: TokenRepository, ) : AuthRepository { - override suspend fun sendPhoneNumber(phoneNumber: String): Result = + override suspend fun sendPhoneNumber(phoneNumber: String) { authDataSource.sendPhoneNumber(SendPhoneRequest(phoneNumber)) + } override suspend fun confirmAuthCode( phoneNumber: String, authCode: String, - ): Result = authDataSource.confirmAuthCode( - ConfirmAuthCodeRequest( - phoneNumber = phoneNumber, - authCode = authCode, + ) { + authDataSource.confirmAuthCode( + ConfirmAuthCodeRequest( + phoneNumber = phoneNumber, + authCode = authCode, + ) ) - ) + } override suspend fun signUpCenter( identifier: String, @@ -48,43 +51,38 @@ class AuthRepositoryImpl @Inject constructor( phoneNumber: String, managerName: String, businessRegistrationNumber: String - ): Result = authDataSource.signUpCenter( - SignUpCenterRequest( - identifier = identifier, - password = password, - phoneNumber = phoneNumber, - managerName = managerName, - businessRegistrationNumber = businessRegistrationNumber, + ) { + authDataSource.signUpCenter( + SignUpCenterRequest( + identifier = identifier, + password = password, + phoneNumber = phoneNumber, + managerName = managerName, + businessRegistrationNumber = businessRegistrationNumber, + ) ) - ) + } - override suspend fun signInCenter(identifier: String, password: String): Result = - authDataSource.signInCenter( + override suspend fun signInCenter(identifier: String, password: String) { + val tokenResponse: TokenResponse = authDataSource.signInCenter( SignInCenterRequest(identifier = identifier, password = password) - ).fold( - onSuccess = { tokenResponse -> - coroutineScope { - handleSignInSuccess(tokenResponse, UserType.CENTER.apiValue) - - val profile = profileRepository.getMyCenterProfile().getOrNull() - if (profile != null) { - userInfoDataSource.setUserInfo(profile.toString()) - } - - Result.success(Unit) - } - }, - onFailure = { Result.failure(it) } ) + coroutineScope { + handleSignInSuccess(tokenResponse, UserType.CENTER.apiValue) + val profile = profileRepository.getMyCenterProfile() + userInfoDataSource.setUserInfo(profile.toString()) + } + } - override suspend fun validateIdentifier(identifier: String): Result = + override suspend fun validateIdentifier(identifier: String) { authDataSource.validateIdentifier(identifier) + } override suspend fun validateBusinessRegistrationNumber( businessRegistrationNumber: String, - ): Result = + ): BusinessRegistrationInfo = authDataSource.validateBusinessRegistrationNumber(businessRegistrationNumber) - .mapCatching { it.toVO() } + .toVO() override suspend fun signUpWorker( name: String, @@ -93,93 +91,79 @@ class AuthRepositoryImpl @Inject constructor( phoneNumber: String, roadNameAddress: String, lotNumberAddress: String, - ): Result = authDataSource.signUpWorker( - SignUpWorkerRequest( - name = name, - birthYear = birthYear, - genderType = genderType, - phoneNumber = phoneNumber, - roadNameAddress = roadNameAddress, - lotNumberAddress = lotNumberAddress, + ) { + val tokenResponse: TokenResponse = authDataSource.signUpWorker( + SignUpWorkerRequest( + name = name, + birthYear = birthYear, + genderType = genderType, + phoneNumber = phoneNumber, + roadNameAddress = roadNameAddress, + lotNumberAddress = lotNumberAddress, + ) ) - ).fold( - onSuccess = { tokenResponse -> - coroutineScope { - handleSignInSuccess(tokenResponse, UserType.WORKER.apiValue) - - val profile = profileRepository.getMyWorkerProfile().getOrNull() - if (profile != null) { - userInfoDataSource.setUserInfo(profile.toString()) - } - - Result.success(Unit) - } - }, - onFailure = { Result.failure(it) } - ) + coroutineScope { + handleSignInSuccess(tokenResponse, UserType.WORKER.apiValue) + val profile = profileRepository.getMyWorkerProfile() + userInfoDataSource.setUserInfo(profile.toString()) + } + } override suspend fun signInWorker( phoneNumber: String, authCode: String, - ): Result = authDataSource.signInWorker( - SignInWorkerRequest(phoneNumber = phoneNumber, authCode = authCode) - ).fold( - onSuccess = { tokenResponse -> - coroutineScope { - handleSignInSuccess(tokenResponse, UserType.WORKER.apiValue) - - val updatedProfile = profileRepository.getMyWorkerProfile().getOrNull() - if (updatedProfile != null) { - userInfoDataSource.setUserInfo(updatedProfile.toString()) - } - - Result.success(Unit) - } - }, - onFailure = { Result.failure(it) } - ) - - override suspend fun logoutWorker(): Result { - tokenRepository.deleteDeviceToken(getDeviceToken()) - - return authDataSource.logoutWorker() - .onSuccess { clearUserData() } + ) { + val tokenResponse: TokenResponse = authDataSource.signInWorker( + SignInWorkerRequest(phoneNumber = phoneNumber, authCode = authCode) + ) + coroutineScope { + handleSignInSuccess(tokenResponse, UserType.WORKER.apiValue) + val updatedProfile = profileRepository.getMyWorkerProfile() + userInfoDataSource.setUserInfo(updatedProfile.toString()) + } } - override suspend fun logoutCenter(): Result { + override suspend fun logoutWorker() { tokenRepository.deleteDeviceToken(getDeviceToken()) - - return authDataSource.logoutCenter() - .onSuccess { clearUserData() } + authDataSource.logoutWorker() + clearUserData() } - override suspend fun withdrawalCenter(reason: String, password: String): Result { + override suspend fun logoutCenter() { tokenRepository.deleteDeviceToken(getDeviceToken()) + authDataSource.logoutCenter() + clearUserData() + } - return authDataSource.withdrawalCenter( + override suspend fun withdrawalCenter(reason: String, password: String) { + tokenRepository.deleteDeviceToken(getDeviceToken()) + authDataSource.withdrawalCenter( WithdrawalCenterRequest(reason = reason, password = password) - ).onSuccess { clearUserData() } + ) + clearUserData() } - override suspend fun withdrawalWorker(reason: String): Result { + override suspend fun withdrawalWorker(reason: String) { tokenRepository.deleteDeviceToken(getDeviceToken()) - - return authDataSource.withdrawalWorker(WithdrawalWorkerRequest(reason)) - .onSuccess { clearUserData() } + authDataSource.withdrawalWorker(WithdrawalWorkerRequest(reason)) + clearUserData() } override suspend fun generateNewPassword( newPassword: String, phoneNumber: String - ): Result = authDataSource.generateNewPassword( - GenerateNewPasswordRequest( - newPassword = newPassword, - phoneNumber = phoneNumber + ) { + authDataSource.generateNewPassword( + GenerateNewPasswordRequest( + newPassword = newPassword, + phoneNumber = phoneNumber + ) ) - ) + } - override suspend fun sendCenterVerificationRequest(): Result = + override suspend fun sendCenterVerificationRequest() { authDataSource.sendCenterVerificationRequest() + } private suspend fun handleSignInSuccess( tokenResponse: TokenResponse, diff --git a/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt index 4192c9f0..82915c4d 100644 --- a/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt @@ -24,27 +24,30 @@ class ChatRepositoryImpl @Inject constructor( private val chatDataSource: ChatDataSource, private val localChatDataSource: LocalChatDataSource, ) : ChatRepository { - override suspend fun connectWebSocket(): Result = chatDataSource.connectWebSocket() + override suspend fun connectWebSocket() { + chatDataSource.connectWebSocket() + } - override suspend fun disconnectWebSocket(): Result = + override suspend fun disconnectWebSocket() { chatDataSource.disconnectWebSocket() + } - override suspend fun retrieveChatRooms(userId: String): Result> = runCatching { - localChatDataSource.getChatRooms(userId) + override suspend fun retrieveChatRooms(userId: String): List { + return localChatDataSource.getChatRooms(userId) } override suspend fun loadChatRooms( userId: String, userType: UserType - ): Result> = runCatching { - val chatRoomsResponse = when (userType) { - UserType.WORKER -> chatDataSource.getWorkerChatRooms() - UserType.CENTER -> chatDataSource.getCenterChatRooms() - }.getOrThrow() - - val chatRooms = chatRoomsResponse.map { - val chatRoom = it.toVO() + ): List { + val response = if (userType == UserType.WORKER) { + chatDataSource.getWorkerChatRooms() + } else { + chatDataSource.getCenterChatRooms() + } + return response.map { dto -> + val chatRoom = dto.toVO() if (!localChatDataSource.isChatRoomExist(chatRoom.id, userId)) { localChatDataSource.insertChatRoom( myId = userId, @@ -53,24 +56,23 @@ class ChatRepositoryImpl @Inject constructor( opponentId = chatRoom.opponentId, lastMessage = chatRoom.lastMessage, lastMessageTime = chatRoom.lastMessageTime, - unReadMessageCount = chatRoom.unReadMessageCount, + unReadMessageCount = chatRoom.unReadMessageCount ) ) } chatRoom } - chatRooms } override suspend fun retrieveChatRoomMessages( roomId: String, myId: String, messageId: String? - ): Result> = runCatching { - localChatDataSource.getMessages( + ): List { + return localChatDataSource.getMessages( roomId = roomId, myId = myId, - lastMessageId = messageId, + lastMessageId = messageId ) } @@ -79,20 +81,21 @@ class ChatRepositoryImpl @Inject constructor( roomId: String, myId: String, messageId: String?, - unReadMessageCount: Int?, - ): Result> = runCatching { + unReadMessageCount: Int? + ): List { if (messageId == null || !localChatDataSource.isMessageExist(roomId, myId, messageId)) { - val response = when (userType) { - UserType.WORKER -> chatDataSource.getWorkerChatRoomMessages(roomId, messageId) - UserType.CENTER -> chatDataSource.getCenterChatRoomMessages(roomId, messageId) - }.getOrThrow() + val response = if (userType == UserType.WORKER) { + chatDataSource.getWorkerChatRoomMessages(roomId, messageId) + } else { + chatDataSource.getCenterChatRoomMessages(roomId, messageId) + } val allMessages = response.chatMessageInfos .sortedBy { it.sequence } .map { it.toVO() } - val messagesToUse = unReadMessageCount?.let { - allMessages.takeLast(it) + val messagesToUse = unReadMessageCount?.let { count -> + allMessages.takeLast(count) } ?: allMessages val maxSeq = localChatDataSource.getMaxLocalSequence(roomId, myId) ?: Int.MIN_VALUE @@ -106,7 +109,7 @@ class ChatRepositoryImpl @Inject constructor( opponentId = if (myId == message.senderId) message.receiverId else message.senderId, lastMessage = message.content, lastMessageTime = message.createdAt, - unReadMessageCount = 1, + unReadMessageCount = 1 ) ) } @@ -116,47 +119,46 @@ class ChatRepositoryImpl @Inject constructor( } } - val readSequence = response.sequence - allMessages.lastOrNull()?.let { - localChatDataSource.readMessages( - roomId = roomId, - myId = myId, - senderId = it.senderId, - sequence = readSequence, - ) + response.sequence.takeIf { it >= 0 }?.let { seq -> + allMessages.lastOrNull()?.let { + localChatDataSource.readMessages( + roomId = roomId, + myId = myId, + senderId = it.senderId, + sequence = seq + ) + } } } - return@runCatching localChatDataSource.getMessages( + return localChatDataSource.getMessages( roomId = roomId, myId = myId, - lastMessageId = messageId, + lastMessageId = messageId ) } override suspend fun generateChatRooms( userType: UserType, - opponentId: String, - ): Result = - runCatching { - when (userType) { - UserType.WORKER -> chatDataSource.generateWorkerChatRoom(opponentId) - UserType.CENTER -> chatDataSource.generateCenterChatRoom(opponentId) - }.mapCatching { - it.toVO() - }.getOrThrow() + opponentId: String + ): String { + val dto = if (userType == UserType.WORKER) { + chatDataSource.generateWorkerChatRoom(opponentId) + } else { + chatDataSource.generateCenterChatRoom(opponentId) } + return dto.toVO() + } - override suspend fun subscribeChatMessage(userId: String, userType: UserType): Flow = - chatDataSource.subscribeChatMessage(userId) - .map { - val message = it.toVO() - + override suspend fun subscribeChatMessage(userId: String, userType: UserType): Flow { + return chatDataSource.subscribeChatMessage(userId) + .map { response -> + val message = response.toVO() when (message) { is ChatMessage -> { if (!localChatDataSource.isChatRoomExist( roomId = message.roomId, - myId = userId, + myId = userId ) ) { localChatDataSource.insertChatRoom( @@ -166,28 +168,26 @@ class ChatRepositoryImpl @Inject constructor( opponentId = if (userId == message.senderId) message.receiverId else message.senderId, lastMessage = message.content, lastMessageTime = message.createdAt, - unReadMessageCount = 1, + unReadMessageCount = 1 ) ) } - localChatDataSource.insertMessage(message, userId) } - is ReadMessage -> { if (message.opponentId != userId) { localChatDataSource.readMessages( roomId = message.chatroomId, myId = userId, senderId = message.opponentId, - sequence = message.sequence, + sequence = message.sequence ) } } } - message - }.retryWhen { cause, attempt -> + } + .retryWhen { cause, attempt -> if (cause is IOException && attempt < MAX_RETRY_ATTEMPTS) { connectWebSocket() delay(calculateBackoffTime(attempt.toInt())) @@ -196,6 +196,7 @@ class ChatRepositoryImpl @Inject constructor( false } } + } override suspend fun sendMessage( chatroomId: String, @@ -203,36 +204,39 @@ class ChatRepositoryImpl @Inject constructor( receiverId: String, senderName: String, content: String, - userType: UserType, - ): Result = chatDataSource.sendMessage( - userType = userType, - sendMessageRequest = SendMessageRequest( - chatroomId = chatroomId, - receiverId = receiverId, - senderName = senderName, - content = content, + userType: UserType + ) { + chatDataSource.sendMessage( + userType = userType, + sendMessageRequest = SendMessageRequest( + chatroomId = chatroomId, + receiverId = receiverId, + senderName = senderName, + content = content + ) ) - ) + } override suspend fun readMessage( chatroomId: String, myId: String, opponentId: String, userType: UserType, - sequence: Int, - ): Result = chatDataSource.readMessage( - userType = userType, - readMessageRequest = ReadMessageRequest( - chatroomId = chatroomId, - opponentId = opponentId, - sequence = sequence, + sequence: Int + ) { + chatDataSource.readMessage( + userType = userType, + readMessageRequest = ReadMessageRequest( + chatroomId = chatroomId, + opponentId = opponentId, + sequence = sequence + ) ) - ).onSuccess { localChatDataSource.readMessages( roomId = chatroomId, myId = myId, senderId = opponentId, - sequence = sequence, + sequence = sequence ) } } diff --git a/core/data/src/main/java/com/idle/data/repository/ConfigRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/ConfigRepositoryImpl.kt index d9bbd202..84872385 100644 --- a/core/data/src/main/java/com/idle/data/repository/ConfigRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/ConfigRepositoryImpl.kt @@ -9,10 +9,8 @@ import javax.inject.Inject class ConfigRepositoryImpl @Inject constructor( private val configDataSource: ConfigDataSource, ) : ConfigRepository { - override suspend fun getForceUpdate(): Result = runCatching { - configDataSource.getReferenceType( - key = ConfigDataSource.FORCE_UPDATE, - defaultValue = ForceUpdateResponse(), - ).toVO() - } + override suspend fun getForceUpdate(): ForceUpdate = configDataSource.getReferenceType( + key = ConfigDataSource.FORCE_UPDATE, + defaultValue = ForceUpdateResponse(), + ).toVO() } diff --git a/core/data/src/main/java/com/idle/data/repository/JobPostingRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/JobPostingRepositoryImpl.kt index 3f0f132e..643435b8 100644 --- a/core/data/src/main/java/com/idle/data/repository/JobPostingRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/JobPostingRepositoryImpl.kt @@ -60,7 +60,7 @@ class JobPostingRepositoryImpl @Inject constructor( applyMethod: List, applyDeadLineType: ApplyDeadlineType, applyDeadline: String?, - ): Result = jobPostingDataSource.postJobPosting( + ) = jobPostingDataSource.postJobPosting( JobPostingRequest( weekdays = weekdays.map { it.name }, startTime = startTime, @@ -113,7 +113,7 @@ class JobPostingRepositoryImpl @Inject constructor( applyMethod: List?, applyDeadLineType: ApplyDeadlineType, applyDeadline: String?, - ): Result = jobPostingDataSource.updateJobPosting( + ) = jobPostingDataSource.updateJobPosting( jobPostingId = jobPostingId, jobPostingRequest = JobPostingRequest( weekdays = weekdays.map { it.name }, @@ -142,83 +142,77 @@ class JobPostingRepositoryImpl @Inject constructor( ) ) - override suspend fun getCenterJobPostingDetail(jobPostingId: String): Result = - jobPostingDataSource.getCenterJobPostingDetail(jobPostingId) - .mapCatching { it.toVO() } + override suspend fun getCenterJobPostingDetail(jobPostingId: String): CenterJobPostingDetail = + jobPostingDataSource.getCenterJobPostingDetail(jobPostingId).toVO() - override suspend fun getWorkerJobPostingDetail(jobPostingId: String): Result = - jobPostingDataSource.getWorkerJobPostingDetail(jobPostingId) - .mapCatching { it.toVO() } + override suspend fun getWorkerJobPostingDetail(jobPostingId: String): WorkerJobPostingDetail = + jobPostingDataSource.getWorkerJobPostingDetail(jobPostingId).toVO() override suspend fun getJobPostings( next: String?, limit: Int - ): Result = jobPostingDataSource.getJobPostings( + ): WorkerJobPostingPage = jobPostingDataSource.getJobPostings( next = next, limit = limit - ).mapCatching { it.toVO() } + ).toVO() override suspend fun getJobPostingsApplied( next: String?, limit: Int - ): Result = jobPostingDataSource.getJobPostingsApplied( + ): WorkerJobPostingPage = jobPostingDataSource.getJobPostingsApplied( next = next, limit = limit - ).mapCatching { it.toVO() } + ).toVO() - override suspend fun getMyFavoritesJobPostings(): Result> = - jobPostingDataSource.getMyFavoriteJobPostings() - .mapCatching { it.toVO() } + override suspend fun getMyFavoritesJobPostings(): List = + jobPostingDataSource.getMyFavoriteJobPostings().toVO() - override suspend fun getMyFavoritesCrawlingJobPostings(): Result> = - jobPostingDataSource.getMyFavoriteCrawlingJobPostings() - .mapCatching { it.toVO() } + override suspend fun getMyFavoritesCrawlingJobPostings(): List = + jobPostingDataSource.getMyFavoriteCrawlingJobPostings().toVO() - override suspend fun getJobPostingsInProgress(): Result> = - jobPostingDataSource.getJobPostingsInProgress().mapCatching { it.toVO() } + override suspend fun getJobPostingsInProgress(): List = + jobPostingDataSource.getJobPostingsInProgress().toVO() - override suspend fun getJobPostingsCompleted(): Result> = - jobPostingDataSource.getJobPostingsCompleted().mapCatching { it.toVO() } + override suspend fun getJobPostingsCompleted(): List = + jobPostingDataSource.getJobPostingsCompleted().toVO() - override suspend fun getApplicantsCount(jobPostingId: String): Result = - jobPostingDataSource.getApplicantCount(jobPostingId).mapCatching { it.applicantCount } + override suspend fun getApplicantsCount(jobPostingId: String) = + jobPostingDataSource.getApplicantCount(jobPostingId).applicantCount override suspend fun applyJobPosting( jobPostingId: String, applyMethod: ApplyMethod, - ): Result = - jobPostingDataSource.applyJobPosting( - ApplyJobPostingRequest(jobPostingId = jobPostingId, applyMethodType = applyMethod.name) - ) + ) = jobPostingDataSource.applyJobPosting( + ApplyJobPostingRequest(jobPostingId = jobPostingId, applyMethodType = applyMethod.name) + ) override suspend fun addFavoriteJobPosting( jobPostingId: String, jobPostingType: JobPostingType, - ): Result = - jobPostingDataSource.addFavoriteJobPosting( - jobPostingId = jobPostingId, - favoriteJobPostingRequest = FavoriteJobPostingRequest(jobPostingType.name), - ) + ) = jobPostingDataSource.addFavoriteJobPosting( + jobPostingId = jobPostingId, + favoriteJobPostingRequest = FavoriteJobPostingRequest(jobPostingType.name), + ) - override suspend fun removeFavoriteJobPosting(jobPostingId: String): Result = + override suspend fun removeFavoriteJobPosting(jobPostingId: String) = jobPostingDataSource.removeFavoriteJobPosting(jobPostingId = jobPostingId) - override suspend fun getApplicants(jobPostingId: String): Result>> = - jobPostingDataSource.getApplicants(jobPostingId).mapCatching { it.toVO() } + override suspend fun getApplicants(jobPostingId: String): Pair> = + jobPostingDataSource.getApplicants(jobPostingId).toVO() - override suspend fun endJobPosting(jobPostingId: String): Result = + override suspend fun endJobPosting(jobPostingId: String) = jobPostingDataSource.endJobPosting(jobPostingId) - override suspend fun deleteJobPosting(jobPostingId: String): Result = + override suspend fun deleteJobPosting(jobPostingId: String) = jobPostingDataSource.deleteJobPosting(jobPostingId) override suspend fun getCrawlingJobPostings( next: String?, limit: Int, distance: Int, - ): Result = - jobPostingDataSource.getCrawlingJobPostings(next, limit, distance).mapCatching { it.toVO() } + ): CrawlingJobPostingPage = + jobPostingDataSource.getCrawlingJobPostings(next, limit, distance).toVO() - override suspend fun getCrawlingJobPostingDetail(jobPostingId: String): Result = - jobPostingDataSource.getCrawlingJobPostingsDetail(jobPostingId).mapCatching { it.toVO() } + override suspend fun getCrawlingJobPostingDetail(jobPostingId: String): CrawlingJobPostingDetail = + jobPostingDataSource.getCrawlingJobPostingsDetail(jobPostingId).toVO() } diff --git a/core/data/src/main/java/com/idle/data/repository/NotificationRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/NotificationRepositoryImpl.kt index 88b96f59..48f542b6 100644 --- a/core/data/src/main/java/com/idle/data/repository/NotificationRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/NotificationRepositoryImpl.kt @@ -11,14 +11,13 @@ class NotificationRepositoryImpl @Inject constructor( override suspend fun getMyNotifications( next: String?, limit: Int - ): Result>> = - notificationDataSource.getMyNotifications(next = next, limit = limit) - .map { it.toVO() } + ): Pair> = + notificationDataSource.getMyNotifications(next = next, limit = limit).toVO() - override suspend fun readNotification(notificationId: String): Result = + override suspend fun readNotification(notificationId: String) = notificationDataSource.readNotification(notificationId) - override suspend fun getUnreadNotificationCount(): Result = + override suspend fun getUnreadNotificationCount(): Int = notificationDataSource.getUnreadNotificationCount() - .mapCatching { it.unreadNotificationCount ?: 0 } + .unreadNotificationCount ?: 0 } diff --git a/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt index e0bc929c..ca23232d 100644 --- a/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt @@ -34,51 +34,50 @@ class ProfileRepositoryImpl @Inject constructor( ) : ProfileRepository { override suspend fun getMyUserType() = userInfoDataSource.userType.first() - override suspend fun getMyCenterProfile(): Result = - profileDataSource.getMyCenterProfile() - .mapCatching { it.toVO() } - .onSuccess { - userInfoDataSource.setUserInfo(it.toString()) - Result.success(it) - } + override suspend fun getMyCenterProfile(): CenterProfile { + val centerProfile = profileDataSource.getMyCenterProfile() + .toVO() + + userInfoDataSource.setUserInfo(centerProfile.toString()) + return centerProfile + } - override suspend fun getLocalMyCenterProfile(): Result = + override suspend fun getLocalMyCenterProfile(): CenterProfile = userInfoDataSource.getLocalCenterProfile() - override suspend fun getCenterProfile(centerId: String): Result = - profileDataSource.getCenterProfile(centerId).mapCatching { it.toVO() } + override suspend fun getCenterProfile(centerId: String): CenterProfile = + profileDataSource.getCenterProfile(centerId).toVO() - override suspend fun getMyWorkerProfile(): Result = - profileDataSource.getMyWorkerProfile().mapCatching { it.toVo() } - .onSuccess { - userInfoDataSource.setUserInfo(it.toString()) - Result.success(it) - } + override suspend fun getMyWorkerProfile(): WorkerProfile { + val workerProfile = profileDataSource.getMyWorkerProfile().toVo() - override suspend fun getLocalMyWorkerProfile(): Result = + userInfoDataSource.setUserInfo(workerProfile.toString()) + return workerProfile + } + + override suspend fun getLocalMyWorkerProfile(): WorkerProfile = userInfoDataSource.getLocalWorkerProfile() - override suspend fun getWorkerProfile(workerId: String): Result = - profileDataSource.getWorkerProfile(workerId).mapCatching { it.toVo() } + override suspend fun getWorkerProfile(workerId: String): WorkerProfile = + profileDataSource.getWorkerProfile(workerId).toVo() override suspend fun updateCenterProfile( officeNumber: String, introduce: String?, - ): Result = profileDataSource.updateMyCenterProfile( - UpdateCenterProfileRequest(officeNumber = officeNumber, introduce = introduce) - ).onSuccess { - val updatedProfile = getMyCenterProfile().getOrNull() - if (updatedProfile != null) { - userInfoDataSource.setUserInfo(updatedProfile.toString()) - } + ) { + profileDataSource.updateMyCenterProfile( + UpdateCenterProfileRequest(officeNumber = officeNumber, introduce = introduce) + ) + + val updatedProfile = getMyCenterProfile() + userInfoDataSource.setUserInfo(updatedProfile.toString()) } - override suspend fun getWorkerId(): Result = profileDataSource.getWorkerId() - .mapCatching { it.carerId } + override suspend fun getWorkerId(): String = profileDataSource.getWorkerId() + .carerId - override suspend fun getCenterStatus(): Result = - profileDataSource.getCenterStatus() - .mapCatching { it.toVO() } + override suspend fun getCenterStatus(): CenterRegistrationStatus = + profileDataSource.getCenterStatus().toVO() override suspend fun updateWorkerProfile( experienceYear: Int?, @@ -87,20 +86,20 @@ class ProfileRepositoryImpl @Inject constructor( jobSearchStatus: JobSearchStatus, introduce: String?, speciality: String - ): Result = profileDataSource.updateWorkerProfile( - UpdateWorkerProfileRequest( - experienceYear = experienceYear, - roadNameAddress = roadNameAddress, - lotNumberAddress = lotNumberAddress, - jobSearchStatus = jobSearchStatus.name, - introduce = introduce, - speciality = speciality + ) { + profileDataSource.updateWorkerProfile( + UpdateWorkerProfileRequest( + experienceYear = experienceYear, + roadNameAddress = roadNameAddress, + lotNumberAddress = lotNumberAddress, + jobSearchStatus = jobSearchStatus.name, + introduce = introduce, + speciality = speciality + ) ) - ).onSuccess { - val updatedProfile = getMyWorkerProfile().getOrNull() - if (updatedProfile != null) { - userInfoDataSource.setUserInfo(updatedProfile.toString()) - } + + val updatedProfile = getMyWorkerProfile() + userInfoDataSource.setUserInfo(updatedProfile.toString()) } override suspend fun registerCenterProfile( @@ -110,7 +109,7 @@ class ProfileRepositoryImpl @Inject constructor( lotNumberAddress: String, officeNumber: String, roadNameAddress: String - ): Result = profileDataSource.registerCenterProfile( + ) = profileDataSource.registerCenterProfile( RegisterCenterProfileRequest( centerName = centerName, detailedAddress = detailedAddress, @@ -126,7 +125,7 @@ class ProfileRepositoryImpl @Inject constructor( imageFileUri: String, reqWidth: Int, reqHeight: Int - ): Result = runCatching { + ) { val resizeImage = resizeImage( context = context, uri = imageFileUri.toUri(), @@ -142,30 +141,30 @@ class ProfileRepositoryImpl @Inject constructor( val profileImageUploadUrlResponse = getProfileImageUploadUrl( userType = userType, imageFileExtension = imageFormat.name, - ).getOrThrow() + ) uploadProfileImage( uploadUrl = profileImageUploadUrlResponse.uploadUrl, imageFileExtension = profileImageUploadUrlResponse.imageFileExtension, imageInputStream = inputStream, - ).getOrThrow() + ) callbackImageUpload( userType = userType, imageId = profileImageUploadUrlResponse.imageId, imageFileExtension = profileImageUploadUrlResponse.imageFileExtension - ).getOrThrow() + ) when (userType) { UserType.CENTER.apiValue -> { - val updatedProfile = getMyCenterProfile().getOrThrow() + val updatedProfile = getMyCenterProfile() .copy(profileImageUrl = imageFileUri) userInfoDataSource.setUserInfo(updatedProfile.toString()) } UserType.WORKER.apiValue -> { - val updatedProfile = getMyWorkerProfile().getOrThrow() + val updatedProfile = getMyWorkerProfile() .copy(profileImageUrl = imageFileUri) userInfoDataSource.setUserInfo(updatedProfile.toString()) @@ -237,14 +236,14 @@ class ProfileRepositoryImpl @Inject constructor( private suspend fun getProfileImageUploadUrl( userType: String, imageFileExtension: String - ): Result = + ): UploadProfileImageUrlResponse = profileDataSource.getProfileImageUploadUrl(userType, imageFileExtension) private suspend fun uploadProfileImage( uploadUrl: String, imageFileExtension: String, imageInputStream: InputStream, - ): Result = profileDataSource.uploadProfileImage( + ) = profileDataSource.uploadProfileImage( uploadUrl = uploadUrl, imageFileExtension = imageFileExtension, imageInputStream = imageInputStream, @@ -254,7 +253,7 @@ class ProfileRepositoryImpl @Inject constructor( userType: String, imageId: String, imageFileExtension: String - ): Result = profileDataSource.callbackImageUpload( + ) = profileDataSource.callbackImageUpload( userType = userType, callbackImageUploadRequest = CallbackImageUploadRequest( imageId = imageId, diff --git a/core/data/src/main/java/com/idle/data/repository/TokenRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/TokenRepositoryImpl.kt index ecaa9cf3..c308186e 100644 --- a/core/data/src/main/java/com/idle/data/repository/TokenRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/TokenRepositoryImpl.kt @@ -18,7 +18,7 @@ class TokenRepositoryImpl @Inject constructor( tokenDataSource.accessToken.first() } - override suspend fun postDeviceToken(deviceToken: String, userType: String): Result = + override suspend fun postDeviceToken(deviceToken: String, userType: String) = notificationDataSource.postFCMToken( PostFcmTokenRequest( deviceToken = deviceToken, @@ -26,6 +26,6 @@ class TokenRepositoryImpl @Inject constructor( ) ) - override suspend fun deleteDeviceToken(deviceToken: String): Result = + override suspend fun deleteDeviceToken(deviceToken: String) = notificationDataSource.deleteFCMToken(DeleteFcmTokenRequest(deviceToken)) } diff --git a/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt b/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt index 5328b23e..11908800 100644 --- a/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt +++ b/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt @@ -37,7 +37,7 @@ class UserInfoDataSource @Inject constructor( dataStore.clear(USER_INFO) } - suspend fun getLocalCenterProfile(): Result = runCatching { + suspend fun getLocalCenterProfile(): CenterProfile { val userInfoString = userInfo.first().takeIf { it.isNotBlank() } ?: throw NullPointerException("Missing UserInfo") @@ -54,7 +54,7 @@ class UserInfoDataSource @Inject constructor( key to value } - CenterProfile( + return CenterProfile( centerId = properties["centerId"] ?: throw NullPointerException("Missing CenterId"), centerName = properties["centerName"] ?: throw NullPointerException("Missing centerName"), @@ -75,7 +75,7 @@ class UserInfoDataSource @Inject constructor( ) } - suspend fun getLocalWorkerProfile(): Result = runCatching { + suspend fun getLocalWorkerProfile(): WorkerProfile { val userInfoString = userInfo.first().takeIf { it.isNotBlank() } ?: throw NullPointerException("Missing UserInfo") @@ -92,7 +92,7 @@ class UserInfoDataSource @Inject constructor( key to value } - WorkerProfile( + return WorkerProfile( workerId = properties["workerId"] ?: throw NullPointerException("Missing workerId"), workerName = properties["workerName"] ?: throw NullPointerException("Missing workerName"), diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/AuthRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/AuthRepository.kt index 9f770a6a..8bcdd791 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/AuthRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/AuthRepository.kt @@ -3,12 +3,11 @@ package com.idle.domain.repositorry import com.idle.domain.model.auth.BusinessRegistrationInfo interface AuthRepository { - suspend fun sendPhoneNumber(phoneNumber: String): Result - suspend fun confirmAuthCode(phoneNumber: String, authCode: String): Result - - suspend fun validateIdentifier(identifier: String): Result + suspend fun sendPhoneNumber(phoneNumber: String) + suspend fun confirmAuthCode(phoneNumber: String, authCode: String) + suspend fun validateIdentifier(identifier: String) suspend fun validateBusinessRegistrationNumber(businessRegistrationNumber: String): - Result + BusinessRegistrationInfo suspend fun signUpWorker( name: String, @@ -17,7 +16,7 @@ interface AuthRepository { phoneNumber: String, roadNameAddress: String, lotNumberAddress: String, - ): Result + ) suspend fun signUpCenter( identifier: String, @@ -25,17 +24,15 @@ interface AuthRepository { phoneNumber: String, managerName: String, businessRegistrationNumber: String, - ): Result - - suspend fun signInWorker(phoneNumber: String, authCode: String): Result - suspend fun signInCenter(identifier: String, password: String): Result - - suspend fun logoutWorker(): Result - suspend fun logoutCenter(): Result + ) - suspend fun withdrawalCenter(reason: String, password: String): Result - suspend fun withdrawalWorker(reason: String): Result + suspend fun signInWorker(phoneNumber: String, authCode: String) + suspend fun signInCenter(identifier: String, password: String) + suspend fun logoutWorker() + suspend fun logoutCenter() - suspend fun generateNewPassword(newPassword: String, phoneNumber: String): Result - suspend fun sendCenterVerificationRequest(): Result + suspend fun withdrawalCenter(reason: String, password: String) + suspend fun withdrawalWorker(reason: String) + suspend fun generateNewPassword(newPassword: String, phoneNumber: String) + suspend fun sendCenterVerificationRequest() } diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ChatRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ChatRepository.kt index 5de82b42..f4ec2b3d 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ChatRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ChatRepository.kt @@ -8,20 +8,19 @@ import com.idle.domain.model.chat.Message import kotlinx.coroutines.flow.Flow interface ChatRepository { - suspend fun connectWebSocket(): Result - suspend fun disconnectWebSocket(): Result - - suspend fun retrieveChatRooms(userId: String): Result> + suspend fun connectWebSocket() + suspend fun disconnectWebSocket() + suspend fun retrieveChatRooms(userId: String): List suspend fun loadChatRooms( userId: String, userType: UserType - ): Result> + ): List suspend fun retrieveChatRoomMessages( roomId: String, myId: String, messageId: String?, - ): Result> + ): List suspend fun getChatRoomMessages( userType: UserType, @@ -29,12 +28,12 @@ interface ChatRepository { myId: String, messageId: String?, unReadMessageCount: Int?, - ): Result> + ): List suspend fun generateChatRooms( userType: UserType, opponentId: String, - ): Result + ): String suspend fun subscribeChatMessage( userId: String, @@ -48,7 +47,7 @@ interface ChatRepository { senderName: String, content: String, userType: UserType, - ): Result + ) suspend fun readMessage( chatroomId: String, @@ -56,5 +55,5 @@ interface ChatRepository { opponentId: String, userType: UserType, sequence: Int, - ): Result + ) } diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ConfigRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ConfigRepository.kt index 060afc38..b35b1ef6 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ConfigRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ConfigRepository.kt @@ -3,5 +3,5 @@ package com.idle.domain.repositorry import com.idle.domain.model.config.ForceUpdate interface ConfigRepository { - suspend fun getForceUpdate(): Result + suspend fun getForceUpdate(): ForceUpdate } diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/JobPostingRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/JobPostingRepository.kt index 73b88659..40e5aee3 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/JobPostingRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/JobPostingRepository.kt @@ -47,7 +47,7 @@ interface JobPostingRepository { applyMethod: List, applyDeadLineType: ApplyDeadlineType, applyDeadline: String?, - ): Result + ) suspend fun updateJobPosting( jobPostingId: String, @@ -74,52 +74,51 @@ interface JobPostingRepository { applyMethod: List?, applyDeadLineType: ApplyDeadlineType, applyDeadline: String?, - ): Result + ) - suspend fun getCenterJobPostingDetail(jobPostingId: String): Result + suspend fun getCenterJobPostingDetail(jobPostingId: String): CenterJobPostingDetail - suspend fun getWorkerJobPostingDetail(jobPostingId: String): Result + suspend fun getWorkerJobPostingDetail(jobPostingId: String): WorkerJobPostingDetail suspend fun getJobPostings( next: String?, limit: Int = 10, - ): Result + ): WorkerJobPostingPage suspend fun getJobPostingsApplied( next: String?, limit: Int = 10, - ): Result + ): WorkerJobPostingPage - suspend fun getMyFavoritesJobPostings(): Result> + suspend fun getMyFavoritesJobPostings(): List - suspend fun getMyFavoritesCrawlingJobPostings(): Result> + suspend fun getMyFavoritesCrawlingJobPostings(): List - suspend fun getJobPostingsInProgress(): Result> + suspend fun getJobPostingsInProgress(): List - suspend fun getJobPostingsCompleted(): Result> + suspend fun getJobPostingsCompleted(): List - suspend fun getApplicantsCount(jobPostingId: String): Result + suspend fun getApplicantsCount(jobPostingId: String): Int - suspend fun applyJobPosting(jobPostingId: String, applyMethod: ApplyMethod): Result + suspend fun applyJobPosting(jobPostingId: String, applyMethod: ApplyMethod) suspend fun addFavoriteJobPosting( jobPostingId: String, jobPostingType: JobPostingType, - ): Result + ) - suspend fun removeFavoriteJobPosting(jobPostingId: String): Result + suspend fun removeFavoriteJobPosting(jobPostingId: String) - suspend fun getApplicants(jobPostingId: String): Result>> + suspend fun getApplicants(jobPostingId: String): Pair> - suspend fun endJobPosting(jobPostingId: String): Result - - suspend fun deleteJobPosting(jobPostingId: String): Result + suspend fun endJobPosting(jobPostingId: String) + suspend fun deleteJobPosting(jobPostingId: String) suspend fun getCrawlingJobPostings( next: String?, limit: Int = 10, distance: Int = 15, - ): Result + ): CrawlingJobPostingPage - suspend fun getCrawlingJobPostingDetail(jobPostingId: String): Result + suspend fun getCrawlingJobPostingDetail(jobPostingId: String): CrawlingJobPostingDetail } diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/NotificationRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/NotificationRepository.kt index 1890d778..ee1ab3fd 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/NotificationRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/NotificationRepository.kt @@ -6,8 +6,8 @@ interface NotificationRepository { suspend fun getMyNotifications( next: String?, limit: Int = 10, - ): Result>> + ): Pair> - suspend fun readNotification(notificationId: String): Result - suspend fun getUnreadNotificationCount(): Result + suspend fun readNotification(notificationId: String) + suspend fun getUnreadNotificationCount(): Int } diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt index b294edf3..c9a391a4 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt @@ -7,21 +7,15 @@ import com.idle.domain.model.profile.WorkerProfile interface ProfileRepository { suspend fun getMyUserType(): String - - suspend fun getMyCenterProfile(): Result - - suspend fun getLocalMyCenterProfile(): Result - - suspend fun getCenterProfile(centerId: String): Result - - suspend fun getMyWorkerProfile(): Result - - suspend fun getLocalMyWorkerProfile(): Result - - suspend fun getWorkerProfile(workerId: String): Result - - suspend fun updateCenterProfile(officeNumber: String, introduce: String?): Result - + suspend fun getMyCenterProfile(): CenterProfile + suspend fun getLocalMyCenterProfile(): CenterProfile + suspend fun getCenterProfile(centerId: String): CenterProfile + suspend fun getMyWorkerProfile(): WorkerProfile + suspend fun getLocalMyWorkerProfile(): WorkerProfile + suspend fun getWorkerProfile(workerId: String): WorkerProfile + suspend fun updateCenterProfile(officeNumber: String, introduce: String?) + suspend fun getWorkerId(): String + suspend fun getCenterStatus(): CenterRegistrationStatus suspend fun updateWorkerProfile( experienceYear: Int?, roadNameAddress: String, @@ -29,7 +23,7 @@ interface ProfileRepository { jobSearchStatus: JobSearchStatus, introduce: String?, speciality: String, - ): Result + ) suspend fun registerCenterProfile( centerName: String, @@ -38,16 +32,12 @@ interface ProfileRepository { lotNumberAddress: String, officeNumber: String, roadNameAddress: String, - ): Result + ) suspend fun updateProfileImage( userType: String, imageFileUri: String, reqWidth: Int, reqHeight: Int - ): Result - - suspend fun getWorkerId(): Result - - suspend fun getCenterStatus(): Result + ) } diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/TokenRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/TokenRepository.kt index 152ad3bf..d5e02cfd 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/TokenRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/TokenRepository.kt @@ -2,6 +2,6 @@ package com.idle.domain.repositorry interface TokenRepository { suspend fun getAccessToken(): String - suspend fun postDeviceToken(deviceToken: String, userType: String): Result - suspend fun deleteDeviceToken(deviceToken: String): Result + suspend fun postDeviceToken(deviceToken: String, userType: String) + suspend fun deleteDeviceToken(deviceToken: String) } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt index 7783b9fa..02736b69 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt @@ -3,9 +3,6 @@ package com.idle.domain.usecase.chat import com.idle.domain.model.auth.UserType import com.idle.domain.model.chat.ChatRoom import com.idle.domain.model.chat.ChatRoomWithOpponentInfo -import com.idle.domain.model.profile.CenterProfile -import com.idle.domain.model.profile.Profile -import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.ProfileRepository import kotlinx.coroutines.async @@ -20,40 +17,39 @@ class GetChatRoomsUseCase @Inject constructor( suspend operator fun invoke( userType: UserType, userId: String, - ): Result> = runCatching { - val chatRooms = chatRepository.retrieveChatRooms(userId).getOrThrow() + ): List = coroutineScope { + // 로컬 저장소에서 채팅 방 목록 조회 + val chatRooms: List = chatRepository.retrieveChatRooms(userId) - coroutineScope { - val opponentProfiles = chatRooms.map { chatRoom -> - async { - when (userType) { - UserType.CENTER -> - profileRepository.getWorkerProfile(chatRoom.opponentId).getOrThrow() + val opponentProfiles = chatRooms.map { chatRoom -> + async { + when (userType) { + UserType.CENTER -> + profileRepository.getWorkerProfile(chatRoom.opponentId) - UserType.WORKER -> - profileRepository.getCenterProfile(chatRoom.opponentId).getOrThrow() - } + UserType.WORKER -> + profileRepository.getCenterProfile(chatRoom.opponentId) } - }.awaitAll() + } + }.awaitAll() - chatRooms.zip(opponentProfiles) { chatRoom, profile -> - mapToRoomWithOpponentInfo(chatRoom, profile) - }.sortedBy { it.lastMessageTime } + // 채팅 방과 상대 프로필을 결합하여 UI용 모델 생성 + chatRooms.zip(opponentProfiles) { chatRoom, profile -> + mapToRoomWithOpponentInfo(chatRoom, profile) } + .sortedBy { it.lastMessageTime } } private fun mapToRoomWithOpponentInfo( chatRoom: ChatRoom, - profile: Profile, + profile: com.idle.domain.model.profile.Profile, ): ChatRoomWithOpponentInfo { val (opponentName, profileUrl) = when (profile) { - is WorkerProfile -> { + is com.idle.domain.model.profile.WorkerProfile -> profile.workerName to profile.profileImageUrl - } - is CenterProfile -> { + is com.idle.domain.model.profile.CenterProfile -> profile.centerName to profile.profileImageUrl - } } return ChatRoomWithOpponentInfo( diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/jobposting/GetJobPostingsInProgressUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/jobposting/GetJobPostingsInProgressUseCase.kt index 0ccbff94..87dba94f 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/jobposting/GetJobPostingsInProgressUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/jobposting/GetJobPostingsInProgressUseCase.kt @@ -9,16 +9,14 @@ class GetJobPostingsInProgressUseCase @Inject constructor( private val jobPostingRepository: JobPostingRepository, ) { suspend operator fun invoke() = coroutineScope { - val jobPosting = jobPostingRepository.getJobPostingsInProgress() - jobPosting.mapCatching { jobPostings -> - val deferredResults = jobPostings.map { jobPosting -> - async { - val applicantCount = jobPostingRepository.getApplicantsCount(jobPosting.id).getOrThrow() - jobPosting.copy(applicantCount = applicantCount) - } + val jobPostings = jobPostingRepository.getJobPostingsInProgress() + val deferredResults = jobPostings.map { posting -> + async { + val applicantCount = jobPostingRepository.getApplicantsCount(posting.id) + posting.copy(applicantCount = applicantCount) } - - deferredResults.map { it.await() } } + + deferredResults.map { it.await() } } } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt index cd5e66b2..681023f5 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt @@ -7,9 +7,9 @@ import javax.inject.Inject class GetMyCenterProfileUseCase @Inject constructor( private val profileRepository: ProfileRepository ) { - suspend operator fun invoke(): Result { - return profileRepository.getLocalMyCenterProfile().recoverCatching { - profileRepository.getMyCenterProfile().getOrThrow() - } + suspend operator fun invoke(): CenterProfile = try { + profileRepository.getLocalMyCenterProfile() + } catch (e: Exception) { + profileRepository.getMyCenterProfile() } } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt index 20da3252..5a2faa38 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt @@ -7,9 +7,9 @@ import javax.inject.Inject class GetMyWorkerProfileUseCase @Inject constructor( private val profileRepository: ProfileRepository ) { - suspend operator fun invoke(): Result { - return profileRepository.getLocalMyWorkerProfile().recoverCatching { - profileRepository.getMyWorkerProfile().getOrThrow() - } + suspend operator fun invoke(): WorkerProfile = try { + profileRepository.getLocalMyWorkerProfile() + } catch (e: Exception) { + profileRepository.getMyWorkerProfile() } } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt index 956f064b..d996baaf 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt @@ -17,34 +17,32 @@ class RegisterCenterProfileUseCase @Inject constructor( officeNumber: String, roadNameAddress: String, imageFileUri: String?, - ) = runCatching { - coroutineScope { - val registerProfileJob = launch { - profileRepository.registerCenterProfile( - centerName = centerName, - detailedAddress = detailedAddress, - introduce = introduce, - lotNumberAddress = lotNumberAddress, - officeNumber = officeNumber, - roadNameAddress = roadNameAddress - ).getOrThrow() - } - - val updateProfileImageJob = imageFileUri?.let { uri -> - if (uri.startsWith("content://")) { - launch { - profileRepository.updateProfileImage( - userType = UserType.CENTER.apiValue, - imageFileUri = uri, - reqWidth = 1340, - reqHeight = 1016, - ).getOrThrow() - } - } else null - } + ) = coroutineScope { + val registerProfileJob = launch { + profileRepository.registerCenterProfile( + centerName = centerName, + detailedAddress = detailedAddress, + introduce = introduce, + lotNumberAddress = lotNumberAddress, + officeNumber = officeNumber, + roadNameAddress = roadNameAddress + ) + } - registerProfileJob.join() - updateProfileImageJob?.join() + val updateProfileImageJob = imageFileUri?.let { uri -> + if (uri.startsWith("content://")) { + launch { + profileRepository.updateProfileImage( + userType = UserType.CENTER.apiValue, + imageFileUri = uri, + reqWidth = 1340, + reqHeight = 1016, + ) + } + } else null } + + registerProfileJob.join() + updateProfileImageJob?.join() } } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt index 6325b910..90b10367 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt @@ -13,30 +13,28 @@ class UpdateCenterProfileUseCase @Inject constructor( officeNumber: String, introduce: String?, imageFileUri: String?, - ): Result = runCatching { - coroutineScope { - val updateProfileJob = launch { - profileRepository.updateCenterProfile( - officeNumber = officeNumber, - introduce = introduce, - ).getOrThrow() - } - - val updateProfileImageJob = imageFileUri?.let { uri -> - if (uri.startsWith("content://")) { - launch { - profileRepository.updateProfileImage( - userType = UserType.CENTER.apiValue, - imageFileUri = uri, - reqWidth = 1340, - reqHeight = 1016, - ).getOrThrow() - } - } else null - } + ) = coroutineScope { + val updateProfileJob = launch { + profileRepository.updateCenterProfile( + officeNumber = officeNumber, + introduce = introduce, + ) + } - updateProfileJob.join() - updateProfileImageJob?.join() + val updateProfileImageJob = imageFileUri?.let { uri -> + if (uri.startsWith("content://")) { + launch { + profileRepository.updateProfileImage( + userType = UserType.CENTER.apiValue, + imageFileUri = uri, + reqWidth = 1340, + reqHeight = 1016, + ) + } + } else null } + + updateProfileJob.join() + updateProfileImageJob?.join() } } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt index c02f1f33..5f312f27 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt @@ -18,34 +18,32 @@ class UpdateWorkerProfileUseCase @Inject constructor( speciality: String, jobSearchStatus: JobSearchStatus, imageFileUri: String?, - ) = runCatching { - coroutineScope { - val updateProfileJob = launch { - profileRepository.updateWorkerProfile( - experienceYear = experienceYear, - roadNameAddress = roadNameAddress, - lotNumberAddress = lotNumberAddress, - jobSearchStatus = jobSearchStatus, - introduce = introduce, - speciality = speciality - ).getOrThrow() - } - - val updateProfileImageJob = imageFileUri?.let { uri -> - if (uri.startsWith("content://")) { - launch { - profileRepository.updateProfileImage( - userType = UserType.WORKER.apiValue, - imageFileUri = uri, - reqWidth = 384, - reqHeight = 384, - ).getOrThrow() - } - } else null - } + ) = coroutineScope { + val updateProfileJob = launch { + profileRepository.updateWorkerProfile( + experienceYear = experienceYear, + roadNameAddress = roadNameAddress, + lotNumberAddress = lotNumberAddress, + jobSearchStatus = jobSearchStatus, + introduce = introduce, + speciality = speciality + ) + } - updateProfileJob.join() - updateProfileImageJob?.join() + val updateProfileImageJob = imageFileUri?.let { uri -> + if (uri.startsWith("content://")) { + launch { + profileRepository.updateProfileImage( + userType = UserType.WORKER.apiValue, + imageFileUri = uri, + reqWidth = 384, + reqHeight = 384, + ) + } + } else null } + + updateProfileJob.join() + updateProfileImageJob?.join() } } diff --git a/core/network/src/main/java/com/idle/network/authenticator/CareAuthenticator.kt b/core/network/src/main/java/com/idle/network/authenticator/CareAuthenticator.kt index 1ede0560..168121fb 100644 --- a/core/network/src/main/java/com/idle/network/authenticator/CareAuthenticator.kt +++ b/core/network/src/main/java/com/idle/network/authenticator/CareAuthenticator.kt @@ -46,12 +46,16 @@ class CareAuthenticator @Inject constructor( return null } - val token = runBlocking { - lock.withLock { - authApi.get().refreshToken(RefreshTokenRequest(tokenManager.getRefreshToken())) - .onResponse() + val token = try { + runBlocking { + lock.withLock { + authApi.get().refreshToken(RefreshTokenRequest(tokenManager.getRefreshToken())) + .onResponse() + } } - }.getOrNull() ?: return null + } catch (e: Exception) { + return null + } runBlocking { val job = launch { tokenManager.setRefreshToken(token.refreshToken) } diff --git a/core/network/src/main/java/com/idle/network/source/AuthDataSource.kt b/core/network/src/main/java/com/idle/network/source/AuthDataSource.kt index 1c91c92b..26c82449 100644 --- a/core/network/src/main/java/com/idle/network/source/AuthDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/AuthDataSource.kt @@ -13,7 +13,7 @@ import com.idle.network.model.auth.SignUpWorkerRequest import com.idle.network.model.auth.WithdrawalCenterRequest import com.idle.network.model.auth.WithdrawalWorkerRequest import com.idle.network.model.token.TokenResponse -import com.idle.network.util.safeApiCall +import com.idle.network.util.onResponse import kotlinx.coroutines.suspendCancellableCoroutine import javax.inject.Inject import javax.inject.Singleton @@ -25,48 +25,48 @@ class AuthDataSource @Inject constructor( private val authApi: AuthApi, private val firebaseMessaging: FirebaseMessaging, ) { - suspend fun sendPhoneNumber(sendPhoneRequest: SendPhoneRequest): Result = - safeApiCall { authApi.sendPhoneNumber(sendPhoneRequest) } + suspend fun sendPhoneNumber(sendPhoneRequest: SendPhoneRequest): Unit = + authApi.sendPhoneNumber(sendPhoneRequest).onResponse() - suspend fun confirmAuthCode(confirmAuthCodeRequest: ConfirmAuthCodeRequest): Result = - safeApiCall { authApi.confirmAuthCode(confirmAuthCodeRequest) } + suspend fun confirmAuthCode(confirmAuthCodeRequest: ConfirmAuthCodeRequest): Unit = + authApi.confirmAuthCode(confirmAuthCodeRequest).onResponse() - suspend fun signUpCenter(signUpCenterRequest: SignUpCenterRequest): Result = - safeApiCall { authApi.signUpCenter(signUpCenterRequest) } + suspend fun signUpCenter(signUpCenterRequest: SignUpCenterRequest): Unit = + authApi.signUpCenter(signUpCenterRequest).onResponse() - suspend fun signInCenter(signInCenterRequest: SignInCenterRequest): Result = - safeApiCall { authApi.signInCenter(signInCenterRequest) } + suspend fun signInCenter(signInCenterRequest: SignInCenterRequest): TokenResponse = + authApi.signInCenter(signInCenterRequest).onResponse() - suspend fun signUpWorker(signUpWorkerRequest: SignUpWorkerRequest): Result = - safeApiCall { authApi.signUpWorker(signUpWorkerRequest) } + suspend fun signUpWorker(signUpWorkerRequest: SignUpWorkerRequest): TokenResponse = + authApi.signUpWorker(signUpWorkerRequest).onResponse() - suspend fun signInWorker(signInWorkerRequest: SignInWorkerRequest): Result = - safeApiCall { authApi.signInWorker(signInWorkerRequest) } + suspend fun signInWorker(signInWorkerRequest: SignInWorkerRequest): TokenResponse = + authApi.signInWorker(signInWorkerRequest).onResponse() - suspend fun logoutWorker(): Result = safeApiCall { authApi.logoutWorker() } + suspend fun logoutWorker(): Unit = authApi.logoutWorker().onResponse() - suspend fun logoutCenter(): Result = safeApiCall { authApi.logoutCenter() } + suspend fun logoutCenter(): Unit = authApi.logoutCenter().onResponse() - suspend fun withdrawalCenter(withdrawalCenterRequest: WithdrawalCenterRequest): Result = - safeApiCall { authApi.withdrawalCenter(withdrawalCenterRequest) } + suspend fun withdrawalCenter(withdrawalCenterRequest: WithdrawalCenterRequest): Unit = + authApi.withdrawalCenter(withdrawalCenterRequest).onResponse() - suspend fun withdrawalWorker(withdrawalWorkerRequest: WithdrawalWorkerRequest): Result = - safeApiCall { authApi.withdrawalWorker(withdrawalWorkerRequest) } + suspend fun withdrawalWorker(withdrawalWorkerRequest: WithdrawalWorkerRequest): Unit = + authApi.withdrawalWorker(withdrawalWorkerRequest).onResponse() - suspend fun validateIdentifier(identifier: String): Result = - safeApiCall { authApi.validateIdentifier(identifier) } + suspend fun validateIdentifier(identifier: String): Unit = + authApi.validateIdentifier(identifier).onResponse() suspend fun validateBusinessRegistrationNumber( businessRegistrationNumber: String - ): Result = - safeApiCall { authApi.validateBusinessRegistrationNumber(businessRegistrationNumber) } + ): BusinessRegistrationResponse = + authApi.validateBusinessRegistrationNumber(businessRegistrationNumber).onResponse() suspend fun generateNewPassword( generateNewPasswordRequest: GenerateNewPasswordRequest - ): Result = safeApiCall { authApi.generateNewPassword(generateNewPasswordRequest) } + ): Unit = authApi.generateNewPassword(generateNewPasswordRequest).onResponse() - suspend fun sendCenterVerificationRequest(): Result = - safeApiCall { authApi.sendCenterVerificationRequest() } + suspend fun sendCenterVerificationRequest(): Unit = + authApi.sendCenterVerificationRequest().onResponse() suspend fun getDeviceToken(): String = suspendCancellableCoroutine { continuation -> firebaseMessaging.token.addOnCompleteListener { task -> diff --git a/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt b/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt index d40f815b..839205bd 100644 --- a/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt @@ -1,7 +1,6 @@ package com.idle.network.source import com.idle.domain.model.auth.UserType -import com.idle.network.BuildConfig import com.idle.network.api.ChatApi import com.idle.network.di.TokenManager import com.idle.network.model.chat.ChatResponse @@ -14,7 +13,7 @@ import com.idle.network.serializer.ChatResponseSerializer import com.idle.network.util.MAX_RETRY_ATTEMPTS import com.idle.network.util.MAX_WAIT_TIME import com.idle.network.util.calculateBackoffTime -import com.idle.network.util.safeApiCall +import com.idle.network.util.onResponse import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -36,95 +35,87 @@ class ChatDataSource @Inject constructor( private val chatResponseSerializer: ChatResponseSerializer, private val json: Json, ) { - suspend fun getWorkerChatRooms(): Result> = - safeApiCall { chatApi.getWorkerChatRooms() } + suspend fun getWorkerChatRooms(): List = + chatApi.getWorkerChatRooms().onResponse() - suspend fun getCenterChatRooms(): Result> = - safeApiCall { chatApi.getCenterChatRooms() } + suspend fun getCenterChatRooms(): List = + chatApi.getCenterChatRooms().onResponse() suspend fun getWorkerChatRoomMessages( roomId: String, messageId: String?, - ): Result = - safeApiCall { - chatApi.getWorkerChatRoomMessages( - chatRoomId = roomId, - messageId = messageId - ) - } + ): GetChatMessageResponse = + chatApi.getWorkerChatRoomMessages( + chatRoomId = roomId, + messageId = messageId + ).onResponse() suspend fun getCenterChatRoomMessages( roomId: String, messageId: String?, - ): Result = - safeApiCall { - chatApi.getCenterChatRoomMessages( - chatRoomId = roomId, - messageId = messageId - ) - } + ): GetChatMessageResponse = + chatApi.getCenterChatRoomMessages( + chatRoomId = roomId, + messageId = messageId + ).onResponse() - suspend fun generateWorkerChatRoom(opponentId: String): Result = - safeApiCall { chatApi.generateWorkerChatRoom(opponentId) } + suspend fun generateWorkerChatRoom(opponentId: String): GenerateChatRoomResponse = + chatApi.generateWorkerChatRoom(opponentId).onResponse() - suspend fun generateCenterChatRoom(opponentId: String): Result = - safeApiCall { chatApi.generateCenterChatRoom(opponentId) } + suspend fun generateCenterChatRoom(opponentId: String): GenerateChatRoomResponse = + chatApi.generateCenterChatRoom(opponentId).onResponse() private var session: StompSessionWithKxSerialization? = null private var connectionAttempts = 0 - suspend fun connectWebSocket(): Result = runCatching { + suspend fun connectWebSocket() { val accessToken = tokenManager.getAccessToken() - - session = client.connect( - url = "${BuildConfig.CARE_WEBSOCKET_URL}/ws", - headers = mapOf("Authorization" to accessToken) - ).stomp(StompConfig()) - .withJsonConversions(json) - - connectionAttempts = 0 - }.recoverCatching { throwable -> - if (connectionAttempts < MAX_RETRY_ATTEMPTS) { - val waitTime = minOf(calculateBackoffTime(connectionAttempts), MAX_WAIT_TIME) - delay(waitTime) - connectionAttempts++ - connectWebSocket().getOrThrow() - } else { - throw throwable + try { + session = client.connect( + url = "${'$'}{BuildConfig.CARE_WEBSOCKET_URL}/ws", + headers = mapOf("Authorization" to accessToken) + ).stomp(StompConfig()) + .withJsonConversions(json) + connectionAttempts = 0 + } catch (e: Throwable) { + if (connectionAttempts < MAX_RETRY_ATTEMPTS) { + val waitTime = minOf(calculateBackoffTime(connectionAttempts), MAX_WAIT_TIME) + delay(waitTime) + connectionAttempts++ + connectWebSocket() + } else { + throw e + } } } - suspend fun disconnectWebSocket(reason: String? = null): Result = try { + suspend fun disconnectWebSocket() { session?.disconnect() - Result.success(Unit) - } catch (e: Exception) { - Result.failure(e) } suspend fun subscribeChatMessage(userId: String): Flow = session?.subscribe( - StompSubscribeHeaders(destination = "/sub/${userId}"), + StompSubscribeHeaders(destination = "/sub/${'$'}{userId}"), chatResponseSerializer, ) ?: flow { throw IOException("웹소켓을 먼저 연결해주세요.") } suspend fun sendMessage( userType: UserType, sendMessageRequest: SendMessageRequest - ): Result = - runCatching { - val result = session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/send/${userType.apiValue.lowercase()}"), - body = sendMessageRequest, - serializer = SendMessageRequest.serializer(), - ) - } + ) { + session?.convertAndSend( + headers = StompSendHeaders(destination = "/pub/send/${'$'}{userType.apiValue.lowercase()}"), + body = sendMessageRequest, + serializer = SendMessageRequest.serializer(), + ) + } suspend fun readMessage( userType: UserType, readMessageRequest: ReadMessageRequest - ): Result = runCatching { + ) { session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/read/${userType.apiValue.lowercase()}"), + headers = StompSendHeaders(destination = "/pub/read/${'$'}{userType.apiValue.lowercase()}"), body = readMessageRequest, serializer = ReadMessageRequest.serializer(), ) diff --git a/core/network/src/main/java/com/idle/network/source/JobPostingDataSource.kt b/core/network/src/main/java/com/idle/network/source/JobPostingDataSource.kt index 5b7c03c3..2ac50e1d 100644 --- a/core/network/src/main/java/com/idle/network/source/JobPostingDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/JobPostingDataSource.kt @@ -14,7 +14,7 @@ import com.idle.network.model.jobposting.GetJobPostingsCenterResponse import com.idle.network.model.jobposting.GetJobPostingsResponse import com.idle.network.model.jobposting.GetWorkerJobPostingDetailResponse import com.idle.network.model.jobposting.JobPostingRequest -import com.idle.network.util.safeApiCall +import com.idle.network.util.onResponse import javax.inject.Inject import javax.inject.Singleton @@ -22,84 +22,79 @@ import javax.inject.Singleton class JobPostingDataSource @Inject constructor( private val jobPostingApi: JobPostingApi ) { - suspend fun postJobPosting(jobPostingRequest: JobPostingRequest): Result = - safeApiCall { jobPostingApi.postJobPosting(jobPostingRequest) } + suspend fun postJobPosting(jobPostingRequest: JobPostingRequest): Unit = + jobPostingApi.postJobPosting(jobPostingRequest).onResponse() suspend fun updateJobPosting( jobPostingId: String, jobPostingRequest: JobPostingRequest - ): Result = safeApiCall { + ): Unit = jobPostingApi.updateJobPosting( jobPostingId = jobPostingId, jobPostingRequest = jobPostingRequest - ) - } + ).onResponse() - suspend fun getCenterJobPostingDetail(jobPostingId: String): - Result = safeApiCall { - jobPostingApi.getJobPostingDetailCenter(jobPostingId) - } + suspend fun getCenterJobPostingDetail(jobPostingId: String): GetCenterJobPostingDetailResponse = + jobPostingApi.getJobPostingDetailCenter(jobPostingId).onResponse() - suspend fun getWorkerJobPostingDetail(jobPostingId: String): - Result = safeApiCall { - jobPostingApi.getJobPostingDetailWorker(jobPostingId) - } + suspend fun getWorkerJobPostingDetail(jobPostingId: String): GetWorkerJobPostingDetailResponse = + jobPostingApi.getJobPostingDetailWorker(jobPostingId).onResponse() - suspend fun getJobPostings(next: String?, limit: Int): Result = - safeApiCall { jobPostingApi.getJobPostings(next = next, limit = limit) } + suspend fun getJobPostings(next: String?, limit: Int): GetJobPostingsResponse = + jobPostingApi.getJobPostings(next = next, limit = limit).onResponse() - suspend fun getJobPostingsApplied(next: String?, limit: Int): Result = - safeApiCall { jobPostingApi.getJobPostingsApplied(next = next, limit = limit) } + suspend fun getJobPostingsApplied(next: String?, limit: Int): GetJobPostingsResponse = + jobPostingApi.getJobPostingsApplied(next = next, limit = limit).onResponse() - suspend fun getMyFavoriteJobPostings(): Result = - safeApiCall { jobPostingApi.getMyFavoriteJobPostings() } + suspend fun getMyFavoriteJobPostings(): GetFavoriteJobPostingsResponse = + jobPostingApi.getMyFavoriteJobPostings().onResponse() - suspend fun getMyFavoriteCrawlingJobPostings(): Result = - safeApiCall { jobPostingApi.getMyFavoriteCrawlingJobPostings() } + suspend fun getMyFavoriteCrawlingJobPostings(): GetFavoriteCrawlingJobPostingsResponse = + jobPostingApi.getMyFavoriteCrawlingJobPostings().onResponse() - suspend fun getJobPostingsInProgress(): Result = - safeApiCall { jobPostingApi.getJobPostingsInProgress() } + suspend fun getJobPostingsInProgress(): GetJobPostingsCenterResponse = + jobPostingApi.getJobPostingsInProgress().onResponse() - suspend fun getJobPostingsCompleted(): Result = - safeApiCall { jobPostingApi.getJobPostingsCompleted() } + suspend fun getJobPostingsCompleted(): GetJobPostingsCenterResponse = + jobPostingApi.getJobPostingsCompleted().onResponse() - suspend fun getApplicantCount(jobPostingId: String): Result = - safeApiCall { jobPostingApi.getApplicantCount(jobPostingId) } + suspend fun getApplicantCount(jobPostingId: String): GetApplicantCountResponse = + jobPostingApi.getApplicantCount(jobPostingId).onResponse() - suspend fun applyJobPosting(applyJobPostingRequest: ApplyJobPostingRequest): Result = - safeApiCall { jobPostingApi.applyJobPosting(applyJobPostingRequest) } + suspend fun applyJobPosting(applyJobPostingRequest: ApplyJobPostingRequest): Unit = + jobPostingApi.applyJobPosting(applyJobPostingRequest).onResponse() suspend fun addFavoriteJobPosting( jobPostingId: String, favoriteJobPostingRequest: FavoriteJobPostingRequest, - ): Result = safeApiCall { + ): Unit = jobPostingApi.addFavoriteJobPosting( jobPostingId = jobPostingId, favoriteJobPostingRequest = favoriteJobPostingRequest - ) - } + ).onResponse() - suspend fun removeFavoriteJobPosting(jobPostingId: String): Result = - safeApiCall { jobPostingApi.removeFavoriteJobPosting(jobPostingId) } + suspend fun removeFavoriteJobPosting(jobPostingId: String): Unit = + jobPostingApi.removeFavoriteJobPosting(jobPostingId).onResponse() - suspend fun getApplicants(jobPostingId: String): Result = - safeApiCall { jobPostingApi.getApplicants(jobPostingId) } + suspend fun getApplicants(jobPostingId: String): GetApplicantsResponse = + jobPostingApi.getApplicants(jobPostingId).onResponse() - suspend fun endJobPosting(jobPostingId: String): Result = - safeApiCall { jobPostingApi.endJobPosting(jobPostingId) } + suspend fun endJobPosting(jobPostingId: String): Unit = + jobPostingApi.endJobPosting(jobPostingId).onResponse() - suspend fun deleteJobPosting(jobPostingId: String): Result = - safeApiCall { jobPostingApi.deleteJobPosting(jobPostingId) } + suspend fun deleteJobPosting(jobPostingId: String): Unit = + jobPostingApi.deleteJobPosting(jobPostingId).onResponse() suspend fun getCrawlingJobPostings( next: String?, limit: Int, distance: Int, - ): Result = - safeApiCall { jobPostingApi.getCrawlingJobPostings(next = next, limit = limit, distance = distance) } + ): GetCrawlingJobPostingsResponse = + jobPostingApi.getCrawlingJobPostings(next = next, limit = limit, distance = distance) + .onResponse() suspend fun getCrawlingJobPostingsDetail( jobPostingId: String - ): Result = - safeApiCall { jobPostingApi.getCrawlingJobPostingsDetail(jobPostingId) } + ): GetCrawlingJobPostingDetailResponse = + jobPostingApi.getCrawlingJobPostingsDetail(jobPostingId).onResponse() } diff --git a/core/network/src/main/java/com/idle/network/source/NotificationDataSource.kt b/core/network/src/main/java/com/idle/network/source/NotificationDataSource.kt index 85d06306..f2815f76 100644 --- a/core/network/src/main/java/com/idle/network/source/NotificationDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/NotificationDataSource.kt @@ -2,10 +2,10 @@ package com.idle.network.source import com.idle.network.api.NotificationApi import com.idle.network.model.notification.DeleteFcmTokenRequest -import com.idle.network.model.notification.PostFcmTokenRequest import com.idle.network.model.notification.GetMyNotificationResponse import com.idle.network.model.notification.GetUnreadNotificationCountResponse -import com.idle.network.util.safeApiCall +import com.idle.network.model.notification.PostFcmTokenRequest +import com.idle.network.util.onResponse import javax.inject.Inject import javax.inject.Singleton @@ -13,21 +13,21 @@ import javax.inject.Singleton class NotificationDataSource @Inject constructor( private val notificationApi: NotificationApi ) { - suspend fun postFCMToken(postFcmTokenRequest: PostFcmTokenRequest): Result = - safeApiCall { notificationApi.postFCMToken(postFcmTokenRequest) } + suspend fun postFCMToken(postFcmTokenRequest: PostFcmTokenRequest): Unit = + notificationApi.postFCMToken(postFcmTokenRequest).onResponse() - suspend fun deleteFCMToken(deleteFcmTokenRequest: DeleteFcmTokenRequest): Result = - safeApiCall { notificationApi.deleteFCMToken(deleteFcmTokenRequest) } + suspend fun deleteFCMToken(deleteFcmTokenRequest: DeleteFcmTokenRequest): Unit = + notificationApi.deleteFCMToken(deleteFcmTokenRequest).onResponse() suspend fun getMyNotifications( next: String?, limit: Int - ): Result = - safeApiCall { notificationApi.getMyNotifications(next = next, limit = limit) } + ): GetMyNotificationResponse = + notificationApi.getMyNotifications(next = next, limit = limit).onResponse() - suspend fun readNotification(notificationId: String): Result = - safeApiCall { notificationApi.readNotification(notificationId) } + suspend fun readNotification(notificationId: String): Unit = + notificationApi.readNotification(notificationId).onResponse() - suspend fun getUnreadNotificationCount(): Result = - safeApiCall { notificationApi.getUnreadNotificationCount() } + suspend fun getUnreadNotificationCount(): GetUnreadNotificationCountResponse = + notificationApi.getUnreadNotificationCount().onResponse() } diff --git a/core/network/src/main/java/com/idle/network/source/ProfileDataSource.kt b/core/network/src/main/java/com/idle/network/source/ProfileDataSource.kt index 59ab9bbe..fcc02b98 100644 --- a/core/network/src/main/java/com/idle/network/source/ProfileDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/ProfileDataSource.kt @@ -10,7 +10,7 @@ import com.idle.network.model.profile.RegisterCenterProfileRequest import com.idle.network.model.profile.UpdateCenterProfileRequest import com.idle.network.model.profile.UpdateWorkerProfileRequest import com.idle.network.model.profile.UploadProfileImageUrlResponse -import com.idle.network.util.safeApiCall +import com.idle.network.util.onResponse import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody import java.io.InputStream @@ -21,60 +21,54 @@ import javax.inject.Singleton class ProfileDataSource @Inject constructor( private val userApi: UserApi, ) { - suspend fun getMyCenterProfile(): Result = - safeApiCall { userApi.getMyCenterProfile() } + suspend fun getMyCenterProfile(): GetCenterProfileResponse = + userApi.getMyCenterProfile().onResponse() - suspend fun updateMyCenterProfile(updateCenterProfileRequest: UpdateCenterProfileRequest): Result = - safeApiCall { userApi.updateMyCenterProfile(updateCenterProfileRequest) } + suspend fun updateMyCenterProfile(updateCenterProfileRequest: UpdateCenterProfileRequest): Unit = + userApi.updateMyCenterProfile(updateCenterProfileRequest).onResponse() - suspend fun getCenterProfile(centerId: String): Result = - safeApiCall { userApi.getCenterProfile(centerId) } + suspend fun getCenterProfile(centerId: String): GetCenterProfileResponse = + userApi.getCenterProfile(centerId).onResponse() suspend fun getProfileImageUploadUrl( userType: String, imageFileExtension: String - ): Result = safeApiCall { - userApi.getImageUploadUrl(userType, imageFileExtension) - } + ): UploadProfileImageUrlResponse = + userApi.getImageUploadUrl(userType, imageFileExtension).onResponse() suspend fun uploadProfileImage( uploadUrl: String, imageFileExtension: String, imageInputStream: InputStream, - ): Result { + ) { val requestImage = imageInputStream.readBytes() .toRequestBody(imageFileExtension.toMediaTypeOrNull()) - return safeApiCall { - userApi.uploadProfileImage(uploadUrl = uploadUrl, requestImage = requestImage) - } + userApi.uploadProfileImage(uploadUrl = uploadUrl, requestImage = requestImage) + .onResponse() } suspend fun callbackImageUpload( userType: String, callbackImageUploadRequest: CallbackImageUploadRequest, - ): Result = safeApiCall { - userApi.callbackImageUpload( - userType = userType, - callbackImageUploadRequest = callbackImageUploadRequest - ) - } + ): Unit = userApi.callbackImageUpload( + userType = userType, + callbackImageUploadRequest = callbackImageUploadRequest + ).onResponse() - suspend fun getMyWorkerProfile(): Result = - safeApiCall { userApi.getMyWorkerProfile() } + suspend fun getMyWorkerProfile(): GetWorkerProfileResponse = + userApi.getMyWorkerProfile().onResponse() - suspend fun getWorkerProfile(workerId: String): Result = - safeApiCall { userApi.getWorkerProfile(workerId) } + suspend fun getWorkerProfile(workerId: String): GetWorkerProfileResponse = + userApi.getWorkerProfile(workerId).onResponse() - suspend fun updateWorkerProfile(updateWorkerProfileRequest: UpdateWorkerProfileRequest): Result = - safeApiCall { userApi.updateWorkerProfile(updateWorkerProfileRequest) } + suspend fun updateWorkerProfile(updateWorkerProfileRequest: UpdateWorkerProfileRequest): Unit = + userApi.updateWorkerProfile(updateWorkerProfileRequest).onResponse() - suspend fun registerCenterProfile(registerCenterProfileRequest: RegisterCenterProfileRequest): Result = - safeApiCall { userApi.registerCenterProfile(registerCenterProfileRequest) } + suspend fun registerCenterProfile(registerCenterProfileRequest: RegisterCenterProfileRequest): Unit = + userApi.registerCenterProfile(registerCenterProfileRequest).onResponse() - suspend fun getWorkerId(): Result = - safeApiCall { userApi.getWorkerId() } + suspend fun getWorkerId(): GetWorkerIdResponse = userApi.getWorkerId().onResponse() - suspend fun getCenterStatus(): Result = - safeApiCall { userApi.getCenterStatus() } + suspend fun getCenterStatus(): GetCenterStatusResponse = userApi.getCenterStatus().onResponse() } diff --git a/core/network/src/main/java/com/idle/network/util/ResponseExtensions.kt b/core/network/src/main/java/com/idle/network/util/ResponseExtensions.kt new file mode 100644 index 00000000..f838e02d --- /dev/null +++ b/core/network/src/main/java/com/idle/network/util/ResponseExtensions.kt @@ -0,0 +1,30 @@ +package com.idle.network.util + +import com.idle.domain.model.error.ApiErrorCode +import com.idle.domain.model.error.HttpResponseException +import com.idle.domain.model.error.HttpResponseStatus +import com.idle.network.model.error.ErrorResponse +import kotlinx.serialization.json.Json +import retrofit2.Response + +private val json = Json { ignoreUnknownKeys = true } + +internal fun Response.onResponse(): T { + if (isSuccessful) { + return body() ?: Unit as T + } else { + errorBody()?.let { + val errorResponse = json.decodeFromString(it.string()) + + throw HttpResponseException( + status = HttpResponseStatus.create(code()), + apiErrorCode = ApiErrorCode.create(errorResponse.code), + msg = errorResponse.message, + ) + } ?: throw HttpResponseException( + status = HttpResponseStatus.create(-1), + apiErrorCode = ApiErrorCode.UnknownError, + msg = "알 수 없는 에러입니다." + ) + } +} diff --git a/core/network/src/main/java/com/idle/network/util/safeApiCall.kt b/core/network/src/main/java/com/idle/network/util/safeApiCall.kt deleted file mode 100644 index 1c0c7714..00000000 --- a/core/network/src/main/java/com/idle/network/util/safeApiCall.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.idle.network.util - -import com.idle.domain.model.error.ApiErrorCode -import com.idle.domain.model.error.HttpResponseException -import com.idle.domain.model.error.HttpResponseStatus -import com.idle.network.model.error.ErrorResponse -import kotlinx.serialization.json.Json -import retrofit2.Response - -internal inline fun safeApiCall(apiCall: () -> Response): Result { - return try { - val response = apiCall() - response.onResponse() - } catch (e: Exception) { - Result.failure(e) - } -} - -private val json = Json { ignoreUnknownKeys = true } - -internal fun Response.onResponse(): Result { - if (isSuccessful) { - body()?.let { - return Result.success(it) - } ?: return Result.success(Unit as T) - } else { - errorBody()?.let { - val errorResponse = json.decodeFromString(it.string()) - - return Result.failure( - HttpResponseException( - status = HttpResponseStatus.create(code()), - apiErrorCode = ApiErrorCode.create(errorResponse.code), - msg = errorResponse.message, - ) - ) - } ?: return Result.failure( - HttpResponseException( - status = HttpResponseStatus.create(-1), - apiErrorCode = ApiErrorCode.UnknownError, - msg = "알 수 없는 에러입니다." - ) - ) - } -} diff --git a/feature/center-applicant-inquiry/src/main/java/com/idle/applicant/inquiry/ApplicantInquiryViewModel.kt b/feature/center-applicant-inquiry/src/main/java/com/idle/applicant/inquiry/ApplicantInquiryViewModel.kt index 7ad468ff..9cdc9ecd 100644 --- a/feature/center-applicant-inquiry/src/main/java/com/idle/applicant/inquiry/ApplicantInquiryViewModel.kt +++ b/feature/center-applicant-inquiry/src/main/java/com/idle/applicant/inquiry/ApplicantInquiryViewModel.kt @@ -2,6 +2,7 @@ package com.idle.applicant.inquiry import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.Applicant import com.idle.domain.model.jobposting.JobPostingSummary @@ -25,10 +26,11 @@ class ApplicantInquiryViewModel @Inject constructor( val applicants = _applicants.asStateFlow() suspend fun getApplicantsInfo(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.getApplicants(jobPostingId) - .onSuccess { (jobPostingSummary, applicants) -> - _jobPostingSummary.value = jobPostingSummary - _applicants.value = applicants - }.onFailure { errorHelper.sendError(it) } + suspendRunCatching { + jobPostingRepository.getApplicants(jobPostingId) + }.onSuccess { (jobPostingSummary, applicants) -> + _jobPostingSummary.value = jobPostingSummary + _applicants.value = applicants + }.onFailure { errorHelper.sendError(it) } } } diff --git a/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt b/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt index 88417278..7b24ee9b 100644 --- a/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt +++ b/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt @@ -2,6 +2,7 @@ package com.idle.center.chatting import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.idle.common.suspendRunCatching import com.idle.domain.model.auth.UserType import com.idle.domain.model.chat.ChatMessage import com.idle.domain.model.chat.ChatRoomWithOpponentInfo @@ -16,6 +17,7 @@ import com.idle.navigation.NavigationHelper import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import javax.inject.Inject @@ -38,26 +40,34 @@ class CenterChattingViewModel @Inject constructor( val chatRoomList = _chatRoomList.asStateFlow() internal suspend fun initCenterChatting() { - getMyCenterProfileUseCase().onSuccess { + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { _myProfile.value = it }.onFailure { errorHelper.sendError(it) } } internal fun connectWebsocket() = viewModelScope.launch { - chatRepository.connectWebSocket().onSuccess { + suspendRunCatching { + chatRepository.connectWebSocket() + }.onSuccess { subscribeChatMessage() } } internal fun disconnectWebsocket() = viewModelScope.launch { - chatRepository.disconnectWebSocket() + suspendRunCatching { + chatRepository.disconnectWebSocket() + } } private fun subscribeChatMessage() = viewModelScope.launch { chatRepository.subscribeChatMessage( userId = _myProfile.value?.centerId ?: return@launch, userType = UserType.CENTER, - ).collect { message -> + ).catch { + errorHelper.sendError(it) + }.collect { message -> when (message) { is ChatMessage -> handleChatMessage(message) is ReadMessage -> Unit @@ -79,8 +89,9 @@ class CenterChattingViewModel @Inject constructor( ) } else { // 새로운 방이면 새로 생성 후 최상단에 추가 - val opponentProfile = profileRepository.getWorkerProfile(message.senderId) - .getOrNull() ?: return + val opponentProfile = suspendRunCatching { + profileRepository.getWorkerProfile(message.senderId) + }.getOrNull() ?: return val newChatRoom = ChatRoomWithOpponentInfo( id = roomId, @@ -99,10 +110,14 @@ class CenterChattingViewModel @Inject constructor( } internal suspend fun retrieveChatRoomList() { - getChatRoomsUseCase( - userType = UserType.CENTER, - userId = _myProfile.value?.centerId ?: return - ).onSuccess { + val centerId = _myProfile.value?.centerId ?: return + + suspendRunCatching { + getChatRoomsUseCase( + userType = UserType.CENTER, + userId = centerId, + ) + }.onSuccess { val newMap = LinkedHashMap().apply { putAll(_chatRoomMap.value) it.forEach { chatRoom -> this[chatRoom.id] = chatRoom } @@ -113,13 +128,17 @@ class CenterChattingViewModel @Inject constructor( } internal suspend fun loadChatRoomList() { - chatRepository.loadChatRooms( - userId = _myProfile.value?.centerId ?: return, - userType = UserType.CENTER - ).onSuccess { + val centerId = _myProfile.value?.centerId ?: return + + suspendRunCatching { + chatRepository.loadChatRooms( + userId = centerId, + userType = UserType.CENTER + ) + }.onSuccess { response -> val newMap = LinkedHashMap().apply { putAll(_chatRoomMap.value) - it.forEach { chatRoom -> this[chatRoom.id] = chatRoom } + response.forEach { chatRoom -> this[chatRoom.id] = chatRoom } } _chatRoomMap.value = newMap _chatRoomList.value = _chatRoomMap.value.values.toList() diff --git a/feature/center-home/src/main/java/com/idle/center/home/CenterHomeViewModel.kt b/feature/center-home/src/main/java/com/idle/center/home/CenterHomeViewModel.kt index 0d6ed6d8..152fe668 100644 --- a/feature/center-home/src/main/java/com/idle/center/home/CenterHomeViewModel.kt +++ b/feature/center-home/src/main/java/com/idle/center/home/CenterHomeViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType.SUCCESS +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.CenterJobPosting import com.idle.domain.repositorry.JobPostingRepository @@ -38,7 +39,9 @@ class CenterHomeViewModel @Inject constructor( val unreadNotificationCount = _unreadNotificationCount.asStateFlow() internal fun getUnreadNotificationCount() = viewModelScope.launch { - notificationRepository.getUnreadNotificationCount().onSuccess { + suspendRunCatching { + notificationRepository.getUnreadNotificationCount() + }.onSuccess { _unreadNotificationCount.value = it }.onFailure { errorHelper.sendError(it) } } @@ -53,19 +56,25 @@ class CenterHomeViewModel @Inject constructor( } internal fun getJobPostingsInProgress() = viewModelScope.launch { - getJobPostingsInProgressUseCase().onSuccess { + suspendRunCatching { + getJobPostingsInProgressUseCase() + }.onSuccess { _jobPostingsInProgress.value = it }.onFailure { errorHelper.sendError(it) } } internal fun getJobPostingsCompleted() = viewModelScope.launch { - jobPostingRepository.getJobPostingsCompleted().onSuccess { + suspendRunCatching { + jobPostingRepository.getJobPostingsCompleted() + }.onSuccess { _jobPostingsCompleted.value = it }.onFailure { errorHelper.sendError(it) } } internal fun endJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.endJobPosting(jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.endJobPosting(jobPostingId) + }.onSuccess { val jobPostingsInProgress = _jobPostingsInProgress.value ?: emptyList() val jobPostingsCompleted = _jobPostingsCompleted.value ?: emptyList() diff --git a/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt b/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt index e053cffc..55175fe8 100644 --- a/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt +++ b/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.center.job.posting.post.R +import com.idle.common.suspendRunCatching import com.idle.compose.ui.JobPostingBottomSheetType import com.idle.domain.model.auth.Gender import com.idle.domain.model.error.ErrorHelper @@ -310,55 +311,57 @@ class JobPostingViewModel @Inject constructor( internal fun postJobPosting() { viewModelScope.launch { - jobPostingRepository.postJobPosting( - weekdays = _weekDays.value.toList() - .sortedBy { it.ordinal }, - startTime = _workStartTime.value, - endTime = _workEndTime.value, - payType = _payType.value ?: PayType.UNKNOWN, - payAmount = _payAmount.value.toIntOrNull() ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("급여 형식이 잘못되었습니다. 숫자로 입력해주세요.")) - return@launch - }, - roadNameAddress = _roadNameAddress.value, - lotNumberAddress = _lotNumberAddress.value, - clientName = _clientName.value, - gender = _gender.value, - birthYear = _birthYear.value.toIntOrNull() ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("올바른 출생년도를 입력해주세요.")) - return@launch - }, - weight = _weight.value.toIntOrNull(), - careLevel = _careLevel.value.toIntOrNull() ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("올바른 요양 등급을 입력해주세요.")) - return@launch - }, - mentalStatus = _mentalStatus.value, - disease = _disease.value.ifBlank { null }, - isMealAssistance = _isMealAssistance.value ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("식사 보조 여부를 선택해주세요.")) - return@launch - }, - isBowelAssistance = _isBowelAssistance.value ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("배변 보조 여부를 선택해주세요.")) - return@launch - }, - isWalkingAssistance = _isWalkingAssistance.value ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("이동 보조 여부를 선택해주세요.")) - return@launch - }, - lifeAssistance = _lifeAssistance.value.toList().sortedBy { it.ordinal } - .takeIf { it.isNotEmpty() } ?: listOf(LifeAssistance.NONE), - extraRequirement = _extraRequirement.value.ifBlank { null }, - isExperiencePreferred = _isExperiencePreferred.value ?: let { - eventHelper.sendEvent(MainEvent.ShowToast("경력 우대 여부를 선택해주세요.")) - return@launch - }, - applyMethod = _applyMethod.value.toList() - .sortedBy { it.ordinal }, - applyDeadLineType = _applyDeadlineType.value ?: ApplyDeadlineType.UNLIMITED, - applyDeadline = _applyDeadline.value?.toString(), - ).onSuccess { + suspendRunCatching { + jobPostingRepository.postJobPosting( + weekdays = _weekDays.value.toList() + .sortedBy { it.ordinal }, + startTime = _workStartTime.value, + endTime = _workEndTime.value, + payType = _payType.value ?: PayType.UNKNOWN, + payAmount = _payAmount.value.toIntOrNull() ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("급여 형식이 잘못되었습니다. 숫자로 입력해주세요.")) + return@suspendRunCatching + }, + roadNameAddress = _roadNameAddress.value, + lotNumberAddress = _lotNumberAddress.value, + clientName = _clientName.value, + gender = _gender.value, + birthYear = _birthYear.value.toIntOrNull() ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("올바른 출생년도를 입력해주세요.")) + return@suspendRunCatching + }, + weight = _weight.value.toIntOrNull(), + careLevel = _careLevel.value.toIntOrNull() ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("올바른 요양 등급을 입력해주세요.")) + return@suspendRunCatching + }, + mentalStatus = _mentalStatus.value, + disease = _disease.value.ifBlank { null }, + isMealAssistance = _isMealAssistance.value ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("식사 보조 여부를 선택해주세요.")) + return@suspendRunCatching + }, + isBowelAssistance = _isBowelAssistance.value ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("배변 보조 여부를 선택해주세요.")) + return@suspendRunCatching + }, + isWalkingAssistance = _isWalkingAssistance.value ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("이동 보조 여부를 선택해주세요.")) + return@suspendRunCatching + }, + lifeAssistance = _lifeAssistance.value.toList().sortedBy { it.ordinal } + .takeIf { it.isNotEmpty() } ?: listOf(LifeAssistance.NONE), + extraRequirement = _extraRequirement.value.ifBlank { null }, + isExperiencePreferred = _isExperiencePreferred.value ?: let { + eventHelper.sendEvent(MainEvent.ShowToast("경력 우대 여부를 선택해주세요.")) + return@suspendRunCatching + }, + applyMethod = _applyMethod.value.toList() + .sortedBy { it.ordinal }, + applyDeadLineType = _applyDeadlineType.value ?: ApplyDeadlineType.UNLIMITED, + applyDeadline = _applyDeadline.value?.toString(), + ) + }.onSuccess { navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( destination = CenterJobPostingPostComplete, @@ -370,7 +373,9 @@ class JobPostingViewModel @Inject constructor( } private fun getMyCenterProfile() = viewModelScope.launch { - getMyCenterProfileUseCase().onSuccess { + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { _profile.value = it }.onFailure { errorHelper.sendError(it) } } diff --git a/feature/center-pending/src/main/java/com/idle/pending/CenterPendingViewModel.kt b/feature/center-pending/src/main/java/com/idle/pending/CenterPendingViewModel.kt index e153fa57..2a20d70f 100644 --- a/feature/center-pending/src/main/java/com/idle/pending/CenterPendingViewModel.kt +++ b/feature/center-pending/src/main/java/com/idle/pending/CenterPendingViewModel.kt @@ -6,6 +6,7 @@ import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType.SUCCESS import com.idle.center.pending.R +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ApiErrorCode import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.error.HttpResponseException @@ -46,7 +47,9 @@ class CenterPendingViewModel @Inject constructor( } internal fun logout() = viewModelScope.launch { - authRepository.logoutCenter().onSuccess { + suspendRunCatching { + authRepository.logoutCenter() + }.onSuccess { navigationHelper.navigateTo( NavigationEvent.ToAuthWithClearBackStack( toastMsg = "로그아웃이 완료되었습니다.", @@ -57,7 +60,9 @@ class CenterPendingViewModel @Inject constructor( } internal fun sendVerificationRequest() = viewModelScope.launch { - authRepository.sendCenterVerificationRequest().onSuccess { + suspendRunCatching { + authRepository.sendCenterVerificationRequest() + }.onSuccess { _status.value = CenterManagerAccountStatus.PENDING eventHelper.sendEvent(MainEvent.ShowToast("센터 인증 요청이 완료되었습니다.", SUCCESS)) }.onFailure { errorHelper.sendError(it) } @@ -74,7 +79,9 @@ class CenterPendingViewModel @Inject constructor( } private fun getCenterStatus() = viewModelScope.launch { - profileRepository.getCenterStatus().onSuccess { + suspendRunCatching { + profileRepository.getCenterStatus() + }.onSuccess { when (it.centerManagerAccountStatus) { CenterManagerAccountStatus.APPROVED -> { handleApprovedCenterStatus() @@ -87,7 +94,9 @@ class CenterPendingViewModel @Inject constructor( } private fun handleApprovedCenterStatus() = viewModelScope.launch { - profileRepository.getMyCenterProfile().onSuccess { + suspendRunCatching { + profileRepository.getMyCenterProfile() + }.onSuccess { navigationHelper.navigateTo( NavigationEvent.To(CenterHome, R.id.centerPendingFragment) ) diff --git a/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt b/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt index 4ab57885..dfd92f90 100644 --- a/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt +++ b/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType.SUCCESS +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.ProfileRepository @@ -63,7 +64,9 @@ class CenterProfileViewModel @Inject constructor( } internal fun getMyCenterProfile() = viewModelScope.launch { - getMyCenterProfileUseCase().onSuccess { + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { _centerProfile.value = it _centerIntroduce.value = it.introduce ?: "" _centerOfficeNumber.value = it.officeNumber @@ -71,7 +74,9 @@ class CenterProfileViewModel @Inject constructor( } internal fun getCenterProfile(centerId: String) = viewModelScope.launch { - profileRepository.getCenterProfile(centerId).onSuccess { + suspendRunCatching { + profileRepository.getCenterProfile(centerId) + }.onSuccess { _centerProfile.value = it _centerIntroduce.value = it.introduce ?: "" _centerOfficeNumber.value = it.officeNumber @@ -90,11 +95,13 @@ class CenterProfileViewModel @Inject constructor( _isUpdateLoading.value = true - updateCenterProfileUseCase( - officeNumber = _centerOfficeNumber.value, - introduce = _centerIntroduce.value.ifBlank { null }, - imageFileUri = _profileImageUri.value?.toString(), - ).onSuccess { + suspendRunCatching { + updateCenterProfileUseCase( + officeNumber = _centerOfficeNumber.value, + introduce = _centerIntroduce.value.ifBlank { null }, + imageFileUri = _profileImageUri.value?.toString(), + ) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("정보 수정이 완료되었어요.", SUCCESS)) setEditState(false) }.onFailure { diff --git a/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt b/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt index d8e2ad8e..803bff01 100644 --- a/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt +++ b/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt @@ -5,6 +5,7 @@ import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.center.register.info.R +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.usecase.profile.RegisterCenterProfileUseCase import com.idle.navigation.DeepLinkDestination.CenterRegisterComplete @@ -45,15 +46,17 @@ class RegisterCenterInfoViewModel @Inject constructor( val centerProfileImageUri = _centerProfileImageUri.asStateFlow() internal fun registerCenterProfile() = viewModelScope.launch { - registerCenterProfileUseCase( - centerName = _centerName.value, - detailedAddress = _centerDetailAddress.value, - introduce = _centerIntroduce.value, - lotNumberAddress = _lotNumberAddress.value, - officeNumber = _centerNumber.value, - roadNameAddress = _roadNameAddress.value, - imageFileUri = _centerProfileImageUri.value.toString(), - ).onSuccess { + suspendRunCatching { + registerCenterProfileUseCase( + centerName = _centerName.value, + detailedAddress = _centerDetailAddress.value, + introduce = _centerIntroduce.value, + lotNumberAddress = _lotNumberAddress.value, + officeNumber = _centerNumber.value, + roadNameAddress = _roadNameAddress.value, + imageFileUri = _centerProfileImageUri.value.toString(), + ) + }.onSuccess { navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( CenterRegisterComplete, diff --git a/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt b/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt index ba57e21d..6053cc00 100644 --- a/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt +++ b/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent +import com.idle.common.suspendRunCatching import com.idle.domain.model.profile.CenterProfile import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel @@ -23,11 +24,13 @@ class RegisterCenterInfoCompleteViewModel @Inject constructor( init { viewModelScope.launch { - getMyCenterProfileUseCase() - .onSuccess { _centerProfile.value = it } - .onFailure { - eventHelper.sendEvent(MainEvent.ShowToast(it.toString())) - } + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { + _centerProfile.value = it + }.onFailure { + eventHelper.sendEvent(MainEvent.ShowToast(it.toString())) + } } } } diff --git a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt index 8dd41d4c..1ed71995 100644 --- a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt +++ b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt @@ -3,6 +3,7 @@ package com.idle.chatting_detail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.idle.common.suspendRunCatching import com.idle.domain.model.auth.UserType import com.idle.domain.model.chat.ChatMessage import com.idle.domain.model.chat.ReadMessage @@ -73,14 +74,18 @@ class ChattingDetailViewModel @Inject constructor( when (myUserType) { UserType.CENTER -> { launch { - profileRepository.getWorkerProfile(opponentId).onSuccess { + suspendRunCatching { + profileRepository.getWorkerProfile(opponentId) + }.onSuccess { _workerProfile.value = it }.onFailure { errorHelper.sendError(it) } } - getMyCenterProfileUseCase().onSuccess { + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { _centerProfile.value = it }.onFailure { errorHelper.sendError(it) @@ -89,14 +94,18 @@ class ChattingDetailViewModel @Inject constructor( UserType.WORKER -> { launch { - profileRepository.getCenterProfile(opponentId).onSuccess { + suspendRunCatching { + profileRepository.getCenterProfile(opponentId) + }.onSuccess { _centerProfile.value = it }.onFailure { errorHelper.sendError(it) } } - getMyWorkerProfileUseCase().onSuccess { + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { _workerProfile.value = it }.onFailure { errorHelper.sendError(it) @@ -109,11 +118,13 @@ class ChattingDetailViewModel @Inject constructor( if (_callType.value == MessageCallType.END) return if (fromJobPosting) { - chatRepository.retrieveChatRoomMessages( - roomId = chatroomId, - myId = myId, - messageId = _chatMessages.value?.first()?.id, - ).onSuccess { messages -> + suspendRunCatching { + chatRepository.retrieveChatRoomMessages( + roomId = chatroomId, + myId = myId, + messageId = _chatMessages.value?.first()?.id, + ) + }.onSuccess { messages -> if (messages.isEmpty()) _callType.value = MessageCallType.END _chatMessages.value = messages.plus(_chatMessages.value ?: emptyList()) @@ -121,13 +132,15 @@ class ChattingDetailViewModel @Inject constructor( errorHelper.sendError(it) } } else { - chatRepository.getChatRoomMessages( - roomId = chatroomId, - messageId = _chatMessages.value?.first()?.id, - userType = myUserType, - myId = myId, - unReadMessageCount = if (unReadMessageCount >= 0) unReadMessageCount else null, - ).onSuccess { messages -> + suspendRunCatching { + chatRepository.getChatRoomMessages( + roomId = chatroomId, + messageId = _chatMessages.value?.first()?.id, + userType = myUserType, + myId = myId, + unReadMessageCount = if (unReadMessageCount >= 0) unReadMessageCount else null, + ) + }.onSuccess { messages -> if (messages.isEmpty()) _callType.value = MessageCallType.END unReadMessageCount -= messages.size @@ -139,7 +152,9 @@ class ChattingDetailViewModel @Inject constructor( } internal fun connectWebsocket() = viewModelScope.launch { - chatRepository.connectWebSocket().onSuccess { + suspendRunCatching { + chatRepository.connectWebSocket() + }.onSuccess { subscribeChatMessage() } } @@ -173,14 +188,16 @@ class ChattingDetailViewModel @Inject constructor( val senderName = if (myUserType == UserType.CENTER) _centerProfile.value!!.centerName else _workerProfile.value!!.workerName - chatRepository.sendMessage( - chatroomId = chatroomId, - myId = myId, - userType = myUserType, - receiverId = opponentId, - senderName = senderName, - content = _writingText.value, - ).onSuccess { + suspendRunCatching { + chatRepository.sendMessage( + chatroomId = chatroomId, + myId = myId, + userType = myUserType, + receiverId = opponentId, + senderName = senderName, + content = _writingText.value, + ) + }.onSuccess { _writingText.value = "" }.onFailure { errorHelper.sendError(it) } } @@ -188,13 +205,15 @@ class ChattingDetailViewModel @Inject constructor( internal suspend fun readMessage() { val lastOpponentMessageSequence = _chatMessages.value?.lastOrNull()?.sequence ?: return - chatRepository.readMessage( - chatroomId = chatroomId, - myId = myId, - opponentId = opponentId, - userType = myUserType, - sequence = lastOpponentMessageSequence - ).onSuccess { + suspendRunCatching { + chatRepository.readMessage( + chatroomId = chatroomId, + myId = myId, + opponentId = opponentId, + userType = myUserType, + sequence = lastOpponentMessageSequence + ) + }.onSuccess { _chatMessages.value = _chatMessages.value?.map { if (it.receiverId == myId) it.copy(isRead = true) else it } diff --git a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt index c411e0f3..ba036c9b 100644 --- a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt +++ b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.CenterJobPostingDetail import com.idle.domain.model.jobposting.EditJobPostingDetail @@ -45,19 +46,24 @@ class CenterJobPostingDetailViewModel @Inject constructor( } private fun getMyCenterProfile() = viewModelScope.launch { - getMyCenterProfileUseCase().onSuccess { + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { _profile.value = it }.onFailure { errorHelper.sendError(it) } } internal fun getCenterJobPostingDetail(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.getCenterJobPostingDetail(jobPostingId) - .onSuccess { _jobPostingDetail.value = it } + suspendRunCatching { + jobPostingRepository.getCenterJobPostingDetail(jobPostingId) + }.onSuccess { _jobPostingDetail.value = it } .onFailure { errorHelper.sendError(it) } } internal fun getApplicantsCount(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.getApplicantsCount(jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.getApplicantsCount(jobPostingId) + }.onSuccess { _applicantsCount.value = it }.onFailure { errorHelper.sendError(it) } } @@ -75,37 +81,39 @@ class CenterJobPostingDetailViewModel @Inject constructor( return@launch } - jobPostingRepository.updateJobPosting( - jobPostingId = _jobPostingDetail.value?.id ?: return@launch, - weekdays = editJobPostingDetail.weekdays.toList() - .sortedBy { it.ordinal }, - startTime = editJobPostingDetail.startTime, - endTime = editJobPostingDetail.endTime, - payType = editJobPostingDetail.payType, - payAmount = editJobPostingDetail.payAmount.toIntOrNull() ?: return@launch, - roadNameAddress = editJobPostingDetail.roadNameAddress, - lotNumberAddress = editJobPostingDetail.lotNumberAddress, - clientName = editJobPostingDetail.clientName, - gender = editJobPostingDetail.gender, - birthYear = editJobPostingDetail.birthYear.toIntOrNull() ?: return@launch, - weight = editJobPostingDetail.weight?.toIntOrNull(), - careLevel = editJobPostingDetail.careLevel.toIntOrNull() ?: return@launch, - mentalStatus = editJobPostingDetail.mentalStatus, - disease = editJobPostingDetail.disease.ifBlank { null }, - isMealAssistance = editJobPostingDetail.isMealAssistance, - isBowelAssistance = editJobPostingDetail.isBowelAssistance, - isWalkingAssistance = editJobPostingDetail.isWalkingAssistance, - lifeAssistance = editJobPostingDetail.lifeAssistance.toList() - .sortedBy { it.ordinal } - .ifEmpty { listOf(LifeAssistance.NONE) }, - extraRequirement = editJobPostingDetail.extraRequirement, - isExperiencePreferred = editJobPostingDetail.isExperiencePreferred, - applyMethod = editJobPostingDetail.applyMethod.toList() - .sortedBy { it.ordinal }, - applyDeadLineType = editJobPostingDetail.applyDeadlineType, - applyDeadline = editJobPostingDetail.applyDeadline.toString() - .ifBlank { null }, - ).onSuccess { + suspendRunCatching { + jobPostingRepository.updateJobPosting( + jobPostingId = _jobPostingDetail.value?.id ?: return@suspendRunCatching, + weekdays = editJobPostingDetail.weekdays.toList() + .sortedBy { it.ordinal }, + startTime = editJobPostingDetail.startTime, + endTime = editJobPostingDetail.endTime, + payType = editJobPostingDetail.payType, + payAmount = editJobPostingDetail.payAmount.toIntOrNull() ?: return@suspendRunCatching, + roadNameAddress = editJobPostingDetail.roadNameAddress, + lotNumberAddress = editJobPostingDetail.lotNumberAddress, + clientName = editJobPostingDetail.clientName, + gender = editJobPostingDetail.gender, + birthYear = editJobPostingDetail.birthYear.toIntOrNull() ?: return@suspendRunCatching, + weight = editJobPostingDetail.weight?.toIntOrNull(), + careLevel = editJobPostingDetail.careLevel.toIntOrNull() ?: return@suspendRunCatching, + mentalStatus = editJobPostingDetail.mentalStatus, + disease = editJobPostingDetail.disease.ifBlank { null }, + isMealAssistance = editJobPostingDetail.isMealAssistance, + isBowelAssistance = editJobPostingDetail.isBowelAssistance, + isWalkingAssistance = editJobPostingDetail.isWalkingAssistance, + lifeAssistance = editJobPostingDetail.lifeAssistance.toList() + .sortedBy { it.ordinal } + .ifEmpty { listOf(LifeAssistance.NONE) }, + extraRequirement = editJobPostingDetail.extraRequirement, + isExperiencePreferred = editJobPostingDetail.isExperiencePreferred, + applyMethod = editJobPostingDetail.applyMethod.toList() + .sortedBy { it.ordinal }, + applyDeadLineType = editJobPostingDetail.applyDeadlineType, + applyDeadline = editJobPostingDetail.applyDeadline.toString() + .ifBlank { null }, + ) + }.onSuccess { getCenterJobPostingDetail(_jobPostingDetail.value?.id ?: return@launch) eventHelper.sendEvent( MainEvent.ShowToast("수정이 완료되었어요.", ToastType.SUCCESS) @@ -115,7 +123,9 @@ class CenterJobPostingDetailViewModel @Inject constructor( } internal fun endJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.endJobPosting(jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.endJobPosting(jobPostingId) + }.onSuccess { _jobPostingDetail.value = _jobPostingDetail.value?.copy(jobPostingStatus = JobPostingStatus.COMPLETED) eventHelper.sendEvent( @@ -125,7 +135,9 @@ class CenterJobPostingDetailViewModel @Inject constructor( } internal fun deleteJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.deleteJobPosting(jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.deleteJobPosting(jobPostingId) + }.onSuccess { navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( com.idle.navigation.DeepLinkDestination.CenterHome, diff --git a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt index f9cc682d..8ecd10d1 100644 --- a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt +++ b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt @@ -10,6 +10,7 @@ import com.idle.analytics.AnalyticsHelper import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType.SUCCESS +import com.idle.common.suspendRunCatching import com.idle.domain.model.auth.UserType import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.ApplyMethod @@ -47,7 +48,9 @@ class WorkerJobPostingDetailViewModel @Inject constructor( val workerJobPostingDetail = _workerJobPostingDetail.asStateFlow() internal fun getMyProfile() = viewModelScope.launch { - getMyWorkerProfileUseCase().onSuccess { + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { _profile.value = it }.onFailure { errorHelper.sendError(it) } } @@ -57,15 +60,15 @@ class WorkerJobPostingDetailViewModel @Inject constructor( jobPostingType: String, ) = viewModelScope.launch { when (jobPostingType) { - JobPostingType.CAREMEET.name -> jobPostingRepository.getWorkerJobPostingDetail( - jobPostingId - ).onSuccess { + JobPostingType.CAREMEET.name -> suspendRunCatching { + jobPostingRepository.getWorkerJobPostingDetail(jobPostingId) + }.onSuccess { _workerJobPostingDetail.value = it }.onFailure { errorHelper.sendError(it) } - JobPostingType.WORKNET.name -> jobPostingRepository.getCrawlingJobPostingDetail( - jobPostingId - ).onSuccess { + JobPostingType.WORKNET.name -> suspendRunCatching { + jobPostingRepository.getCrawlingJobPostingDetail(jobPostingId) + }.onSuccess { _workerJobPostingDetail.value = it }.onFailure { errorHelper.sendError(it) } } @@ -73,10 +76,12 @@ class WorkerJobPostingDetailViewModel @Inject constructor( internal fun applyJobPosting(jobPostingId: String, applyMethod: ApplyMethod) = viewModelScope.launch { - jobPostingRepository.applyJobPosting( - jobPostingId = jobPostingId, - applyMethod = applyMethod, - ).onSuccess { + suspendRunCatching { + jobPostingRepository.applyJobPosting( + jobPostingId = jobPostingId, + applyMethod = applyMethod, + ) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("지원이 완료되었어요.", SUCCESS)) if (_workerJobPostingDetail.value?.jobPostingType == JobPostingType.CAREMEET) { @@ -101,10 +106,12 @@ class WorkerJobPostingDetailViewModel @Inject constructor( jobPostingId: String, jobPostingType: JobPostingType, ) = viewModelScope.launch { - jobPostingRepository.addFavoriteJobPosting( - jobPostingId = jobPostingId, - jobPostingType = jobPostingType, - ).onSuccess { + suspendRunCatching { + jobPostingRepository.addFavoriteJobPosting( + jobPostingId = jobPostingId, + jobPostingType = jobPostingType, + ) + }.onSuccess { eventHelper.sendEvent( MainEvent.ShowToast("즐겨찾기에 추가되었어요.", SUCCESS) ) @@ -127,7 +134,9 @@ class WorkerJobPostingDetailViewModel @Inject constructor( jobPostingId: String, jobPostingType: JobPostingType, ) = viewModelScope.launch { - jobPostingRepository.removeFavoriteJobPosting(jobPostingId = jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.removeFavoriteJobPosting(jobPostingId = jobPostingId) + }.onSuccess { eventHelper.sendEvent( MainEvent.ShowToast("즐겨찾기에서 제거되었어요.", SUCCESS) ) @@ -147,10 +156,12 @@ class WorkerJobPostingDetailViewModel @Inject constructor( } internal fun generateChatRoom(opponentId: String) = viewModelScope.launch { - chatRepository.generateChatRooms( - userType = UserType.WORKER, - opponentId = opponentId, - ).onSuccess { + suspendRunCatching { + chatRepository.generateChatRooms( + userType = UserType.WORKER, + opponentId = opponentId, + ) + }.onSuccess { navigationHelper.navigateTo( NavigationEvent.To( DeepLinkDestination.ChattingDetail( diff --git a/feature/notification/src/main/java/com/idle/notification/NotificationViewModel.kt b/feature/notification/src/main/java/com/idle/notification/NotificationViewModel.kt index 8c074449..2e07418f 100644 --- a/feature/notification/src/main/java/com/idle/notification/NotificationViewModel.kt +++ b/feature/notification/src/main/java/com/idle/notification/NotificationViewModel.kt @@ -2,6 +2,7 @@ package com.idle.notification import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.JobPostingType import com.idle.domain.model.notification.Notification @@ -35,7 +36,9 @@ class NotificationViewModel @Inject constructor( return@launch } - notificationRepository.getMyNotifications(next.value).onSuccess { (nextId, notifications) -> + suspendRunCatching { + notificationRepository.getMyNotifications(next.value) + }.onSuccess { (nextId, notifications) -> _myNotifications.value = _myNotifications.value?.plus(notifications) ?: notifications next.value = nextId @@ -47,7 +50,9 @@ class NotificationViewModel @Inject constructor( internal fun onNotificationClick(notification: Notification) = viewModelScope.launch { launch { - notificationRepository.readNotification(notification.id).onSuccess { + suspendRunCatching { + notificationRepository.readNotification(notification.id) + }.onSuccess { _myNotifications.value = _myNotifications.value?.map { if (it.id == notification.id) { notification.copy(isRead = true) diff --git a/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt b/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt index 613a9ab1..a9b0c082 100644 --- a/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt +++ b/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt @@ -3,6 +3,7 @@ package com.idle.setting.center import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.analytics.AnalyticsHelper +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.AuthRepository @@ -37,13 +38,17 @@ class CenterSettingViewModel @Inject constructor( } private fun getMyProfile() = viewModelScope.launch { - getMyCenterProfileUseCase().onSuccess { + suspendRunCatching { + getMyCenterProfileUseCase() + }.onSuccess { _centerProfile.value = it }.onFailure { errorHelper.sendError(it) } } fun logout() = viewModelScope.launch { - authRepository.logoutCenter().onSuccess { + suspendRunCatching { + authRepository.logoutCenter() + }.onSuccess { analyticsHelper.setUserId(null) navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.ToAuthWithClearBackStack( diff --git a/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt b/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt index 94121bee..05ebc07f 100644 --- a/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt +++ b/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt @@ -3,6 +3,7 @@ package com.idle.setting.worker import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.analytics.AnalyticsHelper +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.AuthRepository @@ -33,13 +34,17 @@ class WorkerSettingViewModel @Inject constructor( } private fun getMyProfile() = viewModelScope.launch { - getMyWorkerProfileUseCase().onSuccess { + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { _workerProfile.value = it }.onFailure { errorHelper.sendError(it) } } fun logout() = viewModelScope.launch { - authRepository.logoutWorker().onSuccess { + suspendRunCatching { + authRepository.logoutWorker() + }.onSuccess { analyticsHelper.setUserId(null) navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.ToAuthWithClearBackStack( diff --git a/feature/signin/src/main/java/com/idle/signin/center/CenterSignInViewModel.kt b/feature/signin/src/main/java/com/idle/signin/center/CenterSignInViewModel.kt index 5af940bf..55acb8bc 100644 --- a/feature/signin/src/main/java/com/idle/signin/center/CenterSignInViewModel.kt +++ b/feature/signin/src/main/java/com/idle/signin/center/CenterSignInViewModel.kt @@ -7,6 +7,7 @@ import com.idle.analytics.AnalyticsEvent.PropertiesKeys.ACTION_NAME import com.idle.analytics.AnalyticsEvent.PropertiesKeys.ACTION_RESULT import com.idle.analytics.AnalyticsHelper import com.idle.binding.EventHelper +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ApiErrorCode import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.error.HttpResponseException @@ -62,33 +63,38 @@ class CenterSignInViewModel @Inject constructor( } internal fun signInCenter() = viewModelScope.launch { - authRepository.signInCenter(identifier = _centerId.value, password = _centerPassword.value) - .onSuccess { - analyticsHelper.setUserId(_centerId.value) - handleCenterLoginSuccess() + suspendRunCatching { + authRepository.signInCenter( + identifier = _centerId.value, + password = _centerPassword.value + ) + }.onSuccess { + analyticsHelper.setUserId(_centerId.value) + handleCenterLoginSuccess() + }.onFailure { + if (it is HttpResponseException && it.status == HttpResponseStatus.Unauthorized) { + _isLoginError.value = true + return@launch } - .onFailure { - if (it is HttpResponseException && it.status == HttpResponseStatus.Unauthorized) { - _isLoginError.value = true - return@launch - } - errorHelper.sendError(it) + errorHelper.sendError(it) - analyticsHelper.logEvent( - AnalyticsEvent( - type = AnalyticsEvent.Types.ACTION, - properties = mutableMapOf( - ACTION_NAME to "center_login", - ACTION_RESULT to false, - ) + analyticsHelper.logEvent( + AnalyticsEvent( + type = AnalyticsEvent.Types.ACTION, + properties = mutableMapOf( + ACTION_NAME to "center_login", + ACTION_RESULT to false, ) ) - } + ) + } } private fun handleCenterLoginSuccess() = viewModelScope.launch { - profileRepository.getCenterStatus().onSuccess { centerStatusResponse -> + suspendRunCatching { + profileRepository.getCenterStatus() + }.onSuccess { centerStatusResponse -> navigateBasedOnCenterStatus(centerStatusResponse.centerManagerAccountStatus) }.onFailure { errorHelper.sendError(it) } } @@ -106,7 +112,9 @@ class CenterSignInViewModel @Inject constructor( } private fun fetchAndNavigateToProfile() = viewModelScope.launch { - profileRepository.getMyCenterProfile().onSuccess { + suspendRunCatching { + profileRepository.getMyCenterProfile() + }.onSuccess { navigationHelper.navigateTo(To(CenterHome, R.id.centerSignInFragment)) }.onFailure { val error = it as HttpResponseException diff --git a/feature/signin/src/main/java/com/idle/signin/center/newpassword/NewPasswordViewModel.kt b/feature/signin/src/main/java/com/idle/signin/center/newpassword/NewPasswordViewModel.kt index 1893bcd6..e278d235 100644 --- a/feature/signin/src/main/java/com/idle/signin/center/newpassword/NewPasswordViewModel.kt +++ b/feature/signin/src/main/java/com/idle/signin/center/newpassword/NewPasswordViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent +import com.idle.common.suspendRunCatching import com.idle.domain.model.CountDownTimer import com.idle.domain.model.CountDownTimer.Companion.SECONDS_PER_MINUTE import com.idle.domain.model.CountDownTimer.Companion.TICK_INTERVAL @@ -149,16 +150,20 @@ class NewPasswordViewModel @Inject constructor( } internal fun sendPhoneNumber() = viewModelScope.launch { - authRepository.sendPhoneNumber(formatPhoneNumber(_phoneNumber.value)) - .onSuccess { startTimer() } - .onFailure { eventHelper.sendEvent(MainEvent.ShowToast(it.message.toString())) } + suspendRunCatching { + authRepository.sendPhoneNumber(formatPhoneNumber(_phoneNumber.value)) + }.onSuccess { + startTimer() + }.onFailure { eventHelper.sendEvent(MainEvent.ShowToast(it.message.toString())) } } internal fun confirmAuthCode() = viewModelScope.launch { - authRepository.confirmAuthCode( - formatPhoneNumber(_phoneNumber.value), - this@NewPasswordViewModel._authCode.value - ).onSuccess { + suspendRunCatching { + authRepository.confirmAuthCode( + formatPhoneNumber(_phoneNumber.value), + this@NewPasswordViewModel._authCode.value + ) + }.onSuccess { cancelTimer() _isConfirmAuthCode.value = true _newPasswordProcess.value = GENERATE_NEW_PASSWORD @@ -180,10 +185,12 @@ class NewPasswordViewModel @Inject constructor( return@launch } - authRepository.generateNewPassword( - newPassword = _newPassword.value, - phoneNumber = formatPhoneNumber(_phoneNumber.value), - ).onSuccess { + suspendRunCatching { + authRepository.generateNewPassword( + newPassword = _newPassword.value, + phoneNumber = formatPhoneNumber(_phoneNumber.value), + ) + }.onSuccess { navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( destination = com.idle.navigation.DeepLinkDestination.CenterSignIn("새 비밀번호를 발급하였습니다."), diff --git a/feature/signup/src/main/java/com/idle/signup/center/CenterSignUpViewModel.kt b/feature/signup/src/main/java/com/idle/signup/center/CenterSignUpViewModel.kt index 89b68ef4..68bb04e0 100644 --- a/feature/signup/src/main/java/com/idle/signup/center/CenterSignUpViewModel.kt +++ b/feature/signup/src/main/java/com/idle/signup/center/CenterSignUpViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent +import com.idle.common.suspendRunCatching import com.idle.domain.model.CountDownTimer import com.idle.domain.model.CountDownTimer.Companion.SECONDS_PER_MINUTE import com.idle.domain.model.CountDownTimer.Companion.TICK_INTERVAL @@ -208,29 +209,32 @@ class CenterSignUpViewModel @Inject constructor( } internal fun sendPhoneNumber() = viewModelScope.launch { - authRepository.sendPhoneNumber(formatPhoneNumber(_centerPhoneNumber.value)) - .onSuccess { startTimer() } - .onFailure { errorHelper.sendError(it) } + suspendRunCatching { + authRepository.sendPhoneNumber(formatPhoneNumber(_centerPhoneNumber.value)) + }.onSuccess { + startTimer() + }.onFailure { errorHelper.sendError(it) } } internal fun confirmAuthCode() = viewModelScope.launch { - authRepository.confirmAuthCode( - formatPhoneNumber(_centerPhoneNumber.value), - _centerAuthCode.value - ) - .onSuccess { - cancelTimer() - _isConfirmAuthCode.value = true - - setCenterSignUpStep(CenterSignUpStep.BUSINESS_REGISTRATION) - }.onFailure { - if (it is HttpResponseException && it.status == HttpResponseStatus.BadRequest) { - _isAuthCodeError.value = true - return@launch - } - - errorHelper.sendError(it) + suspendRunCatching { + authRepository.confirmAuthCode( + formatPhoneNumber(_centerPhoneNumber.value), + _centerAuthCode.value + ) + }.onSuccess { + cancelTimer() + _isConfirmAuthCode.value = true + + setCenterSignUpStep(CenterSignUpStep.BUSINESS_REGISTRATION) + }.onFailure { + if (it is HttpResponseException && it.status == HttpResponseStatus.BadRequest) { + _isAuthCodeError.value = true + return@launch } + + errorHelper.sendError(it) + } } internal fun signUpCenter() = viewModelScope.launch { @@ -241,44 +245,49 @@ class CenterSignUpViewModel @Inject constructor( return@launch } - authRepository.signUpCenter( - identifier = _centerId.value, - password = _centerPassword.value, - phoneNumber = formatPhoneNumber(_centerPhoneNumber.value), - managerName = _centerName.value, - businessRegistrationNumber = formatBusinessRegistrationNumber( - _businessRegistrationNumber.value - ), - ).onSuccess { + suspendRunCatching { + authRepository.signUpCenter( + identifier = _centerId.value, + password = _centerPassword.value, + phoneNumber = formatPhoneNumber(_centerPhoneNumber.value), + managerName = _centerName.value, + businessRegistrationNumber = formatBusinessRegistrationNumber( + _businessRegistrationNumber.value + ), + ) + }.onSuccess { navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( com.idle.navigation.DeepLinkDestination.CenterSignIn("회원가입을 성공하였습니다."), R.id.centerSignUpFragment ) ) - } - .onFailure { errorHelper.sendError(it) } + }.onFailure { errorHelper.sendError(it) } } internal fun validateIdentifier() = viewModelScope.launch { eventHelper.sendEvent(MainEvent.DismissToast) - authRepository.validateIdentifier(_centerId.value) - .onSuccess { _centerIdResult.value = true } - .onFailure { - if (it is HttpResponseException && it.apiErrorCode == ApiErrorCode.DuplicateIdentifier) { - _centerIdResult.value = false - return@onFailure - } - - errorHelper.sendError(it) + suspendRunCatching { + authRepository.validateIdentifier(_centerId.value) + }.onSuccess { + _centerIdResult.value = true + }.onFailure { + if (it is HttpResponseException && it.apiErrorCode == ApiErrorCode.DuplicateIdentifier) { + _centerIdResult.value = false + return@onFailure } + + errorHelper.sendError(it) + } } internal fun validateBusinessRegistrationNumber() = viewModelScope.launch { - authRepository.validateBusinessRegistrationNumber( - formatBusinessRegistrationNumber(_businessRegistrationNumber.value) - ).onSuccess { + suspendRunCatching { + authRepository.validateBusinessRegistrationNumber( + formatBusinessRegistrationNumber(_businessRegistrationNumber.value) + ) + }.onSuccess { _businessRegistrationInfo.value = it }.onFailure { errorHelper.sendError(it) } } diff --git a/feature/signup/src/main/java/com/idle/signup/worker/WorkerSignUpViewModel.kt b/feature/signup/src/main/java/com/idle/signup/worker/WorkerSignUpViewModel.kt index f7414074..b79f2484 100644 --- a/feature/signup/src/main/java/com/idle/signup/worker/WorkerSignUpViewModel.kt +++ b/feature/signup/src/main/java/com/idle/signup/worker/WorkerSignUpViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.idle.analytics.AnalyticsHelper import com.idle.binding.EventHelper +import com.idle.common.suspendRunCatching import com.idle.domain.model.CountDownTimer import com.idle.domain.model.CountDownTimer.Companion.SECONDS_PER_MINUTE import com.idle.domain.model.CountDownTimer.Companion.TICK_INTERVAL @@ -113,9 +114,11 @@ class WorkerSignUpViewModel @Inject constructor( } internal fun sendPhoneNumber() = viewModelScope.launch { - authRepository.sendPhoneNumber(formatPhoneNumber(_workerPhoneNumber.value)) - .onSuccess { startTimer() } - .onFailure { errorHelper.sendError(it) } + suspendRunCatching { + authRepository.sendPhoneNumber(formatPhoneNumber(_workerPhoneNumber.value)) + }.onSuccess { + startTimer() + }.onFailure { errorHelper.sendError(it) } } private fun startTimer() { @@ -146,11 +149,15 @@ class WorkerSignUpViewModel @Inject constructor( } internal fun confirmAuthCode() = viewModelScope.launch { - authRepository.signInWorker( - phoneNumber = formatPhoneNumber(_workerPhoneNumber.value), - authCode = _workerAuthCode.value, - ).onSuccess { - profileRepository.getWorkerId().onSuccess { analyticsHelper.setUserId(it) } + suspendRunCatching { + authRepository.signInWorker( + phoneNumber = formatPhoneNumber(_workerPhoneNumber.value), + authCode = _workerAuthCode.value, + ) + }.onSuccess { + suspendRunCatching { + profileRepository.getWorkerId() + }.onSuccess { analyticsHelper.setUserId(it) } navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( WorkerHome, @@ -158,10 +165,12 @@ class WorkerSignUpViewModel @Inject constructor( ) ) }.onFailure { - authRepository.confirmAuthCode( - formatPhoneNumber(_workerPhoneNumber.value), - _workerAuthCode.value - ).onSuccess { + suspendRunCatching { + authRepository.confirmAuthCode( + formatPhoneNumber(_workerPhoneNumber.value), + _workerAuthCode.value + ) + }.onSuccess { cancelTimer() _isConfirmAuthCode.value = true _signUpStep.value = WorkerSignUpStep.findStep(PHONE_NUMBER.step + 1) @@ -177,15 +186,19 @@ class WorkerSignUpViewModel @Inject constructor( } internal fun signUpWorker() = viewModelScope.launch { - authRepository.signUpWorker( - name = _workerName.value, - birthYear = _birthYear.value.toIntOrNull() ?: return@launch, - genderType = _gender.value.name, - phoneNumber = formatPhoneNumber(_workerPhoneNumber.value), - roadNameAddress = _roadNameAddress.value, - lotNumberAddress = _lotNumberAddress.value, - ).onSuccess { - profileRepository.getWorkerId().onSuccess { analyticsHelper.setUserId(it) } + suspendRunCatching { + authRepository.signUpWorker( + name = _workerName.value, + birthYear = _birthYear.value.toIntOrNull() ?: return@suspendRunCatching, + genderType = _gender.value.name, + phoneNumber = formatPhoneNumber(_workerPhoneNumber.value), + roadNameAddress = _roadNameAddress.value, + lotNumberAddress = _lotNumberAddress.value, + ) + }.onSuccess { + suspendRunCatching { + profileRepository.getWorkerId() + }.onSuccess { analyticsHelper.setUserId(it) } navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.To( SignUpComplete, diff --git a/feature/withdrawal/src/main/java/com/idle/withdrawal/WithdrawalViewModel.kt b/feature/withdrawal/src/main/java/com/idle/withdrawal/WithdrawalViewModel.kt index 50d71a2d..85300b5d 100644 --- a/feature/withdrawal/src/main/java/com/idle/withdrawal/WithdrawalViewModel.kt +++ b/feature/withdrawal/src/main/java/com/idle/withdrawal/WithdrawalViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.idle.analytics.AnalyticsHelper import com.idle.binding.EventHelper import com.idle.binding.MainEvent +import com.idle.common.suspendRunCatching import com.idle.domain.model.CountDownTimer import com.idle.domain.model.CountDownTimer.Companion.SECONDS_PER_MINUTE import com.idle.domain.model.CountDownTimer.Companion.TICK_INTERVAL @@ -87,9 +88,11 @@ class WithdrawalViewModel @Inject constructor( } internal fun sendPhoneNumber() = viewModelScope.launch { - authRepository.sendPhoneNumber(formatPhoneNumber(_phoneNumber.value)) - .onSuccess { startTimer() } - .onFailure { errorHelper.sendError(it) } + suspendRunCatching { + authRepository.sendPhoneNumber(formatPhoneNumber(_phoneNumber.value)) + }.onSuccess { + startTimer() + }.onFailure { errorHelper.sendError(it) } } internal fun setInconvenientReason(reason: String) { @@ -134,12 +137,12 @@ class WithdrawalViewModel @Inject constructor( } internal fun confirmAuthCode() = viewModelScope.launch { - authRepository.confirmAuthCode(formatPhoneNumber(_phoneNumber.value), _authCode.value) - .onSuccess { - cancelTimer() - _isConfirmAuthCode.value = true - } - .onFailure { errorHelper.sendError(it) } + suspendRunCatching { + authRepository.confirmAuthCode(formatPhoneNumber(_phoneNumber.value), _authCode.value) + }.onSuccess { + cancelTimer() + _isConfirmAuthCode.value = true + }.onFailure { errorHelper.sendError(it) } } internal fun withdrawal(userType: UserType) = viewModelScope.launch { @@ -150,20 +153,22 @@ class WithdrawalViewModel @Inject constructor( } private suspend fun withdrawalCenter() { - authRepository.withdrawalCenter( - reason = _withdrawalReason.value - .sortedBy { it.ordinal } - .map { reason -> - when (reason) { - WithdrawalReason.INCONVENIENT_PLATFORM_USE -> "${reason} : ${_inconvenientReason.value}" - WithdrawalReason.USING_ANOTHER_PLATFORM -> "${reason} : ${_anotherPlatformReason.value}" - WithdrawalReason.LACK_OF_DESIRED_FEATURES -> "${reason} : ${_lackFeaturesReason.value}" - else -> reason + suspendRunCatching { + authRepository.withdrawalCenter( + reason = _withdrawalReason.value + .sortedBy { it.ordinal } + .map { reason -> + when (reason) { + WithdrawalReason.INCONVENIENT_PLATFORM_USE -> "${reason} : ${_inconvenientReason.value}" + WithdrawalReason.USING_ANOTHER_PLATFORM -> "${reason} : ${_anotherPlatformReason.value}" + WithdrawalReason.LACK_OF_DESIRED_FEATURES -> "${reason} : ${_lackFeaturesReason.value}" + else -> reason + } } - } - .joinToString("|"), - password = password.value - ).onSuccess { + .joinToString("|"), + password = password.value + ) + }.onSuccess { analyticsHelper.setUserId(null) navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.ToAuthWithClearBackStack( @@ -181,11 +186,13 @@ class WithdrawalViewModel @Inject constructor( } private suspend fun withdrawalWorker() { - authRepository.withdrawalWorker( - _withdrawalReason.value - .sortedBy { it.ordinal } - .joinToString("|"), - ).onSuccess { + suspendRunCatching { + authRepository.withdrawalWorker( + _withdrawalReason.value + .sortedBy { it.ordinal } + .joinToString("|"), + ) + }.onSuccess { analyticsHelper.setUserId(null) navigationHelper.navigateTo( com.idle.navigation.NavigationEvent.ToAuthWithClearBackStack( diff --git a/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt b/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt index a05be80c..3668eec2 100644 --- a/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt +++ b/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt @@ -2,6 +2,7 @@ package com.idle.worker.chatting import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.idle.common.suspendRunCatching import com.idle.domain.model.auth.UserType import com.idle.domain.model.chat.ChatMessage import com.idle.domain.model.chat.ChatRoomWithOpponentInfo @@ -44,16 +45,22 @@ class WorkerChattingViewModel @Inject constructor( ) internal suspend fun initWorkerChatting() { - getMyWorkerProfileUseCase().onSuccess { profile -> + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { profile -> _myProfile.value = profile }.onFailure { errorHelper.sendError(it) } } internal suspend fun retrieveChatRoomList() { - getChatRoomsUseCase( - userType = UserType.WORKER, - userId = _myProfile.value?.workerId ?: return, - ).onSuccess { + val workerId = _myProfile.value?.workerId ?: return + + suspendRunCatching { + getChatRoomsUseCase( + userType = UserType.WORKER, + userId = workerId, + ) + }.onSuccess { val newMap = LinkedHashMap(_chatRoomMap.value) it.forEach { chatRoom -> newMap[chatRoom.id] = chatRoom } _chatRoomMap.value = newMap @@ -61,10 +68,14 @@ class WorkerChattingViewModel @Inject constructor( } internal suspend fun loadChatRoomList() { - chatRepository.loadChatRooms( - userId = _myProfile.value?.workerId ?: return, - userType = UserType.WORKER, - ).onSuccess { + val workerId = _myProfile.value?.workerId ?: return + + suspendRunCatching { + chatRepository.loadChatRooms( + userId = workerId, + userType = UserType.WORKER, + ) + }.onSuccess { val newMap = LinkedHashMap(_chatRoomMap.value) it.forEach { chatRoom -> newMap[chatRoom.id] = chatRoom } _chatRoomMap.value = newMap @@ -72,7 +83,9 @@ class WorkerChattingViewModel @Inject constructor( } internal fun connectWebsocket() = viewModelScope.launch { - chatRepository.connectWebSocket().onSuccess { + suspendRunCatching { + chatRepository.connectWebSocket() + }.onSuccess { subscribeChatMessage() } } @@ -107,8 +120,9 @@ class WorkerChattingViewModel @Inject constructor( ) } else { // 새로운 방이면 새로 생성 후 최상단에 추가 - val opponentProfile = profileRepository.getCenterProfile(chatMessage.senderId) - .getOrNull() ?: return + val opponentProfile = suspendRunCatching { + profileRepository.getCenterProfile(chatMessage.senderId) + }.getOrNull() ?: return val newChatRoom = ChatRoomWithOpponentInfo( id = roomId, diff --git a/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt b/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt index 305027df..1b41ccf3 100644 --- a/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt +++ b/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.ApplyMethod import com.idle.domain.model.jobposting.CrawlingJobPosting @@ -65,16 +66,20 @@ class WorkerHomeViewModel @Inject constructor( } internal fun getUnreadNotificationCount() = viewModelScope.launch { - notificationRepository.getUnreadNotificationCount().onSuccess { + suspendRunCatching { + notificationRepository.getUnreadNotificationCount() + }.onSuccess { _unreadNotificationCount.value = it }.onFailure { errorHelper.sendError(it) } } internal fun applyJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.applyJobPosting( - jobPostingId = jobPostingId, - applyMethod = ApplyMethod.APP - ).onSuccess { + suspendRunCatching { + jobPostingRepository.applyJobPosting( + jobPostingId = jobPostingId, + applyMethod = ApplyMethod.APP + ) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("지원이 완료되었어요.", ToastType.SUCCESS)) _jobPostings.value = _jobPostings.value?.map { @@ -90,10 +95,12 @@ class WorkerHomeViewModel @Inject constructor( jobPostingId: String, jobPostingType: JobPostingType, ) = viewModelScope.launch { - jobPostingRepository.addFavoriteJobPosting( - jobPostingId = jobPostingId, - jobPostingType = jobPostingType, - ).onSuccess { + suspendRunCatching { + jobPostingRepository.addFavoriteJobPosting( + jobPostingId = jobPostingId, + jobPostingType = jobPostingType, + ) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("즐겨찾기에 추가되었어요.", ToastType.SUCCESS)) _jobPostings.value = _jobPostings.value?.map { @@ -113,7 +120,9 @@ class WorkerHomeViewModel @Inject constructor( } internal fun removeFavoriteJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.removeFavoriteJobPosting(jobPostingId = jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.removeFavoriteJobPosting(jobPostingId = jobPostingId) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("즐겨찾기에서 제거되었어요", ToastType.SUCCESS)) _jobPostings.value = _jobPostings.value?.map { @@ -133,7 +142,9 @@ class WorkerHomeViewModel @Inject constructor( } internal fun getMyWorkerProfile() = viewModelScope.launch { - getMyWorkerProfileUseCase().onSuccess { + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { _profile.value = it }.onFailure { eventHelper.sendEvent(MainEvent.ShowToast(it.message.toString())) @@ -154,25 +165,28 @@ class WorkerHomeViewModel @Inject constructor( } private suspend fun fetchInAppJobPostings() { - jobPostingRepository.getJobPostings(next = nextCursorId) - .onSuccess { (nextId, postings) -> - nextCursorId = nextId - if (nextId == null) { - _callType.value = JobPostingCallType.CRAWLING - } - _jobPostings.value = _jobPostings.value?.plus(postings) ?: postings + suspendRunCatching { + jobPostingRepository.getJobPostings(next = nextCursorId) + }.onSuccess { (nextId, postings) -> + nextCursorId = nextId + if (nextId == null) { + _callType.value = JobPostingCallType.CRAWLING + } + _jobPostings.value = _jobPostings.value?.plus(postings) ?: postings - if (_jobPostings.value?.isEmpty() != false) { - getJobPostings() - } - }.onFailure { errorHelper.sendError(it) } + if (_jobPostings.value?.isEmpty() != false) { + getJobPostings() + } + }.onFailure { errorHelper.sendError(it) } } private suspend fun fetchCrawlingJobPostings() { - jobPostingRepository.getCrawlingJobPostings( - next = nextCursorId, - distance = nextDistance, - ).onSuccess { pageInfo -> + suspendRunCatching { + jobPostingRepository.getCrawlingJobPostings( + next = nextCursorId, + distance = nextDistance, + ) + }.onSuccess { pageInfo -> nextCursorId = pageInfo.nextCursor nextDistance = pageInfo.nextDistance diff --git a/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt b/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt index 4214cd04..833c9e06 100644 --- a/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt +++ b/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.jobposting.ApplyMethod import com.idle.domain.model.jobposting.CrawlingJobPosting @@ -49,7 +50,9 @@ class WorkerJobPostingViewModel @Inject constructor( init { viewModelScope.launch { - getMyWorkerProfileUseCase().onSuccess { + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { _profile.value = it } } @@ -74,17 +77,18 @@ class WorkerJobPostingViewModel @Inject constructor( return@launch } - jobPostingRepository.getJobPostingsApplied(next = nextCursorId) - .onSuccess { nextPage -> - nextCursorId = nextPage.nextCursor + suspendRunCatching { + jobPostingRepository.getJobPostingsApplied(next = nextCursorId) + }.onSuccess { nextPage -> + nextCursorId = nextPage.nextCursor - if (nextPage.nextCursor == null) { - appliedJobPostingCallType = JobPostingCallType.END - } + if (nextPage.nextCursor == null) { + appliedJobPostingCallType = JobPostingCallType.END + } - _appliedJobPostings.value = - _appliedJobPostings.value?.plus(nextPage.items) ?: nextPage.items - }.onFailure { errorHelper.sendError(it) } + _appliedJobPostings.value = + _appliedJobPostings.value?.plus(nextPage.items) ?: nextPage.items + }.onFailure { errorHelper.sendError(it) } } finally { isLoading = false } @@ -96,22 +100,28 @@ class WorkerJobPostingViewModel @Inject constructor( } private suspend fun getFavoriteCareMeetJobPostings() { - jobPostingRepository.getMyFavoritesJobPostings().onSuccess { postings -> + suspendRunCatching { + jobPostingRepository.getMyFavoritesJobPostings() + }.onSuccess { postings -> _favoriteJobPostings.value = _favoriteJobPostings.value?.plus(postings) ?: postings }.onFailure { errorHelper.sendError(it) } } private suspend fun getFavoriteCrawlingJobPostings() { - jobPostingRepository.getMyFavoritesCrawlingJobPostings().onSuccess { postings -> + suspendRunCatching { + jobPostingRepository.getMyFavoritesCrawlingJobPostings() + }.onSuccess { postings -> _favoriteJobPostings.value = _favoriteJobPostings.value?.plus(postings) ?: postings }.onFailure { errorHelper.sendError(it) } } internal fun applyJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.applyJobPosting( - jobPostingId = jobPostingId, - applyMethod = ApplyMethod.APP - ).onSuccess { + suspendRunCatching { + jobPostingRepository.applyJobPosting( + jobPostingId = jobPostingId, + applyMethod = ApplyMethod.APP + ) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("지원이 완료되었어요.", ToastType.SUCCESS)) _appliedJobPostings.value = _appliedJobPostings.value?.map { @@ -134,10 +144,12 @@ class WorkerJobPostingViewModel @Inject constructor( jobPostingId: String, jobPostingType: JobPostingType, ) = viewModelScope.launch { - jobPostingRepository.addFavoriteJobPosting( - jobPostingId = jobPostingId, - jobPostingType = jobPostingType, - ).onSuccess { + suspendRunCatching { + jobPostingRepository.addFavoriteJobPosting( + jobPostingId = jobPostingId, + jobPostingType = jobPostingType, + ) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("즐겨찾기에 추가되었어요.", ToastType.SUCCESS)) _appliedJobPostings.value = _appliedJobPostings.value?.map { @@ -171,7 +183,9 @@ class WorkerJobPostingViewModel @Inject constructor( } internal fun removeFavoriteJobPosting(jobPostingId: String) = viewModelScope.launch { - jobPostingRepository.removeFavoriteJobPosting(jobPostingId = jobPostingId).onSuccess { + suspendRunCatching { + jobPostingRepository.removeFavoriteJobPosting(jobPostingId = jobPostingId) + }.onSuccess { eventHelper.sendEvent(MainEvent.ShowToast("즐겨찾기에서 제거했어요.", ToastType.SUCCESS)) _appliedJobPostings.value = _appliedJobPostings.value?.map { diff --git a/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt b/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt index 71c1d4e3..bfe1982a 100644 --- a/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt +++ b/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.viewModelScope import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.binding.ToastType +import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.JobSearchStatus import com.idle.domain.model.profile.WorkerProfile @@ -92,7 +93,9 @@ class WorkerProfileViewModel @Inject constructor( } internal fun getMyWorkerProfile() = viewModelScope.launch { - getMyWorkerProfileUseCase().onSuccess { + suspendRunCatching { + getMyWorkerProfileUseCase() + }.onSuccess { _workerProfile.value = it _workerIntroduce.value = it.introduce ?: "" _specialty.value = it.speciality ?: "" @@ -105,7 +108,9 @@ class WorkerProfileViewModel @Inject constructor( } internal fun getWorkerProfile(workerId: String) = viewModelScope.launch { - profileRepository.getWorkerProfile(workerId).onSuccess { + suspendRunCatching { + profileRepository.getWorkerProfile(workerId) + }.onSuccess { _workerProfile.value = it _workerIntroduce.value = it.introduce ?: "" _specialty.value = it.speciality ?: "" @@ -126,15 +131,17 @@ class WorkerProfileViewModel @Inject constructor( _isUpdateLoading.value = true - updateWorkerProfileUseCase( - experienceYear = _experienceYear.value, - roadNameAddress = _roadNameAddress.value, - lotNumberAddress = _lotNumberAddress.value, - speciality = _specialty.value, - introduce = _workerIntroduce.value.ifBlank { null }, - imageFileUri = _profileImageUri.value?.toString(), - jobSearchStatus = _jobSearchStatus.value, - ).onSuccess { + suspendRunCatching { + updateWorkerProfileUseCase( + experienceYear = _experienceYear.value, + roadNameAddress = _roadNameAddress.value, + lotNumberAddress = _lotNumberAddress.value, + speciality = _specialty.value, + introduce = _workerIntroduce.value.ifBlank { null }, + imageFileUri = _profileImageUri.value?.toString(), + jobSearchStatus = _jobSearchStatus.value, + ) + }.onSuccess { getMyWorkerProfile() eventHelper.sendEvent( MainEvent.ShowToast( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2e540c76..795d6e7e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -60,7 +60,7 @@ okhttp = "4.12.0" retrofit = "2.11.0" # KrossBBow ## https://github.com/joffrey-bion/krossbow -krossbow = "9.0.0" +krossbow = "9.3.0" ## Kotlin # https://github.com/JetBrains/kotlin diff --git a/presentation/src/main/java/com/idle/presentation/MainViewModel.kt b/presentation/src/main/java/com/idle/presentation/MainViewModel.kt index 0cc52733..ddc5c391 100644 --- a/presentation/src/main/java/com/idle/presentation/MainViewModel.kt +++ b/presentation/src/main/java/com/idle/presentation/MainViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.idle.auth.R import com.idle.binding.EventHelper import com.idle.binding.MainEvent.ShowToast +import com.idle.common.suspendRunCatching import com.idle.domain.model.auth.UserType import com.idle.domain.model.config.ForceUpdate import com.idle.domain.model.error.ApiErrorCode @@ -59,7 +60,9 @@ class MainViewModel @Inject constructor( } internal fun getForceUpdateInfo() = viewModelScope.launch { - configRepository.getForceUpdate().onSuccess { + suspendRunCatching { + configRepository.getForceUpdate() + }.onSuccess { _forceUpdate.value = it }.onFailure { errorHelper.sendError(it) } } @@ -72,7 +75,9 @@ class MainViewModel @Inject constructor( } if (userRole == UserType.WORKER.apiValue) { - profileRepository.getMyWorkerProfile().onFailure { + suspendRunCatching { + profileRepository.getMyWorkerProfile() + }.onFailure { return@launch } } @@ -85,8 +90,9 @@ class MainViewModel @Inject constructor( } internal fun readNotification(notificationId: String) = viewModelScope.launch { - notificationRepository.readNotification(notificationId) - .onFailure { errorHelper.sendError(it) } + suspendRunCatching { + notificationRepository.readNotification(notificationId) + }.onFailure { errorHelper.sendError(it) } } private suspend fun getAccessTokenAndUserRole(): Pair = coroutineScope { @@ -109,10 +115,11 @@ class MainViewModel @Inject constructor( } } - private suspend fun getCenterStatus() = - profileRepository.getCenterStatus().onSuccess { centerStatusResponse -> - handleCenterStatus(centerStatusResponse.centerManagerAccountStatus) - } + private suspend fun getCenterStatus() = suspendRunCatching { + profileRepository.getCenterStatus() + }.onSuccess { centerStatusResponse -> + handleCenterStatus(centerStatusResponse.centerManagerAccountStatus) + } private fun handleCenterStatus(status: CenterManagerAccountStatus) { when (status) { @@ -127,7 +134,9 @@ class MainViewModel @Inject constructor( } private fun handleApprovedCenterStatus() = viewModelScope.launch { - profileRepository.getMyCenterProfile().onSuccess { + suspendRunCatching { + profileRepository.getMyCenterProfile() + }.onSuccess { navigationHelper.navigateTo(NavigationEvent.To(CenterHome, R.id.authFragment)) }.onFailure { val error = it as HttpResponseException From c9f7ad7c2fc7d76f5c315e5ff4baf05f76cde2cb Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Mon, 21 Jul 2025 18:16:56 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[IDLE-000]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B0=9D=EC=B2=B4=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20NotificationChannel=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B0=A9=EB=B2=95=EC=9D=98=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20NotificationService=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 --- .../java/com/idle/care/CareApplication.kt | 20 +----- .../care/notification/NotificationHandler.kt | 55 -------------- .../care/notification/NotificationService.kt | 71 +++++++++++++++++-- 3 files changed, 69 insertions(+), 77 deletions(-) delete mode 100644 app/src/main/java/com/idle/care/notification/NotificationHandler.kt diff --git a/app/src/main/java/com/idle/care/CareApplication.kt b/app/src/main/java/com/idle/care/CareApplication.kt index 8de3a496..7109a244 100644 --- a/app/src/main/java/com/idle/care/CareApplication.kt +++ b/app/src/main/java/com/idle/care/CareApplication.kt @@ -1,12 +1,9 @@ package com.idle.care import android.app.Application -import android.app.NotificationChannel -import android.app.NotificationManager import com.appsflyer.AppsFlyerLib import com.appsflyer.attribution.AppsFlyerRequestListener -import com.idle.care.notification.NotificationHandler.Companion.BACKGROUND_CHANNEL -import com.idle.care.notification.NotificationHandler.Companion.BACKGROUND_DESCRIPTION +import com.idle.care.notification.NotificationService.Companion.initNotification import com.idle.domain.model.error.ErrorHelper import com.kakao.sdk.common.KakaoSdk import dagger.hilt.android.HiltAndroidApp @@ -22,24 +19,11 @@ class CareApplication : Application() { override fun onCreate() { super.onCreate() - initNotification() + initNotification(this) initKakao() initAppsFlyer() } - private fun initNotification() { - val channel = - NotificationChannel( - BACKGROUND_CHANNEL, - BACKGROUND_CHANNEL, - NotificationManager.IMPORTANCE_DEFAULT - ) - channel.description = BACKGROUND_DESCRIPTION - - val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(channel) - } - private fun initKakao() { KakaoSdk.init(this, BuildConfig.KAKAO_APP_KEY) } diff --git a/app/src/main/java/com/idle/care/notification/NotificationHandler.kt b/app/src/main/java/com/idle/care/notification/NotificationHandler.kt deleted file mode 100644 index e1d46148..00000000 --- a/app/src/main/java/com/idle/care/notification/NotificationHandler.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.idle.care.notification - -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat -import com.idle.designsystem.binding.R -import com.idle.presentation.MainActivity -import javax.inject.Inject - -class NotificationHandler @Inject constructor(private val context: Context) { - private val notificationManager: NotificationManager = - context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - internal fun deliverNotification( - title: String, - body: String, - data: Map - ) { - val intent = Intent(context, MainActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP - } - - if (data.isNotEmpty()) { - data.forEach { (key, value) -> - intent.putExtra(key, value) - } - } - - val pendingIntent = PendingIntent.getActivity( - context, - 0, - intent, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) - - val builder = NotificationCompat.Builder(context, BACKGROUND_CHANNEL) - .setSmallIcon(com.idle.care.R.drawable.ic_notification_icon) - .setColor(ContextCompat.getColor(context, R.color.orange_500)) - .setContentTitle(title) - .setContentText(body) - .setContentIntent(pendingIntent) - .setAutoCancel(true) - - notificationManager.notify(System.currentTimeMillis().toInt(), builder.build()) - } - - companion object { - const val BACKGROUND_CHANNEL = "백그라운드 알림" - const val BACKGROUND_DESCRIPTION = - "센터장 : 공고 지원자 확인, 요양보호사 : 희망 공고가 게시되었을 때의 알림을 받을 수 있는 채널입니다." - } -} \ No newline at end of file diff --git a/app/src/main/java/com/idle/care/notification/NotificationService.kt b/app/src/main/java/com/idle/care/notification/NotificationService.kt index cfd2c78e..8a28123e 100644 --- a/app/src/main/java/com/idle/care/notification/NotificationService.kt +++ b/app/src/main/java/com/idle/care/notification/NotificationService.kt @@ -1,10 +1,19 @@ package com.idle.care.notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import com.idle.designsystem.binding.R import com.idle.domain.model.error.ErrorHelper import com.idle.domain.repositorry.ProfileRepository import com.idle.domain.repositorry.TokenRepository +import com.idle.presentation.MainActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope @@ -23,12 +32,13 @@ class NotificationService : FirebaseMessagingService() { @Inject lateinit var profileRepository: ProfileRepository - @Inject - lateinit var notificationHandler: NotificationHandler - @Inject lateinit var errorHelper: ErrorHelper + private val notificationManager: NotificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> errorHelper.logError(throwable) } @@ -56,15 +66,68 @@ class NotificationService : FirebaseMessagingService() { val body = message.notification?.body ?: "" val data = message.data - notificationHandler.deliverNotification( + deliverNotification( title = title, body = body, data = data, ) } + internal fun deliverNotification( + title: String, + body: String, + data: Map + ) { + val intent = Intent(this, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + } + + if (data.isNotEmpty()) { + data.forEach { (key, value) -> + intent.putExtra(key, value) + } + } + + val pendingIntent = PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + + val builder = NotificationCompat.Builder(this, BACKGROUND_CHANNEL) + .setSmallIcon(com.idle.care.R.drawable.ic_notification_icon) + .setColor(ContextCompat.getColor(this, R.color.orange_500)) + .setContentTitle(title) + .setContentText(body) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + + notificationManager.notify(System.currentTimeMillis().toInt(), builder.build()) + } + override fun onDestroy() { super.onDestroy() scope.cancel() } + + companion object { + private const val BACKGROUND_CHANNEL = "백그라운드 알림" + private const val BACKGROUND_DESCRIPTION = + "센터장 : 공고 지원자 확인, 요양보호사 : 희망 공고가 게시되었을 때의 알림을 받을 수 있는 채널입니다." + + fun initNotification(context: Context) { + val channel = + NotificationChannel( + BACKGROUND_CHANNEL, + BACKGROUND_CHANNEL, + NotificationManager.IMPORTANCE_DEFAULT + ) + channel.description = BACKGROUND_DESCRIPTION + + val notificationManager = + context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } } From 4631d07495516826fb419cee7a13bf59b9bbc8f4 Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Mon, 21 Jul 2025 18:40:01 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[IDLE-000]=20=EC=B1=84=ED=8C=85=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EC=97=90=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A7=80=EC=88=98=20=EB=B0=B1=EC=98=A4=ED=94=84=20?= =?UTF-8?q?->=20EqualJitter=20=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/idle/care/di/AppModule.kt | 21 ------- .../data/repository/ChatRepositoryImpl.kt | 59 ++++++++++--------- .../com/idle/network/source/ChatDataSource.kt | 33 +++++------ .../java/com/idle/network/util/BackOff.kt | 16 ++++- 4 files changed, 62 insertions(+), 67 deletions(-) delete mode 100644 app/src/main/java/com/idle/care/di/AppModule.kt diff --git a/app/src/main/java/com/idle/care/di/AppModule.kt b/app/src/main/java/com/idle/care/di/AppModule.kt deleted file mode 100644 index 7f60936a..00000000 --- a/app/src/main/java/com/idle/care/di/AppModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.idle.care.di - -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@InstallIn(SingletonComponent::class) -@Module -object AppModule { - - @Provides - @Singleton - fun provideNotificationHandler( - @ApplicationContext context: Context - ): com.idle.care.notification.NotificationHandler = - com.idle.care.notification.NotificationHandler(context) -} \ No newline at end of file diff --git a/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt index 82915c4d..0ee3c783 100644 --- a/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt @@ -12,7 +12,7 @@ import com.idle.network.model.chat.ReadMessageRequest import com.idle.network.model.chat.SendMessageRequest import com.idle.network.source.ChatDataSource import com.idle.network.util.MAX_RETRY_ATTEMPTS -import com.idle.network.util.calculateBackoffTime +import com.idle.network.util.calculateRetryTime import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -48,6 +48,8 @@ class ChatRepositoryImpl @Inject constructor( return response.map { dto -> val chatRoom = dto.toVO() + + // 로컬에 채팅방이 존재하지 않으면 채팅방을 개설 if (!localChatDataSource.isChatRoomExist(chatRoom.id, userId)) { localChatDataSource.insertChatRoom( myId = userId, @@ -68,13 +70,11 @@ class ChatRepositoryImpl @Inject constructor( roomId: String, myId: String, messageId: String? - ): List { - return localChatDataSource.getMessages( - roomId = roomId, - myId = myId, - lastMessageId = messageId - ) - } + ): List = localChatDataSource.getMessages( + roomId = roomId, + myId = myId, + lastMessageId = messageId + ) override suspend fun getChatRoomMessages( userType: UserType, @@ -83,6 +83,8 @@ class ChatRepositoryImpl @Inject constructor( messageId: String?, unReadMessageCount: Int? ): List { + // Paging형식으로 동작, MessageId가 Null이라는 것은 가장 첫번째 페이징 호출이므로 서버 먼저 호출 + // 서버에서 읽지않은 메세지를 받아오는데 만약 로컬에 해당 메시지가 이미 저장되어 있다면 로컬에서 가져옴 if (messageId == null || !localChatDataSource.isMessageExist(roomId, myId, messageId)) { val response = if (userType == UserType.WORKER) { chatDataSource.getWorkerChatRoomMessages(roomId, messageId) @@ -90,17 +92,13 @@ class ChatRepositoryImpl @Inject constructor( chatDataSource.getCenterChatRoomMessages(roomId, messageId) } - val allMessages = response.chatMessageInfos + val messages = response.chatMessageInfos .sortedBy { it.sequence } .map { it.toVO() } - val messagesToUse = unReadMessageCount?.let { count -> - allMessages.takeLast(count) - } ?: allMessages - val maxSeq = localChatDataSource.getMaxLocalSequence(roomId, myId) ?: Int.MIN_VALUE - - messagesToUse.forEach { message -> + messages.forEach { message -> + // 만약 메시지를 저장할 채팅방이 존재하지 않으면 채팅방을 먼저 로컬에 생성 if (!localChatDataSource.isChatRoomExist(message.roomId, myId)) { localChatDataSource.insertChatRoom( myId = myId, @@ -114,20 +112,20 @@ class ChatRepositoryImpl @Inject constructor( ) } + // 받아온 메시지 Sequence가 현재 로컬에 저장된 메시지 Sequence보다 클 경우, 로컬에 메시지 삽입 if (message.sequence > maxSeq) { localChatDataSource.insertMessage(message, myId) } } - response.sequence.takeIf { it >= 0 }?.let { seq -> - allMessages.lastOrNull()?.let { - localChatDataSource.readMessages( - roomId = roomId, - myId = myId, - senderId = it.senderId, - sequence = seq - ) - } + // 메시지의 마지막 SequnceNumber까지 모두 읽음 처리 + messages.lastOrNull()?.let { + localChatDataSource.readMessages( + roomId = roomId, + myId = myId, + senderId = it.senderId, + sequence = response.sequence, + ) } } @@ -154,8 +152,10 @@ class ChatRepositoryImpl @Inject constructor( return chatDataSource.subscribeChatMessage(userId) .map { response -> val message = response.toVO() + when (message) { is ChatMessage -> { + // 만약 채팅 메시지를 수신했다면, if (!localChatDataSource.isChatRoomExist( roomId = message.roomId, myId = userId @@ -172,10 +172,15 @@ class ChatRepositoryImpl @Inject constructor( ) ) } + + // 로컬에 해당 메시지를 저장 localChatDataSource.insertMessage(message, userId) } + is ReadMessage -> { + // 상대방이 읽었다는 메시지를 수신했다면, if (message.opponentId != userId) { + // 해당 메시지를 읽음 처리 localChatDataSource.readMessages( roomId = message.chatroomId, myId = userId, @@ -186,11 +191,11 @@ class ChatRepositoryImpl @Inject constructor( } } message - } - .retryWhen { cause, attempt -> + }.retryWhen { cause, attempt -> + // 최대 5번까지 네트워크 연결 재시도 if (cause is IOException && attempt < MAX_RETRY_ATTEMPTS) { connectWebSocket() - delay(calculateBackoffTime(attempt.toInt())) + delay(calculateRetryTime(attempt.toInt())) true } else { false diff --git a/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt b/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt index 839205bd..2a1605b8 100644 --- a/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt @@ -1,6 +1,7 @@ package com.idle.network.source import com.idle.domain.model.auth.UserType +import com.idle.network.BuildConfig import com.idle.network.api.ChatApi import com.idle.network.di.TokenManager import com.idle.network.model.chat.ChatResponse @@ -11,8 +12,7 @@ import com.idle.network.model.chat.ReadMessageRequest import com.idle.network.model.chat.SendMessageRequest import com.idle.network.serializer.ChatResponseSerializer import com.idle.network.util.MAX_RETRY_ATTEMPTS -import com.idle.network.util.MAX_WAIT_TIME -import com.idle.network.util.calculateBackoffTime +import com.idle.network.util.calculateRetryTime import com.idle.network.util.onResponse import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -44,20 +44,18 @@ class ChatDataSource @Inject constructor( suspend fun getWorkerChatRoomMessages( roomId: String, messageId: String?, - ): GetChatMessageResponse = - chatApi.getWorkerChatRoomMessages( - chatRoomId = roomId, - messageId = messageId - ).onResponse() + ): GetChatMessageResponse = chatApi.getWorkerChatRoomMessages( + chatRoomId = roomId, + messageId = messageId + ).onResponse() suspend fun getCenterChatRoomMessages( roomId: String, messageId: String?, - ): GetChatMessageResponse = - chatApi.getCenterChatRoomMessages( - chatRoomId = roomId, - messageId = messageId - ).onResponse() + ): GetChatMessageResponse = chatApi.getCenterChatRoomMessages( + chatRoomId = roomId, + messageId = messageId + ).onResponse() suspend fun generateWorkerChatRoom(opponentId: String): GenerateChatRoomResponse = chatApi.generateWorkerChatRoom(opponentId).onResponse() @@ -72,14 +70,14 @@ class ChatDataSource @Inject constructor( val accessToken = tokenManager.getAccessToken() try { session = client.connect( - url = "${'$'}{BuildConfig.CARE_WEBSOCKET_URL}/ws", + url = "${BuildConfig.CARE_WEBSOCKET_URL}/ws", headers = mapOf("Authorization" to accessToken) ).stomp(StompConfig()) .withJsonConversions(json) connectionAttempts = 0 } catch (e: Throwable) { if (connectionAttempts < MAX_RETRY_ATTEMPTS) { - val waitTime = minOf(calculateBackoffTime(connectionAttempts), MAX_WAIT_TIME) + val waitTime = calculateRetryTime(connectionAttempts) delay(waitTime) connectionAttempts++ connectWebSocket() @@ -95,16 +93,17 @@ class ChatDataSource @Inject constructor( suspend fun subscribeChatMessage(userId: String): Flow = session?.subscribe( - StompSubscribeHeaders(destination = "/sub/${'$'}{userId}"), + StompSubscribeHeaders(destination = "/sub/${userId}"), chatResponseSerializer, ) ?: flow { throw IOException("웹소켓을 먼저 연결해주세요.") } + suspend fun sendMessage( userType: UserType, sendMessageRequest: SendMessageRequest ) { session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/send/${'$'}{userType.apiValue.lowercase()}"), + headers = StompSendHeaders(destination = "/pub/send/$${userType.apiValue.lowercase()}"), body = sendMessageRequest, serializer = SendMessageRequest.serializer(), ) @@ -115,7 +114,7 @@ class ChatDataSource @Inject constructor( readMessageRequest: ReadMessageRequest ) { session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/read/${'$'}{userType.apiValue.lowercase()}"), + headers = StompSendHeaders(destination = "/pub/read/$${userType.apiValue.lowercase()}"), body = readMessageRequest, serializer = ReadMessageRequest.serializer(), ) diff --git a/core/network/src/main/java/com/idle/network/util/BackOff.kt b/core/network/src/main/java/com/idle/network/util/BackOff.kt index 579a8286..84ec1f72 100644 --- a/core/network/src/main/java/com/idle/network/util/BackOff.kt +++ b/core/network/src/main/java/com/idle/network/util/BackOff.kt @@ -1,9 +1,21 @@ package com.idle.network.util import com.idle.domain.model.CountDownTimer.Companion.TICK_INTERVAL +import kotlin.math.min import kotlin.math.pow +import kotlin.random.Random const val MAX_RETRY_ATTEMPTS = 5 -const val MAX_WAIT_TIME = 10_000L +private const val MAX_WAIT_TIME = 10_000L -fun calculateBackoffTime(attempt: Int): Long = (2.0.pow(attempt) * TICK_INTERVAL).toLong() +fun calculateRetryTime(attempt: Int): Long { + return min(MAX_WAIT_TIME, calculateEqualJitter(attempt)) +} + +private fun calculateEqualJitter(attempt: Int): Long { + val baseDelay = calculateBackoffTime(attempt) + val randomJitter = Random.nextLong(0, baseDelay / 2 + 1) + return baseDelay / 2 + randomJitter +} + +private fun calculateBackoffTime(attempt: Int): Long = (2.0.pow(attempt) * TICK_INTERVAL).toLong() From 6cda5fb8cedf564317ca60a4dcb53a0bf2e0184f Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Mon, 21 Jul 2025 18:16:56 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[IDLE-000]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EA=B0=9D=EC=B2=B4=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20NotificationChannel=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B0=A9=EB=B2=95=EC=9D=98=20=EC=B1=85=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20NotificationService=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 --- .../java/com/idle/care/CareApplication.kt | 20 +----- .../care/notification/NotificationHandler.kt | 55 -------------- .../care/notification/NotificationService.kt | 71 +++++++++++++++++-- 3 files changed, 69 insertions(+), 77 deletions(-) delete mode 100644 app/src/main/java/com/idle/care/notification/NotificationHandler.kt diff --git a/app/src/main/java/com/idle/care/CareApplication.kt b/app/src/main/java/com/idle/care/CareApplication.kt index 8de3a496..7109a244 100644 --- a/app/src/main/java/com/idle/care/CareApplication.kt +++ b/app/src/main/java/com/idle/care/CareApplication.kt @@ -1,12 +1,9 @@ package com.idle.care import android.app.Application -import android.app.NotificationChannel -import android.app.NotificationManager import com.appsflyer.AppsFlyerLib import com.appsflyer.attribution.AppsFlyerRequestListener -import com.idle.care.notification.NotificationHandler.Companion.BACKGROUND_CHANNEL -import com.idle.care.notification.NotificationHandler.Companion.BACKGROUND_DESCRIPTION +import com.idle.care.notification.NotificationService.Companion.initNotification import com.idle.domain.model.error.ErrorHelper import com.kakao.sdk.common.KakaoSdk import dagger.hilt.android.HiltAndroidApp @@ -22,24 +19,11 @@ class CareApplication : Application() { override fun onCreate() { super.onCreate() - initNotification() + initNotification(this) initKakao() initAppsFlyer() } - private fun initNotification() { - val channel = - NotificationChannel( - BACKGROUND_CHANNEL, - BACKGROUND_CHANNEL, - NotificationManager.IMPORTANCE_DEFAULT - ) - channel.description = BACKGROUND_DESCRIPTION - - val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(channel) - } - private fun initKakao() { KakaoSdk.init(this, BuildConfig.KAKAO_APP_KEY) } diff --git a/app/src/main/java/com/idle/care/notification/NotificationHandler.kt b/app/src/main/java/com/idle/care/notification/NotificationHandler.kt deleted file mode 100644 index e1d46148..00000000 --- a/app/src/main/java/com/idle/care/notification/NotificationHandler.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.idle.care.notification - -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import androidx.core.app.NotificationCompat -import androidx.core.content.ContextCompat -import com.idle.designsystem.binding.R -import com.idle.presentation.MainActivity -import javax.inject.Inject - -class NotificationHandler @Inject constructor(private val context: Context) { - private val notificationManager: NotificationManager = - context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - - internal fun deliverNotification( - title: String, - body: String, - data: Map - ) { - val intent = Intent(context, MainActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP - } - - if (data.isNotEmpty()) { - data.forEach { (key, value) -> - intent.putExtra(key, value) - } - } - - val pendingIntent = PendingIntent.getActivity( - context, - 0, - intent, - PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) - - val builder = NotificationCompat.Builder(context, BACKGROUND_CHANNEL) - .setSmallIcon(com.idle.care.R.drawable.ic_notification_icon) - .setColor(ContextCompat.getColor(context, R.color.orange_500)) - .setContentTitle(title) - .setContentText(body) - .setContentIntent(pendingIntent) - .setAutoCancel(true) - - notificationManager.notify(System.currentTimeMillis().toInt(), builder.build()) - } - - companion object { - const val BACKGROUND_CHANNEL = "백그라운드 알림" - const val BACKGROUND_DESCRIPTION = - "센터장 : 공고 지원자 확인, 요양보호사 : 희망 공고가 게시되었을 때의 알림을 받을 수 있는 채널입니다." - } -} \ No newline at end of file diff --git a/app/src/main/java/com/idle/care/notification/NotificationService.kt b/app/src/main/java/com/idle/care/notification/NotificationService.kt index cfd2c78e..8a28123e 100644 --- a/app/src/main/java/com/idle/care/notification/NotificationService.kt +++ b/app/src/main/java/com/idle/care/notification/NotificationService.kt @@ -1,10 +1,19 @@ package com.idle.care.notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import com.idle.designsystem.binding.R import com.idle.domain.model.error.ErrorHelper import com.idle.domain.repositorry.ProfileRepository import com.idle.domain.repositorry.TokenRepository +import com.idle.presentation.MainActivity import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope @@ -23,12 +32,13 @@ class NotificationService : FirebaseMessagingService() { @Inject lateinit var profileRepository: ProfileRepository - @Inject - lateinit var notificationHandler: NotificationHandler - @Inject lateinit var errorHelper: ErrorHelper + private val notificationManager: NotificationManager = + getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + private val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> errorHelper.logError(throwable) } @@ -56,15 +66,68 @@ class NotificationService : FirebaseMessagingService() { val body = message.notification?.body ?: "" val data = message.data - notificationHandler.deliverNotification( + deliverNotification( title = title, body = body, data = data, ) } + internal fun deliverNotification( + title: String, + body: String, + data: Map + ) { + val intent = Intent(this, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + } + + if (data.isNotEmpty()) { + data.forEach { (key, value) -> + intent.putExtra(key, value) + } + } + + val pendingIntent = PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + + val builder = NotificationCompat.Builder(this, BACKGROUND_CHANNEL) + .setSmallIcon(com.idle.care.R.drawable.ic_notification_icon) + .setColor(ContextCompat.getColor(this, R.color.orange_500)) + .setContentTitle(title) + .setContentText(body) + .setContentIntent(pendingIntent) + .setAutoCancel(true) + + notificationManager.notify(System.currentTimeMillis().toInt(), builder.build()) + } + override fun onDestroy() { super.onDestroy() scope.cancel() } + + companion object { + private const val BACKGROUND_CHANNEL = "백그라운드 알림" + private const val BACKGROUND_DESCRIPTION = + "센터장 : 공고 지원자 확인, 요양보호사 : 희망 공고가 게시되었을 때의 알림을 받을 수 있는 채널입니다." + + fun initNotification(context: Context) { + val channel = + NotificationChannel( + BACKGROUND_CHANNEL, + BACKGROUND_CHANNEL, + NotificationManager.IMPORTANCE_DEFAULT + ) + channel.description = BACKGROUND_DESCRIPTION + + val notificationManager = + context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + } } From 524c0101d11549191b5ae33dd384e67ffb4ede6e Mon Sep 17 00:00:00 2001 From: tgyuuAn Date: Mon, 21 Jul 2025 18:40:01 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[IDLE-000]=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20UseCase=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 +- .../main/java/com/idle/care/di/AppModule.kt | 21 --- .../data/repository/ChatRepositoryImpl.kt | 59 ++++--- .../data/repository/ProfileRepositoryImpl.kt | 154 +++++++++++++----- .../datasource/UserInfoDataSource.kt | 131 +++++++-------- .../domain/repositorry/ProfileRepository.kt | 18 +- .../usecase/chat/GetChatRoomsUseCase.kt | 12 +- .../profile/GetMyCenterProfileUseCase.kt | 15 -- .../profile/GetMyWorkerProfileUseCase.kt | 15 -- .../profile/RegisterCenterProfileUseCase.kt | 48 ------ .../profile/UpdateCenterProfileUseCase.kt | 40 ----- .../profile/UpdateWorkerProfileUseCase.kt | 49 ------ .../com/idle/network/source/ChatDataSource.kt | 33 ++-- .../java/com/idle/network/util/BackOff.kt | 16 +- .../chatting/CenterChattingViewModel.kt | 4 +- .../center/jobposting/JobPostingViewModel.kt | 6 +- .../center/profile/CenterProfileViewModel.kt | 8 +- .../register/RegisterCenterInfoViewModel.kt | 10 +- .../RegisterCenterInfoCompleteViewModel.kt | 6 +- .../ChattingDetailViewModel.kt | 8 +- .../center/CenterJobPostingDetailViewModel.kt | 15 +- .../worker/WorkerJobPostingDetailViewModel.kt | 6 +- .../setting/center/CenterSettingViewModel.kt | 6 +- .../setting/worker/WorkerSettingViewModel.kt | 6 +- .../chatting/WorkerChattingViewModel.kt | 4 +- .../idle/worker/home/WorkerHomeViewModel.kt | 6 +- .../job/posting/WorkerJobPostingViewModel.kt | 6 +- .../worker/profile/WorkerProfileViewModel.kt | 8 +- 28 files changed, 292 insertions(+), 420 deletions(-) delete mode 100644 app/src/main/java/com/idle/care/di/AppModule.kt delete mode 100644 core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt delete mode 100644 core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt delete mode 100644 core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt delete mode 100644 core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt delete mode 100644 core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4804b960..16569147 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,7 +14,7 @@ android { defaultConfig { versionCode = 21 - versionName = "1.2.6" + versionName = "1.2.7" targetSdk = 35 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/idle/care/di/AppModule.kt b/app/src/main/java/com/idle/care/di/AppModule.kt deleted file mode 100644 index 7f60936a..00000000 --- a/app/src/main/java/com/idle/care/di/AppModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.idle.care.di - -import android.content.Context -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@InstallIn(SingletonComponent::class) -@Module -object AppModule { - - @Provides - @Singleton - fun provideNotificationHandler( - @ApplicationContext context: Context - ): com.idle.care.notification.NotificationHandler = - com.idle.care.notification.NotificationHandler(context) -} \ No newline at end of file diff --git a/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt index 82915c4d..0ee3c783 100644 --- a/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/ChatRepositoryImpl.kt @@ -12,7 +12,7 @@ import com.idle.network.model.chat.ReadMessageRequest import com.idle.network.model.chat.SendMessageRequest import com.idle.network.source.ChatDataSource import com.idle.network.util.MAX_RETRY_ATTEMPTS -import com.idle.network.util.calculateBackoffTime +import com.idle.network.util.calculateRetryTime import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -48,6 +48,8 @@ class ChatRepositoryImpl @Inject constructor( return response.map { dto -> val chatRoom = dto.toVO() + + // 로컬에 채팅방이 존재하지 않으면 채팅방을 개설 if (!localChatDataSource.isChatRoomExist(chatRoom.id, userId)) { localChatDataSource.insertChatRoom( myId = userId, @@ -68,13 +70,11 @@ class ChatRepositoryImpl @Inject constructor( roomId: String, myId: String, messageId: String? - ): List { - return localChatDataSource.getMessages( - roomId = roomId, - myId = myId, - lastMessageId = messageId - ) - } + ): List = localChatDataSource.getMessages( + roomId = roomId, + myId = myId, + lastMessageId = messageId + ) override suspend fun getChatRoomMessages( userType: UserType, @@ -83,6 +83,8 @@ class ChatRepositoryImpl @Inject constructor( messageId: String?, unReadMessageCount: Int? ): List { + // Paging형식으로 동작, MessageId가 Null이라는 것은 가장 첫번째 페이징 호출이므로 서버 먼저 호출 + // 서버에서 읽지않은 메세지를 받아오는데 만약 로컬에 해당 메시지가 이미 저장되어 있다면 로컬에서 가져옴 if (messageId == null || !localChatDataSource.isMessageExist(roomId, myId, messageId)) { val response = if (userType == UserType.WORKER) { chatDataSource.getWorkerChatRoomMessages(roomId, messageId) @@ -90,17 +92,13 @@ class ChatRepositoryImpl @Inject constructor( chatDataSource.getCenterChatRoomMessages(roomId, messageId) } - val allMessages = response.chatMessageInfos + val messages = response.chatMessageInfos .sortedBy { it.sequence } .map { it.toVO() } - val messagesToUse = unReadMessageCount?.let { count -> - allMessages.takeLast(count) - } ?: allMessages - val maxSeq = localChatDataSource.getMaxLocalSequence(roomId, myId) ?: Int.MIN_VALUE - - messagesToUse.forEach { message -> + messages.forEach { message -> + // 만약 메시지를 저장할 채팅방이 존재하지 않으면 채팅방을 먼저 로컬에 생성 if (!localChatDataSource.isChatRoomExist(message.roomId, myId)) { localChatDataSource.insertChatRoom( myId = myId, @@ -114,20 +112,20 @@ class ChatRepositoryImpl @Inject constructor( ) } + // 받아온 메시지 Sequence가 현재 로컬에 저장된 메시지 Sequence보다 클 경우, 로컬에 메시지 삽입 if (message.sequence > maxSeq) { localChatDataSource.insertMessage(message, myId) } } - response.sequence.takeIf { it >= 0 }?.let { seq -> - allMessages.lastOrNull()?.let { - localChatDataSource.readMessages( - roomId = roomId, - myId = myId, - senderId = it.senderId, - sequence = seq - ) - } + // 메시지의 마지막 SequnceNumber까지 모두 읽음 처리 + messages.lastOrNull()?.let { + localChatDataSource.readMessages( + roomId = roomId, + myId = myId, + senderId = it.senderId, + sequence = response.sequence, + ) } } @@ -154,8 +152,10 @@ class ChatRepositoryImpl @Inject constructor( return chatDataSource.subscribeChatMessage(userId) .map { response -> val message = response.toVO() + when (message) { is ChatMessage -> { + // 만약 채팅 메시지를 수신했다면, if (!localChatDataSource.isChatRoomExist( roomId = message.roomId, myId = userId @@ -172,10 +172,15 @@ class ChatRepositoryImpl @Inject constructor( ) ) } + + // 로컬에 해당 메시지를 저장 localChatDataSource.insertMessage(message, userId) } + is ReadMessage -> { + // 상대방이 읽었다는 메시지를 수신했다면, if (message.opponentId != userId) { + // 해당 메시지를 읽음 처리 localChatDataSource.readMessages( roomId = message.chatroomId, myId = userId, @@ -186,11 +191,11 @@ class ChatRepositoryImpl @Inject constructor( } } message - } - .retryWhen { cause, attempt -> + }.retryWhen { cause, attempt -> + // 최대 5번까지 네트워크 연결 재시도 if (cause is IOException && attempt < MAX_RETRY_ATTEMPTS) { connectWebSocket() - delay(calculateBackoffTime(attempt.toInt())) + delay(calculateRetryTime(attempt.toInt())) true } else { false diff --git a/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt b/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt index ca23232d..c9e925cf 100644 --- a/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt +++ b/core/data/src/main/java/com/idle/data/repository/ProfileRepositoryImpl.kt @@ -21,7 +21,9 @@ import com.idle.network.model.profile.UpdateWorkerProfileRequest import com.idle.network.model.profile.UploadProfileImageUrlResponse import com.idle.network.source.ProfileDataSource import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream @@ -35,49 +37,66 @@ class ProfileRepositoryImpl @Inject constructor( override suspend fun getMyUserType() = userInfoDataSource.userType.first() override suspend fun getMyCenterProfile(): CenterProfile { - val centerProfile = profileDataSource.getMyCenterProfile() - .toVO() + val localProfile = userInfoDataSource.getLocalCenterProfile() + if (localProfile != null) return localProfile + val centerProfile = profileDataSource.getMyCenterProfile().toVO() userInfoDataSource.setUserInfo(centerProfile.toString()) return centerProfile } - override suspend fun getLocalMyCenterProfile(): CenterProfile = - userInfoDataSource.getLocalCenterProfile() - override suspend fun getCenterProfile(centerId: String): CenterProfile = profileDataSource.getCenterProfile(centerId).toVO() override suspend fun getMyWorkerProfile(): WorkerProfile { - val workerProfile = profileDataSource.getMyWorkerProfile().toVo() + val localProfile = userInfoDataSource.getLocalWorkerProfile() + if (localProfile != null) return localProfile + val workerProfile = profileDataSource.getMyWorkerProfile().toVo() userInfoDataSource.setUserInfo(workerProfile.toString()) return workerProfile } - override suspend fun getLocalMyWorkerProfile(): WorkerProfile = - userInfoDataSource.getLocalWorkerProfile() - override suspend fun getWorkerProfile(workerId: String): WorkerProfile = profileDataSource.getWorkerProfile(workerId).toVo() + override suspend fun getWorkerId(): String = profileDataSource.getWorkerId() + .carerId + + override suspend fun getCenterStatus(): CenterRegistrationStatus = + profileDataSource.getCenterStatus().toVO() + override suspend fun updateCenterProfile( officeNumber: String, introduce: String?, + imageFileUri: String?, ) { - profileDataSource.updateMyCenterProfile( - UpdateCenterProfileRequest(officeNumber = officeNumber, introduce = introduce) - ) - - val updatedProfile = getMyCenterProfile() - userInfoDataSource.setUserInfo(updatedProfile.toString()) - } + coroutineScope { + val updateProfileJob = launch { + profileDataSource.updateMyCenterProfile( + UpdateCenterProfileRequest(officeNumber = officeNumber, introduce = introduce) + ) + } - override suspend fun getWorkerId(): String = profileDataSource.getWorkerId() - .carerId + val profileImageJob = imageFileUri?.let { uri -> + launch { + if (uri.isContentUri()) { + updateProfileImage( + userType = UserType.CENTER.apiValue, + imageFileUri = imageFileUri, + reqWidth = 1340, + reqHeight = 1016, + ) + } + } + } - override suspend fun getCenterStatus(): CenterRegistrationStatus = - profileDataSource.getCenterStatus().toVO() + updateProfileJob.join() + profileImageJob?.join() + val updatedProfile = getMyCenterProfile() + userInfoDataSource.setUserInfo(updatedProfile.toString()) + } + } override suspend fun updateWorkerProfile( experienceYear: Int?, @@ -85,21 +104,41 @@ class ProfileRepositoryImpl @Inject constructor( lotNumberAddress: String, jobSearchStatus: JobSearchStatus, introduce: String?, - speciality: String + speciality: String, + imageFileUri: String?, ) { - profileDataSource.updateWorkerProfile( - UpdateWorkerProfileRequest( - experienceYear = experienceYear, - roadNameAddress = roadNameAddress, - lotNumberAddress = lotNumberAddress, - jobSearchStatus = jobSearchStatus.name, - introduce = introduce, - speciality = speciality - ) - ) + coroutineScope { + val updateProfileJob = launch { + profileDataSource.updateWorkerProfile( + UpdateWorkerProfileRequest( + experienceYear = experienceYear, + roadNameAddress = roadNameAddress, + lotNumberAddress = lotNumberAddress, + jobSearchStatus = jobSearchStatus.name, + introduce = introduce, + speciality = speciality + ) + ) + } - val updatedProfile = getMyWorkerProfile() - userInfoDataSource.setUserInfo(updatedProfile.toString()) + val updateProfileImageJob = imageFileUri?.let { uri -> + if (uri.isContentUri()) { + launch { + updateProfileImage( + userType = UserType.WORKER.apiValue, + imageFileUri = uri, + reqWidth = 384, + reqHeight = 384, + ) + } + } else null + } + + updateProfileJob.join() + updateProfileImageJob?.join() + val updatedProfile = getMyWorkerProfile() + userInfoDataSource.setUserInfo(updatedProfile.toString()) + } } override suspend fun registerCenterProfile( @@ -108,19 +147,42 @@ class ProfileRepositoryImpl @Inject constructor( introduce: String, lotNumberAddress: String, officeNumber: String, - roadNameAddress: String - ) = profileDataSource.registerCenterProfile( - RegisterCenterProfileRequest( - centerName = centerName, - detailedAddress = detailedAddress, - introduce = introduce, - lotNumberAddress = lotNumberAddress, - officeNumber = officeNumber, - roadNameAddress = roadNameAddress, - ) - ) + roadNameAddress: String, + imageFileUri: String? + ) { + coroutineScope { + val registerProfileJob = launch { + profileDataSource.registerCenterProfile( + RegisterCenterProfileRequest( + centerName = centerName, + detailedAddress = detailedAddress, + introduce = introduce, + lotNumberAddress = lotNumberAddress, + officeNumber = officeNumber, + roadNameAddress = roadNameAddress, + ) + ) + } + + val profileImageJob = imageFileUri?.let { uri -> + launch { + if (uri.isContentUri()) { + updateProfileImage( + userType = UserType.CENTER.apiValue, + imageFileUri = imageFileUri, + reqWidth = 1340, + reqHeight = 1016, + ) + } + } + } - override suspend fun updateProfileImage( + registerProfileJob.join() + profileImageJob?.join() + } + } + + private suspend fun updateProfileImage( userType: String, imageFileUri: String, reqWidth: Int, @@ -260,4 +322,6 @@ class ProfileRepositoryImpl @Inject constructor( imageFileExtension = imageFileExtension, ) ) + + private fun String?.isContentUri(): Boolean = this?.startsWith("content://") == true } diff --git a/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt b/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt index 11908800..829c0fdc 100644 --- a/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt +++ b/core/datastore/src/main/java/com/idle/datastore/datasource/UserInfoDataSource.kt @@ -19,7 +19,7 @@ class UserInfoDataSource @Inject constructor( @Named("userInfo") private val dataStore: DataStore ) { val userType: Flow = dataStore.getValue(USER_TYPE, "") - val userInfo: Flow = dataStore.getValue(USER_INFO, "") + private val userInfo: Flow = dataStore.getValue(USER_INFO, "") suspend fun setUserType(userRole: String) { dataStore.setValue(USER_TYPE, userRole) @@ -37,81 +37,76 @@ class UserInfoDataSource @Inject constructor( dataStore.clear(USER_INFO) } - suspend fun getLocalCenterProfile(): CenterProfile { - val userInfoString = userInfo.first().takeIf { it.isNotBlank() } - ?: throw NullPointerException("Missing UserInfo") + suspend fun getLocalCenterProfile(): CenterProfile? { + try { + val userInfoString = userInfo.first().takeIf { it.isNotBlank() } + ?: return null - if (!userInfoString.startsWith("CenterProfile(")) { - throw NullPointerException("Stored UserInfo is not a CenterProfile") - } - - val properties = userInfoString - .removePrefix("CenterProfile(") - .removeSuffix(")") - .split(", ") - .associate { - val (key, value) = it.split("=") - key to value + if (!userInfoString.startsWith("CenterProfile(")) { + return null } - return CenterProfile( - centerId = properties["centerId"] ?: throw NullPointerException("Missing CenterId"), - centerName = properties["centerName"] - ?: throw NullPointerException("Missing centerName"), - officeNumber = properties["officeNumber"] - ?: throw NullPointerException("Missing officeNumber"), - roadNameAddress = properties["roadNameAddress"] - ?: throw NullPointerException("Missing roadNameAddress"), - lotNumberAddress = properties["lotNumberAddress"] - ?: throw NullPointerException("Missing lotNumberAddress"), - detailedAddress = properties["detailedAddress"] - ?: throw NullPointerException("Missing detailedAddress"), - longitude = properties["longitude"]?.toDoubleOrNull() - ?: throw NullPointerException("Invalid longitude format"), - latitude = properties["latitude"]?.toDoubleOrNull() - ?: throw NullPointerException("Invalid latitude format"), - introduce = properties["introduce"].takeIf { it != "null" }, - profileImageUrl = properties["profileImageUrl"].takeIf { it != "null" }, - ) + val properties = userInfoString + .removePrefix("CenterProfile(") + .removeSuffix(")") + .split(", ") + .associate { + val (key, value) = it.split("=") + key to value + } + + return CenterProfile( + centerId = properties["centerId"] ?: return null, + centerName = properties["centerName"] ?: return null, + officeNumber = properties["officeNumber"] ?: return null, + roadNameAddress = properties["roadNameAddress"] ?: return null, + lotNumberAddress = properties["lotNumberAddress"] ?: return null, + detailedAddress = properties["detailedAddress"] ?: return null, + longitude = properties["longitude"]?.toDoubleOrNull() ?: return null, + latitude = properties["latitude"]?.toDoubleOrNull() ?: return null, + introduce = properties["introduce"].takeIf { it != "null" }, + profileImageUrl = properties["profileImageUrl"].takeIf { it != "null" }, + ) + } catch (e: Exception) { + return null + } } - suspend fun getLocalWorkerProfile(): WorkerProfile { - val userInfoString = userInfo.first().takeIf { it.isNotBlank() } - ?: throw NullPointerException("Missing UserInfo") + suspend fun getLocalWorkerProfile(): WorkerProfile? { + return try { + val userInfoString = userInfo.first() + .takeIf { it.isNotBlank() } ?: return null - if (!userInfoString.startsWith("WorkerProfile(")) { - throw NullPointerException("Stored UserInfo is not a WorkerProfile") - } + if (!userInfoString.startsWith("WorkerProfile(")) return null - val properties = userInfoString - .removePrefix("WorkerProfile(") - .removeSuffix(")") - .split(", ") - .associate { - val (key, value) = it.split("=") - key to value - } + val properties = userInfoString + .removePrefix("WorkerProfile(") + .removeSuffix(")") + .split(", ") + .associate { + val (key, value) = it.split("=") + key to value + } - return WorkerProfile( - workerId = properties["workerId"] ?: throw NullPointerException("Missing workerId"), - workerName = properties["workerName"] - ?: throw NullPointerException("Missing workerName"), - age = properties["age"]?.toInt() ?: throw NullPointerException("Invalid age format"), - gender = Gender.create(properties["gender"]), - experienceYear = properties["experienceYear"]?.toIntOrNull(), - phoneNumber = properties["phoneNumber"] - ?: throw NullPointerException("Missing phoneNumber"), - roadNameAddress = properties["roadNameAddress"] - ?: throw NullPointerException("Missing roadNameAddress"), - lotNumberAddress = properties["lotNumberAddress"] - ?: throw NullPointerException("Missing lotNumberAddress"), - longitude = properties["longitude"] ?: throw NullPointerException("Missing longitude"), - latitude = properties["latitude"] ?: throw NullPointerException("Missing latitude"), - jobSearchStatus = JobSearchStatus.create(properties["jobSearchStatus"]), - introduce = properties["introduce"].takeIf { it != "null" }, - speciality = properties["speciality"].takeIf { it != "null" }, - profileImageUrl = properties["profileImageUrl"].takeIf { it != "null" } - ) + WorkerProfile( + workerId = properties["workerId"] ?: return null, + workerName = properties["workerName"] ?: return null, + age = properties["age"]?.toIntOrNull() ?: return null, + gender = Gender.create(properties["gender"]), + experienceYear = properties["experienceYear"]?.toIntOrNull(), + phoneNumber = properties["phoneNumber"] ?: return null, + roadNameAddress = properties["roadNameAddress"] ?: return null, + lotNumberAddress = properties["lotNumberAddress"] ?: return null, + longitude = properties["longitude"] ?: return null, + latitude = properties["latitude"] ?: return null, + jobSearchStatus = JobSearchStatus.create(properties["jobSearchStatus"]), + introduce = properties["introduce"].takeIf { it != "null" }, + speciality = properties["speciality"].takeIf { it != "null" }, + profileImageUrl = properties["profileImageUrl"].takeIf { it != "null" } + ) + } catch (e: Exception) { + null + } } companion object { diff --git a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt index c9a391a4..ebfe78c7 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/repositorry/ProfileRepository.kt @@ -8,14 +8,17 @@ import com.idle.domain.model.profile.WorkerProfile interface ProfileRepository { suspend fun getMyUserType(): String suspend fun getMyCenterProfile(): CenterProfile - suspend fun getLocalMyCenterProfile(): CenterProfile suspend fun getCenterProfile(centerId: String): CenterProfile suspend fun getMyWorkerProfile(): WorkerProfile - suspend fun getLocalMyWorkerProfile(): WorkerProfile suspend fun getWorkerProfile(workerId: String): WorkerProfile - suspend fun updateCenterProfile(officeNumber: String, introduce: String?) suspend fun getWorkerId(): String suspend fun getCenterStatus(): CenterRegistrationStatus + suspend fun updateCenterProfile( + officeNumber: String, + introduce: String?, + imageFileUri: String?, + ) + suspend fun updateWorkerProfile( experienceYear: Int?, roadNameAddress: String, @@ -23,6 +26,7 @@ interface ProfileRepository { jobSearchStatus: JobSearchStatus, introduce: String?, speciality: String, + imageFileUri: String?, ) suspend fun registerCenterProfile( @@ -32,12 +36,6 @@ interface ProfileRepository { lotNumberAddress: String, officeNumber: String, roadNameAddress: String, - ) - - suspend fun updateProfileImage( - userType: String, - imageFileUri: String, - reqWidth: Int, - reqHeight: Int + imageFileUri: String?, ) } diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt index 02736b69..68b26f8a 100644 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt +++ b/core/domain/src/main/kotlin/com/idle/domain/usecase/chat/GetChatRoomsUseCase.kt @@ -3,6 +3,9 @@ package com.idle.domain.usecase.chat import com.idle.domain.model.auth.UserType import com.idle.domain.model.chat.ChatRoom import com.idle.domain.model.chat.ChatRoomWithOpponentInfo +import com.idle.domain.model.profile.CenterProfile +import com.idle.domain.model.profile.Profile +import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.ProfileRepository import kotlinx.coroutines.async @@ -42,14 +45,11 @@ class GetChatRoomsUseCase @Inject constructor( private fun mapToRoomWithOpponentInfo( chatRoom: ChatRoom, - profile: com.idle.domain.model.profile.Profile, + profile: Profile, ): ChatRoomWithOpponentInfo { val (opponentName, profileUrl) = when (profile) { - is com.idle.domain.model.profile.WorkerProfile -> - profile.workerName to profile.profileImageUrl - - is com.idle.domain.model.profile.CenterProfile -> - profile.centerName to profile.profileImageUrl + is WorkerProfile -> profile.workerName to profile.profileImageUrl + is CenterProfile -> profile.centerName to profile.profileImageUrl } return ChatRoomWithOpponentInfo( diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt deleted file mode 100644 index 681023f5..00000000 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyCenterProfileUseCase.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.idle.domain.usecase.profile - -import com.idle.domain.model.profile.CenterProfile -import com.idle.domain.repositorry.ProfileRepository -import javax.inject.Inject - -class GetMyCenterProfileUseCase @Inject constructor( - private val profileRepository: ProfileRepository -) { - suspend operator fun invoke(): CenterProfile = try { - profileRepository.getLocalMyCenterProfile() - } catch (e: Exception) { - profileRepository.getMyCenterProfile() - } -} diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt deleted file mode 100644 index 5a2faa38..00000000 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/GetMyWorkerProfileUseCase.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.idle.domain.usecase.profile - -import com.idle.domain.model.profile.WorkerProfile -import com.idle.domain.repositorry.ProfileRepository -import javax.inject.Inject - -class GetMyWorkerProfileUseCase @Inject constructor( - private val profileRepository: ProfileRepository -) { - suspend operator fun invoke(): WorkerProfile = try { - profileRepository.getLocalMyWorkerProfile() - } catch (e: Exception) { - profileRepository.getMyWorkerProfile() - } -} diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt deleted file mode 100644 index d996baaf..00000000 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/RegisterCenterProfileUseCase.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.idle.domain.usecase.profile - -import com.idle.domain.model.auth.UserType -import com.idle.domain.repositorry.ProfileRepository -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import javax.inject.Inject - -class RegisterCenterProfileUseCase @Inject constructor( - private val profileRepository: ProfileRepository, -) { - suspend operator fun invoke( - centerName: String, - detailedAddress: String, - introduce: String, - lotNumberAddress: String, - officeNumber: String, - roadNameAddress: String, - imageFileUri: String?, - ) = coroutineScope { - val registerProfileJob = launch { - profileRepository.registerCenterProfile( - centerName = centerName, - detailedAddress = detailedAddress, - introduce = introduce, - lotNumberAddress = lotNumberAddress, - officeNumber = officeNumber, - roadNameAddress = roadNameAddress - ) - } - - val updateProfileImageJob = imageFileUri?.let { uri -> - if (uri.startsWith("content://")) { - launch { - profileRepository.updateProfileImage( - userType = UserType.CENTER.apiValue, - imageFileUri = uri, - reqWidth = 1340, - reqHeight = 1016, - ) - } - } else null - } - - registerProfileJob.join() - updateProfileImageJob?.join() - } -} diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt deleted file mode 100644 index 90b10367..00000000 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateCenterProfileUseCase.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.idle.domain.usecase.profile - -import com.idle.domain.model.auth.UserType -import com.idle.domain.repositorry.ProfileRepository -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import javax.inject.Inject - -class UpdateCenterProfileUseCase @Inject constructor( - private val profileRepository: ProfileRepository -) { - suspend operator fun invoke( - officeNumber: String, - introduce: String?, - imageFileUri: String?, - ) = coroutineScope { - val updateProfileJob = launch { - profileRepository.updateCenterProfile( - officeNumber = officeNumber, - introduce = introduce, - ) - } - - val updateProfileImageJob = imageFileUri?.let { uri -> - if (uri.startsWith("content://")) { - launch { - profileRepository.updateProfileImage( - userType = UserType.CENTER.apiValue, - imageFileUri = uri, - reqWidth = 1340, - reqHeight = 1016, - ) - } - } else null - } - - updateProfileJob.join() - updateProfileImageJob?.join() - } -} diff --git a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt b/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt deleted file mode 100644 index 5f312f27..00000000 --- a/core/domain/src/main/kotlin/com/idle/domain/usecase/profile/UpdateWorkerProfileUseCase.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.idle.domain.usecase.profile - -import com.idle.domain.model.auth.UserType -import com.idle.domain.model.profile.JobSearchStatus -import com.idle.domain.repositorry.ProfileRepository -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.launch -import javax.inject.Inject - -class UpdateWorkerProfileUseCase @Inject constructor( - private val profileRepository: ProfileRepository, -) { - suspend operator fun invoke( - experienceYear: Int?, - roadNameAddress: String, - lotNumberAddress: String, - introduce: String?, - speciality: String, - jobSearchStatus: JobSearchStatus, - imageFileUri: String?, - ) = coroutineScope { - val updateProfileJob = launch { - profileRepository.updateWorkerProfile( - experienceYear = experienceYear, - roadNameAddress = roadNameAddress, - lotNumberAddress = lotNumberAddress, - jobSearchStatus = jobSearchStatus, - introduce = introduce, - speciality = speciality - ) - } - - val updateProfileImageJob = imageFileUri?.let { uri -> - if (uri.startsWith("content://")) { - launch { - profileRepository.updateProfileImage( - userType = UserType.WORKER.apiValue, - imageFileUri = uri, - reqWidth = 384, - reqHeight = 384, - ) - } - } else null - } - - updateProfileJob.join() - updateProfileImageJob?.join() - } -} diff --git a/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt b/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt index 839205bd..2a1605b8 100644 --- a/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt +++ b/core/network/src/main/java/com/idle/network/source/ChatDataSource.kt @@ -1,6 +1,7 @@ package com.idle.network.source import com.idle.domain.model.auth.UserType +import com.idle.network.BuildConfig import com.idle.network.api.ChatApi import com.idle.network.di.TokenManager import com.idle.network.model.chat.ChatResponse @@ -11,8 +12,7 @@ import com.idle.network.model.chat.ReadMessageRequest import com.idle.network.model.chat.SendMessageRequest import com.idle.network.serializer.ChatResponseSerializer import com.idle.network.util.MAX_RETRY_ATTEMPTS -import com.idle.network.util.MAX_WAIT_TIME -import com.idle.network.util.calculateBackoffTime +import com.idle.network.util.calculateRetryTime import com.idle.network.util.onResponse import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -44,20 +44,18 @@ class ChatDataSource @Inject constructor( suspend fun getWorkerChatRoomMessages( roomId: String, messageId: String?, - ): GetChatMessageResponse = - chatApi.getWorkerChatRoomMessages( - chatRoomId = roomId, - messageId = messageId - ).onResponse() + ): GetChatMessageResponse = chatApi.getWorkerChatRoomMessages( + chatRoomId = roomId, + messageId = messageId + ).onResponse() suspend fun getCenterChatRoomMessages( roomId: String, messageId: String?, - ): GetChatMessageResponse = - chatApi.getCenterChatRoomMessages( - chatRoomId = roomId, - messageId = messageId - ).onResponse() + ): GetChatMessageResponse = chatApi.getCenterChatRoomMessages( + chatRoomId = roomId, + messageId = messageId + ).onResponse() suspend fun generateWorkerChatRoom(opponentId: String): GenerateChatRoomResponse = chatApi.generateWorkerChatRoom(opponentId).onResponse() @@ -72,14 +70,14 @@ class ChatDataSource @Inject constructor( val accessToken = tokenManager.getAccessToken() try { session = client.connect( - url = "${'$'}{BuildConfig.CARE_WEBSOCKET_URL}/ws", + url = "${BuildConfig.CARE_WEBSOCKET_URL}/ws", headers = mapOf("Authorization" to accessToken) ).stomp(StompConfig()) .withJsonConversions(json) connectionAttempts = 0 } catch (e: Throwable) { if (connectionAttempts < MAX_RETRY_ATTEMPTS) { - val waitTime = minOf(calculateBackoffTime(connectionAttempts), MAX_WAIT_TIME) + val waitTime = calculateRetryTime(connectionAttempts) delay(waitTime) connectionAttempts++ connectWebSocket() @@ -95,16 +93,17 @@ class ChatDataSource @Inject constructor( suspend fun subscribeChatMessage(userId: String): Flow = session?.subscribe( - StompSubscribeHeaders(destination = "/sub/${'$'}{userId}"), + StompSubscribeHeaders(destination = "/sub/${userId}"), chatResponseSerializer, ) ?: flow { throw IOException("웹소켓을 먼저 연결해주세요.") } + suspend fun sendMessage( userType: UserType, sendMessageRequest: SendMessageRequest ) { session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/send/${'$'}{userType.apiValue.lowercase()}"), + headers = StompSendHeaders(destination = "/pub/send/$${userType.apiValue.lowercase()}"), body = sendMessageRequest, serializer = SendMessageRequest.serializer(), ) @@ -115,7 +114,7 @@ class ChatDataSource @Inject constructor( readMessageRequest: ReadMessageRequest ) { session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/read/${'$'}{userType.apiValue.lowercase()}"), + headers = StompSendHeaders(destination = "/pub/read/$${userType.apiValue.lowercase()}"), body = readMessageRequest, serializer = ReadMessageRequest.serializer(), ) diff --git a/core/network/src/main/java/com/idle/network/util/BackOff.kt b/core/network/src/main/java/com/idle/network/util/BackOff.kt index 579a8286..84ec1f72 100644 --- a/core/network/src/main/java/com/idle/network/util/BackOff.kt +++ b/core/network/src/main/java/com/idle/network/util/BackOff.kt @@ -1,9 +1,21 @@ package com.idle.network.util import com.idle.domain.model.CountDownTimer.Companion.TICK_INTERVAL +import kotlin.math.min import kotlin.math.pow +import kotlin.random.Random const val MAX_RETRY_ATTEMPTS = 5 -const val MAX_WAIT_TIME = 10_000L +private const val MAX_WAIT_TIME = 10_000L -fun calculateBackoffTime(attempt: Int): Long = (2.0.pow(attempt) * TICK_INTERVAL).toLong() +fun calculateRetryTime(attempt: Int): Long { + return min(MAX_WAIT_TIME, calculateEqualJitter(attempt)) +} + +private fun calculateEqualJitter(attempt: Int): Long { + val baseDelay = calculateBackoffTime(attempt) + val randomJitter = Random.nextLong(0, baseDelay / 2 + 1) + return baseDelay / 2 + randomJitter +} + +private fun calculateBackoffTime(attempt: Int): Long = (2.0.pow(attempt) * TICK_INTERVAL).toLong() diff --git a/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt b/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt index 7b24ee9b..766061fd 100644 --- a/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt +++ b/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingViewModel.kt @@ -12,7 +12,6 @@ import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.ProfileRepository import com.idle.domain.usecase.chat.GetChatRoomsUseCase -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase import com.idle.navigation.NavigationHelper import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -23,7 +22,6 @@ import javax.inject.Inject @HiltViewModel class CenterChattingViewModel @Inject constructor( - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, private val getChatRoomsUseCase: GetChatRoomsUseCase, private val profileRepository: ProfileRepository, private val chatRepository: ChatRepository, @@ -41,7 +39,7 @@ class CenterChattingViewModel @Inject constructor( internal suspend fun initCenterChatting() { suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _myProfile.value = it }.onFailure { errorHelper.sendError(it) } diff --git a/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt b/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt index 55175fe8..e3000449 100644 --- a/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt +++ b/feature/center-job-posting-post/src/main/java/com/idle/center/jobposting/JobPostingViewModel.kt @@ -18,7 +18,7 @@ import com.idle.domain.model.jobposting.MentalStatus import com.idle.domain.model.jobposting.PayType import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.JobPostingRepository -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.navigation.DeepLinkDestination.CenterJobPostingPostComplete import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -36,7 +36,7 @@ import javax.inject.Inject @HiltViewModel class JobPostingViewModel @Inject constructor( - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, + private val profileRepository: ProfileRepository, private val jobPostingRepository: JobPostingRepository, private val errorHelper: ErrorHelper, val eventHelper: EventHelper, @@ -374,7 +374,7 @@ class JobPostingViewModel @Inject constructor( private fun getMyCenterProfile() = viewModelScope.launch { suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _profile.value = it }.onFailure { errorHelper.sendError(it) } diff --git a/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt b/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt index dfd92f90..1040b3d6 100644 --- a/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt +++ b/feature/center-profile/src/main/java/com/idle/center/profile/CenterProfileViewModel.kt @@ -10,8 +10,6 @@ import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.ProfileRepository -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase -import com.idle.domain.usecase.profile.UpdateCenterProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -21,8 +19,6 @@ import javax.inject.Inject @HiltViewModel class CenterProfileViewModel @Inject constructor( private val profileRepository: ProfileRepository, - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, - private val updateCenterProfileUseCase: UpdateCenterProfileUseCase, private val errorHelper: ErrorHelper, private val eventHelper: EventHelper, ) : ViewModel() { @@ -65,7 +61,7 @@ class CenterProfileViewModel @Inject constructor( internal fun getMyCenterProfile() = viewModelScope.launch { suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _centerProfile.value = it _centerIntroduce.value = it.introduce ?: "" @@ -96,7 +92,7 @@ class CenterProfileViewModel @Inject constructor( _isUpdateLoading.value = true suspendRunCatching { - updateCenterProfileUseCase( + profileRepository.updateCenterProfile( officeNumber = _centerOfficeNumber.value, introduce = _centerIntroduce.value.ifBlank { null }, imageFileUri = _profileImageUri.value?.toString(), diff --git a/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt b/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt index 803bff01..5f6259c0 100644 --- a/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt +++ b/feature/center-register-info/src/main/java/com/idle/center/register/RegisterCenterInfoViewModel.kt @@ -7,8 +7,9 @@ import androidx.lifecycle.viewModelScope import com.idle.center.register.info.R import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper -import com.idle.domain.usecase.profile.RegisterCenterProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.navigation.DeepLinkDestination.CenterRegisterComplete +import com.idle.navigation.NavigationHelper import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,11 +18,10 @@ import javax.inject.Inject @HiltViewModel class RegisterCenterInfoViewModel @Inject constructor( - private val registerCenterProfileUseCase: RegisterCenterProfileUseCase, + private val profileRepository: ProfileRepository, private val errorHelper: ErrorHelper, - private val navigationHelper: com.idle.navigation.NavigationHelper, + private val navigationHelper: NavigationHelper, ) : ViewModel() { - private val _registrationStep = MutableStateFlow(RegistrationStep.INFO) val registrationStep = _registrationStep.asStateFlow() @@ -47,7 +47,7 @@ class RegisterCenterInfoViewModel @Inject constructor( internal fun registerCenterProfile() = viewModelScope.launch { suspendRunCatching { - registerCenterProfileUseCase( + profileRepository.registerCenterProfile( centerName = _centerName.value, detailedAddress = _centerDetailAddress.value, introduce = _centerIntroduce.value, diff --git a/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt b/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt index 6053cc00..67c107fa 100644 --- a/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt +++ b/feature/center-register-info/src/main/java/com/idle/center/register/complete/RegisterCenterInfoCompleteViewModel.kt @@ -6,7 +6,7 @@ import com.idle.binding.EventHelper import com.idle.binding.MainEvent import com.idle.common.suspendRunCatching import com.idle.domain.model.profile.CenterProfile -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -15,7 +15,7 @@ import javax.inject.Inject @HiltViewModel class RegisterCenterInfoCompleteViewModel @Inject constructor( - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, + private val profileRepository: ProfileRepository, private val eventHelper: EventHelper, val navigationHelper: com.idle.navigation.NavigationHelper, ) : ViewModel() { @@ -25,7 +25,7 @@ class RegisterCenterInfoCompleteViewModel @Inject constructor( init { viewModelScope.launch { suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _centerProfile.value = it }.onFailure { diff --git a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt index 1ed71995..415e7957 100644 --- a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt +++ b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailViewModel.kt @@ -12,8 +12,6 @@ import com.idle.domain.model.profile.CenterProfile import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.ProfileRepository -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase import com.idle.navigation.NavigationHelper import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.coroutineScope @@ -27,8 +25,6 @@ import javax.inject.Inject class ChattingDetailViewModel @Inject constructor( private val profileRepository: ProfileRepository, private val chatRepository: ChatRepository, - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, private val errorHelper: ErrorHelper, val navigationHelper: NavigationHelper, private val savedStateHandle: SavedStateHandle, @@ -84,7 +80,7 @@ class ChattingDetailViewModel @Inject constructor( } suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _centerProfile.value = it }.onFailure { @@ -104,7 +100,7 @@ class ChattingDetailViewModel @Inject constructor( } suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { _workerProfile.value = it }.onFailure { diff --git a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt index ba036c9b..78dfe672 100644 --- a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt +++ b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/center/CenterJobPostingDetailViewModel.kt @@ -13,7 +13,7 @@ import com.idle.domain.model.jobposting.JobPostingStatus import com.idle.domain.model.jobposting.LifeAssistance import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.JobPostingRepository -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.job.posting.detail.R import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -23,7 +23,7 @@ import javax.inject.Inject @HiltViewModel class CenterJobPostingDetailViewModel @Inject constructor( - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, + private val profileRepository: ProfileRepository, private val jobPostingRepository: JobPostingRepository, private val errorHelper: ErrorHelper, val eventHelper: EventHelper, @@ -47,7 +47,7 @@ class CenterJobPostingDetailViewModel @Inject constructor( private fun getMyCenterProfile() = viewModelScope.launch { suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _profile.value = it }.onFailure { errorHelper.sendError(it) } @@ -89,14 +89,17 @@ class CenterJobPostingDetailViewModel @Inject constructor( startTime = editJobPostingDetail.startTime, endTime = editJobPostingDetail.endTime, payType = editJobPostingDetail.payType, - payAmount = editJobPostingDetail.payAmount.toIntOrNull() ?: return@suspendRunCatching, + payAmount = editJobPostingDetail.payAmount.toIntOrNull() + ?: return@suspendRunCatching, roadNameAddress = editJobPostingDetail.roadNameAddress, lotNumberAddress = editJobPostingDetail.lotNumberAddress, clientName = editJobPostingDetail.clientName, gender = editJobPostingDetail.gender, - birthYear = editJobPostingDetail.birthYear.toIntOrNull() ?: return@suspendRunCatching, + birthYear = editJobPostingDetail.birthYear.toIntOrNull() + ?: return@suspendRunCatching, weight = editJobPostingDetail.weight?.toIntOrNull(), - careLevel = editJobPostingDetail.careLevel.toIntOrNull() ?: return@suspendRunCatching, + careLevel = editJobPostingDetail.careLevel.toIntOrNull() + ?: return@suspendRunCatching, mentalStatus = editJobPostingDetail.mentalStatus, disease = editJobPostingDetail.disease.ifBlank { null }, isMealAssistance = editJobPostingDetail.isMealAssistance, diff --git a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt index 8ecd10d1..8e31dc41 100644 --- a/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt +++ b/feature/job-posting-detail/src/main/java/com/idle/worker/job/posting/detail/worker/WorkerJobPostingDetailViewModel.kt @@ -21,7 +21,7 @@ import com.idle.domain.model.jobposting.WorkerJobPostingDetail import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.JobPostingRepository -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.navigation.DeepLinkDestination import com.idle.navigation.NavigationEvent import dagger.hilt.android.lifecycle.HiltViewModel @@ -33,7 +33,7 @@ import javax.inject.Inject @HiltViewModel class WorkerJobPostingDetailViewModel @Inject constructor( - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, + private val profileRepository: ProfileRepository, private val jobPostingRepository: JobPostingRepository, private val chatRepository: ChatRepository, private val analyticsHelper: AnalyticsHelper, @@ -49,7 +49,7 @@ class WorkerJobPostingDetailViewModel @Inject constructor( internal fun getMyProfile() = viewModelScope.launch { suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { _profile.value = it }.onFailure { errorHelper.sendError(it) } diff --git a/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt b/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt index a9b0c082..357c238b 100644 --- a/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt +++ b/feature/setting/src/main/java/com/idle/setting/center/CenterSettingViewModel.kt @@ -7,7 +7,7 @@ import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.CenterProfile import com.idle.domain.repositorry.AuthRepository -import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.setting.SettingEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -19,7 +19,7 @@ import javax.inject.Inject @HiltViewModel class CenterSettingViewModel @Inject constructor( - private val getMyCenterProfileUseCase: GetMyCenterProfileUseCase, + private val profileRepository: ProfileRepository, private val authRepository: AuthRepository, private val analyticsHelper: AnalyticsHelper, private val errorHelper: ErrorHelper, @@ -39,7 +39,7 @@ class CenterSettingViewModel @Inject constructor( private fun getMyProfile() = viewModelScope.launch { suspendRunCatching { - getMyCenterProfileUseCase() + profileRepository.getMyCenterProfile() }.onSuccess { _centerProfile.value = it }.onFailure { errorHelper.sendError(it) } diff --git a/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt b/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt index 05ebc07f..a621838e 100644 --- a/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt +++ b/feature/setting/src/main/java/com/idle/setting/worker/WorkerSettingViewModel.kt @@ -7,7 +7,7 @@ import com.idle.common.suspendRunCatching import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.AuthRepository -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.setting.SettingEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -18,7 +18,7 @@ import javax.inject.Inject @HiltViewModel class WorkerSettingViewModel @Inject constructor( - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, + private val profileRepository: ProfileRepository, private val authRepository: AuthRepository, private val analyticsHelper: AnalyticsHelper, private val errorHelper: ErrorHelper, @@ -35,7 +35,7 @@ class WorkerSettingViewModel @Inject constructor( private fun getMyProfile() = viewModelScope.launch { suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { _workerProfile.value = it }.onFailure { errorHelper.sendError(it) } diff --git a/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt b/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt index 3668eec2..4a00cff4 100644 --- a/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt +++ b/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingViewModel.kt @@ -12,7 +12,6 @@ import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.ProfileRepository import com.idle.domain.usecase.chat.GetChatRoomsUseCase -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -26,7 +25,6 @@ import javax.inject.Inject class WorkerChattingViewModel @Inject constructor( private val profileRepository: ProfileRepository, private val getChatRoomsUseCase: GetChatRoomsUseCase, - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, private val chatRepository: ChatRepository, private val errorHelper: ErrorHelper, val navigationHelper: com.idle.navigation.NavigationHelper, @@ -46,7 +44,7 @@ class WorkerChattingViewModel @Inject constructor( internal suspend fun initWorkerChatting() { suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { profile -> _myProfile.value = profile }.onFailure { errorHelper.sendError(it) } diff --git a/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt b/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt index 1b41ccf3..f2316ba5 100644 --- a/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt +++ b/feature/worker-home/src/main/java/com/idle/worker/home/WorkerHomeViewModel.kt @@ -15,7 +15,7 @@ import com.idle.domain.model.jobposting.WorkerJobPosting import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.JobPostingRepository import com.idle.domain.repositorry.NotificationRepository -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import com.idle.navigation.DeepLinkDestination import com.idle.navigation.NavigationEvent import com.idle.navigation.NavigationHelper @@ -28,7 +28,7 @@ import javax.inject.Inject @HiltViewModel class WorkerHomeViewModel @Inject constructor( - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, + private val profileRepository: ProfileRepository, private val notificationRepository: NotificationRepository, private val jobPostingRepository: JobPostingRepository, private val errorHelper: ErrorHelper, @@ -143,7 +143,7 @@ class WorkerHomeViewModel @Inject constructor( internal fun getMyWorkerProfile() = viewModelScope.launch { suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { _profile.value = it }.onFailure { diff --git a/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt b/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt index 833c9e06..2855f068 100644 --- a/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt +++ b/feature/worker-job-posting/src/main/java/com/idle/worker/job/posting/WorkerJobPostingViewModel.kt @@ -14,7 +14,7 @@ import com.idle.domain.model.jobposting.JobPostingType import com.idle.domain.model.jobposting.WorkerJobPosting import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.JobPostingRepository -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase +import com.idle.domain.repositorry.ProfileRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -24,7 +24,7 @@ import javax.inject.Inject @HiltViewModel class WorkerJobPostingViewModel @Inject constructor( - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, + private val profileRepository: ProfileRepository, private val jobPostingRepository: JobPostingRepository, private val errorHelper: ErrorHelper, private val eventHelper: EventHelper, @@ -51,7 +51,7 @@ class WorkerJobPostingViewModel @Inject constructor( init { viewModelScope.launch { suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { _profile.value = it } diff --git a/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt b/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt index bfe1982a..fa99b8ec 100644 --- a/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt +++ b/feature/worker-profile/src/main/java/com/idle/worker/profile/WorkerProfileViewModel.kt @@ -12,8 +12,6 @@ import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.profile.JobSearchStatus import com.idle.domain.model.profile.WorkerProfile import com.idle.domain.repositorry.ProfileRepository -import com.idle.domain.usecase.profile.GetMyWorkerProfileUseCase -import com.idle.domain.usecase.profile.UpdateWorkerProfileUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -23,8 +21,6 @@ import javax.inject.Inject @HiltViewModel class WorkerProfileViewModel @Inject constructor( private val profileRepository: ProfileRepository, - private val getMyWorkerProfileUseCase: GetMyWorkerProfileUseCase, - private val updateWorkerProfileUseCase: UpdateWorkerProfileUseCase, private val errorHelper: ErrorHelper, private val eventHelper: EventHelper, ) : ViewModel() { @@ -94,7 +90,7 @@ class WorkerProfileViewModel @Inject constructor( internal fun getMyWorkerProfile() = viewModelScope.launch { suspendRunCatching { - getMyWorkerProfileUseCase() + profileRepository.getMyWorkerProfile() }.onSuccess { _workerProfile.value = it _workerIntroduce.value = it.introduce ?: "" @@ -132,7 +128,7 @@ class WorkerProfileViewModel @Inject constructor( _isUpdateLoading.value = true suspendRunCatching { - updateWorkerProfileUseCase( + profileRepository.updateWorkerProfile( experienceYear = _experienceYear.value, roadNameAddress = _roadNameAddress.value, lotNumberAddress = _lotNumberAddress.value,