diff --git a/.github/workflows/android_cd.yml b/.github/workflows/android_cd.yml index cd64aef1b..57156db54 100644 --- a/.github/workflows/android_cd.yml +++ b/.github/workflows/android_cd.yml @@ -34,6 +34,8 @@ jobs: run: | echo "CARE_DEV_BASE_URL=${{ secrets.CARE_DEV_BASE_URL }}" >> local.properties echo "CARE_PROD_BASE_URL=${{ secrets.CARE_PROD_BASE_URL }}" >> local.properties + echo "CARE_DEV_WEBSOCKET_URL=${{ secrets.CARE_DEV_WEBSOCKET_URL }}" >> local.properties + echo "CARE_PROD_WEBSOCKET_URL=${{ secrets.CARE_PROD_WEBSOCKET_URL }}" >> local.properties echo "NAVER_BASE_URL=${{ secrets.NAVER_BASE_URL }}" >> local.properties echo "NAVER_CLIENT_ID=${{ secrets.NAVER_CLIENT_ID }}" >> local.properties echo "NAVER_CLIENT_SECRET=${{ secrets.NAVER_CLIENT_SECRET }}" >> local.properties diff --git a/.github/workflows/android_ci.yml b/.github/workflows/android_ci.yml index 800138e13..739094f12 100644 --- a/.github/workflows/android_ci.yml +++ b/.github/workflows/android_ci.yml @@ -37,6 +37,8 @@ jobs: run: | echo "CARE_DEV_BASE_URL=${{ secrets.CARE_DEV_BASE_URL }}" >> local.properties echo "CARE_PROD_BASE_URL=${{ secrets.CARE_PROD_BASE_URL }}" >> local.properties + echo "CARE_DEV_WEBSOCKET_URL=${{ secrets.CARE_DEV_WEBSOCKET_URL }}" >> local.properties + echo "CARE_PROD_WEBSOCKET_URL=${{ secrets.CARE_PROD_WEBSOCKET_URL }}" >> local.properties echo "NAVER_BASE_URL=${{ secrets.NAVER_BASE_URL }}" >> local.properties echo "NAVER_CLIENT_ID=${{ secrets.NAVER_CLIENT_ID }}" >> local.properties echo "NAVER_CLIENT_SECRET=${{ secrets.NAVER_CLIENT_SECRET }}" >> local.properties 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 2685a0a2e..40f1d6d45 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 @@ -161,8 +161,10 @@ class ChatRepositoryImpl @Inject constructor( receiverId: String, senderName: String, content: String, + userType: UserType, ): Result = chatDataSource.sendMessage( - SendMessageRequest( + userType = userType, + sendMessageRequest = SendMessageRequest( chatroomId = chatroomId, receiverId = receiverId, senderName = senderName, @@ -170,9 +172,14 @@ class ChatRepositoryImpl @Inject constructor( ) ) - override suspend fun readMessage(chatroomId: String, opponentId: String): Result = + override suspend fun readMessage( + chatroomId: String, + opponentId: String, + userType: UserType, + ): Result = chatDataSource.readMessage( - ReadMessageRequest( + userType = userType, + readMessageRequest = ReadMessageRequest( chatroomId = chatroomId, opponentId = opponentId, ) 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 8e8e220e7..acedbec2a 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 @@ -41,10 +41,12 @@ interface ChatRepository { receiverId: String, senderName: String, content: String, + userType: UserType, ): Result suspend fun readMessage( chatroomId: String, opponentId: String, + userType: UserType, ): Result } diff --git a/core/navigation/src/main/java/com/idle/navigation/DeepLinkDestination.kt b/core/navigation/src/main/java/com/idle/navigation/DeepLinkDestination.kt index 9a9541a04..7dda55bff 100644 --- a/core/navigation/src/main/java/com/idle/navigation/DeepLinkDestination.kt +++ b/core/navigation/src/main/java/com/idle/navigation/DeepLinkDestination.kt @@ -46,16 +46,16 @@ sealed class DeepLinkDestination( data class ChattingDetail( val chattingRoomId: String, val myId: String, - val receiverUserType: String, + val myUserType: String, val opponentId: String, val fromJobPosting: Boolean, ) : DeepLinkDestination( addressRes = R.string.chatting_detail_deeplink_url, params = mapOf( "chattingRoomId" to chattingRoomId, - "receiverId" to myId, - "receiverUserType" to receiverUserType, - "senderId" to opponentId, + "myId" to myId, + "myUserType" to myUserType, + "opponentId" to opponentId, "fromJobPosting" to fromJobPosting, ) ) diff --git a/core/navigation/src/main/res/values/deeplinks.xml b/core/navigation/src/main/res/values/deeplinks.xml index f22042df0..828c66b36 100644 --- a/core/navigation/src/main/res/values/deeplinks.xml +++ b/core/navigation/src/main/res/values/deeplinks.xml @@ -6,7 +6,7 @@ care://withdrawal/{userType} care://signup/complete care://newPassword - care://chatting/detail/{chattingRoomId}/{receiverId}/{receiverUserType}/{senderId}/{fromJobPosting} + care://chatting/detail/{chattingRoomId}/{myId}/{myUserType}/{opponentId}/{fromJobPosting} care://chatting/seeallchat/{allChatContents} care://signup/worker diff --git a/core/network/src/main/java/com/idle/network/api/AuthApi.kt b/core/network/src/main/java/com/idle/network/api/AuthApi.kt index 421c4a341..cb1e1f9f1 100644 --- a/core/network/src/main/java/com/idle/network/api/AuthApi.kt +++ b/core/network/src/main/java/com/idle/network/api/AuthApi.kt @@ -20,56 +20,56 @@ import retrofit2.http.POST import retrofit2.http.Path interface AuthApi { - @POST("/api/v1/auth/common/refresh") + @POST("api/v1/auth/common/refresh") suspend fun refreshToken(@Body refreshTokenRequest: RefreshTokenRequest): Response - @POST("/api/v1/auth/common/send") + @POST("api/v1/auth/common/send") suspend fun sendPhoneNumber(@Body sendPhoneRequest: SendPhoneRequest): Response - @POST("/api/v1/auth/common/confirm") + @POST("api/v1/auth/common/confirm") suspend fun confirmAuthCode(@Body confirmAuthCodeRequest: ConfirmAuthCodeRequest): Response - @POST("/api/v1/auth/center/join") + @POST("api/v1/auth/center/join") suspend fun signUpCenter(@Body signUpCenterRequest: SignUpCenterRequest): Response - @POST("/api/v1/auth/center/login") + @POST("api/v1/auth/center/login") suspend fun signInCenter(@Body signInCenterRequest: SignInCenterRequest): Response - @POST("/api/v1/auth/carer/join") + @POST("api/v1/auth/carer/join") suspend fun signUpWorker(@Body signUpWorkerRequest: SignUpWorkerRequest): Response - @POST("/api/v1/auth/carer/login") + @POST("api/v1/auth/carer/login") suspend fun signInWorker(@Body signInWorkerRequest: SignInWorkerRequest): Response - @POST("/api/v1/auth/center/logout") + @POST("api/v1/auth/center/logout") suspend fun logoutCenter(): Response - @POST("/api/v1/auth/carer/logout") + @POST("api/v1/auth/carer/logout") suspend fun logoutWorker(): Response - @POST("/api/v1/auth/center/withdraw") + @POST("api/v1/auth/center/withdraw") suspend fun withdrawalCenter( @Body withdrawalCenterRequest: WithdrawalCenterRequest ): Response - @POST("/api/v1/auth/carer/withdraw") + @POST("api/v1/auth/carer/withdraw") suspend fun withdrawalWorker( @Body withdrawalWorkerRequest: WithdrawalWorkerRequest ): Response - @GET("/api/v1/auth/center/validation/{identifier}") + @GET("api/v1/auth/center/validation/{identifier}") suspend fun validateIdentifier(@Path("identifier") identifier: String): Response - @GET("/api/v1/auth/center/authentication/{businessRegistrationNumber}") + @GET("api/v1/auth/center/authentication/{businessRegistrationNumber}") suspend fun validateBusinessRegistrationNumber( @Path("businessRegistrationNumber") businessRegistrationNumber: String ): Response - @PATCH("/api/v1/auth/center/password/new") + @PATCH("api/v1/auth/center/password/new") suspend fun generateNewPassword( @Body generateNewPasswordRequest: GenerateNewPasswordRequest ): Response - @PATCH("/api/v1/auth/center/join/verify") + @PATCH("api/v1/auth/center/join/verify") suspend fun sendCenterVerificationRequest(): Response -} \ No newline at end of file +} diff --git a/core/network/src/main/java/com/idle/network/api/ChatApi.kt b/core/network/src/main/java/com/idle/network/api/ChatApi.kt index 2711bdc98..222a5d94e 100644 --- a/core/network/src/main/java/com/idle/network/api/ChatApi.kt +++ b/core/network/src/main/java/com/idle/network/api/ChatApi.kt @@ -10,25 +10,25 @@ import retrofit2.http.Path import retrofit2.http.Query interface ChatApi { - @GET("/api/v2/chat/carer/chatrooms") + @GET("api/v2/chat/carer/chatrooms") suspend fun getWorkerChatRooms(): Response> - @GET("/api/v2/chat/center/chatrooms") + @GET("api/v2/chat/center/chatrooms") suspend fun getCenterChatRooms(): Response> - @POST("/api/v2/chat/carer/chatrooms") + @POST("api/v2/chat/carer/chatrooms") suspend fun generateWorkerChatRoom(@Query("opponentId") opponentId: String): Response - @POST("/api/v2/chat/carer/chatrooms") + @POST("api/v2/chat/carer/chatrooms") suspend fun generateCenterChatRoom(@Query("opponentId") opponentId: String): Response - @GET("/api/v2/chat/carer/chatrooms/{chatroom-id}/messages") + @GET("api/v2/chat/carer/chatrooms/{chatroom-id}/messages") suspend fun getWorkerChatRoomMessages( @Path("chatroom-id") chatRoomId: String, @Query("message-id") messageId: String?, ): Response> - @GET("/api/v2/chat/center/chatrooms/{chatroom-id}/messages") + @GET("api/v2/chat/center/chatrooms/{chatroom-id}/messages") suspend fun getCenterChatRoomMessages( @Path("chatroom-id") chatRoomId: String, @Query("message-id") messageId: String?, diff --git a/core/network/src/main/java/com/idle/network/api/JobPostingApi.kt b/core/network/src/main/java/com/idle/network/api/JobPostingApi.kt index 5ce6f9d68..746111644 100644 --- a/core/network/src/main/java/com/idle/network/api/JobPostingApi.kt +++ b/core/network/src/main/java/com/idle/network/api/JobPostingApi.kt @@ -23,84 +23,84 @@ import retrofit2.http.Path import retrofit2.http.Query interface JobPostingApi { - @POST("/api/v1/job-postings") + @POST("api/v1/job-postings") suspend fun postJobPosting(@Body jobPostingRequest: JobPostingRequest): Response - @PATCH("/api/v1/job-postings/{job-posting-id}") + @PATCH("api/v1/job-postings/{job-posting-id}") suspend fun updateJobPosting( @Path("job-posting-id") jobPostingId: String, @Body jobPostingRequest: JobPostingRequest, ): Response - @GET("/api/v1/job-postings/{job-posting-id}/center") + @GET("api/v1/job-postings/{job-posting-id}/center") suspend fun getJobPostingDetailCenter( @Path("job-posting-id") jobPostingId: String ): Response - @GET("/api/v1/job-postings") + @GET("api/v1/job-postings") suspend fun getJobPostings( @Query("next") next: String?, @Query("limit") limit: Int, ): Response - @GET("/api/v1/job-postings/carer/my/applied") + @GET("api/v1/job-postings/carer/my/applied") suspend fun getJobPostingsApplied( @Query("next") next: String?, @Query("limit") limit: Int, ): Response - @GET("/api/v1/job-postings/my/favorites") + @GET("api/v1/job-postings/my/favorites") suspend fun getMyFavoriteJobPostings(): Response - @GET("/api/v1/crawling-job-postings/my/favorites") + @GET("api/v1/crawling-job-postings/my/favorites") suspend fun getMyFavoriteCrawlingJobPostings(): Response - @GET("/api/v1/job-postings/status/in-progress") + @GET("api/v1/job-postings/status/in-progress") suspend fun getJobPostingsInProgress(): Response - @GET("/api/v1/job-postings/status/completed") + @GET("api/v1/job-postings/status/completed") suspend fun getJobPostingsCompleted(): Response - @GET("/api/v1/job-postings/{job-posting-id}/applicant-count") + @GET("api/v1/job-postings/{job-posting-id}/applicant-count") suspend fun getApplicantCount( @Path("job-posting-id") jobPostingId: String ): Response - @GET("/api/v1/job-postings/{job-posting-id}/carer") + @GET("api/v1/job-postings/{job-posting-id}/carer") suspend fun getJobPostingDetailWorker( @Path("job-posting-id") jobPostingId: String ): Response - @GET("/api/v1/job-postings/{job-posting-id}/applicants") + @GET("api/v1/job-postings/{job-posting-id}/applicants") suspend fun getApplicants( @Path("job-posting-id") jobPostingId: String ): Response - @POST("/api/v1/applys") + @POST("api/v1/applys") suspend fun applyJobPosting(@Body applyJobPostingRequest: ApplyJobPostingRequest): Response - @POST("/api/v1/job-postings/{job-posting-id}/favorites") + @POST("api/v1/job-postings/{job-posting-id}/favorites") suspend fun addFavoriteJobPosting( @Path("job-posting-id") jobPostingId: String, @Body favoriteJobPostingRequest: FavoriteJobPostingRequest, ): Response - @DELETE("/api/v1/job-postings/{job-posting-id}/remove-favorites") + @DELETE("api/v1/job-postings/{job-posting-id}/remove-favorites") suspend fun removeFavoriteJobPosting(@Path("job-posting-id") jobPostingId: String): Response - @PATCH("/api/v1/job-postings/{job-posting-id}/end") + @PATCH("api/v1/job-postings/{job-posting-id}/end") suspend fun endJobPosting(@Path("job-posting-id") jobPostingId: String): Response - @DELETE("/api/v1/job-postings/{job-posting-id}") + @DELETE("api/v1/job-postings/{job-posting-id}") suspend fun deleteJobPosting(@Path("job-posting-id") jobPostingId: String): Response - @GET("/api/v1/crawling-job-postings") + @GET("api/v1/crawling-job-postings") suspend fun getCrawlingJobPostings( @Query("next") next: String?, @Query("limit") limit: Int, ): Response - @GET("/api/v1/crawling-job-postings/{crawling-job-posting-id}") + @GET("api/v1/crawling-job-postings/{crawling-job-posting-id}") suspend fun getCrawlingJobPostingsDetail( @Path("crawling-job-posting-id") jobPostingId: String ): Response diff --git a/core/network/src/main/java/com/idle/network/api/NotificationApi.kt b/core/network/src/main/java/com/idle/network/api/NotificationApi.kt index 9d0ed9f8c..dd2ef609e 100644 --- a/core/network/src/main/java/com/idle/network/api/NotificationApi.kt +++ b/core/network/src/main/java/com/idle/network/api/NotificationApi.kt @@ -14,21 +14,21 @@ import retrofit2.http.Path import retrofit2.http.Query interface NotificationApi { - @POST("/api/v1/fcm/token") + @POST("api/v1/fcm/token") suspend fun postFCMToken(@Body postFcmTokenRequest: PostFcmTokenRequest): Response @HTTP(method = "DELETE", path = "/api/v1/fcm/token", hasBody = true) suspend fun deleteFCMToken(@Body deleteFcmTokenRequest: DeleteFcmTokenRequest): Response - @GET("/api/v1/notifications/my") + @GET("api/v1/notifications/my") suspend fun getMyNotifications( @Query("next") next: String?, @Query("limit") limit: Int, ): Response - @PATCH("/api/v1/notifications/{notification-id}") + @PATCH("api/v1/notifications/{notification-id}") suspend fun readNotification(@Path("notification-id") notificationId: String): Response - @GET("/api/v1/notifications/count") + @GET("api/v1/notifications/count") suspend fun getUnreadNotificationCount(): Response } diff --git a/core/network/src/main/java/com/idle/network/api/UserApi.kt b/core/network/src/main/java/com/idle/network/api/UserApi.kt index 368e769d7..1928f34f2 100644 --- a/core/network/src/main/java/com/idle/network/api/UserApi.kt +++ b/core/network/src/main/java/com/idle/network/api/UserApi.kt @@ -21,20 +21,20 @@ import retrofit2.http.Query import retrofit2.http.Url interface UserApi { - @GET("/api/v1/users/center/my/profile") + @GET("api/v1/users/center/my/profile") suspend fun getMyCenterProfile(): Response - @PATCH("/api/v1/users/center/my/profile") + @PATCH("api/v1/users/center/my/profile") suspend fun updateMyCenterProfile( @Body updateCenterProfileRequest: UpdateCenterProfileRequest ): Response - @GET("/api/v1/users/center/profile/{center-id}") + @GET("api/v1/users/center/profile/{center-id}") suspend fun getCenterProfile( @Path("center-id") centerId: String, ): Response - @GET("/api/v1/users/{user-type}/my/profile-image/upload-url") + @GET("api/v1/users/{user-type}/my/profile-image/upload-url") suspend fun getImageUploadUrl( @Path("user-type") userType: String, @Query("imageFileExtension") imageFileExtension: String, @@ -46,33 +46,33 @@ interface UserApi { @Body requestImage: RequestBody, ): Response - @POST("/api/v1/users/{user-type}/my/profile-image/upload-callback") + @POST("api/v1/users/{user-type}/my/profile-image/upload-callback") suspend fun callbackImageUpload( @Path("user-type") userType: String, @Body callbackImageUploadRequest: CallbackImageUploadRequest, ): Response - @GET("/api/v1/users/carer/my/profile") + @GET("api/v1/users/carer/my/profile") suspend fun getMyWorkerProfile(): Response - @GET("/api/v1/users/carer/profile/{carer-id}") + @GET("api/v1/users/carer/profile/{carer-id}") suspend fun getWorkerProfile( @Path("carer-id") workerId: String, ): Response - @PATCH("/api/v1/users/carer/my/profile") + @PATCH("api/v1/users/carer/my/profile") suspend fun updateWorkerProfile( @Body updateWorkerProfileRequest: UpdateWorkerProfileRequest ): Response - @POST("/api/v1/users/center/my/profile") + @POST("api/v1/users/center/my/profile") suspend fun registerCenterProfile( @Body registerCenterProfileRequest: RegisterCenterProfileRequest ): Response - @GET("/api/v1/logs/users/carer/my") + @GET("api/v1/logs/users/carer/my") suspend fun getWorkerId(): Response - @GET("/api/v1/auth/center/join/status") + @GET("api/v1/auth/center/join/status") suspend fun getCenterStatus(): Response -} \ No newline at end of file +} 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 909807ee3..1ede05608 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 @@ -72,4 +72,4 @@ class CareAuthenticator @Inject constructor( private const val MAX_RETRY_COUNT = 3 private const val RETRY_HEADER = "Retry-Count" } -} \ No newline at end of file +} diff --git a/core/network/src/main/java/com/idle/network/interceptor/AuthInterceptor.kt b/core/network/src/main/java/com/idle/network/interceptor/AuthInterceptor.kt index 03afec222..7e98a9053 100644 --- a/core/network/src/main/java/com/idle/network/interceptor/AuthInterceptor.kt +++ b/core/network/src/main/java/com/idle/network/interceptor/AuthInterceptor.kt @@ -33,7 +33,11 @@ class AuthInterceptor @Inject constructor( return false } - if (request.url.host.contains("aws-caremeet-upload.s3.ap-northeast-2.amazonaws.com")) { + if (request.url.host.contains("/aws-caremeet-upload.s3.ap-northeast-2.amazonaws.com")) { + return false + } + + if (request.url.host.contains("/api/v1/auth/common/refresh")) { return false } @@ -42,7 +46,6 @@ class AuthInterceptor @Inject constructor( "/api/v1/auth/common/confirm" -> false "/api/v1/auth/center/join" -> false "/api/v1/auth/center/login" -> false - "/api/v1/auth/common/refresh" -> false "/api/v1/auth/carer/login" -> false "/api/v1/auth/carer/join" -> false "/api/v1/auth/center/password/new" -> false diff --git a/core/network/src/main/java/com/idle/network/model/chat/GetChatRoomResponse.kt b/core/network/src/main/java/com/idle/network/model/chat/GetChatRoomResponse.kt index 12db6d208..dc19ede54 100644 --- a/core/network/src/main/java/com/idle/network/model/chat/GetChatRoomResponse.kt +++ b/core/network/src/main/java/com/idle/network/model/chat/GetChatRoomResponse.kt @@ -13,7 +13,7 @@ data class GetChatRoomResponse( val count: Int = 0, val opponentId: String = "", val opponentName: String = "", - val opponentProfileImageUrl: String = "", + val opponentProfileImageUrl: String? = null, ) { fun toVO() = ChatRoomWithOpponentInfo( id = chatRoomId, 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 cf25d24db..d1a471caf 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 android.util.Log +import com.idle.domain.model.auth.UserType import com.idle.network.BuildConfig import com.idle.network.api.ChatApi import com.idle.network.di.TokenManager @@ -91,6 +92,7 @@ class ChatDataSource @Inject constructor( connectionAttempts++ connectWebSocket().getOrThrow() } else { + Log.d("test connect", throwable.stackTraceToString()) throw throwable } } @@ -99,6 +101,7 @@ class ChatDataSource @Inject constructor( session?.disconnect() Result.success(Unit) } catch (e: Exception) { + Log.d("test disconnect", e.stackTraceToString()) Result.failure(e) } @@ -111,18 +114,26 @@ class ChatDataSource @Inject constructor( it } ?: flow { throw IOException("웹소켓을 먼저 연결해주세요.") } - suspend fun sendMessage(sendMessageRequest: SendMessageRequest): Result = + suspend fun sendMessage( + userType: UserType, + sendMessageRequest: SendMessageRequest + ): Result = runCatching { + Log.d("test", "/pub/send/${userType.apiValue.lowercase()}") + session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/send"), + headers = StompSendHeaders(destination = "/pub/send/${userType.apiValue.lowercase()}"), body = sendMessageRequest, serializer = SendMessageRequest.serializer(), ) } - suspend fun readMessage(readMessageRequest: ReadMessageRequest): Result = runCatching { + suspend fun readMessage( + userType: UserType, + readMessageRequest: ReadMessageRequest + ): Result = runCatching { session?.convertAndSend( - headers = StompSendHeaders(destination = "/pub/read"), + headers = StompSendHeaders(destination = "/pub/read/${userType.apiValue.lowercase()}"), body = readMessageRequest, serializer = ReadMessageRequest.serializer(), ) diff --git a/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingFragment.kt b/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingFragment.kt index b6e8d2375..799c166a0 100644 --- a/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingFragment.kt +++ b/feature/center-chatting/src/main/java/com/idle/center/chatting/CenterChattingFragment.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage import com.idle.compose.base.BaseComposeFragment @@ -65,15 +66,17 @@ internal class CenterChattingFragment : BaseComposeFragment() { initCenterChatting() retrieveChatRoomList() loadChatRoomList() - subscribeChatMessage() + } + + LifecycleStartEffect(fragmentViewModel) { + connectWebsocket() + onStopOrDispose { disconnectWebsocket() } } CenterChattingScreen( chatRoomList = chatRoomList, myProfile = myProfile, - navigateTo = { - navigationHelper.navigateTo(NavigationEvent.To(it)) - }, + navigateTo = { navigationHelper.navigateTo(NavigationEvent.To(it)) }, ) } } @@ -183,7 +186,7 @@ internal fun ChatRoomItem( chattingRoomId = chatRoom.id, myId = myProfile!!.centerId, opponentId = chatRoom.opponentId, - receiverUserType = UserType.WORKER.apiValue, + myUserType = UserType.CENTER.apiValue, fromJobPosting = false, ) ) 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 5ca9bc4a6..25fc60b76 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 @@ -15,10 +15,7 @@ import com.idle.domain.usecase.profile.GetMyCenterProfileUseCase import com.idle.navigation.NavigationHelper import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -36,13 +33,9 @@ class CenterChattingViewModel @Inject constructor( private val _chatRoomMap = MutableStateFlow>(LinkedHashMap()) - val chatRoomList = _chatRoomMap - .map { it.values.toList() } - .stateIn( - scope = viewModelScope, - started = SharingStarted.Lazily, - initialValue = null, - ) + + private val _chatRoomList = MutableStateFlow?>(null) + val chatRoomList = _chatRoomList.asStateFlow() internal suspend fun initCenterChatting() { getMyCenterProfileUseCase().onSuccess { @@ -50,7 +43,17 @@ class CenterChattingViewModel @Inject constructor( }.onFailure { errorHelper.sendError(it) } } - internal fun subscribeChatMessage() = viewModelScope.launch { + internal fun connectWebsocket() = viewModelScope.launch { + chatRepository.connectWebSocket().onSuccess { + subscribeChatMessage() + } + } + + internal fun disconnectWebsocket() = viewModelScope.launch { + chatRepository.disconnectWebSocket() + } + + private fun subscribeChatMessage() = viewModelScope.launch { chatRepository.subscribeChatMessage(_myProfile.value?.centerId ?: return@launch) .collect { message -> when (message) { @@ -90,6 +93,7 @@ class CenterChattingViewModel @Inject constructor( } _chatRoomMap.value = updatedMap + updateChatRoomListFromMap() } internal suspend fun retrieveChatRoomList() { @@ -97,9 +101,12 @@ class CenterChattingViewModel @Inject constructor( userType = UserType.CENTER, userId = _myProfile.value?.centerId ?: return ).onSuccess { - val newMap = LinkedHashMap(_chatRoomMap.value) - it.forEach { chatRoom -> newMap[chatRoom.id] = chatRoom } + val newMap = LinkedHashMap().apply { + putAll(_chatRoomMap.value) + it.forEach { chatRoom -> this[chatRoom.id] = chatRoom } + } _chatRoomMap.value = newMap + updateChatRoomListFromMap() }.onFailure { errorHelper.sendError(it) } } @@ -108,9 +115,16 @@ class CenterChattingViewModel @Inject constructor( userId = _myProfile.value?.centerId ?: return, userType = UserType.CENTER ).onSuccess { - val newMap = LinkedHashMap(_chatRoomMap.value) - it.forEach { chatRoom -> newMap[chatRoom.id] = chatRoom } + val newMap = LinkedHashMap().apply { + putAll(_chatRoomMap.value) + it.forEach { chatRoom -> this[chatRoom.id] = chatRoom } + } _chatRoomMap.value = newMap + updateChatRoomListFromMap() } } + + private fun updateChatRoomListFromMap() { + _chatRoomList.value = _chatRoomMap.value.values.toList() + } } diff --git a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailFragment.kt b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailFragment.kt index a93f2eef1..ab65c18f1 100644 --- a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailFragment.kt +++ b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ChattingDetailFragment.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.fragment.findNavController import com.idle.chatting_detail.ui.ChattingDetailLoadingScreen @@ -23,8 +24,8 @@ internal class ChattingDetailFragment : BaseComposeFragment() { val scope = rememberCoroutineScope() fragmentViewModel.apply { - val receiverUserType = receiverUserType - val receiverId = receiverId + val receiverUserType = myUserType + val receiverId = myId val writingText by writingText.collectAsStateWithLifecycle() val chatMessages by chatMessages.collectAsStateWithLifecycle() @@ -34,10 +35,15 @@ internal class ChattingDetailFragment : BaseComposeFragment() { LaunchedEffect(Unit) { getUserProfile() getChatMessages() - subscribeChatMessage() readMessage() } + LifecycleStartEffect(fragmentViewModel) { + connectWebsocket() + onStopOrDispose { disconnectWebsocket() } + } + + if (chatMessages != null && workerProfile != null && centerProfile != null) { ChattingDetailScreen( receiverId = receiverId, 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 f00a58562..6aa5f05e5 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 @@ -1,5 +1,6 @@ package com.idle.chatting_detail +import android.util.Log import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -35,14 +36,15 @@ class ChattingDetailViewModel @Inject constructor( private val chattingRoomId: String = requireNotNull(savedStateHandle["chattingRoomId"]) { "chattingRoomId is missing in savedStateHandle" } - private val senderId: String = requireNotNull(savedStateHandle["senderId"]) { - "senderId is missing in savedStateHandle" + private val opponentId: String = requireNotNull(savedStateHandle["opponentId"]) { + "opponentId is missing in savedStateHandle" } - val receiverUserType: UserType = UserType.create( - requireNotNull(savedStateHandle["receiverUserType"]) { "receiverUserType is missing" } - ) - val receiverId: String = requireNotNull(savedStateHandle["receiverId"]) { - "receiverId is missing in savedStateHandle" + val myUserType: UserType = + UserType.create( + requireNotNull(savedStateHandle["myUserType"]) { "myUserType is missing" } + ) + val myId: String = requireNotNull(savedStateHandle["myId"]) { + "myId is missing in savedStateHandle" } private val fromJobPosting: Boolean = requireNotNull(savedStateHandle["fromJobPosting"]) { "fromJobPosting is missing in savedStateHandle" @@ -67,10 +69,10 @@ class ChattingDetailViewModel @Inject constructor( } internal suspend fun getUserProfile() = coroutineScope { - when (receiverUserType) { + when (myUserType) { UserType.CENTER -> { launch { - profileRepository.getWorkerProfile(senderId).onSuccess { + profileRepository.getWorkerProfile(opponentId).onSuccess { _workerProfile.value = it }.onFailure { errorHelper.sendError(it) @@ -86,7 +88,7 @@ class ChattingDetailViewModel @Inject constructor( UserType.WORKER -> { launch { - profileRepository.getCenterProfile(senderId).onSuccess { + profileRepository.getCenterProfile(opponentId).onSuccess { _centerProfile.value = it }.onFailure { errorHelper.sendError(it) @@ -105,11 +107,6 @@ class ChattingDetailViewModel @Inject constructor( internal suspend fun getChatMessages() { if (_callType.value == MessageCallType.END) return - val myId = when (receiverUserType) { - UserType.WORKER -> _workerProfile.value?.workerId ?: return - UserType.CENTER -> _centerProfile.value?.centerId ?: return - } - if (fromJobPosting) { chatRepository.retrieveChatRoomMessages( roomId = chattingRoomId, @@ -125,7 +122,7 @@ class ChattingDetailViewModel @Inject constructor( chatRepository.getChatRoomMessages( roomId = chattingRoomId, messageId = _chatMessages.value?.first()?.id, - userType = receiverUserType, + userType = myUserType, myId = myId, ).onSuccess { messages -> if (messages.isEmpty()) _callType.value = MessageCallType.END @@ -137,12 +134,17 @@ class ChattingDetailViewModel @Inject constructor( } } - internal suspend fun subscribeChatMessage() { - val myId = when (receiverUserType) { - UserType.WORKER -> _workerProfile.value?.workerId ?: return - UserType.CENTER -> _centerProfile.value?.centerId ?: return + internal fun connectWebsocket() = viewModelScope.launch { + chatRepository.connectWebSocket().onSuccess { + subscribeChatMessage() } + } + + internal fun disconnectWebsocket() = viewModelScope.launch { + chatRepository.disconnectWebSocket() + } + private suspend fun subscribeChatMessage() { chatRepository.subscribeChatMessage(myId) .catch { errorHelper.sendError(it) @@ -150,7 +152,6 @@ class ChattingDetailViewModel @Inject constructor( when (message) { is ChatMessage -> { if (message.senderId != myId) readMessage() - _chatMessages.value = (_chatMessages.value ?: emptyList()) + message } @@ -164,37 +165,30 @@ class ChattingDetailViewModel @Inject constructor( } internal fun sendMessage() = viewModelScope.launch { - val receiverId = if (receiverUserType == UserType.CENTER) _workerProfile.value!!.workerId - else _centerProfile.value!!.centerId + val senderName = if (myUserType == UserType.CENTER) _centerProfile.value!!.centerName + else _workerProfile.value!!.workerName - val myId = if (receiverUserType == UserType.CENTER) _centerProfile.value!!.centerId - else _workerProfile.value!!.workerId - - val senderName = - if (receiverUserType == UserType.CENTER) _centerProfile.value!!.centerName - else _workerProfile.value!!.workerName + Log.d("test", "$myId $myUserType $chattingRoomId") chatRepository.sendMessage( chatroomId = chattingRoomId, myId = myId, - receiverId = receiverId, + receiverId = opponentId, senderName = senderName, content = _writingText.value, + userType = myUserType, ).onSuccess { _writingText.value = "" }.onFailure { errorHelper.sendError(it) } } internal suspend fun readMessage() { - val opponentId = if (receiverUserType == UserType.CENTER) _workerProfile.value!!.workerId - else _centerProfile.value!!.centerId - - val myId = if (receiverUserType == UserType.CENTER) _centerProfile.value!!.centerId - else _workerProfile.value!!.workerId + Log.d("test", "$opponentId $myUserType $chattingRoomId") chatRepository.readMessage( chatroomId = chattingRoomId, opponentId = opponentId, + userType = myUserType, ).onSuccess { _chatMessages.value = _chatMessages.value?.map { if (it.receiverId == myId) it.copy(isRead = true) else it diff --git a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/CareChatTextBubble.kt b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/CareChatTextBubble.kt index 0dea0a8b9..64e93b5f4 100644 --- a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/CareChatTextBubble.kt +++ b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/CareChatTextBubble.kt @@ -50,10 +50,8 @@ fun CareChatSenderTextBubbleWithImage( modifier = modifier.fillMaxWidth() ) { AsyncImage( - model = imageUrl - ?: painterResource(com.idle.designresource.R.drawable.ic_notification_placeholder), - placeholder = - painterResource(com.idle.designresource.R.drawable.ic_notification_placeholder), + model = imageUrl ?: com.idle.designresource.R.drawable.ic_notification_placeholder, + placeholder = painterResource(com.idle.designresource.R.drawable.ic_notification_placeholder), error = painterResource(com.idle.designresource.R.drawable.ic_notification_placeholder), onError = { Log.d("test", imageUrl.toString()) }, contentDescription = null, diff --git a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/ChattingInput.kt b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/ChattingInput.kt index 83c3ac8c5..7c50714c2 100644 --- a/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/ChattingInput.kt +++ b/feature/chatting-detail/src/main/java/com/idle/chatting_detail/ui/component/ChattingInput.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.idle.compose.ui.clickable @@ -20,6 +21,8 @@ fun ChattingInput( sendMessage: () -> Unit, modifier: Modifier = Modifier, ) { + val focusManager = LocalFocusManager.current + Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp), @@ -39,7 +42,10 @@ fun ChattingInput( contentDescription = "", modifier = Modifier .size(32.dp) - .clickable(throttleTime = 1000L) { sendMessage() }, + .clickable(throttleTime = 1000L) { + sendMessage() + focusManager.clearFocus() + }, ) } } diff --git a/feature/chatting-detail/src/main/res/navigation/nav_chatting_detail.xml b/feature/chatting-detail/src/main/res/navigation/nav_chatting_detail.xml index c073585d7..3fec3e5ad 100644 --- a/feature/chatting-detail/src/main/res/navigation/nav_chatting_detail.xml +++ b/feature/chatting-detail/src/main/res/navigation/nav_chatting_detail.xml @@ -13,13 +13,13 @@ android:name="chattingRoomId" app:argType="string" /> ( - CenterProfile("", "", "", "", "", "", 0.0, 0.0, "", "") - ) + private val _centerProfile = MutableStateFlow( + CenterProfile("", "", "", "", "", "", 0.0, 0.0, "", "") + ) val centerProfile = _centerProfile.asStateFlow() private val _centerSettingEvent = MutableSharedFlow() 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 81f7db295..5af940bfe 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 @@ -107,9 +107,7 @@ class CenterSignInViewModel @Inject constructor( private fun fetchAndNavigateToProfile() = viewModelScope.launch { profileRepository.getMyCenterProfile().onSuccess { - navigationHelper.navigateTo( - To(CenterHome, R.id.centerSignInFragment) - ) + navigationHelper.navigateTo(To(CenterHome, R.id.centerSignInFragment)) }.onFailure { val error = it as HttpResponseException if (error.apiErrorCode == ApiErrorCode.CenterNotFound) { diff --git a/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingFragment.kt b/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingFragment.kt index 930dcd144..02cc21a35 100644 --- a/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingFragment.kt +++ b/feature/worker-chatting/src/main/java/com/idle/worker/chatting/WorkerChattingFragment.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.LifecycleStartEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import coil.compose.AsyncImage import com.idle.compose.base.BaseComposeFragment @@ -50,7 +51,6 @@ import com.idle.domain.util.formatUnReadNumber import com.idle.navigation.DeepLinkDestination import com.idle.navigation.NavigationEvent import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch @AndroidEntryPoint internal class WorkerChattingFragment : BaseComposeFragment() { @@ -64,11 +64,13 @@ internal class WorkerChattingFragment : BaseComposeFragment() { LaunchedEffect(Unit) { initWorkerChatting() - launch { - retrieveChatRoomList() - loadChatRoomList() - } - launch { subscribeChatMessage() } + retrieveChatRoomList() + loadChatRoomList() + } + + LifecycleStartEffect(fragmentViewModel) { + connectWebsocket() + onStopOrDispose { disconnectWebsocket() } } if (chatRoomList != null) { @@ -187,7 +189,7 @@ internal fun ChatRoomItem( DeepLinkDestination.ChattingDetail( chattingRoomId = chatRoom.id, myId = myProfile!!.workerId, - receiverUserType = UserType.WORKER.apiValue, + myUserType = UserType.WORKER.apiValue, opponentId = chatRoom.opponentId, fromJobPosting = false, ) 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 dd731748c..cf8912d54 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 @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -71,7 +72,17 @@ class WorkerChattingViewModel @Inject constructor( } } - internal suspend fun subscribeChatMessage() { + internal fun connectWebsocket() = viewModelScope.launch { + chatRepository.connectWebSocket().onSuccess { + subscribeChatMessage() + } + } + + internal fun disconnectWebsocket() = viewModelScope.launch { + chatRepository.disconnectWebSocket() + } + + private suspend fun subscribeChatMessage() { chatRepository.subscribeChatMessage(_myProfile.value?.workerId ?: return) .collect { message -> when (message) { diff --git a/presentation/src/main/java/com/idle/presentation/MainActivity.kt b/presentation/src/main/java/com/idle/presentation/MainActivity.kt index e88f33094..98c954c15 100644 --- a/presentation/src/main/java/com/idle/presentation/MainActivity.kt +++ b/presentation/src/main/java/com/idle/presentation/MainActivity.kt @@ -129,18 +129,6 @@ class MainActivity : AppCompatActivity() { } } - override fun onStart() { - super.onStart() - if (networkMonitor.networkState.value != NetworkState.NotConnected) { - viewModel.connectWebSocket() - } - } - - override fun onStop() { - super.onStop() - viewModel.disconnectWebSocket() - } - override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) diff --git a/presentation/src/main/java/com/idle/presentation/MainViewModel.kt b/presentation/src/main/java/com/idle/presentation/MainViewModel.kt index 7f2e7cfe5..0cc527337 100644 --- a/presentation/src/main/java/com/idle/presentation/MainViewModel.kt +++ b/presentation/src/main/java/com/idle/presentation/MainViewModel.kt @@ -12,7 +12,6 @@ import com.idle.domain.model.error.ErrorHelper import com.idle.domain.model.error.HttpResponseException import com.idle.domain.model.jobposting.SharedJobPostingInfo import com.idle.domain.model.profile.CenterManagerAccountStatus -import com.idle.domain.repositorry.ChatRepository import com.idle.domain.repositorry.ConfigRepository import com.idle.domain.repositorry.JobPostingRepository import com.idle.domain.repositorry.NotificationRepository @@ -22,6 +21,7 @@ import com.idle.navigation.DeepLinkDestination.CenterHome import com.idle.navigation.DeepLinkDestination.CenterPending import com.idle.navigation.DeepLinkDestination.CenterRegister import com.idle.navigation.DeepLinkDestination.WorkerHome +import com.idle.navigation.NavigationEvent import com.idle.navigation.NavigationHelper import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async @@ -40,7 +40,6 @@ class MainViewModel @Inject constructor( private val profileRepository: ProfileRepository, private val notificationRepository: NotificationRepository, private val jobPostingRepository: JobPostingRepository, - private val chatRepository: ChatRepository, private val errorHelper: ErrorHelper, internal val eventHelper: EventHelper, internal val navigationHelper: NavigationHelper, @@ -55,14 +54,6 @@ class MainViewModel @Inject constructor( handleError() } - internal fun connectWebSocket() = viewModelScope.launch { - chatRepository.connectWebSocket() - } - - internal fun disconnectWebSocket() = viewModelScope.launch { - chatRepository.connectWebSocket() - } - internal fun setNavigationMenuType(navigationMenuType: NavigationMenuType) { _navigationMenuType.value = navigationMenuType } @@ -107,7 +98,10 @@ class MainViewModel @Inject constructor( private suspend fun navigateToDestination(userRole: String) { when (userRole) { UserType.WORKER.apiValue -> navigationHelper.navigateTo( - com.idle.navigation.NavigationEvent.To(WorkerHome, R.id.authFragment) + NavigationEvent.To( + WorkerHome, + R.id.authFragment + ) ) UserType.CENTER.apiValue -> getCenterStatus() @@ -124,7 +118,7 @@ class MainViewModel @Inject constructor( when (status) { CenterManagerAccountStatus.APPROVED -> handleApprovedCenterStatus() else -> navigationHelper.navigateTo( - com.idle.navigation.NavigationEvent.To( + NavigationEvent.To( CenterPending(status.name), R.id.authFragment ) @@ -134,14 +128,12 @@ class MainViewModel @Inject constructor( private fun handleApprovedCenterStatus() = viewModelScope.launch { profileRepository.getMyCenterProfile().onSuccess { - navigationHelper.navigateTo( - com.idle.navigation.NavigationEvent.To(CenterHome, R.id.authFragment) - ) + navigationHelper.navigateTo(NavigationEvent.To(CenterHome, R.id.authFragment)) }.onFailure { val error = it as HttpResponseException if (error.apiErrorCode == ApiErrorCode.CenterNotFound) { navigationHelper.navigateTo( - com.idle.navigation.NavigationEvent.To( + NavigationEvent.To( CenterRegister, R.id.authFragment ) @@ -159,12 +151,13 @@ class MainViewModel @Inject constructor( ApiErrorCode.TokenNotValid, ApiErrorCode.TokenExpiredException, ApiErrorCode.TokenNotFound, - ApiErrorCode.NotSupportUserTokenType -> + ApiErrorCode.NotSupportUserTokenType -> { navigationHelper.navigateTo( - com.idle.navigation.NavigationEvent.ToAuthWithClearBackStack( + NavigationEvent.ToAuthWithClearBackStack( exception.print() ) ) + } else -> eventHelper.sendEvent(ShowToast(exception.print())) }