From f12ad358230b9c3e5b9382c294288230347bca6e Mon Sep 17 00:00:00 2001
From: hejianhao <15708179461@qq.com>
Date: 星期一, 25 五月 2026 11:45:04 +0800
Subject: [PATCH] refactor(robotaxi): 提取通用图片上传钩子并优化地图视口逻辑

---
 src/robotaxi/hooks/use-image-upload.ts                       |  225 ++++++++++++++++++++++
 src/robotaxi/pages/begin-route/components/evaluate-popup.tsx |  158 ++-------------
 src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts     |  197 ++++++++++++++++---
 3 files changed, 415 insertions(+), 165 deletions(-)

diff --git a/src/robotaxi/hooks/use-image-upload.ts b/src/robotaxi/hooks/use-image-upload.ts
new file mode 100644
index 0000000..e82b86d
--- /dev/null
+++ b/src/robotaxi/hooks/use-image-upload.ts
@@ -0,0 +1,225 @@
+import Taro from '@tarojs/taro';
+import { useCallback, useMemo, useState } from 'react';
+
+/**
+ * 上传成功后的服务端文件信息。
+ */
+export type UploadedServerFile = {
+  id: string;
+  name: string;
+  url: string;
+};
+
+/**
+ * 图片上传项状态。
+ */
+export type ImageUploadItem = {
+  id: string;
+  localUrl: string;
+  server: UploadedServerFile | null;
+  status: 'uploading' | 'success' | 'failed';
+};
+
+/**
+ * 图片上传 Hook 配置。
+ */
+type UseImageUploadOptions = {
+  maxCount: number;
+  uploadUrl: string;
+  compress?: boolean;
+  sizeLimitBytes?: number;
+};
+
+const DEFAULT_IMAGE_SIZE_LIMIT_BYTES = 5 * 1024 * 1024;
+const IMAGE_COMPRESS_QUALITIES = [80, 60, 40];
+
+/**
+ * 图片上传:封装选择、压缩、上传、重试、删除与重置。
+ */
+export default function useImageUpload(options: UseImageUploadOptions) {
+  const [images, setImages] = useState<ImageUploadItem[]>([]);
+
+  /**
+   * 更新指定图片项。
+   */
+  const updateImageById = useCallback((imageId: string, updater: (item: ImageUploadItem) => ImageUploadItem) => {
+    setImages((prev) => prev.map((item) => (item.id === imageId ? updater(item) : item)));
+  }, []);
+
+  /**
+   * 获取文件大小。
+   */
+  const getFileSize = useCallback(async (filePath: string) => {
+    const fs = Taro.getFileSystemManager();
+    const info: any = await new Promise((resolve, reject) => {
+      fs.getFileInfo({
+        filePath,
+        success: resolve,
+        fail: reject
+      });
+    });
+    return Number(info?.size || 0);
+  }, []);
+
+  /**
+   * 压缩图片到体积限制内。
+   */
+  const ensureSizeUnderLimit = useCallback(async (filePath: string) => {
+    if (options.compress === false) {
+      return filePath;
+    }
+    const sizeLimitBytes = options.sizeLimitBytes || DEFAULT_IMAGE_SIZE_LIMIT_BYTES;
+    try {
+      let currentFilePath = filePath;
+      const currentSize = await getFileSize(currentFilePath);
+      if (currentSize <= sizeLimitBytes) {
+        return currentFilePath;
+      }
+      for (const quality of IMAGE_COMPRESS_QUALITIES) {
+        try {
+          const compressResult: any = await Taro.compressImage({
+            src: currentFilePath,
+            quality
+          });
+          const compressedPath = compressResult?.tempFilePath || currentFilePath;
+          const compressedSize = await getFileSize(compressedPath);
+          currentFilePath = compressedPath;
+          if (compressedSize <= sizeLimitBytes) {
+            return currentFilePath;
+          }
+        } catch (error) {
+          // 单次压缩失败时继续尝试更低质量。
+        }
+      }
+      return currentFilePath;
+    } catch (error) {
+      return filePath;
+    }
+  }, [getFileSize, options.compress, options.sizeLimitBytes]);
+
+  /**
+   * 上传单张图片。
+   */
+  const uploadSingle = useCallback(async (imageId: string, filePath: string) => {
+    updateImageById(imageId, (item) => ({
+      ...item,
+      status: 'uploading'
+    }));
+    try {
+      const finalFilePath = await ensureSizeUnderLimit(filePath);
+      const uploadResult = await Taro.uploadFile({
+        url: options.uploadUrl,
+        filePath: finalFilePath,
+        name: 'file'
+      });
+      let responseData: any = uploadResult.data;
+      try {
+        if (typeof responseData === 'string') {
+          responseData = JSON.parse(responseData);
+        }
+      } catch (error) {
+        responseData = null;
+      }
+      const payload = responseData?.data;
+      const serverFile = {
+        id: String(payload?.id || ''),
+        name: String(payload?.name || ''),
+        url: String(payload?.url || '')
+      };
+      if (!serverFile.id || !serverFile.url) {
+        throw new Error('invalid upload response');
+      }
+      updateImageById(imageId, (item) => ({
+        ...item,
+        server: serverFile,
+        status: 'success'
+      }));
+    } catch (error) {
+      updateImageById(imageId, (item) => ({
+        ...item,
+        status: 'failed'
+      }));
+    }
+  }, [ensureSizeUnderLimit, options.uploadUrl, updateImageById]);
+
+  /**
+   * 生成本地图片项。
+   */
+  const createImageItem = useCallback((localUrl: string, index: number): ImageUploadItem => ({
+    id: `${Date.now()}-${index}-${localUrl}`,
+    localUrl,
+    server: null,
+    status: 'uploading'
+  }), []);
+
+  /**
+   * 选择图片并立即上传。
+   */
+  const chooseAndUpload = useCallback(async () => {
+    const remainCount = Math.max(0, options.maxCount - images.length);
+    if (remainCount <= 0) {
+      Taro.showToast({
+        title: `最多可上传${options.maxCount}张图片`,
+        icon: 'none'
+      });
+      return;
+    }
+    try {
+      const chooseResult = await Taro.chooseImage({
+        count: remainCount,
+        sizeType: ['original', 'compressed'],
+        sourceType: ['album', 'camera']
+      });
+      const selectedPaths: string[] = chooseResult?.tempFilePaths || chooseResult?.tempFiles?.map((file: any) => file.path) || [];
+      if (!selectedPaths.length) {
+        return;
+      }
+      const nextItems = selectedPaths.map((path, index) => createImageItem(path, index));
+      setImages((prev) => [...prev, ...nextItems].slice(0, options.maxCount));
+      nextItems.forEach((item) => {
+        void uploadSingle(item.id, item.localUrl);
+      });
+    } catch (error) {
+      console.log('拉起上传图片', error);
+    }
+  }, [createImageItem, images.length, options.maxCount, uploadSingle]);
+
+  /**
+   * 重试上传失败图片。
+   */
+  const retryUpload = useCallback((index: number) => {
+    const imageItem = images[index];
+    if (!imageItem) {
+      return;
+    }
+    void uploadSingle(imageItem.id, imageItem.localUrl);
+  }, [images, uploadSingle]);
+
+  /**
+   * 删除图片。
+   */
+  const removeImage = useCallback((index: number) => {
+    setImages((prev) => prev.filter((_, currentIndex) => currentIndex !== index));
+  }, []);
+
+  /**
+   * 重置全部图片状态。
+   */
+  const resetImages = useCallback(() => {
+    setImages([]);
+  }, []);
+
+  /**
+   * 当前是否存在图片上传中。
+   */
+  const uploading = useMemo(() => images.some((item) => item.status === 'uploading'), [images]);
+
+  return {
+    images,
+    uploading,
+    chooseAndUpload,
+    retryUpload,
+    removeImage,
+    resetImages
+  };
+}
diff --git a/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx b/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
index 82a0ee8..548163d 100644
--- a/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
+++ b/src/robotaxi/pages/begin-route/components/evaluate-popup.tsx
@@ -4,6 +4,7 @@
 import './css-folder/evaluate-popup.less'
 import { getOrderCommentType, submitComplaint, submitOrderComment } from '../../../api/begin-route';
 import { _ROBOTAXI_HOST } from '../../../../constants/api';
+import useImageUpload from '../../../hooks/use-image-upload';
 import { getOrderId } from '../../../utils/order';
 
 type Props = {
@@ -23,11 +24,18 @@
     const [feedbackContent, setFeedbackContent] = useState(''); // 反馈文本内容
     const [typeOptions, setTypeOptions] = useState<any[]>([]); // 问题类型选项
     const [isSubmitting, setIsSubmitting] = useState(false); // 提交中状态
-    const [pictures, setPictures] = useState<Array<{
-        localUrl: string;
-        server?: { id: string; name: string; url: string } | null;
-        status: 'uploading' | 'success' | 'failed';
-    }>>([]); // 图片上传队列
+    const {
+        images: pictures,
+        uploading,
+        chooseAndUpload,
+        retryUpload,
+        removeImage,
+        resetImages
+    } = useImageUpload({
+        maxCount: 3,
+        compress: true,
+        uploadUrl: _ROBOTAXI_HOST + '/operation/base/v1/oss/upload'
+    }); // 图片上传状态与操作
 
     // 控制弹窗开合动画
     useEffect(() => {
@@ -80,6 +88,13 @@
             });
             return;
         }
+        if (uploading) {
+            Taro.showToast({
+                title: '图片上传中,请稍后提交',
+                icon: 'none'
+            });
+            return;
+        }
         setIsSubmitting(true);
         const submitter = submitMode === 'complaint' ? submitComplaint : submitOrderComment;
         const picturePayload = JSON.stringify(
@@ -118,129 +133,6 @@
         });
     }
 
-    // 压缩图片到可上传大小
-    const ensureSizeUnderLimit = async (path: string): Promise<string> => {
-        try {
-            const fs = Taro.getFileSystemManager();
-            const info: any = await new Promise((resolve, reject) => {
-                fs.getFileInfo({
-                    filePath: path,
-                    success: resolve,
-                    fail: reject,
-                });
-            });
-            const size = Number(info?.size || 0);
-            const LIMIT = 5 * 1024 * 1024;
-            if (size <= LIMIT) return path;
-            const qualities = [80, 60, 40];
-            for (const q of qualities) {
-                try {
-                    const res: any = await Taro.compressImage({ src: path, quality: q });
-                    const newPath = res?.tempFilePath || path;
-                    const info2: any = await new Promise((resolve, reject) => {
-                        fs.getFileInfo({
-                            filePath: newPath,
-                            success: resolve,
-                            fail: reject,
-                        });
-                    });
-                    if (Number(info2?.size || 0) <= LIMIT) return newPath;
-                    path = newPath;
-                } catch (e) { }
-            }
-            return path;
-        } catch (e) {
-            return path;
-        }
-    };
-
-    // 上传单张图片并写回状态
-    const uploadSingle = async (index: number, filePath: string) => {
-        setPictures(prev => {
-            const next = [...prev];
-            if (next[index]) next[index].status = 'uploading';
-            return next;
-        });
-        try {
-            const finalPath = await ensureSizeUnderLimit(filePath);
-            const res = await Taro.uploadFile({
-                url: _ROBOTAXI_HOST + '/operation/base/v1/oss/upload',
-                filePath: finalPath,
-                name: 'file',
-            });
-            let data: any = res.data;
-            try {
-                if (typeof data === 'string') data = JSON.parse(data);
-            } catch (e) { }
-            const payload = data?.data;
-            const serverObj = {
-                id: String(payload?.id),
-                name: String(payload?.name),
-                url: String(payload?.url),
-            };
-            if (!serverObj.id || !serverObj.url) {
-                throw new Error('invalid');
-            }
-            setPictures(prev => {
-                const next = [...prev];
-                if (next[index]) {
-                    next[index].server = serverObj;
-                    next[index].status = 'success';
-                }
-                return next;
-            });
-        } catch (e) {
-            setPictures(prev => {
-                const next = [...prev];
-                if (next[index]) next[index].status = 'failed';
-                return next;
-            });
-        }
-    };
-
-    // 选择图片并触发上传
-    const handleChooseImages = async () => {
-        const remain = Math.max(0, 3 - pictures.length);
-        if (remain <= 0) {
-            Taro.showToast({ title: '最多可上传3张图片', icon: 'none' });
-            return;
-        }
-        try {
-            const res = await Taro.chooseImage({
-                count: remain,
-                sizeType: ['original', 'compressed'],
-                sourceType: ['album', 'camera'],
-            });
-            const paths: string[] = res?.tempFilePaths || res?.tempFiles?.map((f: any) => f.path) || [];
-            if (!paths || paths.length === 0) return;
-            const addItems = paths.map(p => ({ localUrl: p, server: null, status: 'uploading' as const }));
-            setPictures(prev => [...prev, ...addItems].slice(0, 3));
-            const baseIndex = pictures.length;
-            paths.forEach((p, i) => {
-                const idx = baseIndex + i;
-                uploadSingle(idx, p);
-            });
-        } catch (e) {
-            console.log('拉起上传图片', e);
-        }
-    };
-
-    // 重试上传失败图片
-    const handleRetry = async (index: number) => {
-        const item = pictures[index];
-        if (!item) return;
-        uploadSingle(index, item.localUrl);
-    };
-
-    // 删除已选图片
-    const handleDelete = (index: number) => {
-        setPictures(prev => {
-            const next = [...prev];
-            next.splice(index, 1);
-            return next;
-        });
-    };
-
     // 拉取问题类型并重置本次填写内容
     useEffect(() => {
         if (!show) {
@@ -248,7 +140,7 @@
         }
         setSelectedTypeId(null);
         setFeedbackContent('');
-        setPictures([]);
+        resetImages();
         getOrderCommentType().then((res: any) => {
             const list = res;
             if (Array.isArray(list)) {
@@ -259,7 +151,7 @@
         }).catch(() => {
             setTypeOptions([]);
         });
-    }, [show]);
+    }, [resetImages, show]);
 
     if (!visible) return null;
 
@@ -307,7 +199,7 @@
                                 }
                                 {
                                     p.status === 'failed' && (
-                                        <View className='evaluate-problem-pic-box-mask failed' onClick={() => handleRetry(idx)}>
+                                        <View className='evaluate-problem-pic-box-mask failed' onClick={() => retryUpload(idx)}>
                                             <Image className='failed-icon' src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/tip-default-icon.png" />
                                             <Text className='mask-text'>请重试</Text>
                                         </View>
@@ -315,7 +207,7 @@
                                 }
                                 {
                                     p.status === 'success' && (
-                                        <View className='evaluate-problem-pic-box-delete' onClick={() => handleDelete(idx)}>
+                                        <View className='evaluate-problem-pic-box-delete' onClick={() => removeImage(idx)}>
                                             <Image className='delete-icon' src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/clear-icon.png" />
                                         </View>
                                     )
@@ -324,7 +216,7 @@
                         ))}
                         {
                             pictures.length < 3 && (
-                                <View className='evaluate-problem-pic-box-upload' onClick={handleChooseImages}>
+                                <View className='evaluate-problem-pic-box-upload' onClick={chooseAndUpload}>
                                     <Image className='evaluate-problem-pic-box-upload-img' src="https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/upload-icon.png" />
                                     <View className='evaluate-problem-pic-box-upload-count'>{pictures.length}/3</View>
                                 </View>
diff --git a/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts b/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts
index c87a35f..69824e0 100644
--- a/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts
+++ b/src/robotaxi/pages/begin-route/hooks/use-map-viewport.ts
@@ -35,6 +35,29 @@
 const DEFAULT_DYNAMIC_SCALE_HOLD_MS = 2500; // 一次性视口调整后,暂停速度缩放的默认冷却时长
 const ACTIVE_TRIP_VIEWPORT_SYNC_MIN_DELTA = 0.00001; // 车辆中心跟随的最小经纬度变化,约 1 米,避免重复写入中心点状态
 
+type ViewportPoint = {
+    latitude: number;
+    longitude: number;
+};
+
+type ViewportPayload = {
+    points: ViewportPoint[];
+    keyParts: any[];
+};
+
+/**
+ * 将合法站点转换为 includePoints 需要的坐标点。
+ */
+const buildSiteViewportPoint = (site: any) => {
+    if (!site?.latitude || !site?.longitude) {
+        return null;
+    }
+    return {
+        latitude: site.latitude,
+        longitude: site.longitude
+    };
+};
+
 export default function useMapViewport(rawDeps?: Partial<Deps>) {
     const deps = { ...defaultDeps, ...(rawDeps || {}) } as Deps;
     // 地图底部留白
@@ -157,52 +180,162 @@
         return { pickupSite, dropoffSite };
     };
 
+    /**
+     * 构建步行路线视口数据。
+     */
+    const buildRouteWalkingViewportPayload = useCallback((viewportType: string): ViewportPayload => {
+        const points: ViewportPoint[] = [];
+        if (deps.routePoints.length > 0) {
+            points.push(...deps.routePoints);
+        }
+        if (deps.walkingPoints.length > 0) {
+            points.push(...deps.walkingPoints);
+        }
+        return {
+            points,
+            keyParts: [
+                viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus,
+                viewportType,
+                deps.routePoints.length,
+                deps.walkingPoints.length
+            ]
+        };
+    }, [deps.routePoints, deps.tripStatus, deps.walkingPoints]);
+
+    /**
+     * 构建上车点视口数据。
+     */
+    const buildPickupViewportPayload = useCallback((viewportType: string, pickupSite: any): ViewportPayload => {
+        const pickupPoint = buildSiteViewportPoint(pickupSite);
+        return {
+            points: pickupPoint ? [pickupPoint] : [],
+            keyParts: [
+                viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus,
+                viewportType,
+                pickupSite?.latitude,
+                pickupSite?.longitude
+            ]
+        };
+    }, [deps.tripStatus]);
+
+    /**
+     * 构建主路线视口数据。
+     */
+    const buildRouteViewportPayload = useCallback((viewportType: string): ViewportPayload => {
+        const points: ViewportPoint[] = [];
+        if (deps.routePoints.length > 0) {
+            points.push(...deps.routePoints);
+        }
+        return {
+            points,
+            keyParts: [
+                viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus,
+                viewportType,
+                deps.routePoints.length
+            ]
+        };
+    }, [deps.routePoints, deps.tripStatus]);
+
+    /**
+     * 构建车辆与用户双点视口数据。
+     */
+    const buildCarUserViewportPayload = useCallback((viewportType: string): ViewportPayload => {
+        const points: ViewportPoint[] = [];
+        if (Number.isFinite(deps.latitude) && Number.isFinite(deps.longitude)) {
+            points.push({
+                latitude: deps.latitude as number,
+                longitude: deps.longitude as number
+            });
+        }
+        if (deps.vehicleLocation?.latitude && deps.vehicleLocation?.longitude) {
+            points.push({
+                latitude: deps.vehicleLocation.latitude,
+                longitude: deps.vehicleLocation.longitude
+            });
+        }
+        return {
+            points,
+            keyParts: [
+                viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus,
+                viewportType,
+                deps.latitude,
+                deps.longitude,
+                deps.vehicleLocation?.latitude,
+                deps.vehicleLocation?.longitude
+            ]
+        };
+    }, [deps.latitude, deps.longitude, deps.tripStatus, deps.vehicleLocation?.latitude, deps.vehicleLocation?.longitude]);
+
+    /**
+     * 构建终点视口数据。
+     */
+    const buildDropoffViewportPayload = useCallback((viewportType: string, dropoffSite: any): ViewportPayload => {
+        const dropoffPoint = buildSiteViewportPoint(dropoffSite);
+        return {
+            points: dropoffPoint ? [dropoffPoint] : [],
+            keyParts: [
+                viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus,
+                viewportType,
+                dropoffSite?.latitude,
+                dropoffSite?.longitude
+            ]
+        };
+    }, [deps.tripStatus]);
+
+    /**
+     * 构建订单详情视口数据。
+     */
+    const buildOrderDetailViewportPayload = useCallback((viewportType: string, pickupSite: any, dropoffSite: any): ViewportPayload => {
+        const points: ViewportPoint[] = [];
+        const pickupPoint = buildSiteViewportPoint(pickupSite);
+        const dropoffPoint = buildSiteViewportPoint(dropoffSite);
+        if (deps.routePoints.length > 0) {
+            points.push(...deps.routePoints);
+        }
+        if (pickupPoint) {
+            points.push(pickupPoint);
+        }
+        if (dropoffPoint) {
+            points.push(dropoffPoint);
+        }
+        return {
+            points,
+            keyParts: [
+                viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus,
+                viewportType,
+                deps.routePoints.length,
+                pickupSite?.latitude,
+                pickupSite?.longitude,
+                dropoffSite?.latitude,
+                dropoffSite?.longitude
+            ]
+        };
+    }, [deps.routePoints, deps.tripStatus]);
+
     // 按视口类型构建 includePoints 入参
     const buildViewportPayload = (viewportType: string, pickupSite: any, dropoffSite: any) => {
-        const points: any[] = [];
-        const keyParts: any[] = [viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus, viewportType];
         if (viewportType === 'route_walking') {
-            if (deps.routePoints.length > 0) points.push(...deps.routePoints);
-            if (deps.walkingPoints.length > 0) points.push(...deps.walkingPoints);
-            keyParts.push(deps.routePoints.length, deps.walkingPoints.length);
-            return { points, keyParts };
+            return buildRouteWalkingViewportPayload(viewportType);
         }
         if (viewportType === 'pickup') {
-            if (pickupSite?.latitude && pickupSite?.longitude) {
-                points.push({ latitude: pickupSite.latitude, longitude: pickupSite.longitude });
-            }
-            keyParts.push(pickupSite?.latitude, pickupSite?.longitude);
-            return { points, keyParts };
+            return buildPickupViewportPayload(viewportType, pickupSite);
         }
         if (viewportType === 'route') {
-            if (deps.routePoints.length > 0) points.push(...deps.routePoints);
-            keyParts.push(deps.routePoints.length);
-            return { points, keyParts };
+            return buildRouteViewportPayload(viewportType);
         }
         if (viewportType === 'car_user') {
-            if (Number.isFinite(deps.latitude) && Number.isFinite(deps.longitude)) {
-                points.push({ latitude: deps.latitude, longitude: deps.longitude });
-            }
-            if (deps.vehicleLocation?.latitude && deps.vehicleLocation?.longitude) {
-                points.push({ latitude: deps.vehicleLocation.latitude, longitude: deps.vehicleLocation.longitude });
-            }
-            keyParts.push(deps.latitude, deps.longitude, deps.vehicleLocation?.latitude, deps.vehicleLocation?.longitude);
-            return { points, keyParts };
+            return buildCarUserViewportPayload(viewportType);
         }
         if (viewportType === 'dropoff') {
-            if (dropoffSite?.latitude && dropoffSite?.longitude) {
-                points.push({ latitude: dropoffSite.latitude, longitude: dropoffSite.longitude });
-            }
-            keyParts.push(dropoffSite?.latitude, dropoffSite?.longitude);
-            return { points, keyParts };
+            return buildDropoffViewportPayload(viewportType, dropoffSite);
         }
         if (viewportType === 'order_detail') {
-            if (deps.routePoints.length > 0) points.push(...deps.routePoints);
-            if (pickupSite?.latitude && pickupSite?.longitude) points.push({ latitude: pickupSite.latitude, longitude: pickupSite.longitude });
-            if (dropoffSite?.latitude && dropoffSite?.longitude) points.push({ latitude: dropoffSite.latitude, longitude: dropoffSite.longitude });
-            keyParts.push(deps.routePoints.length, pickupSite?.latitude, pickupSite?.longitude, dropoffSite?.latitude, dropoffSite?.longitude);
+            return buildOrderDetailViewportPayload(viewportType, pickupSite, dropoffSite);
         }
-        return { points, keyParts };
+        return {
+            points: [],
+            keyParts: [viewportType === 'order_detail' ? TripStatus.TripFinished : deps.tripStatus, viewportType]
+        };
     };
 
     const buildOrderDetailRouteSignature = (pickupSite: any, dropoffSite: any) => {

--
Gitblit v1.7.1