import { View, Text, ScrollView, Image, useWindowDimensions, StyleSheet, Pressable, Platform, Linking, ActivityIndicator, Alert } from 'react-native'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { SceneMap, TabBar, TabView } from 'react-native-tab-view'; import NormalButton from '../global/normal_button'; import { router, useFocusEffect, useLocalSearchParams } from 'expo-router'; import { CheckMarkLogoSvg, DirectionLogoSvg, PreviousPageSvg } from '../global/SVG'; import { SafeAreaView } from 'react-native-safe-area-context'; import { chargeStationService } from '../../service/chargeStationService'; import * as Location from 'expo-location'; import { ChargingDetails, Remark, PriceWeek, Special } from '../../service/type/chargeStationType'; import { useTranslation } from '../../util/hooks/useTranslation'; interface ChargingStationTabViewProps { titles: string[]; pricemodel_id: string; } interface ChargingPeriod{ event_name: string; price: number; from: string; to: string; } const ChargingStationTabView: React.FC = ({ titles, pricemodel_id }) => { const layout = useWindowDimensions(); const [list, setList] = useState([]) const [strWeek, setStrWeek] = useState('') const { t } = useTranslation(); // 添加AM/PM标识但保持24小时制 const addPeriodToTime = (timeString: string): string => { // 假设输入格式为 HH.mm 或 HH:mm const [hours] = timeString.split(/[.:]/).map(Number); if (isNaN(hours)) return timeString; const period = hours >= 12 ? 'PM' : 'AM'; return `${timeString}${period}`; }; const fetchElectricityPrice = () => { chargeStationService.fetchElectricityPrice(pricemodel_id || 'a').then(res => { const date = new Date(); const str = (date.toLocaleString('en-US', { weekday: 'short' })).toLowerCase(); setStrWeek(date.toLocaleString('zh', { weekday: 'long' })) const newList = [] as ChargingPeriod[] res?.forEach((item) => { const obj = item[str as keyof PriceWeek] newList.push({event_name: item.event_name, price: obj.price, from: addPeriodToTime(obj.from),to: addPeriodToTime(obj.to)}) setList(newList) }) }) }; useEffect(() => { // 初始加载 fetchElectricityPrice(); // 计算到下一个整点的时间 const now = new Date(); const nextHour = new Date(now); nextHour.setHours(nextHour.getHours() + 1, 0, 0, 0); // 设置为下一个整点 const timeToNextHour = nextHour.getTime() - now.getTime(); // 设置第一次定时器,在下一个整点触发 const firstTimer = setTimeout(() => { fetchElectricityPrice(); // 之后每小时执行一次 const hourlyInterval = setInterval(fetchElectricityPrice, 60 * 60 * 1000); // 保存interval ID以便清理 intervalRef.current = hourlyInterval; }, timeToNextHour); // 保存timeout ID以便清理 timeoutRef.current = firstTimer; // 清理函数 return () => { clearTimeout(firstTimer); if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [pricemodel_id]); // 使用useRef保存定时器ID const timeoutRef = React.useRef(null); const intervalRef = React.useRef(null); //tab 1 const FirstRoute = () => ( {t('charging.result_detail_page.time_period')} {t('charging.result_detail_page.price_per_kwh')} { list.map((item, index) => ( {item.from} - {item.to} ${item.price} )) } ); //tab 2 const SecondRoute = () => ( ); const renderScene = SceneMap({ firstRoute: FirstRoute, secondRoute: SecondRoute }); const [routes] = React.useState([ { key: 'firstRoute', title: titles[0] }, { key: 'secondRoute', title: titles[1] } ]); const [index, setIndex] = React.useState(0); const renderTabBar = (props: any) => ( ); return ( ( {route.title} ) }} /> ); }; const ResultDetailPageComponent = () => { const params = useLocalSearchParams(); const chargeStationID = params.chargeStationID as string; const chargeStationName = params.chargeStationName as string; const chargeStationAddress = params.chargeStationAddress as string; const pricemodel_id = params.pricemodel_id as string; const imageSourceProps = params.imageSource; const stationLng = params.stationLng as string; const stationLat = params.stationLat as string; const [isLoading, setIsLoading] = useState(true); const [imageSource, setImageSource] = useState(); const [currentLocation, setCurrentLocation] = useState(null); const [price, setPrice] = useState(''); const [newAvailableConnectors, setNewAvailableConnectors] = useState([]); const { t } = useTranslation(); useEffect(() => { const imgObj = imageSourceProps? {uri: imageSourceProps} : require('../../assets/dummyStationPicture.png') setImageSource(imgObj); }, [imageSourceProps]) useEffect(() => { const fetchPrice = async () => { try { const price = await chargeStationService.fetchChargeStationPrice(chargeStationID); setPrice(price); } catch (error) { console.error('Error fetching price:', error); } }; const getCurrentLocation = async () => { let { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { console.error('Permission to access location was denied'); return; } let location = await Location.getLastKnownPositionAsync({}); setCurrentLocation(location); }; getCurrentLocation(); fetchPrice(); }, []); useFocusEffect( useCallback(() => { setIsLoading(true); let isMounted = true; // Simple cleanup flag const fetchAllConnectors = async () => { try { const newAvailableConnectors = await chargeStationService.NewfetchAvailableConnectors(); // Only update state if component is still mounted if (isMounted) { setNewAvailableConnectors(newAvailableConnectors); } } catch (error) { console.error('Fetch error:', error); } }; fetchAllConnectors(); setIsLoading(false); // Simple cleanup - prevents state updates if component unmounts return () => { isMounted = false; }; }, []) ); const handleNavigationPress = (StationLat: string, StationLng: string) => { console.log('starting navigation press in resultDetail', StationLat, StationLng); if (StationLat && StationLng) { const label = encodeURIComponent(chargeStationName); const googleMapsUrl = `https://www.google.com/maps/search/?api=1&query=${StationLat},${StationLng}`; // Fallback URL for web browser const webUrl = `https://www.google.com/maps/dir/?api=1&destination=${StationLat},${StationLng}`; Linking.canOpenURL(googleMapsUrl) .then((supported) => { if (supported) { Linking.openURL(googleMapsUrl); } else { Linking.openURL(webUrl).catch((err) => { console.error('An error occurred', err); Alert.alert( t('common.error'), t('charging.result_detail_page.unable_open_maps'), [{ text: t('common.ok') }], { cancelable: false } ); }); } }) .catch((err) => console.error('An error occurred', err)); } }; return ( { if (router.canGoBack()) { router.back(); } else { router.replace('./'); } }} > {chargeStationName} {chargeStationAddress} {t('charging.result_detail_page.route')} } onPress={() => handleNavigationPress(stationLat, stationLng)} extendedStyle={{ backgroundColor: '#E3F2F8', borderRadius: 61, paddingHorizontal: 20, paddingVertical: 6 }} /> Walk-In {/* {distance} */} {t('charging.result_detail_page.charging_fee')} ${price} {t('charging.result_detail_page.per_kwh')} {t('charging.result_detail_page.available_connectors')} {isLoading ? ( {t('charging.result_detail_page.loading')} ) : ( // { newAvailableConnectors.find( (station: any) => station.stationID === chargeStationID )?.availableConnectors } )} {t('charging.result_detail_page.station_info')} ); }; export default ResultDetailPageComponent; const styles = StyleSheet.create({ text: { fontWeight: 300, color: '#000000' }, leftLable: { width: '65%', fontSize: 17, color:'#000000', textAlign: 'center' }, rightLable: { fontSize: 17, width: '30%', textAlign: 'center', borderLeftWidth: 1, paddingLeft: 0, }, });