import Taro from '@tarojs/taro';
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
import { getRemainingSeconds, parseFlexibleEndTimeMs } from '../../../../../utils/Robotaxi-public-method';
|
import { getWaitTime, queryOrderFinishTime, cancelOrder, queryFareBreakdown } from '../../../../api/begin-route';
|
|
type Props = {
|
tripStatus: number;
|
orderId?: string;
|
estimatedWaitMinutes?: number;
|
finishCountdownEnabled: boolean;
|
handleCancelTimeout?: () => void;
|
handleFinishTimeout?: () => void;
|
priceUpdateVersion?: number;
|
};
|
|
const PICKUP_END_TIME_KEY = 'pickupEndTime';
|
const FINISH_END_TIME_KEY = 'finishEndTime';
|
|
const getTargetOrderId = (orderId?: string) => orderId || Taro.getStorageSync('orderId');
|
|
export default function useTripCountdown({
|
tripStatus,
|
orderId,
|
estimatedWaitMinutes,
|
finishCountdownEnabled,
|
handleCancelTimeout,
|
handleFinishTimeout,
|
priceUpdateVersion
|
}: Props) {
|
const [waitMinutes, setWaitMinutes] = useState<number | null>(null);
|
const [limitMinutes, setLimitMinutes] = useState<number | null>(null);
|
const [pickupCountdownSeconds, setPickupCountdownSeconds] = useState<number | null>(null);
|
const [finishCountdownSeconds, setFinishCountdownSeconds] = useState<number | null>(null);
|
const [fareBreakdown, setFareBreakdown] = useState<any>(null);
|
|
const pickupTimerRef = useRef<any>(null);
|
const finishTimerRef = useRef<any>(null);
|
const countdownFinishedRef = useRef(false);
|
const pickupTripStatusRef = useRef(tripStatus);
|
const pickupEndTimeRef = useRef<number | null>(null);
|
const finishEndTimeRef = useRef<number | null>(null);
|
const lastFareOrderIdRef = useRef<string | null>(null);
|
|
const requestFareBreakdown = useCallback((force = false) => {
|
const targetOrderId = getTargetOrderId(orderId);
|
if (!targetOrderId) return;
|
if (!force && lastFareOrderIdRef.current === targetOrderId) return;
|
lastFareOrderIdRef.current = targetOrderId;
|
queryFareBreakdown(targetOrderId).then((res: any) => {
|
setFareBreakdown(res?.data ?? res?.result ?? res);
|
}).catch(() => { });
|
}, [orderId]);
|
|
const clearPickupTimer = useCallback(() => {
|
if (pickupTimerRef.current) {
|
clearTimeout(pickupTimerRef.current);
|
pickupTimerRef.current = null;
|
}
|
}, []);
|
|
const clearFinishTimer = useCallback(() => {
|
if (finishTimerRef.current) {
|
clearTimeout(finishTimerRef.current);
|
finishTimerRef.current = null;
|
}
|
}, []);
|
|
const scheduleAlignedTick = (endMs: number, runner: () => void) => {
|
const now = Date.now();
|
const diffMs = endMs - now;
|
const delay = diffMs > 0 ? (diffMs % 1000 || 1000) : 1000;
|
return setTimeout(runner, delay);
|
};
|
|
useEffect(() => {
|
if (typeof priceUpdateVersion === 'number' && priceUpdateVersion > 0 && [8, 9].includes(tripStatus)) {
|
requestFareBreakdown(true);
|
}
|
}, [priceUpdateVersion, tripStatus, requestFareBreakdown]);
|
|
useEffect(() => {
|
let active = true;
|
if (tripStatus !== 3) return () => { active = false; };
|
if (typeof estimatedWaitMinutes === 'number' && !Number.isNaN(estimatedWaitMinutes)) {
|
setWaitMinutes(estimatedWaitMinutes);
|
return () => { active = false; };
|
}
|
const startSite = Taro.getStorageSync('startSite');
|
getWaitTime({ latitude: startSite?.latitude, longitude: startSite?.longitude })
|
.then((res) => {
|
const minutes = Number(res);
|
if (active && !Number.isNaN(minutes)) {
|
setWaitMinutes(minutes);
|
}
|
})
|
.catch(() => { });
|
return () => {
|
active = false;
|
};
|
}, [tripStatus, estimatedWaitMinutes]);
|
|
useEffect(() => {
|
if (tripStatus !== 4) return;
|
const startSite = Taro.getStorageSync('startSite');
|
const time = Number(startSite?.time);
|
if (!Number.isNaN(time)) {
|
setLimitMinutes(time);
|
}
|
}, [tripStatus]);
|
|
useEffect(() => {
|
pickupTripStatusRef.current = tripStatus;
|
}, [tripStatus]);
|
|
useEffect(() => {
|
if (tripStatus !== 5) {
|
clearPickupTimer();
|
return;
|
}
|
|
const runPickupTick = () => {
|
// 防止状态已切换后,旧定时回调仍触发取消逻辑
|
if (pickupTripStatusRef.current !== 5) {
|
clearPickupTimer();
|
return;
|
}
|
const remaining = getRemainingSeconds(pickupEndTimeRef.current);
|
if (remaining !== null && remaining <= 0) {
|
if (!countdownFinishedRef.current) {
|
countdownFinishedRef.current = true;
|
const targetOrderId = getTargetOrderId(orderId);
|
if (targetOrderId) {
|
cancelOrder(targetOrderId).finally(() => {
|
Taro.removeStorageSync(PICKUP_END_TIME_KEY);
|
handleCancelTimeout?.();
|
});
|
} else {
|
Taro.removeStorageSync(PICKUP_END_TIME_KEY);
|
handleCancelTimeout?.();
|
}
|
}
|
clearPickupTimer();
|
setPickupCountdownSeconds(0);
|
return;
|
}
|
setPickupCountdownSeconds(remaining);
|
if (!pickupEndTimeRef.current) return;
|
pickupTimerRef.current = scheduleAlignedTick(pickupEndTimeRef.current, runPickupTick);
|
};
|
|
const startSite = Taro.getStorageSync('startSite');
|
const limitMin = Number(startSite?.time);
|
const cachedEndTime = Taro.getStorageSync(PICKUP_END_TIME_KEY);
|
const hasValidCached = typeof cachedEndTime === 'number' && cachedEndTime > Date.now();
|
|
if (hasValidCached) {
|
pickupEndTimeRef.current = cachedEndTime;
|
} else if (!Number.isNaN(limitMin) && limitMin > 0) {
|
pickupEndTimeRef.current = Date.now() + limitMin * 60 * 1000;
|
Taro.setStorageSync(PICKUP_END_TIME_KEY, pickupEndTimeRef.current);
|
} else {
|
pickupEndTimeRef.current = null;
|
}
|
|
if (!pickupEndTimeRef.current) {
|
setPickupCountdownSeconds(null);
|
return;
|
}
|
|
countdownFinishedRef.current = false;
|
setPickupCountdownSeconds(getRemainingSeconds(pickupEndTimeRef.current));
|
clearPickupTimer();
|
runPickupTick();
|
|
return () => {
|
clearPickupTimer();
|
};
|
}, [tripStatus, orderId, handleCancelTimeout, clearPickupTimer]);
|
|
useEffect(() => {
|
if (tripStatus !== 7) {
|
clearFinishTimer();
|
return;
|
}
|
|
const runFinishTick = () => {
|
const remaining = getRemainingSeconds(finishEndTimeRef.current);
|
if (remaining !== null && remaining <= 0) {
|
clearFinishTimer();
|
setFinishCountdownSeconds(0);
|
Taro.removeStorageSync(FINISH_END_TIME_KEY);
|
handleFinishTimeout?.();
|
return;
|
}
|
setFinishCountdownSeconds(remaining);
|
if (!finishEndTimeRef.current) return;
|
finishTimerRef.current = scheduleAlignedTick(finishEndTimeRef.current, runFinishTick);
|
};
|
|
const initCountdown = (endTimeMs: number | null) => {
|
finishEndTimeRef.current = endTimeMs;
|
if (typeof endTimeMs === 'number' && endTimeMs > Date.now()) {
|
Taro.setStorageSync(FINISH_END_TIME_KEY, endTimeMs);
|
} else {
|
Taro.removeStorageSync(FINISH_END_TIME_KEY);
|
}
|
const remaining = getRemainingSeconds(endTimeMs);
|
if (remaining === null) {
|
setFinishCountdownSeconds(null);
|
return;
|
}
|
setFinishCountdownSeconds(remaining);
|
clearFinishTimer();
|
runFinishTick();
|
};
|
|
const cachedEndTime = Taro.getStorageSync(FINISH_END_TIME_KEY);
|
const hasValidCached = typeof cachedEndTime === 'number' && cachedEndTime > Date.now();
|
if (hasValidCached) {
|
initCountdown(cachedEndTime);
|
} else {
|
queryOrderFinishTime().then((res: any) => {
|
initCountdown(parseFlexibleEndTimeMs(res));
|
}).catch(() => {
|
setFinishCountdownSeconds(null);
|
finishEndTimeRef.current = null;
|
Taro.removeStorageSync(FINISH_END_TIME_KEY);
|
});
|
}
|
|
return () => {
|
if (typeof finishEndTimeRef.current === 'number' && finishEndTimeRef.current > Date.now()) {
|
Taro.setStorageSync(FINISH_END_TIME_KEY, finishEndTimeRef.current);
|
}
|
clearFinishTimer();
|
};
|
}, [tripStatus, handleFinishTimeout, finishCountdownEnabled, clearFinishTimer]);
|
|
useEffect(() => {
|
if ([8, 9].includes(tripStatus)) {
|
Taro.removeStorageSync(FINISH_END_TIME_KEY);
|
requestFareBreakdown();
|
}
|
}, [tripStatus, requestFareBreakdown]);
|
|
return {
|
waitMinutes,
|
limitMinutes,
|
pickupCountdownSeconds,
|
finishCountdownSeconds,
|
fareBreakdown
|
};
|
}
|