Initial commit
This commit is contained in:
243
src/screens/home/branches/ui/ListBranches.tsx
Normal file
243
src/screens/home/branches/ui/ListBranches.tsx
Normal file
@@ -0,0 +1,243 @@
|
||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { Branch, branchApi } from 'api/branch';
|
||||
import BottomModal from 'components/BottomModal';
|
||||
import LoadingScreen from 'components/LoadingScreen';
|
||||
import NavbarBack from 'components/NavbarBack';
|
||||
import Navigation from 'components/Navigation';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import WebView from 'react-native-webview';
|
||||
import Minus from 'svg/Minus';
|
||||
import Plus from 'svg/Plus';
|
||||
|
||||
const ListBranches = () => {
|
||||
const route = useRoute<RouteProp<any>>();
|
||||
const webviewRef = useRef<WebView>(null);
|
||||
const [webViewReady, setWebViewReady] = useState(false);
|
||||
const [selectedBranch, setSelectedBranch] = useState<Branch | null>(null);
|
||||
const [isModalVisible, setModalVisible] = useState(false);
|
||||
const navigation = useNavigation<NativeStackNavigationProp<any>>();
|
||||
const { t } = useTranslation();
|
||||
const { data } = useQuery({
|
||||
queryKey: ['branchList'],
|
||||
queryFn: branchApi.branchList,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (webViewReady && route.params?.branchId) {
|
||||
const branch = data && data.find(b => b.id === route?.params?.branchId);
|
||||
if (branch) {
|
||||
setSelectedBranch(branch);
|
||||
setModalVisible(true);
|
||||
|
||||
const jsCode = `
|
||||
map.setCenter([${branch.latitude}, ${branch.longitude}], 14);
|
||||
placemark${branch.id}?.balloon.open();
|
||||
true;
|
||||
`;
|
||||
webviewRef.current?.injectJavaScript(jsCode);
|
||||
}
|
||||
}
|
||||
}, [webViewReady, route.params]);
|
||||
|
||||
const generatePlacemarks = () => {
|
||||
if (!data || !data.length) return '';
|
||||
return data
|
||||
.map(
|
||||
branch => `
|
||||
var placemark${branch.id} = new ymaps.Placemark([${branch.latitude}, ${branch.longitude}], {
|
||||
balloonContent: '${branch.name}'
|
||||
}, {
|
||||
iconLayout: 'default#image',
|
||||
iconImageSize: [30, 30],
|
||||
iconImageOffset: [-15, -30]
|
||||
});
|
||||
placemark${branch.id}.events.add('click', function () {
|
||||
window.ReactNativeWebView.postMessage(JSON.stringify({
|
||||
type: 'branch_click',
|
||||
id: ${branch.id}
|
||||
}));
|
||||
});
|
||||
map.geoObjects.add(placemark${branch.id});
|
||||
`,
|
||||
)
|
||||
.join('\n');
|
||||
};
|
||||
|
||||
const html = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script>
|
||||
<style>
|
||||
html, body, #map {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script>
|
||||
let map;
|
||||
|
||||
ymaps.ready(function () {
|
||||
map = new ymaps.Map("map", {
|
||||
center: [40.5, 67.9],
|
||||
zoom: 6,
|
||||
controls: []
|
||||
});
|
||||
|
||||
${generatePlacemarks()}
|
||||
window.ReactNativeWebView.postMessage("map_ready");
|
||||
});
|
||||
|
||||
function zoomIn() {
|
||||
if (map) map.setZoom(map.getZoom() + 1);
|
||||
}
|
||||
|
||||
function zoomOut() {
|
||||
if (map) map.setZoom(map.getZoom() - 1);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
const handleZoom = (type: 'in' | 'out') => {
|
||||
const command = type === 'in' ? 'zoomIn(); true;' : 'zoomOut(); true;';
|
||||
webviewRef.current?.injectJavaScript(command);
|
||||
};
|
||||
|
||||
if (!data) return <LoadingScreen />;
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<NavbarBack title={t('Filiallar ro’yxati')} />
|
||||
{!webViewReady && (
|
||||
<View style={{ width: '100%', height: '100%', margin: 'auto' }}>
|
||||
<LoadingScreen />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.container}>
|
||||
<WebView
|
||||
ref={webviewRef}
|
||||
originWhitelist={['*']}
|
||||
source={{ html }}
|
||||
javaScriptEnabled
|
||||
domStorageEnabled
|
||||
onMessage={event => {
|
||||
try {
|
||||
const message = event.nativeEvent.data;
|
||||
if (message === 'map_ready') {
|
||||
setWebViewReady(true);
|
||||
return;
|
||||
}
|
||||
const parsed = JSON.parse(message);
|
||||
if (parsed.type === 'branch_click') {
|
||||
const branchItem =
|
||||
data && data.find((b: Branch) => b.id === parsed.id);
|
||||
if (branchItem) {
|
||||
setSelectedBranch(branchItem);
|
||||
setModalVisible(true);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('WebView message parse error:', e);
|
||||
}
|
||||
}}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
|
||||
{webViewReady && (
|
||||
<View style={{ position: 'absolute', bottom: 0 }}>
|
||||
<View style={styles.zoomControls}>
|
||||
<TouchableOpacity
|
||||
style={styles.zoomButton}
|
||||
onPress={() => handleZoom('in')}
|
||||
>
|
||||
<Text>
|
||||
<Plus color="#DEDEDE" />
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.divider} />
|
||||
<TouchableOpacity
|
||||
style={styles.zoomButton}
|
||||
onPress={() => handleZoom('out')}
|
||||
>
|
||||
<Text>
|
||||
<Minus color="#DEDEDE" />
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => navigation.navigate('Branches')}
|
||||
>
|
||||
<Text style={styles.buttonText}>{t('Manzilni tekshirish')}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
<BottomModal
|
||||
visible={isModalVisible}
|
||||
onClose={() => setModalVisible(false)}
|
||||
branch={selectedBranch}
|
||||
/>
|
||||
</View>
|
||||
<Navigation />
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListBranches;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1 },
|
||||
zoomControls: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#373737',
|
||||
borderRadius: 20,
|
||||
width: '10%',
|
||||
padding: 4,
|
||||
marginLeft: '85%',
|
||||
bottom: 20,
|
||||
},
|
||||
zoomButton: {
|
||||
borderRadius: 8,
|
||||
width: 40,
|
||||
height: 40,
|
||||
marginVertical: 4,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
divider: { height: 1, width: '100%', backgroundColor: '#DEDEDE' },
|
||||
button: {
|
||||
bottom: 10,
|
||||
width: '95%',
|
||||
margin: 'auto',
|
||||
alignSelf: 'center',
|
||||
backgroundColor: '#009CFF',
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 28,
|
||||
borderRadius: 10,
|
||||
elevation: 4,
|
||||
},
|
||||
buttonText: {
|
||||
color: '#fff',
|
||||
fontWeight: '600',
|
||||
fontSize: 16,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user