diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b5005ed02..1337bab20 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,8 +30,8 @@ android { applicationId = "com.eatssu.android" minSdk = 28 targetSdk = 35 - versionCode = 45 - versionName = "3.1.7" + versionCode = 46 + versionName = "3.1.8" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -122,7 +122,6 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) - implementation(libs.material) implementation(libs.androidx.constraintlayout) implementation(libs.threetenabp) implementation(libs.material.calendarview) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c84617612..a14e469ad 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -27,10 +27,7 @@ # https://firebase.google.com/docs/database/android/start?hl=ko -keepattributes Signature --keep class com.eatssu.android.data.dto.** { - *; -} --keep class com.eatssu.android.data.enums.** { +-keep class com.eatssu.android.data.remote.dto.** { *; } -keep class com.eatssu.android.data.model.** { diff --git a/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt b/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt index b546c9949..2aad4884f 100644 --- a/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt +++ b/app/src/main/java/com/eatssu/android/data/remote/repository/FirebaseRemoteConfigRepositoryImpl.kt @@ -7,7 +7,6 @@ import com.eatssu.common.enums.Restaurant import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings import com.google.gson.Gson -import com.google.gson.reflect.TypeToken import kotlinx.coroutines.tasks.await import timber.log.Timber import javax.inject.Inject @@ -60,8 +59,7 @@ class FirebaseRemoteConfigRepositoryImpl @Inject constructor( private fun parseCafeteriaJson(json: String): List { return try { val gson = Gson() - val listType = object : TypeToken>() {}.type - val dtoList: List = gson.fromJson(json, listType) + val dtoList = gson.fromJson(json, Array::class.java) ?: emptyArray() dtoList.map { dto -> RestaurantInfo( diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapFragment.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapFragment.kt index 2d4edcd2b..a87d4c8e3 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapFragment.kt @@ -1,11 +1,11 @@ package com.eatssu.android.presentation.map import android.os.Bundle -import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView +import androidx.fragment.app.Fragment import com.eatssu.design_system.theme.EatssuTheme import dagger.hilt.android.AndroidEntryPoint @@ -25,7 +25,7 @@ class MapFragment : Fragment() { return ComposeView(requireContext()).apply { setContent { EatssuTheme { - MapFragmentComposeView() + MapRoute() } } } 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 c9bf86c38..0d0dbf053 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 @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalNaverMapApi::class) + package com.eatssu.android.presentation.map import android.Manifest @@ -17,6 +19,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold +import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberModalBottomSheetState @@ -24,10 +27,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -49,7 +50,6 @@ import com.eatssu.android.presentation.map.component.DepartmentBottomSheet import com.eatssu.android.presentation.map.component.FilterType import com.eatssu.android.presentation.map.component.MapRestaurantBottomSheet import com.eatssu.android.presentation.map.component.PartnershipFilterToggle -import com.eatssu.android.presentation.map.model.PlaceType import com.eatssu.android.presentation.mypage.userinfo.UserInfoActivity import com.eatssu.android.presentation.util.TrackScreenViewEvent import com.eatssu.common.EventLogger @@ -61,6 +61,7 @@ import com.eatssu.design_system.theme.EatssuTheme import com.naver.maps.geometry.LatLng import com.naver.maps.map.CameraPosition import com.naver.maps.map.compose.Align +import com.naver.maps.map.compose.CameraPositionState import com.naver.maps.map.compose.ExperimentalNaverMapApi import com.naver.maps.map.compose.LocationTrackingMode import com.naver.maps.map.compose.MapProperties @@ -80,9 +81,8 @@ private const val DEFAULT_LONGITUDE = 126.95661313346206 private const val DEFAULT_ZOOM = 17.5 private const val PERMISSION_REQUEST_CODE = 1001 -@OptIn(ExperimentalNaverMapApi::class, ExperimentalMaterial3Api::class) @Composable -fun MapFragmentComposeView( +fun MapRoute( viewModel: MapViewModel = viewModel(), mainViewModel: MainViewModel = viewModel() ) { @@ -95,15 +95,15 @@ fun MapFragmentComposeView( } val mainUiState by mainViewModel.uiState.collectAsStateWithLifecycle() - val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val departmentSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val partnershipSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val context = LocalContext.current val activity = remember(context) { context.findActivityOrNull() } ?: throw IllegalStateException("FusedLocationSource는 Activity에서만 사용할 수 있습니다.") val scope = rememberCoroutineScope() - var selectedFilter by remember { mutableStateOf(FilterType.All) } - val departmentId = viewModel.departmentId - val collegeId = viewModel.collegeId + val departmentId by viewModel.departmentId.collectAsStateWithLifecycle() + val collegeId by viewModel.collegeId.collectAsStateWithLifecycle() val cameraPositionState = rememberCameraPositionState { position = CameraPosition( @@ -165,38 +165,30 @@ fun MapFragmentComposeView( // 상태 변화 감지해서 show/hide -> Scrim 잔존 문제 해결 LaunchedEffect(showUserDepartmentBottomSheet) { if (showUserDepartmentBottomSheet) { - sheetState.show() + departmentSheetState.show() } else { - sheetState.hide() + departmentSheetState.hide() } } - var hasLocationPermission by remember { - mutableStateOf( - ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED - ) + // 필터 변경 결과에 따라 학과 입력 BottomSheet 표시 + LaunchedEffect(mapState.filterChangeResult) { + if (mapState.filterChangeResult is MapState.FilterChangeResult.RequiresDepartment) { + departmentSheetState.show() + } } - // 제휴 정보 토글 event - LaunchedEffect(selectedFilter) { - when (selectedFilter) { - FilterType.All -> { - viewModel.loadPartnerships() - EventLogger.clickMap() - } - FilterType.Mine -> { - viewModel.loadUserCollegePartnerships() - - EventLogger.clickMapMine(collegeId, departmentId) - } + // 제휴 정보가 선택되면 BottomSheet 표시 + LaunchedEffect(mapState.restaurantPartnershipInfo) { + if (mapState.restaurantPartnershipInfo != null) { + partnershipSheetState.show() } + Timber.d("선택된 식당 제휴 정보: ${mapState.restaurantPartnershipInfo}") } - // Screen View 기록 TrackScreenViewEvent(ScreenId.MAP_MAIN) - val lifecycleOwner = LocalLifecycleOwner.current // onResume 시마다 학과 정보 반영 @@ -214,6 +206,56 @@ fun MapFragmentComposeView( } } + MapScreen( + mapState = mapState, + viewModel = viewModel, + cameraPositionState = cameraPositionState, + locationSource = locationSource, + departmentSheetState = departmentSheetState, + partnershipSheetState = partnershipSheetState, + showToast = { message -> + scope.launch { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() + } + }, + navigateToUserInfo = { + val intent = Intent(context, UserInfoActivity::class.java) + context.startActivity(intent) + }, + onHideDepartmentSheet = { + scope.launch { departmentSheetState.hide() } + }, + onHidePartnershipSheet = { + scope.launch { partnershipSheetState.hide() } + }, + onSelectedFilterChange = { filter -> + viewModel.setFilter(filter) + }, + departmentId = departmentId, + collegeId = collegeId, + departmentName = departmentName, + selectedFilter = mapState.selectedFilter, + ) +} + +@Composable +internal fun MapScreen( + mapState: MapState, + viewModel: MapViewModel, + cameraPositionState: CameraPositionState, + locationSource: FusedLocationSource, + departmentSheetState: SheetState, + partnershipSheetState: SheetState, + showToast: (String) -> Unit, + navigateToUserInfo: () -> Unit, + onHideDepartmentSheet: () -> Unit = {}, + onHidePartnershipSheet: () -> Unit = {}, + onSelectedFilterChange: (FilterType) -> Unit, + departmentId: Long, + collegeId: Long, + departmentName: String?, + selectedFilter: FilterType, +) { Scaffold( topBar = { CenterAlignedTopAppBar( @@ -232,40 +274,42 @@ fun MapFragmentComposeView( ) { innerPadding -> // 학과 정보가 없을 때 보여줄 BottomSheet - if (sheetState.isVisible) { + if (departmentSheetState.isVisible) { Timber.d("학과 정보가 없습니다. BottomSheet를 표시합니다.") DepartmentBottomSheet( - onDismiss = { viewModel.toggleDepartmentBottomSheet() }, + onDismiss = { + onHideDepartmentSheet() + }, onInputClick = { - viewModel.toggleDepartmentBottomSheet() - val intent = Intent(context, UserInfoActivity::class.java) - context.startActivity(intent) + onHideDepartmentSheet() + navigateToUserInfo() }, - sheetState = sheetState + sheetState = departmentSheetState ) } // 특정 식당에 대한 제휴 정보 BottomSheet - if (mapState.showPartnershipBottomSheet) { + if (partnershipSheetState.isVisible) { mapState.restaurantPartnershipInfo?.let { info -> - EventLogger.clickPartnerRestaurant( - college = collegeId, - major = departmentId, - partnerRestaurantId = info.id.toLong() - ) - MapRestaurantBottomSheet( - storeName = info.storeName, - placeType = when (info.restaurantType) { - RestaurantType.CAFE -> PlaceType.CAFE - RestaurantType.RESTAURANT -> PlaceType.RESTAURANT - RestaurantType.PUB -> PlaceType.PUB - else -> PlaceType.RESTAURANT - }, - mapRestaurantList = mapState.restaurantInfoList, - onDismiss = { viewModel.togglePartnershipBottomSheet() } - ) + mapState.placeType?.let { placeType -> + + EventLogger.clickPartnerRestaurant( + college = collegeId, + major = departmentId, + partnerRestaurantId = info.id.toLong() + ) + + MapRestaurantBottomSheet( + storeName = info.storeName, + placeType = placeType, + mapRestaurantList = mapState.restaurantInfoList, + onDismiss = { + onHidePartnershipSheet() + } + ) + } } } @@ -278,19 +322,23 @@ fun MapFragmentComposeView( NaverMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState, - uiSettings = MapUiSettings(isZoomControlEnabled = false, isLocationButtonEnabled = true), + uiSettings = MapUiSettings( + isZoomControlEnabled = false, + isLocationButtonEnabled = true + ), locationSource = locationSource, contentPadding = PaddingValues(bottom = dimensionResource(R.dimen.bottom_nav_height)), properties = MapProperties( locationTrackingMode = LocationTrackingMode.Follow, ), - onLocationChange = { location -> - // 위치가 업데이트되면 위치 권한 있다고 간주 - hasLocationPermission = true - }, ) { mapState.partnerships.forEach { partnership -> - val markerState = rememberMarkerState(position = LatLng(partnership.latitude, partnership.longitude)) + val markerState = rememberMarkerState( + position = LatLng( + partnership.latitude, + partnership.longitude + ) + ) Marker( icon = OverlayImage.fromResource( @@ -309,11 +357,11 @@ fun MapFragmentComposeView( captionTextSize = 10.sp, onClick = { if (partnership.partnershipInfos.isEmpty()) { - // 제휴 정보가 없을 때는 토스트만 띄우고 바텀시트는 안 띄움 - Toast.makeText(context, "제휴 정보가 없습니다.", Toast.LENGTH_SHORT).show() + showToast("제휴 정보가 없습니다.") true } else { // 제휴 정보가 있을 때만 바텀시트 띄움 + // LaunchedEffect에서 자동으로 표시됨 viewModel.selectPartnershipByStoreName(partnership.storeName) true } @@ -328,22 +376,8 @@ fun MapFragmentComposeView( PartnershipFilterToggle( selected = selectedFilter, onSelectedChange = { next -> - if (mapState.showPartnershipBottomSheet) return@PartnershipFilterToggle - - val hasDepartment = !departmentName.equals("학과") - - if (next == FilterType.Mine && !hasDepartment) { - // 전환 막기: selectedFilter는 그대로 (All 유지) - // 학과 입력 바텀시트 띄우기 - scope.launch { - // suspend 함수이므로 코루틴 내에서 실행 - sheetState.show() - } - return@PartnershipFilterToggle - } - - // 학과 정보가 있거나 All 선택은 정상 전환 - selectedFilter = next + if (partnershipSheetState.isVisible) return@PartnershipFilterToggle + onSelectedFilterChange(next) }, modifier = Modifier.padding(top = 12.dp), departmentName = departmentName.toString() @@ -382,6 +416,6 @@ fun Context.findActivityOrNull(): Activity? = when (this) { @Composable fun MapFragmentComposeViewPreview() { EatssuTheme { - MapFragmentComposeView() + MapRoute() } } 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 dfee98092..1ad6ede3b 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 @@ -4,10 +4,14 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.eatssu.android.domain.model.Partnership import com.eatssu.android.domain.model.PartnershipRestaurant +import com.eatssu.android.domain.model.RestaurantType import com.eatssu.android.domain.repository.PartnershipRepository import com.eatssu.android.domain.usecase.user.GetPartnershipDetailUseCase import com.eatssu.android.domain.usecase.user.GetUserCollegeDepartmentUseCase +import com.eatssu.android.presentation.map.component.FilterType +import com.eatssu.android.presentation.map.model.PlaceType import com.eatssu.android.presentation.map.model.RestaurantInfo +import com.eatssu.common.EventLogger import com.eatssu.common.UiEvent import com.eatssu.common.UiState import dagger.hilt.android.lifecycle.HiltViewModel @@ -21,15 +25,18 @@ import timber.log.Timber import javax.inject.Inject data class MapState( - val showDepartmentBottomSheet: Boolean = false, - val showPartnershipBottomSheet: Boolean = false, val partnerships: List = emptyList(), val restaurantPartnershipInfo: PartnershipRestaurant? = null, val restaurantInfoList: List = emptyList(), - var partnershipToggleText: String = "내 제휴", - val currentCollegeName: String = "", - val currentDepartmentName: String = "", -) + val placeType: PlaceType? = null, + val selectedFilter: FilterType = FilterType.Mine, + val filterChangeResult: FilterChangeResult? = null, +) { + sealed class FilterChangeResult { + object Success : FilterChangeResult() + object RequiresDepartment : FilterChangeResult() + } +} @HiltViewModel class MapViewModel @Inject constructor( @@ -44,42 +51,115 @@ class MapViewModel @Inject constructor( private val _uiEvent = MutableSharedFlow() val uiEvent: SharedFlow = _uiEvent - var departmentId: Long = -1 - private set - var collegeId: Long = -1 - private set + private val _departmentId = MutableStateFlow(-1) + val departmentId: StateFlow = _departmentId.asStateFlow() + + private val _collegeId = MutableStateFlow(-1) + val collegeId: StateFlow = _collegeId.asStateFlow() init { fetchUserCollegeDepartment() - loadPartnerships() } private fun fetchUserCollegeDepartment() { viewModelScope.launch { val userCollegeDepartment = getUserCollegeDepartmentUseCase() - departmentId = userCollegeDepartment.userDepartment.departmentId.toLong() - collegeId = userCollegeDepartment.userCollege.collegeId.toLong() + val newDepartmentId = userCollegeDepartment.userDepartment.departmentId.toLong() + val newCollegeId = userCollegeDepartment.userCollege.collegeId.toLong() + + _departmentId.value = newDepartmentId + _collegeId.value = newCollegeId + + // departmentId가 변경되면 필터 자동 설정 + val current = uiState.value + val currentData = if (current is UiState.Success) current.data else MapState() + val initialFilter = if (newDepartmentId == -1L) FilterType.All else FilterType.Mine + + _uiState.value = UiState.Success( + MapState(selectedFilter = initialFilter) + ) + + // 초기 필터에 따라 데이터 로드 + when (initialFilter) { + FilterType.All -> loadPartnerships() + FilterType.Mine -> loadUserCollegePartnerships() + } + Timber.d("학과 정보 : ${userCollegeDepartment.userDepartment.departmentName}") } } + // 필터 변경 (검증 로직 포함) + fun setFilter(filter: FilterType) { + val current = uiState.value + val currentData = if (current is UiState.Success) current.data else MapState() + + // 학과 정보가 없는데 Mine 필터를 선택하려는 경우 + if (filter == FilterType.Mine && _departmentId.value == -1L) { + // 학과 입력이 필요한 경우 결과를 MapState에 반영 + if (current is UiState.Success) { + _uiState.value = UiState.Success( + currentData.copy(filterChangeResult = MapState.FilterChangeResult.RequiresDepartment) + ) + } + return + } + + // 필터 변경 성공 + val updatedData = currentData.copy( + restaurantPartnershipInfo = null, + selectedFilter = filter, + filterChangeResult = null, + ) + _uiState.value = UiState.Success(updatedData) + + // 필터에 따라 데이터 로드 + when (filter) { + FilterType.All -> { + loadPartnerships() + EventLogger.clickMap() + } + + FilterType.Mine -> { + loadUserCollegePartnerships() + EventLogger.clickMapMine(_collegeId.value, _departmentId.value) + } + } + } + // 제휴 정보 로딩 - fun loadPartnerships() { + private fun loadPartnerships() { viewModelScope.launch { + val current = uiState.value + val currentData = if (current is UiState.Success) current.data else MapState() + _uiState.value = UiState.Loading val partnerships = partnershipRepository.getAllPartnerships() - _uiState.value = UiState.Success(MapState(partnerships = partnerships)) + _uiState.value = UiState.Success( + currentData.copy( + partnerships = partnerships, + filterChangeResult = null + ) + ) } } // 사용자 단과대 제휴 정보 로딩 - fun loadUserCollegePartnerships() { + private fun loadUserCollegePartnerships() { viewModelScope.launch { + val current = uiState.value + val currentData = if (current is UiState.Success) current.data else MapState() + _uiState.value = UiState.Loading val partnerships = partnershipRepository.getUserCollegePartnerships() - _uiState.value = UiState.Success(MapState(partnerships = partnerships)) + _uiState.value = UiState.Success( + currentData.copy( + partnerships = partnerships, + filterChangeResult = null + ) + ) } } @@ -106,36 +186,19 @@ class MapViewModel @Inject constructor( ) } + // Domain 모델(RestaurantType)을 UI 모델(PlaceType)로 변환 + val placeType = when (representative.restaurantType) { + RestaurantType.CAFE -> PlaceType.CAFE + RestaurantType.RESTAURANT -> PlaceType.RESTAURANT + RestaurantType.PUB -> PlaceType.PUB + } + _uiState.value = UiState.Success( data.copy( - showPartnershipBottomSheet = true, restaurantPartnershipInfo = representative, - restaurantInfoList = restaurantInfoList + restaurantInfoList = restaurantInfoList, + placeType = placeType ) ) } - - // 학과 정보 입력 bottomSheet 보여주기 toggle - fun toggleDepartmentBottomSheet() { - val current = uiState.value - if (current is UiState.Success) { - current.data.let { data -> - _uiState.value = UiState.Success( - data.copy(showDepartmentBottomSheet = !data.showDepartmentBottomSheet) - ) - } - } - } - - // 식당별 제휴 정보 bottomSheet 보여주기 toggle - fun togglePartnershipBottomSheet() { - val current = uiState.value - if (current is UiState.Success) { - current.data.let { data -> - _uiState.value = UiState.Success( - data.copy(showPartnershipBottomSheet = !data.showPartnershipBottomSheet) - ) - } - } - } } 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 6c67bf35a..9f11363b5 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 @@ -7,7 +7,6 @@ import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -25,61 +24,69 @@ import com.eatssu.design_system.theme.White import timber.log.Timber enum class FilterType { - All, Mine + Mine, All; // 해당 부분에 쓰는 enum 순서대로 UI 토글 순서에 반영됩니다 + + fun label(departmentName: String): String { + return when (this) { + Mine -> if (departmentName.isBlank() || departmentName == "학과") "내 제휴" else departmentName + All -> "전체" + } + } } @Composable fun PartnershipFilterToggle( selected: FilterType, onSelectedChange: (FilterType) -> Unit, - modifier: Modifier = Modifier, departmentName: String, + modifier: Modifier = Modifier, ) { Timber.d("departmentName = $departmentName") + val items = FilterType.entries.map { + it to it.label(departmentName) + } Row( modifier = modifier - .border(1.dp, Gray300, shape = CircleShape) + .border(1.dp, Gray300, CircleShape) .clip(CircleShape) .background(White) - .padding(6.dp), - verticalAlignment = Alignment.CenterVertically, + .padding(4.dp), + verticalAlignment = Alignment.CenterVertically ) { - PartnershipToggleItem( - text = "전체", - isSelected = selected == FilterType.All, - onClick = { onSelectedChange(FilterType.All) } - ) - PartnershipToggleItem( - text = if(departmentName == "학과" || departmentName.isEmpty()) "내 제휴" else departmentName, - isSelected = selected == FilterType.Mine, - onClick = { onSelectedChange(FilterType.Mine) }, - ) + items.forEach { (type, label) -> + PartnershipToggleItem( + label = label, + selected = selected == type, + onClick = { onSelectedChange(type) } + ) + } } } @Composable fun PartnershipToggleItem( - text: String, - isSelected: Boolean, - onClick: () -> Unit + label: String, + selected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, ) { - val backgroundColor = if (isSelected) Primary else Color.Transparent - val textColor = if (isSelected) Color.White else Gray600 + val backgroundColor = if (selected) Primary else Color.Transparent + val textColor = if (selected) White else Gray600 Box( - modifier = Modifier - .wrapContentWidth() + modifier = modifier .clip(CircleShape) .background(backgroundColor) .padding(horizontal = 20.dp, vertical = 6.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, - indication = null - ) { onClick() }, + indication = null, + onClick = onClick + ), contentAlignment = Alignment.Center ) { Text( - text = text, + text = label, color = textColor, style = EatssuTheme.typography.body2 ) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 645e1e40f..537fef772 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -37,7 +37,6 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) - implementation(libs.material) testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/core/design-system/build.gradle.kts b/core/design-system/build.gradle.kts index 02f4a11bb..8749d23ed 100644 --- a/core/design-system/build.gradle.kts +++ b/core/design-system/build.gradle.kts @@ -46,7 +46,6 @@ dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) - implementation(libs.material) testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuTopBar.kt b/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuTopBar.kt index de747d00a..b37f1e66b 100644 --- a/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuTopBar.kt +++ b/core/design-system/src/main/java/com/eatssu/design_system/component/EatSsuTopBar.kt @@ -3,8 +3,6 @@ package com.eatssu.design_system.component import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Close import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -69,7 +67,7 @@ fun CloseTopBar( actions = { IconButton(onClick = onClose) { Icon( - imageVector = Icons.Filled.Close, + painter = painterResource(id = R.drawable.ic_close), contentDescription = "닫기", tint = Gray500 ) diff --git a/core/design-system/src/main/res/drawable/ic_close.xml b/core/design-system/src/main/res/drawable/ic_close.xml new file mode 100644 index 000000000..4c9a93d95 --- /dev/null +++ b/core/design-system/src/main/res/drawable/ic_close.xml @@ -0,0 +1,13 @@ + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8770324cb..867868e7e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,12 +8,10 @@ hiltAndroidCompiler = "2.42" lifecycleRuntimeCompose = "2.8.7" animation = "1.7.8" coilCompose = "2.5.0" -composeBomVersion = "2025.03.00" composeThemeAdapter = "1.2.1" lifecycleRuntimeKtx = "2.5.2" lifecycleViewmodelCompose = "2.8.7" mapSdk = "3.21.0" -material = "1.8.0" constraintlayout = "2.1.4" naverMapCompose = "1.8.2" naverMapLocation = "21.0.2" @@ -143,7 +141,6 @@ play-services-base = { group = "com.google.android.gms", name = "play-services-b # etc accompanist-appcompat-theme = { group = "com.google.accompanist", name = "accompanist-appcompat-theme", version.ref = "accompanistAppcompatTheme" } compose-theme-adapter = { group = "com.google.android.material", name = "compose-theme-adapter", version.ref = "composeThemeAdapter" } -material = { group = "com.google.android.material", name = "material", version.ref = "material" } threetenabp = { group = "com.jakewharton.threetenabp", name = "threetenabp", version.ref = "threetenabp" } material-calendarview = { group = "com.prolificinteractive", name = "material-calendarview", version.ref = "material-calendarview" } transport-runtime = { group = "com.google.android.datatransport", name = "transport-runtime", version.ref = "transport-runtime" } @@ -164,4 +161,4 @@ android-library = { id = "com.android.library", version.ref = "android" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-android" } google-services = { id = "com.google.gms.google-services", version.ref = "google-services" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } -hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } \ No newline at end of file