Enhanced map with search, auto-redirect to searched locations, and interactive property markers
This commit is contained in:
@@ -10,6 +10,8 @@ import {
|
||||
Dimensions,
|
||||
Animated,
|
||||
ActivityIndicator,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
} from 'react-native';
|
||||
|
||||
import MapView from 'react-native-map-clustering';
|
||||
@@ -18,7 +20,7 @@ import Geolocation from '@react-native-community/geolocation';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
|
||||
/* ---------------- DEFAULT REGION (MUST EXIST) ---------------- */
|
||||
/* ---------------- DEFAULT REGION ---------------- */
|
||||
|
||||
const DEFAULT_REGION: Region = {
|
||||
latitude: 25.48131,
|
||||
@@ -61,14 +63,15 @@ const PriceMarker = ({ item, onPress, selected }: any) => {
|
||||
|
||||
/* ---------------- MAIN SCREEN ---------------- */
|
||||
|
||||
const MapScreen: React.FC = () => {
|
||||
const MapScreen = () => {
|
||||
const mapRef = useRef<MapView>(null);
|
||||
|
||||
const [region, setRegion] = useState<Region>(DEFAULT_REGION);
|
||||
const [mapType, setMapType] = useState<MapType>('standard');
|
||||
const [properties, setProperties] = useState<any[]>([]);
|
||||
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;
|
||||
|
||||
@@ -76,7 +79,6 @@ const MapScreen: React.FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
requestLocation();
|
||||
fetchProperties();
|
||||
}, []);
|
||||
|
||||
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 {
|
||||
setLoading(true);
|
||||
setSelectedItem(null);
|
||||
|
||||
const response = await fetch(
|
||||
'http://64.227.108.180:5000/property-search',
|
||||
@@ -124,7 +129,7 @@ const MapScreen: React.FC = () => {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
location: 'Teliarganj',
|
||||
location: locationName,
|
||||
radius: 500,
|
||||
}),
|
||||
}
|
||||
@@ -132,18 +137,23 @@ const MapScreen: React.FC = () => {
|
||||
|
||||
const json = await response.json();
|
||||
|
||||
if (json?.success && json?.location?.coordinates) {
|
||||
setProperties(json.properties || []);
|
||||
|
||||
const apiRegion = {
|
||||
if (
|
||||
json?.success &&
|
||||
json?.location?.coordinates?.lat &&
|
||||
json?.location?.coordinates?.lng
|
||||
) {
|
||||
const newRegion = {
|
||||
latitude: json.location.coordinates.lat,
|
||||
longitude: json.location.coordinates.lng,
|
||||
latitudeDelta: 0.02,
|
||||
longitudeDelta: 0.02,
|
||||
};
|
||||
|
||||
setRegion(apiRegion);
|
||||
mapRef.current?.animateToRegion(apiRegion, 1000);
|
||||
setProperties(json.properties || []);
|
||||
setRegion(newRegion);
|
||||
|
||||
// ✅ MOVE MAP TO SEARCH LOCATION
|
||||
mapRef.current?.animateToRegion(newRegion, 1000);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('API Error:', error);
|
||||
@@ -161,10 +171,29 @@ const MapScreen: React.FC = () => {
|
||||
|
||||
return (
|
||||
<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
|
||||
ref={mapRef}
|
||||
style={StyleSheet.absoluteFillObject}
|
||||
region={region} // ✅ REQUIRED
|
||||
region={region}
|
||||
onRegionChangeComplete={(r) => {
|
||||
if (!r?.longitudeDelta) return;
|
||||
setRegion(r);
|
||||
@@ -193,7 +222,7 @@ const MapScreen: React.FC = () => {
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* -------- ZILLOW STYLE POPUP -------- */}
|
||||
{/* -------- BOTTOM POPUP -------- */}
|
||||
{selectedItem && (
|
||||
<Animated.View
|
||||
style={[
|
||||
@@ -237,6 +266,33 @@ const MapScreen: React.FC = () => {
|
||||
const styles = StyleSheet.create({
|
||||
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: {
|
||||
backgroundColor: '#8B0000',
|
||||
paddingHorizontal: 12,
|
||||
|
||||
Reference in New Issue
Block a user