Files
jrshikoku/components/ActionSheetComponents/EachTrainInfoCore/hooks/useTrainPosition.ts
T
harukin-expo-dev-env 1ecd026908 feat: Implement official position metadata handling and caching
- Add `currentTrainMetadata.ts` to manage carrying forward official position metadata between train snapshots.
- Introduce `lineDisplayName.ts` for mapping line IDs to display names.
- Create `officialPositionCache.ts` for parsing and serializing official position data with cache validation.
- Enhance `officialPositionService.ts` to support persistent caching of official positions and improve resolver functionality.
- Update `requestCoordinator.ts` to allow cancellation of polling requests without disposing of the coordinator.
- Modify `trainTimeFiltering.ts` to incorporate line ID context when checking for duplicate train data.
- Refactor `useCurrentTrain.tsx` to support polling for current train data with official position enrichment.
- Add tests for new features, including persistent cache behavior and handling of ambiguous train data.
2026-09-15 13:48:21 +09:00

130 lines
4.2 KiB
TypeScript

import { useState, useEffect } from 'react';
import { AS } from '@/storageControl';
import { STORAGE_KEYS } from '@/constants';
import { logger } from '@/utils/logger';
import { useCurrentTrainData } from '@/stateBox/useCurrentTrain';
import { useTrainMenu } from '@/stateBox/useTrainMenu';
import type { trainDataType } from '@/lib/trainPositionTextArray';
import { lineIdFromStationNumber } from '@/lib/checkDuplicateTrainData';
import { findReversalPoints } from '@/lib/eachTrainInfoCoreLib/findReversalPoints';
export const useTrainPosition = (
trainNum: string,
stopStationIDList: string[][]
) => {
const {
getCurrentStationData,
getCurrentStationDataList,
getPosition,
} = useCurrentTrainData();
const { updatePermission } = useTrainMenu();
const [currentTrainData, setCurrentTrainData] = useState<trainDataType | undefined>();
const [currentTrainDataList, setCurrentTrainDataList] = useState<trainDataType[]>([]);
const [currentPosition, setCurrentPosition] = useState<string[]>([]);
const [currentPositionList, setCurrentPositionList] = useState<string[][]>([]);
const [trainPositionSwitch, setTrainPositionSwitch] = useState<string>('false');
useEffect(() => {
const candidates = getCurrentStationDataList(trainNum);
const routeLineCounts = new Map<string, number>();
for (const stationNumber of stopStationIDList.flat()) {
const line = lineIdFromStationNumber(stationNumber);
if (line) routeLineCounts.set(line, (routeLineCounts.get(line) ?? 0) + 1);
}
const routeLineIds = [...routeLineCounts.entries()]
.sort(([, countA], [, countB]) => countB - countA)
.map(([line]) => line);
const selected = getCurrentStationData(trainNum, routeLineIds);
const visible = updatePermission
? candidates
: selected
? [selected]
: [];
logger.debug('Train position data:', visible);
setCurrentTrainDataList(visible);
setCurrentTrainData(visible[0]);
}, [
getCurrentStationData,
getCurrentStationDataList,
stopStationIDList,
trainNum,
updatePermission,
]);
useEffect(() => {
if (!stopStationIDList.length || !currentTrainDataList.length) {
setCurrentPositionList([]);
setCurrentPosition([]);
return;
}
const positions = currentTrainDataList.map((train) => {
const position = getPosition(train);
const next = position ? [...position] : [];
if (next.length > 1) {
// 伊予市駅の特殊処理
const iyoIndex = stopStationIDList.findIndex((d) => d.includes('U14'));
if (next[0] === '-Iyo') {
next[0] = stopStationIDList[iyoIndex - 1]?.[0];
} else if (next[0] === '+Iyo') {
next[0] = stopStationIDList[iyoIndex + 1]?.[0];
}
if (next[1] === '+Iyo') {
next[1] = stopStationIDList[iyoIndex + 1]?.[0];
} else if (next[1] === '-Iyo') {
next[1] = stopStationIDList[iyoIndex - 1]?.[0];
}
}
return next;
});
setCurrentPositionList(positions);
setCurrentPosition(positions[0] ?? []);
}, [currentTrainDataList, stopStationIDList, getPosition]);
useEffect(() => {
AS.getItem(STORAGE_KEYS.TRAIN_POSITION_SWITCH)
.then((value) => {
if (value) setTrainPositionSwitch(value);
})
.catch(() => AS.setItem(STORAGE_KEYS.TRAIN_POSITION_SWITCH, 'true'));
}, []);
const mergePoints = (includeReversal: boolean) => {
const merged = stopStationIDList.map(() => false);
for (const position of currentPositionList) {
const candidate = findReversalPoints(
position,
stopStationIDList,
includeReversal,
);
if (!candidate) continue;
candidate.forEach((point, index) => {
merged[index] = merged[index] || point;
});
}
return merged;
};
const points =
trainPositionSwitch === 'true'
? mergePoints(true)
: stopStationIDList.map(() => false);
const pointsDisplay =
trainPositionSwitch === 'true'
? mergePoints(false)
: stopStationIDList.map(() => false);
return {
currentTrainData,
currentTrainDataList,
currentPosition,
currentPositionList,
points,
pointsDisplay,
trainPositionSwitch,
};
};