From fedc8014f7842596a0eb75f6001061aa3c4c743f Mon Sep 17 00:00:00 2001 From: syddl0 <137189866+shroqkf@users.noreply.github.com> Date: Tue, 13 May 2025 00:15:26 +0900 Subject: [PATCH 1/5] =?UTF-8?q?Fix:=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EC=A7=80=EB=8F=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/search/SearchPage.jsx | 2 +- src/pages/search/components/MapViewer.jsx | 36 ++++ src/pages/search/hooks/useMapViewer.js | 218 ++++++++++++++++++++++ 3 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/pages/search/components/MapViewer.jsx create mode 100644 src/pages/search/hooks/useMapViewer.js diff --git a/src/pages/search/SearchPage.jsx b/src/pages/search/SearchPage.jsx index 5cbb388..98a4983 100644 --- a/src/pages/search/SearchPage.jsx +++ b/src/pages/search/SearchPage.jsx @@ -3,7 +3,6 @@ import SearchBar from "./components/SearchBar"; import RecentSearchList from "./components/RecentSearchList"; import PlaceList from "./components/SearchPlaceList"; import PlaceBottomSheet from "@pages/map/components/PlaceBottomSheet"; -import MapViewer from "../map/components/MapViewer"; import { getDistanceFromLatLon } from "../map/utils/getDistanceFromLatLon"; import { formatDistance } from "../map/utils/formatDistance"; import { getCompanyPreview } from "@apis/company/getCompanyPreview"; @@ -11,6 +10,7 @@ import { useCompanyData } from "./hooks/useCompanyData"; import { useUserCoords } from "./hooks/useUserCoords"; import useAuthStore from "@/store/authStore"; import HaveToLoginModal from "@components/common/HaveToLoginModal"; +import MapViewer from "./components/MapViewer"; const LOCAL_STORAGE_KEY = "recentSearches"; diff --git a/src/pages/search/components/MapViewer.jsx b/src/pages/search/components/MapViewer.jsx new file mode 100644 index 0000000..5a04e70 --- /dev/null +++ b/src/pages/search/components/MapViewer.jsx @@ -0,0 +1,36 @@ +import { useRef } from "react"; +import useMapViewer from "../hooks/useMapViewer"; + +const MapViewer = ({ + places, + onMarkerClick, + userCoords, + moveToCurrentLocation, + onMoveComplete, + resetMap, + center, + markerPosition, + zoom = 11, + selectedPlace, + showOnlyLiked, +}) => { + const mapRef = useRef(null); + + useMapViewer({ + mapRef, + places, + onMarkerClick, + userCoords, + moveToCurrentLocation, + onMoveComplete, + resetMap, + center, + markerPosition, + zoom, + selectedPlace, + }); + + return
; +}; + +export default MapViewer; diff --git a/src/pages/search/hooks/useMapViewer.js b/src/pages/search/hooks/useMapViewer.js new file mode 100644 index 0000000..248166f --- /dev/null +++ b/src/pages/search/hooks/useMapViewer.js @@ -0,0 +1,218 @@ +import { useEffect, useRef, useCallback, useState } from "react"; +import { loadNaverMapScript } from "@pages/map/utils/loadMapScript"; +import { + createMarkerIcon, + createUserMarkerIcon, +} from "@pages/map/utils/mapHelpers"; + +const useMapViewer = ({ + mapRef, + places, + onMarkerClick, + userCoords, + moveToCurrentLocation, + onMoveComplete, + resetMap, + center, + markerPosition, + zoom = 11, + selectedPlace, + showOnlyLiked, +}) => { + const mapInstance = useRef(null); + const userMarkerRef = useRef(null); + const markersRef = useRef({}); + const hasAnimatedRef = useRef(false); + const [isMapInitialized, setIsMapInitialized] = useState(false); + + const handleMarkerClick = useCallback( + (place) => { + Object.values(markersRef.current).forEach((marker) => { + marker.setIcon(createMarkerIcon(false, false)); + }); + + const clickedMarker = markersRef.current[place.id]; + if (clickedMarker) { + clickedMarker.setIcon( + createMarkerIcon(true, showOnlyLiked ? place.liked : false), + ); + } + + onMarkerClick?.(place); + }, + [onMarkerClick, showOnlyLiked], + ); + + useEffect(() => { + if (isMapInitialized || (!userCoords && !center)) return; + + loadNaverMapScript().then(() => { + const mapCenter = center + ? new window.naver.maps.LatLng(center.lat, center.lng) + : new window.naver.maps.LatLng(37.5665, 126.978); + + mapInstance.current = new window.naver.maps.Map(mapRef.current, { + center: mapCenter, + zoom, + }); + + setIsMapInitialized(true); + + places.forEach((place) => { + if (!place.coords?.lat || !place.coords?.lng) return; + + const isHighlighted = + place.isSearchResult || + (selectedPlace && selectedPlace.id === place.id); + + const marker = new window.naver.maps.Marker({ + position: new window.naver.maps.LatLng( + place.coords.lat, + place.coords.lng, + ), + map: mapInstance.current, + icon: createMarkerIcon(isHighlighted, place.liked), + }); + + markersRef.current[place.id] = marker; + + window.naver.maps.Event.addListener(marker, "click", () => + handleMarkerClick(place), + ); + }); + + if (markerPosition) { + new window.naver.maps.Marker({ + position: new window.naver.maps.LatLng( + markerPosition.lat, + markerPosition.lng, + ), + map: mapInstance.current, + icon: createMarkerIcon(true), + }); + } + }); + }, [ + userCoords, + places, + center, + zoom, + selectedPlace, + markerPosition, + handleMarkerClick, + isMapInitialized, + mapRef, + showOnlyLiked, + ]); + + useEffect(() => { + if ( + userCoords && + mapInstance.current && + !hasAnimatedRef.current && + isMapInitialized + ) { + hasAnimatedRef.current = true; + + const target = new window.naver.maps.LatLng( + userCoords.lat, + userCoords.lng, + ); + mapInstance.current.panTo(target); + + const zoomTimeout = setTimeout(() => { + mapInstance.current.setZoom(17, true); + + if (userMarkerRef.current) { + userMarkerRef.current.setMap(null); + } + + userMarkerRef.current = new window.naver.maps.Marker({ + position: target, + map: mapInstance.current, + icon: createUserMarkerIcon(), + }); + }, 1800); + + return () => clearTimeout(zoomTimeout); + } + }, [userCoords, isMapInitialized]); + + useEffect(() => { + if (moveToCurrentLocation && userCoords && mapInstance.current) { + const newCenter = new window.naver.maps.LatLng( + userCoords.lat, + userCoords.lng, + ); + mapInstance.current.setCenter(newCenter); + mapInstance.current.setZoom(17); + + if (userMarkerRef.current) userMarkerRef.current.setMap(null); + + userMarkerRef.current = new window.naver.maps.Marker({ + position: newCenter, + map: mapInstance.current, + icon: createUserMarkerIcon(), + }); + + onMoveComplete?.(); + } + }, [moveToCurrentLocation, userCoords, onMoveComplete]); + + useEffect(() => { + if (resetMap && mapInstance.current) { + mapInstance.current.setCenter( + new window.naver.maps.LatLng(37.5665, 126.978), + ); + mapInstance.current.setZoom(11.5); + + if (userMarkerRef.current) { + userMarkerRef.current.setMap(null); + userMarkerRef.current = null; + } + } + }, [resetMap]); + + useEffect(() => { + if (!mapInstance.current || !isMapInitialized) return; + + const currentMarkerIds = new Set(Object.keys(markersRef.current)); + const newPlaceIds = new Set(places.map((p) => String(p.id))); + + currentMarkerIds.forEach((id) => { + if (!newPlaceIds.has(id)) { + markersRef.current[id].setMap(null); + delete markersRef.current[id]; + } + }); + + places.forEach((place) => { + const isHighlighted = + place.isSearchResult || + (selectedPlace && selectedPlace.id === place.id); + + const marker = markersRef.current[place.id]; + + if (!marker) { + const newMarker = new window.naver.maps.Marker({ + position: new window.naver.maps.LatLng( + place.coords.lat, + place.coords.lng, + ), + map: mapInstance.current, + icon: createMarkerIcon(isHighlighted, place.liked), + }); + + markersRef.current[place.id] = newMarker; + + window.naver.maps.Event.addListener(newMarker, "click", () => + handleMarkerClick(place), + ); + } else { + marker.setIcon(createMarkerIcon(isHighlighted, place.liked)); + } + }); + }, [places, selectedPlace, isMapInitialized, handleMarkerClick]); +}; + +export default useMapViewer; From 157f3d1c8de3f1e3f9054345d75786c78afa9dc7 Mon Sep 17 00:00:00 2001 From: syddl0 <137189866+shroqkf@users.noreply.github.com> Date: Tue, 13 May 2025 00:46:14 +0900 Subject: [PATCH 2/5] =?UTF-8?q?Fix:=20=EC=B0=9C=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/map/components/PlaceContent.jsx | 20 ++++++-- src/pages/map/hooks/useToggleLike.js | 57 +++++++++++++++++------ 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/pages/map/components/PlaceContent.jsx b/src/pages/map/components/PlaceContent.jsx index 6b5cb69..7c033d2 100644 --- a/src/pages/map/components/PlaceContent.jsx +++ b/src/pages/map/components/PlaceContent.jsx @@ -24,6 +24,7 @@ import { useState } from "react"; const PlaceContent = ({ place, onToggleLike, showMapLink = true }) => { const userCoords = useUserCoords(); const [showToast, setShowToast] = useState(false); + const [isLiking, setIsLiking] = useState(false); const handleRouteClick = (e) => { e.preventDefault(); @@ -76,6 +77,20 @@ const PlaceContent = ({ place, onToggleLike, showMapLink = true }) => { } }; + const handleLikeClick = async (e) => { + e.stopPropagation(); + if (isLiking) return; + + setIsLiking(true); + try { + await onToggleLike?.(place.id); + } catch (err) { + console.error("좋아요 처리 실패:", err); + } finally { + setIsLiking(false); + } + }; + const fireIcons = { 10: IcFire10, 20: IcFire20, @@ -157,10 +172,7 @@ const PlaceContent = ({ place, onToggleLike, showMapLink = true }) => { )} +
+ )} + {selectedPlace && isBottomSheetVisible && ( setIsBottomSheetVisible(false)} onExpandChange={setIsBottomSheetExpanded} onToggleLike={handleToggleLike} + onHeightChange={setBottomSheetHeight} /> )} diff --git a/src/pages/search/components/MapViewer.jsx b/src/pages/search/components/MapViewer.jsx index 5a04e70..535ca61 100644 --- a/src/pages/search/components/MapViewer.jsx +++ b/src/pages/search/components/MapViewer.jsx @@ -13,6 +13,7 @@ const MapViewer = ({ zoom = 11, selectedPlace, showOnlyLiked, + disableAutoUserPan = false, }) => { const mapRef = useRef(null); @@ -28,6 +29,7 @@ const MapViewer = ({ markerPosition, zoom, selectedPlace, + disableAutoUserPan, }); return
; diff --git a/src/pages/search/hooks/useMapViewer.js b/src/pages/search/hooks/useMapViewer.js index 248166f..484e5b3 100644 --- a/src/pages/search/hooks/useMapViewer.js +++ b/src/pages/search/hooks/useMapViewer.js @@ -18,6 +18,7 @@ const useMapViewer = ({ zoom = 11, selectedPlace, showOnlyLiked, + disableAutoUserPan, }) => { const mapInstance = useRef(null); const userMarkerRef = useRef(null); @@ -76,9 +77,10 @@ const useMapViewer = ({ markersRef.current[place.id] = marker; - window.naver.maps.Event.addListener(marker, "click", () => - handleMarkerClick(place), - ); + window.naver.maps.Event.addListener(marker, "click", () => { + handleMarkerClick(place); + console.log("marker"); + }); }); if (markerPosition) { @@ -110,7 +112,8 @@ const useMapViewer = ({ userCoords && mapInstance.current && !hasAnimatedRef.current && - isMapInitialized + isMapInitialized && + !disableAutoUserPan ) { hasAnimatedRef.current = true; @@ -122,7 +125,6 @@ const useMapViewer = ({ const zoomTimeout = setTimeout(() => { mapInstance.current.setZoom(17, true); - if (userMarkerRef.current) { userMarkerRef.current.setMap(null); } @@ -136,7 +138,7 @@ const useMapViewer = ({ return () => clearTimeout(zoomTimeout); } - }, [userCoords, isMapInitialized]); + }, [userCoords, isMapInitialized, disableAutoUserPan]); useEffect(() => { if (moveToCurrentLocation && userCoords && mapInstance.current) { From ecd9a98a66b4f7b0d3d2b58e6cacb83f4c4d74e2 Mon Sep 17 00:00:00 2001 From: syddl0 <137189866+shroqkf@users.noreply.github.com> Date: Wed, 14 May 2025 00:40:51 +0900 Subject: [PATCH 4/5] =?UTF-8?q?Fix:=20=ED=98=84=EC=9C=84=EC=B9=98=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=8D=B0=EC=8A=A4=ED=81=AC=ED=83=91=20?= =?UTF-8?q?=EB=86=92=EC=9D=B4=20=EC=A1=B0=EC=A0=88=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/map/components/PlaceBottomSheet.jsx | 10 ++++---- src/pages/search/SearchPage.jsx | 23 +++++++++++++++---- src/pages/search/hooks/useMapViewer.js | 8 ++++++- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/pages/map/components/PlaceBottomSheet.jsx b/src/pages/map/components/PlaceBottomSheet.jsx index 7755005..aa7e5da 100644 --- a/src/pages/map/components/PlaceBottomSheet.jsx +++ b/src/pages/map/components/PlaceBottomSheet.jsx @@ -94,16 +94,15 @@ const PlaceBottomSheet = ({ if (delta < -50) { controls.start({ height: MAX_HEIGHT.current }); setIsExpanded(true); - onHeightChange?.(MAX_HEIGHT.current); } else if (delta > 100) { controls.start({ height: 0 }).then(() => { - onHeightChange?.(0); + onHeightChange?.(0); onClose?.(); }); } else { controls.start({ height: MIN_HEIGHT }); setIsExpanded(false); - onHeightChange?.(MIN_HEIGHT); + onHeightChange?.(MIN_HEIGHT); } }; @@ -111,6 +110,7 @@ const PlaceBottomSheet = ({ if (!isMobile && !isExpanded) { controls.start({ height: MAX_HEIGHT.current }); setIsExpanded(true); + onHeightChange?.(MAX_HEIGHT.current); } }; @@ -180,8 +180,8 @@ const PlaceBottomSheet = ({ turnOnCamera={turnOnCamera} onCloseCamera={() => setTurnOnCamera(false)} onCaptureSuccess={(data) => { - setCompanyInfo(data); - setShowConfirm(true); + setCompanyInfo(data); + setShowConfirm(true); }} /> )} diff --git a/src/pages/search/SearchPage.jsx b/src/pages/search/SearchPage.jsx index 602c060..c414e81 100644 --- a/src/pages/search/SearchPage.jsx +++ b/src/pages/search/SearchPage.jsx @@ -27,6 +27,16 @@ const SearchPage = () => { const [step, setStep] = useState(1); const [showLoginModal, setShowLoginModal] = useState(false); const [bottomSheetHeight, setBottomSheetHeight] = useState(220); + const [isDesktop, setIsDesktop] = useState(false); + + useEffect(() => { + const updateSize = () => { + setIsDesktop(window.innerWidth >= 1024); + }; + updateSize(); + window.addEventListener("resize", updateSize); + return () => window.removeEventListener("resize", updateSize); + }, []); const { companies: allPlaces, loading: isCompanyLoading } = useCompanyData(); const userCoords = useUserCoords(); @@ -229,11 +239,16 @@ const SearchPage = () => { disableAutoUserPan={true} /> - {bottomSheetHeight <= 220 && ( + {!isBottomSheetExpanded && (
+
+
+ +

어느 동네에 사세요?

diff --git a/src/pages/join/components/NameStep.jsx b/src/pages/join/components/NameStep.jsx index ab0e1b6..43e675b 100644 --- a/src/pages/join/components/NameStep.jsx +++ b/src/pages/join/components/NameStep.jsx @@ -21,16 +21,16 @@ const NameStep = ({ onNext, onBack }) => { onSubmit={handleSubmit} className="flex flex-col justify-start mt-48 bg-white h-screen overflow-auto relative" > - +

+ +
-

이름을 입력해주세요.

+

+ 이름을 입력해주세요. +

투명한 리뷰에 사용됩니다.

@@ -79,9 +79,7 @@ const NameStep = ({ onNext, onBack }) => { type="submit" disabled={!name.trim()} className={`w-full py-6 text-center text-b1 font-semibold ${ - name.trim() - ? "bg-primary-8 text-white" - : "bg-gray-4 text-white" + name.trim() ? "bg-primary-8 text-white" : "bg-gray-4 text-white" }`} > 확인 diff --git a/src/pages/join/components/OwnerStep.jsx b/src/pages/join/components/OwnerStep.jsx index b11413a..7d76c7f 100644 --- a/src/pages/join/components/OwnerStep.jsx +++ b/src/pages/join/components/OwnerStep.jsx @@ -17,13 +17,12 @@ const OwnerStep = ({ onNext, onBack }) => { }; return ( -
- +
+
+ +

사회적기업의 사장님이신가요? diff --git a/src/pages/join/components/ProfileImageStep.jsx b/src/pages/join/components/ProfileImageStep.jsx index 92ad3d4..0873fb1 100644 --- a/src/pages/join/components/ProfileImageStep.jsx +++ b/src/pages/join/components/ProfileImageStep.jsx @@ -25,13 +25,11 @@ const ProfileImageStep = ({ onNext, onBack }) => { return (
- +
+ +

프로필사진을 설정해주세요.