diff --git a/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchHistoryResponse.kt b/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchHistoryResponse.kt index 40346cb..8325ee9 100644 --- a/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchHistoryResponse.kt +++ b/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchHistoryResponse.kt @@ -5,6 +5,8 @@ import kotlinx.serialization.Serializable @Serializable data class MapSearchHistoryResponse( + @SerialName("mapId") + val mapId: Long, @SerialName("menuId") val menuId: Long, @SerialName("menuTitle") diff --git a/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchResponse.kt b/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchResponse.kt index e072a88..e9ce095 100644 --- a/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchResponse.kt +++ b/app/src/main/java/com/kuit/ourmenu/data/model/map/response/MapSearchResponse.kt @@ -5,6 +5,10 @@ import kotlinx.serialization.Serializable @Serializable data class MapSearchResponse( + @SerialName("mapId") + val mapId: Long, + @SerialName("menuId") + val menuId: Long, @SerialName("menuTitle") val menuTitle: String, @SerialName("storeTitle") diff --git a/app/src/main/java/com/kuit/ourmenu/ui/common/bottomsheet/MenuInfoBottomSheetContent.kt b/app/src/main/java/com/kuit/ourmenu/ui/common/bottomsheet/MenuInfoBottomSheetContent.kt index a72e025..ada7594 100644 --- a/app/src/main/java/com/kuit/ourmenu/ui/common/bottomsheet/MenuInfoBottomSheetContent.kt +++ b/app/src/main/java/com/kuit/ourmenu/ui/common/bottomsheet/MenuInfoBottomSheetContent.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -137,14 +136,17 @@ fun MenuInfoImage( ) { val imgUrls = menuInfoData.menuImgUrls - Row(modifier = modifier, horizontalArrangement = Arrangement.SpaceBetween) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { for (i in 0 until 3) { Image( painter = if (i < imgUrls.size && imgUrls[i].isNotEmpty()) { rememberAsyncImagePainter( model = ImageRequest.Builder(LocalPlatformContext.current) .data(imgUrls[i]) - .size(104, 80) + .size(108, 80) .build() ) } else { @@ -152,10 +154,10 @@ fun MenuInfoImage( }, contentDescription = null, modifier = Modifier - .size(104.dp, 80.dp) + .weight(1f) + .height(80.dp) .clip(shape = RoundedCornerShape(8.dp)) ) -// if (i != 2) Spacer(modifier = Modifier.padding(end = 4.dp)) } } } diff --git a/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/component/SearchHistory.kt b/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/component/SearchHistory.kt index 85743c5..8b30884 100644 --- a/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/component/SearchHistory.kt +++ b/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/component/SearchHistory.kt @@ -162,21 +162,24 @@ private fun SearchHistoryPreview() { SearchHistoryList( historyList = listOf( MapSearchHistoryResponse( + mapId = 1, + menuId = 1, menuTitle = "피자", storeTitle = "피자헛", - menuId = 1, storeAddress = "서울특별시 강남구 역삼동 123-4" ), MapSearchHistoryResponse( + mapId = 2, + menuId = 2, menuTitle = "치킨", storeTitle = "굽네치킨", - menuId = 2, storeAddress = "서울특별시 강남구 역삼동 456-7" ), MapSearchHistoryResponse( + mapId = 3, + menuId = 3, menuTitle = "햄버거", storeTitle = "맥도날드", - menuId = 3, storeAddress = "서울특별시 강남구 역삼동 987-6" ) ), diff --git a/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/screen/SearchMenuScreen.kt b/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/screen/SearchMenuScreen.kt index 9111aef..2f642eb 100644 --- a/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/screen/SearchMenuScreen.kt +++ b/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/screen/SearchMenuScreen.kt @@ -1,6 +1,7 @@ package com.kuit.ourmenu.ui.searchmenu.screen import android.Manifest +import android.content.Intent import android.util.Log import androidx.activity.compose.BackHandler import androidx.compose.foundation.interaction.MutableInteractionSource @@ -32,6 +33,7 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.net.toUri import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.kuit.ourmenu.R @@ -71,18 +73,22 @@ fun SearchMenuScreen( // 지도 중심 좌표 val currentCenter by viewModel.currentCenter.collectAsStateWithLifecycle() - + // 검색기록 val searchHistory by viewModel.searchHistory.collectAsStateWithLifecycle() - + // 핀 위치에 해당하는 메뉴들 val menusOnPin by viewModel.menusOnPin.collectAsStateWithLifecycle() + // 선택된 라벨 + val activeMapId by viewModel.activeMapId.collectAsStateWithLifecycle() + val density = LocalDensity.current val singleItemHeight = 300.dp // Fixed height for each item - LaunchedEffect(menusOnPin) { - if (menusOnPin != null && menusOnPin?.isNotEmpty() == true) { + // 메뉴핀이 선택되었을 때 바텀시트 상태 변경 + LaunchedEffect(menusOnPin, activeMapId) { + if (activeMapId != null && menusOnPin != null && menusOnPin?.isNotEmpty() == true) { showBottomSheet = true } } @@ -130,11 +136,16 @@ fun SearchMenuScreen( } } - BackHandler(enabled = showSearchBackground) { - if (searchBarFocused) focusManager.clearFocus() - searchActionDone = false - showSearchBackground = false - searchText = "" + BackHandler(enabled = showSearchBackground || showBottomSheet) { + if (showSearchBackground) { + if (searchBarFocused) focusManager.clearFocus() + searchActionDone = false + showSearchBackground = false + searchText = "" + } else if (showBottomSheet) { + showBottomSheet = false + viewModel.clearActiveMapId() + } } BottomSheetScaffold( @@ -160,7 +171,7 @@ fun SearchMenuScreen( } ) }, - sheetPeekHeight = if(showBottomSheet) { + sheetPeekHeight = if (showBottomSheet) { val itemCount = menusOnPin?.size ?: 0 (singleItemHeight * itemCount) + dragHandleHeight } else 0.dp, @@ -190,6 +201,7 @@ fun SearchMenuScreen( // 크롤링 기록 아이템 클릭시 동작 viewModel.getMapMenuDetail(menuId) Log.d("SearchMenuScreen", "검색 기록 아이템 클릭: $menuId") + focusManager.clearFocus() showSearchBackground = false showBottomSheet = true } @@ -210,37 +222,54 @@ fun SearchMenuScreen( // onSearch 함수 if (searchBarFocused) focusManager.clearFocus() searchActionDone = true - + // 검색 시 현재 지도 중심 좌표 사용 if (searchText.isNotEmpty()) { // 검색 직전에 현재 지도 중심 좌표 업데이트 viewModel.updateCurrentCenter() - + val center = viewModel.getCurrentCoordinates() if (center != null) { val (latitude, longitude) = center Log.d("SearchMenuScreen", "검색 위치: $latitude, $longitude") - + // 검색어와 현재 좌표로 스토어 정보 요청 viewModel.getMapSearchResult( query = searchText, long = longitude, lat = latitude ) - + showBottomSheet = true showSearchBackground = false } + }else{ + Log.d("SearchMenuScreen", "검색어가 비어있습니다.") + viewModel.getMyMenus() + showBottomSheet = false + showSearchBackground = false } + searchText = "" } GoToMapButton( modifier = Modifier .align(Alignment.BottomEnd) .padding(bottom = 16.dp, end = 20.dp), - onClick = { - // TODO: 임시로 설정해놓은 카메라 이동, 실제로는 네이버 지도에 해당 가게 검색 결과로 이동 - viewModel.moveCamera(37.5416, 127.0793) + onClick = { + // 네이버 지도에 해당 가게 검색 결과로 이동 + if (activeMapId == null) { + Log.d("SearchMenuScreen", "활성화된 Map ID가 없습니다.") + } else { + scope.launch { + val searchQuery = viewModel.getWebSearchQuery(activeMapId!!) + if (searchQuery.isNotBlank()) { + Log.d("SearchMenuScreen", "intent query: $searchQuery") + val webIntent = Intent(Intent.ACTION_VIEW, searchQuery.toUri()) + context.startActivity(webIntent) + } + } + } }, ) } @@ -252,7 +281,7 @@ fun SearchMenuScreen( @Composable private fun SearchMenuScreenPreview() { SearchMenuScreen( - ){ + ) { } } \ No newline at end of file diff --git a/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/viewmodel/SearchMenuViewModel.kt b/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/viewmodel/SearchMenuViewModel.kt index e719bc5..75fe229 100644 --- a/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/viewmodel/SearchMenuViewModel.kt +++ b/app/src/main/java/com/kuit/ourmenu/ui/searchmenu/viewmodel/SearchMenuViewModel.kt @@ -268,9 +268,33 @@ class SearchMenuViewModel @Inject constructor( response.onSuccess { result -> if (result != null && result.isNotEmpty()) { - Log.d("SearchMenuViewModel", "등록 메뉴 정보 조회 성공: ${result.size}개") + Log.d("SearchMenuViewModel", "등록 메뉴 정보 조회 성공: $result") // 검색 결과 저장 _searchResult.value = result + + // 전체 메뉴 목록을 다시 가져온 후 필터링 + val allMenusResponse = mapRepository.getMap() + allMenusResponse.onSuccess { allMenus -> + if (allMenus != null) { + // 전체 메뉴 중에서 검색 결과와 일치하는 것들만 필터링 + _myMenus.value = allMenus.filter { menu -> + result.any { searchResult -> searchResult.mapId == menu.mapId } + } + // 검색 결과의 첫 번째 항목을 활성화 상태로 설정 + _activeMapId.value = result.firstOrNull()?.mapId + showSearchResultOnMap() + // 첫 번째 검색 결과의 상세 정보를 가져와서 바텀시트에 표시 + _activeMapId.value?.let { mapId -> + getMapDetail(mapId) + } + // 검색 결과를 검색 기록에 반영 + if (result.firstOrNull()?.menuId != null){ + mapRepository.getMapMenuDetail(result.first().menuId) + Log.d("SearchMenuViewModel", "검색 기록에 반영: ${result.first().menuId}") + } + + } + } } }.onFailure { Log.d("SearchMenuViewModel", "등록 메뉴 정보 조회 실패: ${it.message}") @@ -315,22 +339,35 @@ class SearchMenuViewModel @Inject constructor( fun getMapMenuDetail(menuId: Long) { viewModelScope.launch { - val response = mapRepository.getMapMenuDetail(menuId) - response.onSuccess { menuDetail -> - Log.d("SearchMenuViewModel", "메뉴 상세 조회 성공: $menuDetail") - - // myMenus에서 해당 menuId를 가진 메뉴의 위치 정보 찾기 - myMenus.value?.find { it.mapId == menuId }?.let { menu -> - // 해당 위치로 카메라 이동 - moveCamera(menu.mapY, menu.mapX) - // 해당 핀을 활성화 상태로 변경 - _activeMapId.value = menuId - refreshMarkers() - // 메뉴 상세 정보를 바텀시트에 표시하기 위해 설정 - getMapDetail(menuId) + // 먼저 전체 메뉴를 가져옴 + val myMenusResponse = mapRepository.getMap() + myMenusResponse.onSuccess { menus -> + if (menus != null){ + val allMenus = menus + Log.d("SearchMenuViewModel", "menuId로 메뉴 정보 요청: $menuId") + val menuDetailResponse = mapRepository.getMapMenuDetail(menuId) + menuDetailResponse.onSuccess { menuDetail -> + Log.d("SearchMenuViewModel", "메뉴 상세 조회 성공: $menuDetail") + // 검색 기록에서 해당 menuId를 가진 항목 찾기 + searchHistory.value?.find { it.menuId == menuId }?.let { historyItem -> + Log.d("SearchMenuViewModel", "검색 기록에서 찾은 mapId: ${historyItem.mapId}") + // 가져온 전체 메뉴에서 필터링 + _myMenus.value = allMenus.filter { menu -> + menu.mapId == historyItem.mapId + } + // 해당 mapId를 활성화 상태로 설정 + _activeMapId.value = historyItem.mapId + // 지도에 검색 결과 표시 + showSearchResultOnMap() + // 메뉴 상세 정보를 바텀시트에 표시하기 위해 설정 + getMapDetail(historyItem.mapId) + } + }.onFailure { + Log.d("SearchMenuViewModel", "메뉴 상세 조회 실패: ${it.message}") + } } }.onFailure { - Log.d("SearchMenuViewModel", "메뉴 상세 조회 실패: ${it.message}") + Log.d("SearchMenuViewModel", "내 메뉴 조회 실패: ${it.message}") } } } @@ -338,14 +375,46 @@ class SearchMenuViewModel @Inject constructor( // 지도에 검색 결과 핀 추가 fun showSearchResultOnMap() { clearMarkers() - myMenus.value?.forEach { store -> - addMarker(store, store.mapId == _activeMapId.value) - Log.d( - "SearchMenuViewModel", - "mapId: ${store.mapId} lat: (${store.mapY}, long: ${store.mapX})" - ) + _myMenus.value?.let { menus -> + if (menus.isNotEmpty()) { + menus.forEach { store -> + addMarker(store, store.mapId == _activeMapId.value) + Log.d( + "SearchMenuViewModel", + "mapId: ${store.mapId} lat: (${store.mapY}, long: ${store.mapX})" + ) + } + // 첫 번째 검색 결과로 카메라 이동 TODO: 현재 위치랑 가까운 결과로 이동 + moveCamera(menus[0].mapY, menus[0].mapX) + } else { + Log.d("SearchMenuViewModel", "검색 결과가 없습니다.") + } } - // 첫 번째 검색 결과로 카메라 이동 TODO: 현재 위치랑 가까운 결과로 이동 - myMenus.value?.get(0)?.let { moveCamera(it.mapY, it.mapX) } + } + + // 활성화된 맵 ID를 초기화하고 마커를 다시 그림 + fun clearActiveMapId() { + _activeMapId.value = null + refreshMarkers() + } + + // 네이버맵 이동을 위한 가게명 조회 + suspend fun getWebSearchQuery(mapId: Long): String { + val baseUrl = "https://map.naver.com/p/search/" + val response = mapRepository.getMapDetail(mapId) + return response.fold( + onSuccess = { menuList -> + if (menuList.isNullOrEmpty()) { + Log.d("SearchMenuViewModel", "메뉴 상세 조회 실패: 메뉴가 없습니다.") + "" + } else { + baseUrl + menuList.first().storeTitle + } + }, + onFailure = { + Log.d("SearchMenuViewModel", "메뉴 상세 조회 실패: ${it.message}") + "" + } + ) } } \ No newline at end of file