Enhanced map with search, auto-redirect to searched locations, and interactive property markers

This commit is contained in:
2026-01-07 23:16:21 +05:30
parent 8c45659363
commit 4ac0bb0b41

View File

@@ -10,6 +10,8 @@ import {
Dimensions, Dimensions,
Animated, Animated,
ActivityIndicator, ActivityIndicator,
TextInput,
TouchableOpacity,
} from 'react-native'; } from 'react-native';
import MapView from 'react-native-map-clustering'; import MapView from 'react-native-map-clustering';
@@ -18,7 +20,7 @@ import Geolocation from '@react-native-community/geolocation';
const { width } = Dimensions.get('window'); const { width } = Dimensions.get('window');
/* ---------------- DEFAULT REGION (MUST EXIST) ---------------- */ /* ---------------- DEFAULT REGION ---------------- */
const DEFAULT_REGION: Region = { const DEFAULT_REGION: Region = {
latitude: 25.48131, latitude: 25.48131,
@@ -61,14 +63,15 @@ const PriceMarker = ({ item, onPress, selected }: any) => {
/* ---------------- MAIN SCREEN ---------------- */ /* ---------------- MAIN SCREEN ---------------- */
const MapScreen: React.FC = () => { const MapScreen = () => {
const mapRef = useRef<MapView>(null); const mapRef = useRef<MapView>(null);
const [region, setRegion] = useState<Region>(DEFAULT_REGION); const [region, setRegion] = useState<Region>(DEFAULT_REGION);
const [mapType, setMapType] = useState<MapType>('standard'); const [mapType, setMapType] = useState<MapType>('standard');
const [properties, setProperties] = useState<any[]>([]); const [properties, setProperties] = useState<any[]>([]);
const [selectedItem, setSelectedItem] = useState<any>(null); const [selectedItem, setSelectedItem] = useState<any>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(false);
const [searchText, setSearchText] = useState('');
const popupAnim = useRef(new Animated.Value(0)).current; const popupAnim = useRef(new Animated.Value(0)).current;
@@ -76,7 +79,6 @@ const MapScreen: React.FC = () => {
useEffect(() => { useEffect(() => {
requestLocation(); requestLocation();
fetchProperties();
}, []); }, []);
useEffect(() => { useEffect(() => {
@@ -112,11 +114,14 @@ const MapScreen: React.FC = () => {
}); });
}; };
/* ---------------- API CALL ---------------- */ /* ---------------- SEARCH API ---------------- */
const fetchProperties = async (locationName: string) => {
if (!locationName) return;
const fetchProperties = async () => {
try { try {
setLoading(true); setLoading(true);
setSelectedItem(null);
const response = await fetch( const response = await fetch(
'http://64.227.108.180:5000/property-search', 'http://64.227.108.180:5000/property-search',
@@ -124,7 +129,7 @@ const MapScreen: React.FC = () => {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
location: 'Teliarganj', location: locationName,
radius: 500, radius: 500,
}), }),
} }
@@ -132,18 +137,23 @@ const MapScreen: React.FC = () => {
const json = await response.json(); const json = await response.json();
if (json?.success && json?.location?.coordinates) { if (
setProperties(json.properties || []); json?.success &&
json?.location?.coordinates?.lat &&
const apiRegion = { json?.location?.coordinates?.lng
) {
const newRegion = {
latitude: json.location.coordinates.lat, latitude: json.location.coordinates.lat,
longitude: json.location.coordinates.lng, longitude: json.location.coordinates.lng,
latitudeDelta: 0.02, latitudeDelta: 0.02,
longitudeDelta: 0.02, longitudeDelta: 0.02,
}; };
setRegion(apiRegion); setProperties(json.properties || []);
mapRef.current?.animateToRegion(apiRegion, 1000); setRegion(newRegion);
// ✅ MOVE MAP TO SEARCH LOCATION
mapRef.current?.animateToRegion(newRegion, 1000);
} }
} catch (error) { } catch (error) {
console.log('API Error:', error); console.log('API Error:', error);
@@ -161,10 +171,29 @@ const MapScreen: React.FC = () => {
return ( return (
<View style={styles.container}> <View style={styles.container}>
{/* -------- SEARCH BAR -------- */}
<View style={styles.searchContainer}>
<TextInput
value={searchText}
onChangeText={setSearchText}
placeholder="Search area, locality..."
style={styles.searchInput}
returnKeyType="search"
onSubmitEditing={() => fetchProperties(searchText)}
/>
<TouchableOpacity
style={styles.searchButton}
onPress={() => fetchProperties(searchText)}
>
<Text style={styles.searchText}>Search</Text>
</TouchableOpacity>
</View>
{/* -------- MAP -------- */}
<MapView <MapView
ref={mapRef} ref={mapRef}
style={StyleSheet.absoluteFillObject} style={StyleSheet.absoluteFillObject}
region={region} // ✅ REQUIRED region={region}
onRegionChangeComplete={(r) => { onRegionChangeComplete={(r) => {
if (!r?.longitudeDelta) return; if (!r?.longitudeDelta) return;
setRegion(r); setRegion(r);
@@ -193,7 +222,7 @@ const MapScreen: React.FC = () => {
</View> </View>
)} )}
{/* -------- ZILLOW STYLE POPUP -------- */} {/* -------- BOTTOM POPUP -------- */}
{selectedItem && ( {selectedItem && (
<Animated.View <Animated.View
style={[ style={[
@@ -237,6 +266,33 @@ const MapScreen: React.FC = () => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1 }, container: { flex: 1 },
searchContainer: {
position: 'absolute',
top: 40,
left: 16,
right: 16,
zIndex: 10,
flexDirection: 'row',
backgroundColor: '#fff',
borderRadius: 8,
elevation: 6,
},
searchInput: {
flex: 1,
padding: 12,
},
searchButton: {
paddingHorizontal: 16,
justifyContent: 'center',
backgroundColor: '#8B0000',
borderTopRightRadius: 8,
borderBottomRightRadius: 8,
},
searchText: {
color: '#fff',
fontWeight: '600',
},
pin: { pin: {
backgroundColor: '#8B0000', backgroundColor: '#8B0000',
paddingHorizontal: 12, paddingHorizontal: 12,