feat(robotaxi/begin-route):增加USER_OPEN_DOOR WebSocket事件处理器,代表当前订单已拉开过车门。去掉之前的前端蓝牙回调、手动点击解锁逻辑
| | |
| | | tripStatus?: NumericLike; |
| | | /** 订单状态。 */ |
| | | orderStatus?: NumericLike; |
| | | /** 当前订单是否已开过车门。 */ |
| | | isOpenDoor?: boolean | NumericLike; |
| | | /** content 扩展数据。 */ |
| | | content?: OrderRouteResponseContent; |
| | | } |
| | |
| | | orderTrunkStatus?: boolean; |
| | | doorUnlockedInCurrentPage?: boolean; |
| | | handleOpenOverRoutePopup?: () => void; |
| | | onDoorUnlockSuccess?: () => void; |
| | | }; |
| | | |
| | | export default function DoorTrunkControl({ |
| | |
| | | orderId, |
| | | orderTrunkStatus, |
| | | doorUnlockedInCurrentPage = false, |
| | | handleOpenOverRoutePopup, |
| | | onDoorUnlockSuccess |
| | | handleOpenOverRoutePopup |
| | | }: Props) { |
| | | const [trunkStatus, setTrunkStatus] = useState(orderTrunkStatus); // 后备箱开关状态 |
| | | const [lockStatus, setLockStatus] = useState(false); // 车门开关状态 |
| | |
| | | if (doorType === 'door') { |
| | | // Taro.hideLoading(); |
| | | stopLoading(doorType); |
| | | Taro.showToast({ title: '右侧车门已解锁', icon: 'none' }); |
| | | setLockStatus(true); |
| | | onDoorUnlockSuccess?.(); |
| | | Taro.showToast({ title: '开门指令已发送', icon: 'none' }); |
| | | onSuccess?.(); |
| | | } |
| | | }).catch(() => { |
| | |
| | | handleModifyDestination?: () => void; |
| | | handleShareTrip?: () => void; |
| | | showModifyDestination?: boolean; |
| | | showCancelTrip?: boolean; |
| | | }; |
| | | |
| | | export default function TripActionBar({ |
| | |
| | | handleCancelTrip, |
| | | handleModifyDestination, |
| | | handleShareTrip, |
| | | showModifyDestination = true |
| | | showModifyDestination = true, |
| | | showCancelTrip = false |
| | | }: Props) { |
| | | const canShowTripAction = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(tripStatus); |
| | | // 按行程状态控制按钮集合,避免在 JSX 中堆叠过多条件判断 |
| | |
| | | key: "cancel", |
| | | label: "取消行程", |
| | | icon: 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/cancel-trip.png', |
| | | visible: tripStatus === TripStatus.VehicleApproaching, |
| | | visible: showCancelTrip, |
| | | onClick: handleCancelTrip |
| | | }, |
| | | { |
| | |
| | | isArriveSoon: boolean; |
| | | isPickupCountdownSoon: boolean; |
| | | pickupCountdownSeconds: number | null; |
| | | doorUnlockedInCurrentPage: boolean; |
| | | finishCountdownEnabled: boolean; |
| | | finishCountdownSeconds: number | null; |
| | | limitMinutes: number | null; |
| | |
| | | isArriveSoon, |
| | | isPickupCountdownSoon, |
| | | pickupCountdownSeconds, |
| | | doorUnlockedInCurrentPage, |
| | | finishCountdownEnabled, |
| | | finishCountdownSeconds, |
| | | limitMinutes, |
| | |
| | | [TripStatus.VehicleApproaching]: isArriveSoon |
| | | ? <>车辆即将到达,请准备上车</> |
| | | : <>车辆还有<View className="time">{durationDisplay.value}</View>{durationDisplay.unit}到达</>, |
| | | [TripStatus.VehicleArrivedPickup]: <>{isPickupCountdownSoon ? '车辆已到达,请尽快上车' : '车辆已到达上车点'}<View className="time" style={isPickupCountdownSoon ? { color: '#f58900' } : undefined}>{pickupCountdownSeconds !== null ? formatCountdown(pickupCountdownSeconds) : '--:--'}</View></>, |
| | | [TripStatus.VehicleArrivedPickup]: doorUnlockedInCurrentPage |
| | | ? <>车辆已到达,请尽快上车</> |
| | | : <>{isPickupCountdownSoon ? '车辆已到达,请尽快上车' : '车辆已到达上车点'}<View className="time" style={isPickupCountdownSoon ? { color: '#f58900' } : undefined}>{pickupCountdownSeconds !== null ? formatCountdown(pickupCountdownSeconds) : '--:--'}</View></>, |
| | | [TripStatus.InService]: <>预计<View className="time">{inServiceDurationDisplay.value}</View>{inServiceDurationDisplay.unit}抵达目的地</>, |
| | | [TripStatus.ArrivedDestination]: (finishCountdownEnabled || finishCountdownSeconds !== null) |
| | | ? <>已到达目的地<View className="time">{finishCountdownSeconds !== null ? formatCountdown(finishCountdownSeconds) : '--:--'}</View></> |
| | |
| | | const descMap = { |
| | | [TripStatus.CallingCar]: { text: '正全力为您叫车中,请耐心等待...', className: 'calling-car-content-desc' }, |
| | | [TripStatus.VehicleApproaching]: limitMinutes ? { text: `此处限停${limitMinutes}分钟,请尽快前往上车点`, className: 'calling-car-content-warning' } : null, |
| | | [TripStatus.VehicleArrivedPickup]: { text: isPickupCountdownSoon ? `车辆到达后最多等待${limitMinutes}分钟,超时自动取消` : '此处限停,请在倒计时结束前到达上车点', className: 'calling-car-content-desc' }, |
| | | [TripStatus.VehicleArrivedPickup]: doorUnlockedInCurrentPage |
| | | ? { text: '车门已解锁,请尽快上车', className: 'calling-car-content-desc' } |
| | | : { text: isPickupCountdownSoon ? `车辆到达后最多等待${limitMinutes}分钟,超时自动取消` : '此处限停,请在倒计时结束前到达上车点', className: 'calling-car-content-desc' }, |
| | | [TripStatus.InService]: { text: '行程已开始,请全程系好安全带', className: 'calling-car-content-desc' }, |
| | | [TripStatus.ArrivedDestination]: { text: '倒计时结束时车辆将自动驶离,请带好物品安全下车', className: 'calling-car-content-desc' }, |
| | | [TripStatus.TripFinished]: { text: '订单待支付,请核对后完成支付', className: 'calling-car-content-desc' } |
| | |
| | | type Props = { |
| | | tripStatus: TripStatus; |
| | | orderId?: string; |
| | | doorUnlockedInCurrentPage?: boolean; |
| | | estimatedWaitMinutes?: number; |
| | | finishCountdownEnabled: boolean; |
| | | handleCancelTimeout?: () => void; |
| | |
| | | export default function useTripCountdown({ |
| | | tripStatus, |
| | | orderId, |
| | | doorUnlockedInCurrentPage = false, |
| | | estimatedWaitMinutes, |
| | | finishCountdownEnabled, |
| | | handleCancelTimeout, |
| | |
| | | const finishTimerRef = useRef<any>(null); |
| | | const countdownFinishedRef = useRef(false); |
| | | const pickupTripStatusRef = useRef(tripStatus); |
| | | const pickupDoorUnlockedRef = useRef(doorUnlockedInCurrentPage); |
| | | const pickupEndTimeRef = useRef<number | null>(null); |
| | | const finishEndTimeRef = useRef<number | null>(null); |
| | | const lastFareRequestKeyRef = useRef<string | null>(null); // 费用明细请求去重键(订单+优惠券) |
| | |
| | | }, [tripStatus]); |
| | | |
| | | useEffect(() => { |
| | | pickupDoorUnlockedRef.current = doorUnlockedInCurrentPage; |
| | | }, [doorUnlockedInCurrentPage]); |
| | | |
| | | const stopPickupCountdown = useCallback((clearStoredEndTime = false) => { |
| | | clearPickupTimer(); |
| | | pickupEndTimeRef.current = null; |
| | | if (clearStoredEndTime) { |
| | | Taro.removeStorageSync(PICKUP_END_TIME_KEY); |
| | | } |
| | | setPickupCountdownSeconds(null); |
| | | }, [clearPickupTimer]); |
| | | |
| | | useEffect(() => { |
| | | if (tripStatus !== TripStatus.VehicleArrivedPickup) { |
| | | clearPickupTimer(); |
| | | stopPickupCountdown(false); |
| | | return; |
| | | } |
| | | if (doorUnlockedInCurrentPage) { |
| | | stopPickupCountdown(true); |
| | | return; |
| | | } |
| | | |
| | | const runPickupTick = () => { |
| | | // 防止状态已切换后,旧定时回调仍触发取消逻辑 |
| | | if (pickupTripStatusRef.current !== TripStatus.VehicleArrivedPickup) { |
| | | clearPickupTimer(); |
| | | if (pickupTripStatusRef.current !== TripStatus.VehicleArrivedPickup || pickupDoorUnlockedRef.current) { |
| | | stopPickupCountdown(pickupDoorUnlockedRef.current); |
| | | return; |
| | | } |
| | | const remaining = getRemainingSeconds(pickupEndTimeRef.current); |
| | |
| | | return () => { |
| | | clearPickupTimer(); |
| | | }; |
| | | }, [tripStatus, orderId, handleCancelTimeout, clearPickupTimer]); |
| | | }, [tripStatus, orderId, doorUnlockedInCurrentPage, handleCancelTimeout, clearPickupTimer, stopPickupCountdown]); |
| | | |
| | | useEffect(() => { |
| | | if (tripStatus !== TripStatus.ArrivedDestination) { |
| | |
| | | handleCloseSuccessAuthPopup?: () => void; |
| | | handleAuthorizeBluetooth?: () => void; |
| | | authSuccess?: boolean; |
| | | onDoorUnlockSuccess?: () => void; |
| | | pageDidShowVersion?: number; |
| | | onOpenCouponSelect?: () => void; |
| | | queuePollingTimeSeconds?: number; |
| | |
| | | handleCloseSuccessAuthPopup, |
| | | handleAuthorizeBluetooth, |
| | | authSuccess = false, |
| | | onDoorUnlockSuccess, |
| | | pageDidShowVersion = 0, |
| | | onOpenCouponSelect, |
| | | queuePollingTimeSeconds = 10 |
| | |
| | | const isArriveSoon = tripStatus === TripStatus.VehicleApproaching && (typeof routeDurationSeconds === 'number' && routeDurationSeconds <= 60); |
| | | /** 上车倒计时即将结束(<60秒) */ |
| | | const isPickupCountdownSoon = tripStatus === TripStatus.VehicleArrivedPickup && (typeof pickupCountdownSeconds === 'number' && pickupCountdownSeconds < 60); |
| | | /** 开门后,接驾态不再展示取消行程。 */ |
| | | const showCancelTrip = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup].includes(tripStatus) && !doorUnlockedInCurrentPage; |
| | | |
| | | // 按状态组合渲染行程卡片模块 |
| | | return ( |
| | |
| | | isArriveSoon={isArriveSoon} |
| | | isPickupCountdownSoon={isPickupCountdownSoon} |
| | | pickupCountdownSeconds={pickupCountdownSeconds} |
| | | doorUnlockedInCurrentPage={doorUnlockedInCurrentPage} |
| | | finishCountdownEnabled={finishCountdownEnabled} |
| | | finishCountdownSeconds={finishCountdownSeconds} |
| | | limitMinutes={limitMinutes} |
| | |
| | | orderTrunkStatus={orderTrunkStatus} |
| | | doorUnlockedInCurrentPage={doorUnlockedInCurrentPage} |
| | | handleOpenOverRoutePopup={handleOpenOverRoutePopup} |
| | | onDoorUnlockSuccess={onDoorUnlockSuccess} |
| | | /> |
| | | |
| | | <FareDetailCard |
| | |
| | | <TripActionBar |
| | | tripStatus={tripStatus} |
| | | handleCancelTrip={handleCancelTrip} |
| | | showCancelTrip={showCancelTrip} |
| | | handleModifyDestination={handleModifyDestination} |
| | | showModifyDestination={showModifyDestination} |
| | | handleShareTrip={handleShareTrip} |
| | |
| | | orderTrunkStatus={orderTrunkStatus} |
| | | doorUnlockedInCurrentPage={doorUnlockedInCurrentPage} |
| | | handleOpenOverRoutePopup={handleOpenOverRoutePopup} |
| | | onDoorUnlockSuccess={onDoorUnlockSuccess} |
| | | /> |
| | | |
| | | <EvaluationCard |
| | |
| | | type Deps = { |
| | | orderId: string; |
| | | tripStatus: TripStatus; |
| | | onBluetoothDoorUnlockSuccess?: () => void; |
| | | }; |
| | | |
| | | type BluetoothTokenPayload = { |
| | |
| | | |
| | | type ConnectStatus = 0 | 1 | 2 | 3; |
| | | const RETRY_UNLOCK_MESSAGE = '请点击解锁按钮解锁车门'; // 鉴权失败/解锁失败统一提示文案 |
| | | const UNLOCK_SUCCESS_MESSAGE = '车门已解锁,请上车'; // 解锁成功提示文案 |
| | | const BLUETOOTH_AUTH_ACTIVE_TRIP_STATUSES = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup] as const; // 仅这两个状态允许处理蓝牙鉴权回调 |
| | | |
| | | /** |
| | |
| | | /** |
| | | * 蓝牙车控编排:负责凭证获取、蓝牙连接和 RtAuth 鉴权。 |
| | | */ |
| | | export default function useBluetoothCarControl({ orderId, tripStatus, onBluetoothDoorUnlockSuccess }: Deps) { |
| | | export default function useBluetoothCarControl({ orderId, tripStatus }: Deps) { |
| | | const managerRef = useRef<any>(null); // 蓝牙管理器实例引用 |
| | | const lastStartKeyRef = useRef(''); // 最近一次启动鉴权的参数签名 |
| | | const startAuthTaskRef = useRef<Promise<void> | null>(null); // 当前进行中的鉴权任务,防止并发重复请求 |
| | |
| | | const deviceModelRef = useRef(resolveDeviceModel()); // 当前设备型号缓存 |
| | | const latestTripStatusRef = useRef(tripStatus); // 最新行程状态引用,供异步蓝牙回调读取 |
| | | const unlockFinishedRef = useRef(false); // 当前轮次是否已出现解锁成功 |
| | | const manualDoorUnlockRef = useRef(false); // 当前轮次是否已手动解锁,命中后停止后续蓝牙鉴权 |
| | | const acceptBluetoothCallbacksRef = useRef(false); // 当前轮次是否继续接收蓝牙回调,离开 4/5 或收口后立即关闭 |
| | | const [connectStatus, setConnectStatus] = useState<ConnectStatus>(0); // 蓝牙连接状态:0断开/1连接中/2已连接/3已鉴权 |
| | | const [authSuccess, setAuthSuccess] = useState(false); // RtAuth 鉴权是否成功 |
| | |
| | | }); |
| | | }; // 统一展示蓝牙解锁结果提示 |
| | | latestTripStatusRef.current = tripStatus; |
| | | |
| | | /** |
| | | * 收口蓝牙真实解锁成功后的状态、提示与外部 UI 联动。 |
| | | */ |
| | | const handleBluetoothDoorUnlockSuccess = () => { |
| | | if (unlockFinishedRef.current) { |
| | | return; |
| | | } |
| | | unlockFinishedRef.current = true; |
| | | setAuthError(''); |
| | | onBluetoothDoorUnlockSuccess?.(); |
| | | const storageKey = `ROBOTAXI_UNLOCK_TOAST_SHOWN_${orderId}`; |
| | | if (!Taro.getStorageSync(storageKey)) { |
| | | showUnlockToast(UNLOCK_SUCCESS_MESSAGE); |
| | | Taro.setStorageSync(storageKey, true); |
| | | } |
| | | closeConnectionRef.current(); |
| | | }; |
| | | |
| | | /** |
| | | * 判断当前蓝牙异步回调是否仍属于有效轮次。 |
| | |
| | | console.log('[蓝牙] 解锁通知:', result); |
| | | if (result?.type === 'pollingUnlock') { |
| | | const isAlreadyUnlocked = Number(result?.data?.result) === 2; // RtNotification 挂载在 data,result=2 表示车辆已解锁 |
| | | if (isAlreadyUnlocked) { |
| | | handleBluetoothDoorUnlockSuccess(); |
| | | return; |
| | | } |
| | | if (result?.success) { |
| | | handleBluetoothDoorUnlockSuccess(); |
| | | if (isAlreadyUnlocked || result?.success) { |
| | | unlockFinishedRef.current = true; |
| | | setAuthError(''); |
| | | closeConnectionRef.current(); |
| | | setIsAuthorizing(false); |
| | | } else { |
| | | setAuthError(RETRY_UNLOCK_MESSAGE); |
| | | showUnlockToast(RETRY_UNLOCK_MESSAGE); |
| | | showUnlockToast(RETRY_UNLOCK_MESSAGE); |
| | | } |
| | | return; |
| | |
| | | }); |
| | | if (!BLUETOOTH_AUTH_ACTIVE_TRIP_STATUSES.includes(tripStatus)) return; |
| | | if (!orderId) return; |
| | | if (manualDoorUnlockRef.current) { |
| | | console.log('[蓝牙] 当前轮次已手动解锁,跳过后续鉴权请求'); |
| | | return; |
| | | } |
| | | if (startAuthTaskRef.current) return startAuthTaskRef.current; |
| | | |
| | | const startKey = `${orderId}`; |
| | |
| | | const runTask = async () => { |
| | | try { |
| | | const tokenRes = await getBluetoothUnlockToken(orderId); |
| | | if (manualDoorUnlockRef.current) { |
| | | console.log('[蓝牙] 用户已手动解锁,忽略鉴权凭证返回结果'); |
| | | isAuthorizingRef.current = false; |
| | | setIsAuthorizing(false); |
| | | return; |
| | | } |
| | | const tokenPayload: BluetoothTokenPayload = typeof tokenRes === 'object' && tokenRes !== null ? tokenRes as BluetoothTokenPayload : {}; |
| | | const certificate = typeof tokenPayload.token === 'string' ? tokenPayload.token : String(tokenPayload.token || ''); |
| | | const mainMacAddress = normalizeMac(tokenPayload.macAddress); |
| | |
| | | certificate, |
| | | deviceModel: deviceModelRef.current |
| | | }); |
| | | if (manualDoorUnlockRef.current) { |
| | | console.log('[蓝牙] 用户已手动解锁,停止打开蓝牙适配器'); |
| | | isAuthorizingRef.current = false; |
| | | setIsAuthorizing(false); |
| | | return; |
| | | } |
| | | console.log('[蓝牙] 准备打开蓝牙适配器'); |
| | | manager.openBluetoothAdapter(); |
| | | } catch (error: any) { |
| | | if (manualDoorUnlockRef.current) { |
| | | console.log('[蓝牙] 手动解锁后忽略鉴权异常:', error); |
| | | isAuthorizingRef.current = false; |
| | | setIsAuthorizing(false); |
| | | return; |
| | | } |
| | | console.log('[蓝牙] 鉴权流程异常:', error); |
| | | isAuthorizingRef.current = false; |
| | | setAuthError(error?.msg || '获取蓝牙鉴权凭证失败'); |
| | |
| | | */ |
| | | const retryAuth = useCallback(() => { |
| | | console.log('[蓝牙] 重试鉴权'); |
| | | if (manualDoorUnlockRef.current) { |
| | | console.log('[蓝牙] 当前轮次已手动解锁,忽略重试鉴权'); |
| | | return; |
| | | } |
| | | if (!managerRef.current) { |
| | | startAuth(); |
| | | return; |
| | |
| | | setUnlockTokenFailed(false); |
| | | managerRef.current.retryConnection(); |
| | | }, [startAuth]); |
| | | |
| | | /** |
| | | * 用户手动解锁成功后,停止当前轮次剩余的蓝牙鉴权流程。 |
| | | */ |
| | | const markManualDoorUnlock = useCallback(() => { |
| | | console.log('[蓝牙] 用户已手动解锁,停止后续蓝牙鉴权'); |
| | | manualDoorUnlockRef.current = true; |
| | | unlockFinishedRef.current = true; |
| | | if (orderId) { |
| | | Taro.setStorageSync(`ROBOTAXI_UNLOCK_TOAST_SHOWN_${orderId}`, true); |
| | | } |
| | | acceptBluetoothCallbacksRef.current = false; |
| | | setAuthError(''); |
| | | setUnlockTokenFailed(false); |
| | | closeConnectionRef.current(); |
| | | }, [orderId]); |
| | | |
| | | useEffect(() => { |
| | | if (!BLUETOOTH_AUTH_ACTIVE_TRIP_STATUSES.includes(tripStatus)) { |
| | |
| | | authSuccessRef.current = false; |
| | | isAuthorizingRef.current = false; |
| | | unlockFinishedRef.current = false; |
| | | manualDoorUnlockRef.current = false; // 重置手动解锁状态,供下一次使用 |
| | | setAuthError(''); |
| | | setUnlockTokenFailed(false); |
| | | |
| | | // 行程进入 6(InService 行驶中)或以上,或者是回退/取消等情况时,清除该订单已弹出过成功解锁的标识缓存 |
| | | if (orderId) { |
| | | Taro.removeStorageSync(`ROBOTAXI_UNLOCK_TOAST_SHOWN_${orderId}`); |
| | | } |
| | | } |
| | | }, [tripStatus, orderId]); |
| | | }, [tripStatus]); |
| | | |
| | | return { |
| | | connectStatus, |
| | |
| | | isAuthorizing, |
| | | unlockTokenFailed, |
| | | startAuth, |
| | | markManualDoorUnlock, |
| | | retryAuth, |
| | | closeConnection |
| | | }; |
| | |
| | | /** 时长,单位秒。 */ |
| | | durationSeconds: number | null; |
| | | }) => void; |
| | | /** 根据订单路线接口回显同步当前订单开门状态。 */ |
| | | syncDoorUnlockedStateFromOrderRoute?: (isOpenDoor: boolean) => void; |
| | | /** 设置当前用户定位。 */ |
| | | setCurrentUserLocation?: (location: CurrentUserLocation) => void; |
| | | }; |
| | |
| | | return ((direction % 360) + 360) % 360; |
| | | }; |
| | | |
| | | /** 统一解析订单是否已开门。 */ |
| | | const resolveDoorOpenedValue = (payload: any) => { |
| | | const rawValue = payload?.isOpenDoor ?? payload?.content?.isOpenDoor; |
| | | if (rawValue === true || rawValue === 1 || rawValue === '1' || rawValue === 'true') { |
| | | return true; |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | /** 归一化路线选项,补齐校验字段。 */ |
| | | const normalizeRouteOption = (item: any, snapshotId: any, generatedAtMillis: any) => ({ |
| | | ...(item || {}), |
| | |
| | | |
| | | requestHandler({ orderId: targetOrderId, routeType }) |
| | | .then((response: any) => { |
| | | deps.syncDoorUnlockedStateFromOrderRoute?.(resolveDoorOpenedValue(response)); |
| | | const beforeUserSite = parseBeforeUserEndSite( |
| | | response?.endLongitude, |
| | | response?.endLatitude ?? response?.endlatitude |
| | |
| | | /** 去授权蓝牙 */ |
| | | handleAuthorizeBluetooth?: () => void; |
| | | bluetoothAuthSuccess?: boolean; |
| | | /** 手动解锁车门成功 */ |
| | | onDoorUnlockSuccess?: () => void; |
| | | /** 页面 didShow 版本号 */ |
| | | pageDidShowVersion: number; |
| | | /** 排队位次轮询频率(秒) */ |
| | |
| | | } = useTripCountdown({ |
| | | tripStatus: deps.tripStatus, |
| | | orderId: deps.orderId, |
| | | doorUnlockedInCurrentPage: deps.doorUnlockedInCurrentPage, |
| | | estimatedWaitMinutes: deps.fareEstimate?.waitingTime, |
| | | finishCountdownEnabled: deps.finishCountdownEnabled, |
| | | handleCancelTimeout: deps.handlePickupTimeoutAutoCancel, |
| | |
| | | handleCloseSuccessAuthPopup={deps.handleCloseSuccessAuthPopup} |
| | | handleAuthorizeBluetooth={deps.handleAuthorizeBluetooth} |
| | | authSuccess={deps.bluetoothAuthSuccess} |
| | | onDoorUnlockSuccess={deps.onDoorUnlockSuccess} |
| | | pageDidShowVersion={deps.pageDidShowVersion} |
| | | queuePollingTimeSeconds={deps.queuePollingTimeSeconds} |
| | | /> |
| | |
| | | createOpenTrunkSocketHandler, |
| | | createOrderReassignSocketHandler, |
| | | createOrderUpdatePriceSocketHandler, |
| | | createUserOpenDoorSocketHandler, |
| | | createUpdateEndAddressStatusSocketHandler |
| | | } from './web-socket-handlers/create-order-meta-socket-handlers'; |
| | | import { createOrderRouteSocketHandler } from './web-socket-handlers/create-order-route-socket-handler'; |
| | |
| | | } | null; |
| | | setPriceUpdateVersion: (updater: (v: number) => number) => void; |
| | | setFinishCountdownEnabled: (value: boolean) => void; |
| | | setDoorUnlockedInCurrentPage: React.Dispatch<React.SetStateAction<boolean>>; |
| | | setShowAnnouncementsPopup: (value: boolean) => void; |
| | | setRoutePoints: (points: any[]) => void; |
| | | setPolyline: (lines: any[]) => void; |
| | |
| | | | 'order_status' |
| | | | 'order_route' |
| | | | 'open_trunk' |
| | | | 'user_open_door' |
| | | | 'order_update_price' |
| | | | 'order_reassign' |
| | | | 'update_end_address_status'; |
| | |
| | | 'order_status', |
| | | 'order_route', |
| | | 'open_trunk', |
| | | 'user_open_door', |
| | | 'order_update_price', |
| | | 'order_reassign', |
| | | 'update_end_address_status' |
| | |
| | | order_status: false, |
| | | order_route: false, |
| | | open_trunk: false, |
| | | user_open_door: false, |
| | | order_update_price: false, |
| | | order_reassign: false, |
| | | update_end_address_status: false |
| | |
| | | order_status: null, |
| | | order_route: null, |
| | | open_trunk: null, |
| | | user_open_door: null, |
| | | order_update_price: null, |
| | | order_reassign: null, |
| | | update_end_address_status: null |
| | |
| | | order_status: createOrderStatusSocketHandler(depsRef, lastFetchedVehicleOrderIdRef), |
| | | order_route: createOrderRouteSocketHandler(depsRef), |
| | | open_trunk: createOpenTrunkSocketHandler(depsRef), |
| | | user_open_door: createUserOpenDoorSocketHandler(depsRef), |
| | | order_update_price: createOrderUpdatePriceSocketHandler(depsRef), |
| | | order_reassign: createOrderReassignSocketHandler(depsRef, lastFetchedVehicleOrderIdRef), |
| | | update_end_address_status: createUpdateEndAddressStatusSocketHandler(depsRef) |
| | |
| | | }; |
| | | |
| | | /** |
| | | * 创建用户开门成功消息处理器。 |
| | | */ |
| | | export const createUserOpenDoorSocketHandler = (depsRef: WebSocketDepsRef) => (payload: any) => { |
| | | executeWhenOrderMatched({ |
| | | payload, |
| | | deps: depsRef.current, |
| | | resolveIncomingOrderId: resolvePayloadOrderIdFromTopLevelFirst, |
| | | onMatched: () => { |
| | | depsRef.current.setDoorUnlockedInCurrentPage(true); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 创建价格更新消息处理器。 |
| | | */ |
| | | export const createOrderUpdatePriceSocketHandler = (depsRef: WebSocketDepsRef) => (payload: any) => { |
| | |
| | | vehicleMoveAlongPoints, |
| | | setVehicleDirection, |
| | | setOrderRouteMetrics, |
| | | syncDoorUnlockedStateFromOrderRoute: (isOpenDoor) => { |
| | | if (!isOpenDoor) { |
| | | return; |
| | | } |
| | | setDoorUnlockedInCurrentPage(true); |
| | | }, |
| | | setCurrentUserLocation |
| | | }, { |
| | | syncSmoothRouteBaselineRef, |
| | |
| | | vehicleLocation, |
| | | setPriceUpdateVersion, |
| | | setFinishCountdownEnabled, |
| | | setDoorUnlockedInCurrentPage, |
| | | setShowAnnouncementsPopup, |
| | | setRoutePoints, |
| | | setPolyline, |
| | |
| | | useEffect(() => { |
| | | setDoorUnlockedInCurrentPage(false); |
| | | }, [orderId]); |
| | | |
| | | /** |
| | | * 蓝牙真实解锁成功后,同步当前页面的车锁打开态。 |
| | | */ |
| | | const handleBluetoothDoorUnlockSuccess = useCallback(() => { |
| | | setDoorUnlockedInCurrentPage(true); |
| | | }, []); |
| | | |
| | | const { queryPaymentResult: queryOrderPaymentResult } = useOrderPaymentResultQuery({ |
| | | orderId, |
| | |
| | | const { |
| | | authSuccess: bluetoothAuthSuccess, |
| | | startAuth: startBluetoothAuth, |
| | | markManualDoorUnlock, |
| | | unlockTokenFailed |
| | | } = useBluetoothCarControl({ |
| | | orderId, |
| | | tripStatus, |
| | | onBluetoothDoorUnlockSuccess: handleBluetoothDoorUnlockSuccess |
| | | tripStatus |
| | | }); |
| | | /** |
| | | * 手动点击开门成功后,同步车锁打开态并停止后续蓝牙自动解锁流程。 |
| | | */ |
| | | const handleManualDoorUnlockSuccess = useCallback(() => { |
| | | setDoorUnlockedInCurrentPage(true); |
| | | markManualDoorUnlock(); |
| | | }, [markManualDoorUnlock]); |
| | | const { |
| | | bluetoothStatusReady, |
| | | bluetoothCardDescription, |
| | |
| | | |
| | | useDidShow(() => { |
| | | setPageDidShowVersion((v) => v + 1); |
| | | setDoorUnlockedInCurrentPage(false); |
| | | handleDidShow(router.params?.skipPageFlag, router.params?.fromModifyDestination); |
| | | updateMapViewportPadding(); |
| | | }); |
| | |
| | | handleCloseSuccessAuthPopup, |
| | | handleAuthorizeBluetooth, |
| | | bluetoothAuthSuccess, |
| | | onDoorUnlockSuccess: handleManualDoorUnlockSuccess, |
| | | pageDidShowVersion, |
| | | queuePollingTimeSeconds |
| | | }); // 视图渲染委托 |
| | |
| | | const isOpen = content?.isOpen ?? content?.open; |
| | | this.notifyListeners('open_trunk', { isOpen, data, content }); |
| | | } |
| | | if (data?.type === 'USER_OPEN_DOOR') { |
| | | const orderId = content?.orderId ?? data?.orderId; |
| | | this.notifyListeners('user_open_door', { orderId, data, content }); |
| | | } |
| | | if (data?.type === 'UPDATE_END_ADDRESS_STATUS') { |
| | | const status = Number(content?.status); |
| | | this.notifyListeners('update_end_address_status', { status, data, content }); |