diff --git a/app/instructor/src/main/java/com/ssing/instructor/InstructorMainNavHost.kt b/app/instructor/src/main/java/com/ssing/instructor/InstructorMainNavHost.kt index 6867a4dd..90908c4e 100644 --- a/app/instructor/src/main/java/com/ssing/instructor/InstructorMainNavHost.kt +++ b/app/instructor/src/main/java/com/ssing/instructor/InstructorMainNavHost.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController +import androidx.navigation.navOptions import com.ssing.core.ui.extension.clearBackStackNavOptions import com.ssing.core.ui.navigation.Route import com.ssing.core.ui.navigation.SsingNavHost @@ -55,17 +56,20 @@ internal fun InstructorMainNavHost( paddingValues = paddingValues, navigateToMatching = { navController.navigate( - route = InstructorMatching(), + route = InstructorMatching(startFresh = true), + navOptions = navController.backToHomeNavOptions(), ) }, navigateToLessonDetail = { lessonId -> navController.navigate( route = InstructorLesson(lessonId = lessonId ?: 0), + navOptions = navController.backToHomeNavOptions(), ) }, - navigateToMatchingWaiting = { + navigateToMatchingWaiting = { offerId -> navController.navigate( - route = InstructorMatching + route = InstructorMatching(offerId = offerId), + navOptions = navController.backToHomeNavOptions(), ) }, navigateToNotification = { @@ -82,6 +86,7 @@ internal fun InstructorMainNavHost( navigateToLessonDetail = { lessonId -> navController.navigate( route = InstructorLesson(lessonId = lessonId), + navOptions = navController.backToHomeNavOptions(), ) }, ) @@ -89,7 +94,10 @@ internal fun InstructorMainNavHost( paddingValues = paddingValues, navController = navController, navigateToMatching = { - navController.navigate(route = InstructorMatching()) + navController.navigate( + route = InstructorMatching(), + navOptions = navController.backToHomeNavOptions(), + ) }, navigateToHome = { navController.navigate( @@ -109,3 +117,7 @@ internal fun InstructorMainNavHost( ) } } +private fun NavHostController.backToHomeNavOptions() = navOptions { + popUpTo(InstructorHome) { inclusive = false } + launchSingleTop = true +} diff --git a/data/src/main/java/com/ssing/data/lesson/consumer/remote/dto/response/ConsumerLessonDetailResponse.kt b/data/src/main/java/com/ssing/data/lesson/consumer/remote/dto/response/ConsumerLessonDetailResponse.kt index c3313b86..c7282185 100644 --- a/data/src/main/java/com/ssing/data/lesson/consumer/remote/dto/response/ConsumerLessonDetailResponse.kt +++ b/data/src/main/java/com/ssing/data/lesson/consumer/remote/dto/response/ConsumerLessonDetailResponse.kt @@ -69,7 +69,7 @@ data class InstructorProfileResponse( @SerialName("gender") val gender: String, @SerialName("birthYear") val birthYear: Int, @SerialName("level") val level: Int, - @SerialName("profileImageUrl") val profileImageUrl: String? = null, + @SerialName("profileImageUrl") val profileImageUrl: String, ) @Serializable @@ -156,4 +156,4 @@ data class ConsumerMatchingRequestResponse( @SerialName("representativeMemberName") val representativeMemberName: String, @SerialName("headcount") val headcount: Int, @SerialName("participants") val participants: List, -) \ No newline at end of file +) diff --git a/data/src/main/java/com/ssing/data/lesson/consumer/repository/impl/ConsumerLessonRepositoryImpl.kt b/data/src/main/java/com/ssing/data/lesson/consumer/repository/impl/ConsumerLessonRepositoryImpl.kt index 1229f55e..b0c26b1f 100644 --- a/data/src/main/java/com/ssing/data/lesson/consumer/repository/impl/ConsumerLessonRepositoryImpl.kt +++ b/data/src/main/java/com/ssing/data/lesson/consumer/repository/impl/ConsumerLessonRepositoryImpl.kt @@ -144,7 +144,7 @@ internal class ConsumerLessonRepositoryImpl @Inject constructor( gender = gender, birthYear = birthYear, level = level, - profileImageUrl = profileImageUrl.orEmpty(), + profileImageUrl = profileImageUrl, ) private fun ParticipantResponse.toModel() = LessonParticipant(participantId, gender, age) @@ -176,4 +176,4 @@ internal class ConsumerLessonRepositoryImpl @Inject constructor( ) } -} \ No newline at end of file +} diff --git a/data/src/main/java/com/ssing/data/matching/instructormatching/model/InstructorMatchingOffer.kt b/data/src/main/java/com/ssing/data/matching/instructormatching/model/InstructorMatchingOffer.kt index bb108af3..08c3cb5e 100644 --- a/data/src/main/java/com/ssing/data/matching/instructormatching/model/InstructorMatchingOffer.kt +++ b/data/src/main/java/com/ssing/data/matching/instructormatching/model/InstructorMatchingOffer.kt @@ -16,7 +16,5 @@ data class InstructorMatchingLessonSummary( ) data class InstructorMatchingPriceSummary( - val lessonPriceAmount: Int, - val resortPassFeeAmount: Int, - val totalPaymentAmount: Int, + val instructorSettlementAmount: Int, ) diff --git a/data/src/main/java/com/ssing/data/matching/instructormatching/remote/dto/response/InstructorMatchingOffersResponse.kt b/data/src/main/java/com/ssing/data/matching/instructormatching/remote/dto/response/InstructorMatchingOffersResponse.kt index f286cb07..68b645b4 100644 --- a/data/src/main/java/com/ssing/data/matching/instructormatching/remote/dto/response/InstructorMatchingOffersResponse.kt +++ b/data/src/main/java/com/ssing/data/matching/instructormatching/remote/dto/response/InstructorMatchingOffersResponse.kt @@ -39,7 +39,5 @@ internal data class InstructorMatchingLessonSummaryResponse( @Serializable internal data class InstructorMatchingPriceSummaryResponse( - @SerialName("lessonPriceAmount") val lessonPriceAmount: Int, - @SerialName("resortPassFeeAmount") val resortPassFeeAmount: Int, - @SerialName("totalPaymentAmount") val totalPaymentAmount: Int, + @SerialName("instructorSettlementAmount") val instructorSettlementAmount: Int? = null, ) diff --git a/data/src/main/java/com/ssing/data/matching/instructormatching/repository/impl/InstructorMatchingRepositoryImpl.kt b/data/src/main/java/com/ssing/data/matching/instructormatching/repository/impl/InstructorMatchingRepositoryImpl.kt index 4e96e9aa..5932d9f9 100644 --- a/data/src/main/java/com/ssing/data/matching/instructormatching/repository/impl/InstructorMatchingRepositoryImpl.kt +++ b/data/src/main/java/com/ssing/data/matching/instructormatching/repository/impl/InstructorMatchingRepositoryImpl.kt @@ -237,9 +237,7 @@ internal class InstructorMatchingRepositoryImpl @Inject constructor( }, priceSummary = requireNotNull(priceSummary) { "AVAILABLE 응답에 priceSummary 누락" }.let { InstructorMatchingPriceSummary( - lessonPriceAmount = it.lessonPriceAmount, - resortPassFeeAmount = it.resortPassFeeAmount, - totalPaymentAmount = it.totalPaymentAmount, + instructorSettlementAmount = it.instructorSettlementAmount ?: 0, ) }, participants = participants.map { diff --git a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeContract.kt b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeContract.kt index 71c054a5..2527a1fb 100644 --- a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeContract.kt +++ b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeContract.kt @@ -19,6 +19,7 @@ internal interface InstructorHomeContract { val averageRating: Float = 0f, val grade: Grade = Grade.GRADE1, val achievementRate: Int = 0, + val hasActiveLesson: Boolean = false, ) sealed interface Effect { @@ -29,7 +30,7 @@ internal interface InstructorHomeContract { val offerId: Long? = null, ) : Effect - data object NavigateToMatchingWaiting : Effect + data class NavigateToMatchingWaiting(val offerId: Long? = null) : Effect } } diff --git a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeScreen.kt b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeScreen.kt index 64211d69..3832e85c 100644 --- a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeScreen.kt +++ b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeScreen.kt @@ -24,6 +24,8 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ssing.core.ui.R import com.ssing.core.ui.common.component.Empty @@ -48,16 +50,23 @@ internal fun InstructorHomeRoute( navigateToLessonDetail: (Long?) -> Unit, navigateToMatching: () -> Unit, navigateToNotification: () -> Unit, + navigateToMatchingWaiting: (Long?) -> Unit, modifier: Modifier = Modifier, viewModel: InstructorHomeViewModel = hiltViewModel(), - navigateToMatchingWaiting: () -> Unit, ) { val state by viewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current + LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { + viewModel.loadHome() + } + HandleUiEffects(viewModel.uiEffect) { effect -> when (effect) { - is InstructorHomeContract.Effect.NavigateToMatchingWaiting -> navigateToMatchingWaiting + is InstructorHomeContract.Effect.NavigateToMatchingWaiting -> navigateToMatchingWaiting( + effect.offerId + ) + is InstructorHomeContract.Effect.NavigateToLessonDetail -> navigateToLessonDetail(effect.lessonId) is InstructorHomeContract.Effect.NavigateToMatching -> navigateToMatching() is InstructorHomeContract.Effect.ShowToast -> context.toast(effect.message) diff --git a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeViewModel.kt b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeViewModel.kt index 118cdea4..eef5137f 100644 --- a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeViewModel.kt +++ b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/InstructorHomeViewModel.kt @@ -29,10 +29,6 @@ internal class InstructorHomeViewModel @Inject constructor( InstructorHomeContract.State() ) { - init { - loadHome() - } - fun loadHome() { viewModelScope.launch { updateState { @@ -47,6 +43,9 @@ internal class InstructorHomeViewModel @Inject constructor( copy( isLoading = false, lessonCards = result.lessonCards.toUiState(), + hasActiveLesson = result.lessonCards.any { + it.displayStatus == CONFIRMED || it.displayStatus == IN_PROGRESS + }, hasUnreadNotification = result.hasUnreadNotification, instructorName = result.instructorName, matchingPeopleCount = result.matchingPeopleCount, @@ -111,10 +110,9 @@ internal class InstructorHomeViewModel @Inject constructor( WAITING_FOR_INSTRUCTOR, WAITING_FOR_CONFIRMATION, - PAYMENT_PENDING, - IN_PROGRESS -> Status.Matched - - CONFIRMED -> Status.Default + PAYMENT_PENDING -> Status.Matched + CONFIRMED, + IN_PROGRESS -> Status.Default else -> Status.Default } @@ -126,58 +124,33 @@ internal class InstructorHomeViewModel @Inject constructor( const val PAYMENT_PENDING = "PAYMENT_PENDING" const val CONFIRMED = "CONFIRMED" const val IN_PROGRESS = "IN_PROGRESS" + + const val MSG_ACTIVE_LESSON_BLOCK = + "진행 예정이거나 진행 중인 강습이 있어 즉시매칭을 시작할 수 없어요." } fun onMatchingClick() { + // 확정/진행 중인 강습이 있으면 즉시매칭 시작을 막는다. + if (uiState.value.hasActiveLesson) { + sendEffect(InstructorHomeContract.Effect.ShowToast(MSG_ACTIVE_LESSON_BLOCK)) + return + } sendEffect(InstructorHomeContract.Effect.NavigateToMatching) } - fun onLessonClick( - lesson: Reservation, - ) { - Timber.i("lessonId: ${lesson.lessonId} / offerId: $lesson.offerId") - - when (lesson.status) { - Status.Matching -> { - sendEffect( - InstructorHomeContract.Effect.NavigateToMatching - ) - } - - Status.Matched -> { - val lessonId = lesson.lessonId - - if (lessonId == null) { - sendEffect(InstructorHomeContract.Effect.NavigateToMatching) - return - } - - sendEffect( - InstructorHomeContract.Effect.NavigateToLessonDetail( - lessonId = lessonId, - ) - ) - } - - Status.Default -> { - val lessonId = lesson.lessonId - val offerId = lesson.offerId - - if (lessonId == null || offerId == null) { - Timber.e( - "강습 상세 이동에 필요한 lessonId나 offerId가 없습니다. " + - "status=${lesson.status}, offerId=${lesson.offerId}" - ) - return - } - - sendEffect( - InstructorHomeContract.Effect.NavigateToLessonDetail( - lessonId = lessonId, - offerId = offerId, - ) - ) - } + fun onLessonClick(lesson: Reservation) { + Timber.i("lessonId: ${lesson.lessonId} / offerId: ${lesson.offerId}") + + when { + lesson.lessonId != null -> sendEffect( + InstructorHomeContract.Effect.NavigateToLessonDetail(lessonId = lesson.lessonId) + ) + lesson.offerId != null -> sendEffect( + InstructorHomeContract.Effect.NavigateToMatchingWaiting(offerId = lesson.offerId) + ) + else -> sendEffect( + InstructorHomeContract.Effect.NavigateToMatchingWaiting(offerId = null) + ) } } diff --git a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/navigation/InstructorHomeNavigation.kt b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/navigation/InstructorHomeNavigation.kt index 85f67d79..59744ed6 100644 --- a/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/navigation/InstructorHomeNavigation.kt +++ b/presentation/instructor-home/src/main/java/com/ssing/presentation/instructorhome/navigation/InstructorHomeNavigation.kt @@ -15,7 +15,7 @@ fun NavGraphBuilder.instructorHomeNavGraph( navigateToMatching: () -> Unit, navigateToNotification: () -> Unit, navigateToLessonDetail: (Long?) -> Unit, - navigateToMatchingWaiting: () -> Unit, + navigateToMatchingWaiting: (Long?) -> Unit, ) { composable { InstructorHomeRoute( diff --git a/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/MatchingViewModel.kt b/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/MatchingViewModel.kt index 3af5ae54..94a20f21 100644 --- a/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/MatchingViewModel.kt +++ b/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/MatchingViewModel.kt @@ -43,19 +43,17 @@ internal class MatchingViewModel @Inject constructor( BaseViewModel( MatchingContract.State() ) { + private var offerId: Long? = null init { loadMatchingExposure() - // FCM '새 강습 도착' 딥링크로 진입하면 offerId가 담겨온다. - // offerId가 있으면 그 제안 상세를, 없으면(거절/일반 진입) 활성 제안을 조회한다. - val offerId = savedStateHandle.toRoute().offerId - if (offerId != null) { - restoreOfferDetail(offerId) - } else { - restoreActiveOffer() + val route = savedStateHandle.toRoute() + offerId = route.offerId + when { + offerId != null -> restoreOfferDetail(requireNotNull(offerId)) + route.startFresh -> Unit + else -> restoreActiveOffer() } - instructorMatchingRepository.connectSocket() - instructorMatchingRepository.event .onEach { handleMatchingEvent(it) } .launchIn(viewModelScope) @@ -63,6 +61,8 @@ internal class MatchingViewModel @Inject constructor( instructorMatchingRepository.socketState .onEach { handleSocketState(it) } .launchIn(viewModelScope) + + instructorMatchingRepository.connectSocket() } private fun handleMatchingEvent(event: InstructorMatchingEvent) { @@ -76,7 +76,7 @@ internal class MatchingViewModel @Inject constructor( restoreOfferDetail(event.offerId) } } - is InstructorMatchingEvent.OfferClosedEvent -> restoreOfferDetail(event.offerId) + is InstructorMatchingEvent.OfferClosedEvent -> restoreActiveOffer() is InstructorMatchingEvent.MatchingCanceledEvent -> { updateState { copy(phase = MatchingPhase.Waiting) } @@ -97,13 +97,21 @@ internal class MatchingViewModel @Inject constructor( sendEffect(MatchingContract.Effect.ShowToast("연결에 문제가 발생했어요.")) } } - SocketState.Connected -> socketErrorToastShown = false + SocketState.Connected -> { + socketErrorToastShown = false + resyncActiveOffer() + } SocketState.Connecting, SocketState.Disconnected -> Unit } } private fun onMatchingConfirmed(lessonId: Long) = viewModelScope.launch { - Timber.d("매칭 확정 수신 → 강습 상세 이동 (lessonId=$lessonId)") + Timber.d("매칭 확정 수신 → 노출 중단 후 강습 상세 이동 (lessonId=$lessonId)") + // TODO(서버 보강 필요): 이 처리는 '확정 순간 매칭 화면(소켓 연결)에 있을 때'만 동작한다. + // 강사가 홈 등으로 이동해 소켓이 끊긴 뒤 확정되면(FCM만 수신) 노출이 안 꺼질 수 있으므로, + // 완전한 신규 매칭 차단은 서버가 '확정 강습 보유 강사 노출 제외'로 처리해야 한다. + instructorMatchingRepository.cancelMatchingExposure() + .onFailure { Timber.e(it, "확정 후 노출 중단 실패") } try { instructorMatchingRepository.disconnectSocket() } finally { @@ -181,6 +189,8 @@ internal class MatchingViewModel @Inject constructor( exposure = exposure.copy(isSubmitting = false), ) } + // 노출 시작 직후 서버가 즉시 만든 제안이 있으면 바로 반영 (선대기 레이스 복구) + restoreActiveOffer() } .onFailure { Timber.e(it, "matching-exposure 저장 실패") @@ -192,8 +202,12 @@ internal class MatchingViewModel @Inject constructor( } } - fun editExposure() = updateState { - copy(phase = MatchingPhase.SettingExposure) + fun editExposure() { + updateState { copy(phase = MatchingPhase.SettingExposure) } + viewModelScope.launch { + instructorMatchingRepository.cancelMatchingExposure() + .onFailure { Timber.e(it, "조건 수정 진입 - 노출 중단 실패") } + } } fun stopWaiting() = updateState { @@ -205,7 +219,9 @@ internal class MatchingViewModel @Inject constructor( instructorMatchingRepository.cancelMatchingExposure() .onSuccess { isExposed -> Timber.d("즉시노출 중단 응답 isExposed=$isExposed") - updateState { copy(dialog = null, phase = MatchingPhase.SettingExposure) } + // 대기 중지(종료) 시 조건 입력 화면이 아니라 홈으로 나간다. + updateState { copy(dialog = null) } + sendEffect(MatchingContract.Effect.NavigateBack) } .onFailure { Timber.e(it, "즉시노출 중단 실패") @@ -247,7 +263,11 @@ internal class MatchingViewModel @Inject constructor( instructorMatchingRepository.respondMatchingOffer(offer.offerId, DECISION_REJECTED) .onSuccess { result -> Timber.d("매칭 제안 거절 응답: $result") + // 거절한 offer는 무효화하고, 서버가 새로 매칭한 offer(새 offerId)를 + // active 재조회로 받아온다. (없으면 대기 화면 유지) + offerId = null updateState { copy(phase = MatchingPhase.Waiting) } + restoreActiveOffer() } .onFailure { Timber.e(it, "매칭 제안 거절 실패") @@ -262,6 +282,19 @@ internal class MatchingViewModel @Inject constructor( private var restoreJob: Job? = null + /** + * 소켓 연결/재연결·화면 재진입 시 현재 활성 제안을 REST로 재동기화한다. + * + * '조건에 맞는 요청을 찾는 중'(Waiting) 상태에서만 재조회한다. + * 이미 제안 도착/확정 대기(OfferArrived·PendingConfirm)이거나 조건 설정(SettingExposure) 중이면, + * active 재조회가 그 상태를 대기 화면으로 덮어쓰면 안 되므로 건너뛴다. + * (확정 전 홈 이동 → 카드로 재진입 시 대기화면이 잘못 뜨는 문제 방지) + */ + fun resyncActiveOffer() { + if (uiState.value.phase !is MatchingPhase.Waiting) return + restoreActiveOffer() + } + fun restoreActiveOffer() { restoreJob?.cancel() restoreJob = viewModelScope.launch { @@ -270,16 +303,19 @@ internal class MatchingViewModel @Inject constructor( Timber.d("matching-offers 응답: $active") // 저장된 조건은 항상 복원한다(대기 화면 조건 카드/조건 수정용). updateState { copy(exposure = exposure.applyMatchingSetting(active.setting)) } - val offerId = active.offerId + val activeOfferId = active.offerId + offerId = activeOfferId when { - offerId != null -> restoreOfferDetail(offerId) + activeOfferId != null -> restoreOfferDetail(activeOfferId) active.setting.isExposed -> updateState { copy(phase = MatchingPhase.Waiting) } else -> updateState { copy(phase = MatchingPhase.SettingExposure) } } } .onFailure { Timber.e(it, "matching-offers 실패") - if (it is ApiException) { + if (it is ApiException.Conflict) { + updateState { copy(phase = MatchingPhase.SettingExposure) } + } else if (it is ApiException) { sendEffect(MatchingContract.Effect.ShowToast(it.uiMessage)) } } @@ -287,6 +323,7 @@ internal class MatchingViewModel @Inject constructor( } fun restoreOfferDetail(offerId: Long) { + this.offerId = offerId restoreJob?.cancel() restoreJob = viewModelScope.launch { instructorMatchingRepository.fetchOfferDetail(offerId) @@ -302,7 +339,10 @@ internal class MatchingViewModel @Inject constructor( } .onFailure { Timber.e(it, "matching-offer 상세 실패") - if (it is ApiException) { + if (it is ApiException.Conflict) { + // 409 MATCHING_NOT_ACTIVE: 협상 종료 → 홈 재조회 후 확정 강습이면 이동, 아니면 대기 + navigateToLessonIfConfirmed(offerId) + } else if (it is ApiException) { sendEffect(MatchingContract.Effect.ShowToast(it.uiMessage)) } } @@ -370,7 +410,7 @@ internal class MatchingViewModel @Inject constructor( expiresAtMillis = null, nickname = requestSummary.requesterName, teamCount = requestSummary.headcount, - price = priceSummary.totalPaymentAmount, + price = priceSummary.instructorSettlementAmount, participants = participants.map { ParticipantUiModel(age = it.age, isMale = it.gender == GENDER_MALE) }, diff --git a/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/navigation/InstructorMatchingNavigation.kt b/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/navigation/InstructorMatchingNavigation.kt index ea38e6d9..98d73d49 100644 --- a/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/navigation/InstructorMatchingNavigation.kt +++ b/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/navigation/InstructorMatchingNavigation.kt @@ -17,7 +17,7 @@ import kotlinx.serialization.Serializable * @param offerId FCM '새 강습 도착' 딥링크로 진입 시 특정 제안 id. 그 외(거절/일반 진입)엔 null. */ @Serializable -data class InstructorMatching(val offerId: Long? = null) : Route +data class InstructorMatching(val offerId: Long? = null, val startFresh: Boolean = false) : Route fun NavGraphBuilder.instructorMatchingNavGraph( paddingValues: PaddingValues, diff --git a/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/screen/MatchingScreen.kt b/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/screen/MatchingScreen.kt index bc2b8c61..f612f570 100644 --- a/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/screen/MatchingScreen.kt +++ b/presentation/instructor-matching/src/main/java/com/ssing/presentation/instructormatching/screen/MatchingScreen.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.ssing.core.ui.common.component.SsingModal import com.ssing.core.ui.designsystem.theme.SSINGTheme @@ -38,6 +40,10 @@ internal fun MatchingRoute( val state by viewModel.uiState.collectAsStateWithLifecycle() val context = LocalContext.current + LifecycleEventEffect(Lifecycle.Event.ON_RESUME) { + viewModel.resyncActiveOffer() + } + HandleUiEffects(viewModel.uiEffect) { effect -> when (effect) { is MatchingContract.Effect.ShowToast -> context.toast(effect.message)