From 676b5b26d7ca5e4201203d2aedb0687b4be735e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A2=85=EC=88=98?= Date: Fri, 22 May 2026 15:16:02 +0900 Subject: [PATCH] =?UTF-8?q?[bugfix]=20=EC=B6=95=EC=A0=9C=20=EC=A0=9C?= =?UTF-8?q?=ED=9C=B4=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=8A=94=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EC=97=90=EC=84=9C=EB=8F=84=20=EC=82=AC=EB=9D=BC?= =?UTF-8?q?=EC=A7=80=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EB=8F=84=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/map/MapFragmentView.kt | 3 +- .../android/presentation/map/MapViewModel.kt | 27 +++++--- .../map/component/PartnershipToggleItem.kt | 5 +- .../map/MapViewModelBehaviorSpec.kt | 67 ++++++++++++++++++- 4 files changed, 87 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt index 20120d32..b7f5d484 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt @@ -466,7 +466,8 @@ internal fun MapScreen( onSelectedFilterChange(next) }, modifier = Modifier.padding(top = 12.dp), - departmentName = departmentName.toString() + departmentName = departmentName.toString(), + filters = mapState.availableFilters, ) } } diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt index cc091d48..3a2ff1ff 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapViewModel.kt @@ -40,6 +40,7 @@ data class MapState( val restaurantInfoList: List = emptyList(), val storeType: StoreType? = null, val selectedFilter: FilterType = FilterType.Mine, + val availableFilters: List = FilterType.entries, val filterChangeResult: FilterChangeResult? = null, ) { sealed class FilterChangeResult { @@ -83,14 +84,24 @@ class MapViewModel @Inject constructor( _departmentId.value = newDepartmentId _collegeId.value = newCollegeId + val hasFestival = allPartnerships.hasFestivalPartnership() + val availableFilters = if (hasFestival) { + FilterType.entries + } else { + listOf(FilterType.All, FilterType.Mine) + } + // Festival 제휴가 하나라도 있으면 Festival을 우선하고, 없으면 기존 기본 필터 규칙을 따른다. val initialFilter = when { - allPartnerships.hasFestivalPartnership() -> FilterType.Festival + hasFestival -> FilterType.Festival newDepartmentId == -1L -> FilterType.All else -> FilterType.Mine } _uiState.value = UiState.Success( - MapState(selectedFilter = initialFilter), + MapState( + selectedFilter = initialFilter, + availableFilters = availableFilters, + ), ) when (initialFilter) { @@ -181,15 +192,9 @@ class MapViewModel @Inject constructor( if (prefetchedPartnerships == null) _uiState.value = UiState.Loading - val partnerships = partnershipRepository.getAllPartnerships().mapNotNull { - val festivalInfos = - it.partnershipInfos.filter { info -> info.periodType == PeriodType.FESTIVAL } - if (festivalInfos.isEmpty()) return@mapNotNull null - - it.copy( - partnershipInfos = festivalInfos - ) - } + val partnerships = + (prefetchedPartnerships ?: partnershipRepository.getAllPartnerships()) + .festivalPartnerships() _uiState.value = UiState.Success( currentData.copy( diff --git a/app/src/main/java/com/eatssu/android/presentation/map/component/PartnershipToggleItem.kt b/app/src/main/java/com/eatssu/android/presentation/map/component/PartnershipToggleItem.kt index a92e7afd..d9bde5e6 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/component/PartnershipToggleItem.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/component/PartnershipToggleItem.kt @@ -54,10 +54,11 @@ fun PartnershipFilterToggle( selected: FilterType, onSelectedChange: (FilterType) -> Unit, departmentName: String, + filters: List = FilterType.entries, modifier: Modifier = Modifier, ) { Timber.d("departmentName = $departmentName") - val items = FilterType.entries.map { + val items = filters.map { it to it.getLabel(departmentName) } Row( @@ -112,4 +113,4 @@ fun PartnershipToggleItem( style = EatssuTheme.typography.body2 ) } -} \ No newline at end of file +} diff --git a/app/src/test/java/com/eatssu/android/presentation/map/MapViewModelBehaviorSpec.kt b/app/src/test/java/com/eatssu/android/presentation/map/MapViewModelBehaviorSpec.kt index ef922e9c..2e812017 100644 --- a/app/src/test/java/com/eatssu/android/presentation/map/MapViewModelBehaviorSpec.kt +++ b/app/src/test/java/com/eatssu/android/presentation/map/MapViewModelBehaviorSpec.kt @@ -62,6 +62,7 @@ class MapViewModelBehaviorSpec : AppBehaviorSpec({ eventually(2.seconds) { val state = viewModel.uiState.value as UiState.Success state.data.selectedFilter shouldBe FilterType.All + state.data.availableFilters shouldBe listOf(FilterType.All, FilterType.Mine) state.data.partnerships shouldBe allPartnerships } coVerify(atLeast = 1) { partnershipRepository.getAllPartnerships() } @@ -69,6 +70,68 @@ class MapViewModelBehaviorSpec : AppBehaviorSpec({ } } + `when`("Festival 제휴가 있으면") { + val festivalInfo = Partnership.PartnershipInfo( + id = 1, + partnershipType = "DISCOUNT", + collegeName = "IT", + departmentName = "CS", + likeCount = 1, + isLiked = false, + description = "축제 할인", + startDate = "2025-05-01", + endDate = "2025-05-03", + periodType = PeriodType.FESTIVAL, + ) + val normalInfo = Partnership.PartnershipInfo( + id = 2, + partnershipType = "DISCOUNT", + collegeName = "IT", + departmentName = "CS", + likeCount = 1, + isLiked = false, + description = "상시 할인", + startDate = "2025-01-01", + endDate = "2025-12-31", + periodType = PeriodType.NORMAL, + ) + val allPartnerships = listOf( + samplePartnership( + storeName = "Festival Cafe", + infos = listOf(festivalInfo, normalInfo), + ) + ) + coEvery { + getUserCollegeDepartmentUseCase() + } returns sampleUserInfo( + nickname = "eatssu", + college = College(collegeId = 1, collegeName = "IT"), + department = Department(departmentId = 11, departmentName = "컴퓨터학부"), + ) + coEvery { partnershipRepository.getAllPartnerships() } returns allPartnerships + coEvery { partnershipRepository.getUserCollegePartnerships() } returns emptyList() + + val viewModel = MapViewModel( + partnershipRepository = partnershipRepository, + getPartnershipDetailUseCase = getPartnershipDetailUseCase, + getUserCollegeDepartmentUseCase = getUserCollegeDepartmentUseCase, + analyticsTracker = analyticsTracker, + ) + + then("Festival 필터로 시작하고 Festival 필터 버튼을 표시한다") { + runTest { + eventually(2.seconds) { + val state = viewModel.uiState.value as UiState.Success + state.data.selectedFilter shouldBe FilterType.Festival + state.data.availableFilters shouldBe FilterType.entries.toList() + state.data.partnerships.first().partnershipInfos shouldBe listOf( + festivalInfo + ) + } + } + } + } + `when`("학과 정보가 없는 사용자가 Mine 필터를 선택하면") { coEvery { getUserCollegeDepartmentUseCase() @@ -90,7 +153,9 @@ class MapViewModelBehaviorSpec : AppBehaviorSpec({ then("RequiresDepartment 결과를 상태에 반영하고 Mine 데이터를 로드하지 않는다") { runTest { eventually(2.seconds) { - viewModel.uiState.value shouldBe UiState.Success(MapState(selectedFilter = FilterType.All)) + val state = viewModel.uiState.value as UiState.Success + state.data.selectedFilter shouldBe FilterType.All + state.data.availableFilters shouldBe listOf(FilterType.All, FilterType.Mine) } clearMocks(partnershipRepository, answers = false, recordedCalls = true)