667 lines
22 KiB
TypeScript
667 lines
22 KiB
TypeScript
import lineColorList from "@/assets/originData/lineColorList";
|
|
import { StationNumberMaker } from "@/components/駅名表/StationNumberMaker";
|
|
import { checkDuplicateTrainData } from "@/lib/checkDuplicateTrainData";
|
|
import {
|
|
CustomTrainData,
|
|
eachTrainDiagramType,
|
|
StationProps,
|
|
} from "@/lib/CommonTypes";
|
|
import { getCurrentTrainData } from "@/lib/getCurrentTrainData";
|
|
import { getTrainDelayStatus } from "@/lib/getTrainDelayStatus";
|
|
import { getTrainType } from "@/lib/getTrainType";
|
|
import { objectIsEmpty } from "@/lib/objectIsEmpty";
|
|
import { getTime, trainTimeFiltering } from "@/lib/trainTimeFiltering";
|
|
import betweenData from "@/assets/originData/between";
|
|
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
|
|
import { useAreaInfo } from "@/stateBox/useAreaInfo";
|
|
import { useCurrentTrain } from "@/stateBox/useCurrentTrain";
|
|
import { useStationList } from "@/stateBox/useStationList";
|
|
import { useTrainMenu } from "@/stateBox/useTrainMenu";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { useNavigation } from "@react-navigation/native";
|
|
|
|
import { FC, useCallback, useEffect, useRef, useState } from "react";
|
|
import { LayoutAnimation, PermissionsAndroid, Platform, Text, TouchableOpacity, View } from "react-native";
|
|
import Animated, {
|
|
useSharedValue,
|
|
useAnimatedStyle,
|
|
withRepeat,
|
|
withSequence,
|
|
withTiming,
|
|
cancelAnimation,
|
|
} from "react-native-reanimated";
|
|
import { SheetManager } from "react-native-actions-sheet";
|
|
import { useThemeColors } from "@/lib/theme";
|
|
import {
|
|
startStationLockActivity,
|
|
updateStationLockActivity,
|
|
endStationLockActivity,
|
|
isAvailable as isLiveActivityAvailable,
|
|
StationTrainInfo,
|
|
} from "expo-live-activity";
|
|
import { Asset } from "expo-asset";
|
|
import { useAudioPlayer, setAudioModeAsync } from "expo-audio";
|
|
import type { AudioSource } from "expo-audio";
|
|
import { AS } from "@/storageControl";
|
|
import { STORAGE_KEYS } from "@/constants";
|
|
|
|
// ── 遅延速報サウンド設定 ──
|
|
// 将来のファイル差し替えに備えて定数で管理
|
|
const DELAY_ANNOUNCEMENT_SOUND = require("../../../assets/sound/delay-support.wav");
|
|
// 遅延案内の再生間隔: 120〜180 秒をランダムで選択(2〜3分ごと)
|
|
const getAnnouncementIntervalMs = () =>
|
|
(Math.floor(Math.random() * 60) + 120) * 1000;
|
|
|
|
type props = {
|
|
stationID: string;
|
|
};
|
|
|
|
export const FixedStation: FC<props> = ({ stationID }) => {
|
|
const { colors, fixed } = useThemeColors();
|
|
const { mapSwitch, playbackCurrentTimeIso } = useTrainMenu();
|
|
const {
|
|
currentTrain,
|
|
fixedPosition,
|
|
setFixedPosition,
|
|
fixedPositionSize,
|
|
setFixedPositionSize,
|
|
} = useCurrentTrain();
|
|
const { getStationDataFromId } = useStationList();
|
|
const { stationList } = useStationList();
|
|
const { navigate } = useNavigation();
|
|
const [station, setStation] = useState<StationProps[]>([]);
|
|
|
|
// GPS追従中の点滅アニメーション
|
|
const pulseAnim = useSharedValue(1);
|
|
const isGpsFollowing = fixedPosition?.type === "nearestStation";
|
|
useEffect(() => {
|
|
if (!isGpsFollowing) {
|
|
cancelAnimation(pulseAnim);
|
|
pulseAnim.value = 1;
|
|
return;
|
|
}
|
|
pulseAnim.value = withRepeat(
|
|
withSequence(
|
|
withTiming(0.4, { duration: 600 }),
|
|
withTiming(1, { duration: 600 })
|
|
),
|
|
-1
|
|
);
|
|
}, [isGpsFollowing]);
|
|
const pulseStyle = useAnimatedStyle(() => ({
|
|
opacity: pulseAnim.value,
|
|
}));
|
|
useEffect(() => {
|
|
const data = getStationDataFromId(stationID);
|
|
setStation(data);
|
|
}, [stationID]);
|
|
const lineColor =
|
|
station.length > 0
|
|
? lineColorList[station[0]?.StationNumber.slice(0, 1)]
|
|
: "white";
|
|
////
|
|
|
|
const { allTrainDiagram, allCustomTrainData } = useAllTrainDiagram();
|
|
const { areaStationID } = useAreaInfo();
|
|
const [stationDiagram, setStationDiagram] = useState({}); //当該駅の全時刻表
|
|
const [isInfoArea, setIsInfoArea] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// 現在の駅に停車するダイヤを作成する副作用[列車ダイヤと現在駅情報]
|
|
if (!allTrainDiagram) {
|
|
setStationDiagram({});
|
|
return;
|
|
}
|
|
if (station.length == 0) {
|
|
setStationDiagram({});
|
|
return;
|
|
}
|
|
let returnData = {};
|
|
Object.keys(allTrainDiagram).forEach((key) => {
|
|
if (allTrainDiagram[key].match(station[0].Station_JP + ",")) {
|
|
returnData[key] = allTrainDiagram[key];
|
|
}
|
|
});
|
|
setStationDiagram(returnData);
|
|
setIsInfoArea(station.some((s) => areaStationID.includes(s.StationNumber)));
|
|
}, [allTrainDiagram, station]);
|
|
|
|
const [trainTimeAndNumber, setTrainTimeAndNumber] = useState<
|
|
eachTrainDiagramType[]
|
|
>([]);
|
|
|
|
useEffect(() => {
|
|
//現在の駅に停車する列車から時刻を切り出してLEDベースにフォーマット
|
|
if (objectIsEmpty(stationDiagram)) return () => {};
|
|
const getTimeData = getTime(stationDiagram, station[0]);
|
|
setTrainTimeAndNumber(getTimeData);
|
|
}, [stationDiagram]);
|
|
const [selectedTrain, setSelectedTrain] = useState<eachTrainDiagramType[]>(
|
|
[]
|
|
);
|
|
|
|
// ── Live Notification ──
|
|
const [liveNotifyId, setLiveNotifyId] = useState<string | null>(null);
|
|
const liveNotifyIdRef = useRef<string | null>(null);
|
|
const hasStartedRef = useRef(false);
|
|
const [tick, setTick] = useState(0);
|
|
|
|
useEffect(() => {
|
|
if (!trainTimeAndNumber) return () => {};
|
|
if (!currentTrain) return () => {};
|
|
const data = trainTimeAndNumber
|
|
.filter((d) => currentTrain.map((m) => m.num).includes(d.train)) //現在の列車に絞る[ToDo]
|
|
.filter((d) => trainTimeFiltering({ d, currentTrain, station, stationList, now: playbackCurrentTimeIso })) //時間フィルター
|
|
.filter((d) => !d.isThrough)
|
|
.filter((d) => d.lastStation != station[0].Station_JP); //最終列車表示設定
|
|
setSelectedTrain(data);
|
|
}, [trainTimeAndNumber, currentTrain, stationList, playbackCurrentTimeIso, tick /*liveActivity periodic refresh*/]);
|
|
|
|
useEffect(() => {
|
|
liveNotifyIdRef.current = liveNotifyId;
|
|
}, [liveNotifyId]);
|
|
|
|
// Live Activity 起動中は 60 秒ごとに selectedTrain を強制再計算して時刻フィルタを再適用
|
|
useEffect(() => {
|
|
if (!liveNotifyId) return;
|
|
const interval = setInterval(() => setTick((t) => t + 1), 60000);
|
|
return () => clearInterval(interval);
|
|
}, [liveNotifyId]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (liveNotifyIdRef.current) {
|
|
endStationLockActivity(liveNotifyIdRef.current).catch(() => {});
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
// ── 遅延速報サウンド ──
|
|
const [soundDelayEnabled, setSoundDelayEnabled] = useState(false);
|
|
const [resolvedDelaySound, setResolvedDelaySound] = useState<AudioSource>(null);
|
|
|
|
// 設定値の読み込み
|
|
useEffect(() => {
|
|
AS.getItem(STORAGE_KEYS.SOUND_DELAY_ANNOUNCEMENT)
|
|
.then((v) => setSoundDelayEnabled(v === true || v === "true"))
|
|
.catch(() => {});
|
|
}, []);
|
|
|
|
// サウンドアセットの解決 — 設定が ON のときだけ実行
|
|
useEffect(() => {
|
|
if (!soundDelayEnabled) {
|
|
setResolvedDelaySound(null);
|
|
return;
|
|
}
|
|
let mounted = true;
|
|
const resolve = async () => {
|
|
try {
|
|
const asset = Asset.fromModule(DELAY_ANNOUNCEMENT_SOUND);
|
|
await asset.downloadAsync();
|
|
if (!mounted || !asset.localUri) return;
|
|
const source =
|
|
Platform.OS === "android"
|
|
? { uri: asset.localUri.replace(/^\/file:\/\//, "") }
|
|
: { uri: asset.localUri };
|
|
setResolvedDelaySound(source);
|
|
} catch (e) {
|
|
console.warn("[DelaySound] Failed to resolve asset", e);
|
|
}
|
|
};
|
|
resolve();
|
|
return () => {
|
|
mounted = false;
|
|
};
|
|
}, [soundDelayEnabled]);
|
|
|
|
const delayAnnouncementPlayer = useAudioPlayer(resolvedDelaySound);
|
|
|
|
// 最新の状態をrefで保持(intervalの安定したコールバックから参照するため)
|
|
const soundDelayEnabledRef = useRef(soundDelayEnabled);
|
|
const delayAnnouncementPlayerRef = useRef(delayAnnouncementPlayer);
|
|
useEffect(() => {
|
|
soundDelayEnabledRef.current = soundDelayEnabled;
|
|
}, [soundDelayEnabled]);
|
|
useEffect(() => {
|
|
delayAnnouncementPlayerRef.current = delayAnnouncementPlayer;
|
|
}, [delayAnnouncementPlayer]);
|
|
|
|
// 5分以上の遅延がある列車が存在するかを判定
|
|
const hasDelayedTrain = selectedTrain.some((d) => {
|
|
const trainData = checkDuplicateTrainData(
|
|
currentTrain.filter((a) => a.num === d.train),
|
|
stationList
|
|
);
|
|
if (!trainData) return false;
|
|
const delay = trainData.delay;
|
|
return typeof delay === "number" && delay >= 5;
|
|
});
|
|
const hasDelayedTrainRef = useRef(hasDelayedTrain);
|
|
useEffect(() => {
|
|
hasDelayedTrainRef.current = hasDelayedTrain;
|
|
}, [hasDelayedTrain]);
|
|
|
|
// 遅延速報サウンド再生インターバル
|
|
// マウント時に一度だけセットアップし、30秒ごとに条件を確認
|
|
useEffect(() => {
|
|
const CHECK_INTERVAL_MS = 30 * 1000;
|
|
let nextPlayAt = Date.now() + getAnnouncementIntervalMs();
|
|
|
|
const handle = setInterval(() => {
|
|
if (!soundDelayEnabledRef.current || !hasDelayedTrainRef.current) {
|
|
// 条件が成立していない間はタイマーをリセットし続ける
|
|
nextPlayAt = Date.now() + getAnnouncementIntervalMs();
|
|
return;
|
|
}
|
|
const now = Date.now();
|
|
if (now >= nextPlayAt) {
|
|
nextPlayAt = now + getAnnouncementIntervalMs();
|
|
const player = delayAnnouncementPlayerRef.current;
|
|
setAudioModeAsync({
|
|
playsInSilentMode: true,
|
|
shouldPlayInBackground: false,
|
|
interruptionMode: "duckOthers",
|
|
})
|
|
.then(() => {
|
|
if (player.playing) player.pause();
|
|
player.volume = 1;
|
|
return player.seekTo(0);
|
|
})
|
|
.then(() => player.play())
|
|
.catch((e) => console.warn("[DelaySound] Failed to play", e));
|
|
}
|
|
}, CHECK_INTERVAL_MS);
|
|
|
|
return () => clearInterval(handle);
|
|
}, []); // マウント時に一度だけ設定
|
|
|
|
const buildTrainsInfo = useCallback((): StationTrainInfo[] => {
|
|
const stationName = station[0]?.Station_JP;
|
|
// between.ts から当駅の「BetweenStation」を引き当てる。
|
|
// Androidバックグラウンドはこの文字列で Pos と照合し、直前セクションへの接近を判定する。
|
|
const getSectionStation = (): string => {
|
|
const entry = betweenData.find((b) =>
|
|
b.Datas.some((s) => s.StationName === stationName)
|
|
);
|
|
return entry?.BetweenStation || "";
|
|
};
|
|
const sectionStation = getSectionStation();
|
|
return selectedTrain.slice(0, 7).map((d) => {
|
|
const customData = getCurrentTrainData(
|
|
d.train,
|
|
currentTrain,
|
|
allCustomTrainData
|
|
);
|
|
const currentTrainDataForTrain = checkDuplicateTrainData(
|
|
currentTrain.filter((a) => a.num === d.train),
|
|
stationList
|
|
);
|
|
const { name, color: typeColor } = getTrainType({ type: customData.type, whiteMode: true });
|
|
const delayStatus = `${getTrainDelayStatus(
|
|
currentTrainDataForTrain,
|
|
station[0]?.Station_JP
|
|
)}`;
|
|
return {
|
|
time: d.time,
|
|
typeName: name,
|
|
trainName: customData.train_name || "",
|
|
destination: d.lastStation,
|
|
platform: "",
|
|
delayStatus: delayStatus || "定刻",
|
|
typeColor: typeColor || "",
|
|
trainNumber: d.train,
|
|
sectionStation,
|
|
};
|
|
});
|
|
}, [selectedTrain, currentTrain, allCustomTrainData, stationList, station]);
|
|
|
|
useEffect(() => {
|
|
if (!liveNotifyId || station.length === 0) return;
|
|
const trains = buildTrainsInfo();
|
|
updateStationLockActivity(liveNotifyId, {
|
|
nextTrainTime: trains[0]?.time || "",
|
|
nextTrainDestination: trains[0]?.destination || "",
|
|
nextTrainPlatform: trains[0]?.platform || "",
|
|
followingTrainTime: trains[1]?.time || "",
|
|
followingTrainDestination: trains[1]?.destination || "",
|
|
stationName: station[0]?.Station_JP,
|
|
stationNumber: station[0]?.StationNumber,
|
|
lineColor,
|
|
trains,
|
|
}).catch(() => {});
|
|
}, [selectedTrain, currentTrain, liveNotifyId, buildTrainsInfo]);
|
|
|
|
// バナー表示と同時にLive Activityを自動開始(selectedTrainが揃ってから)
|
|
useEffect(() => {
|
|
if (
|
|
!isLiveActivityAvailable() ||
|
|
hasStartedRef.current ||
|
|
station.length === 0
|
|
) {
|
|
return;
|
|
}
|
|
hasStartedRef.current = true;
|
|
const startActivity = async () => {
|
|
if (Platform.OS === 'android' && Platform.Version >= 33) {
|
|
const granted = await PermissionsAndroid.request(
|
|
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
|
|
);
|
|
if (granted !== PermissionsAndroid.RESULTS.GRANTED) return;
|
|
}
|
|
const trains = buildTrainsInfo();
|
|
try {
|
|
const id = await startStationLockActivity({
|
|
stationName: station[0]?.Station_JP || "",
|
|
nextTrainTime: trains[0]?.time || "",
|
|
nextTrainDestination: trains[0]?.destination || "",
|
|
nextTrainPlatform: trains[0]?.platform || "",
|
|
followingTrainTime: trains[1]?.time || "",
|
|
followingTrainDestination: trains[1]?.destination || "",
|
|
stationNumber: station[0]?.StationNumber,
|
|
lineColor,
|
|
trains,
|
|
});
|
|
setLiveNotifyId(id);
|
|
} catch (e) {
|
|
console.warn('[LiveNotify] start error:', e);
|
|
hasStartedRef.current = false;
|
|
}
|
|
};
|
|
startActivity();
|
|
}, [station, selectedTrain]);
|
|
|
|
return (
|
|
<View
|
|
style={{ display: "flex", flexDirection: "column", flex: 1 }}
|
|
pointerEvents="box-none"
|
|
>
|
|
<TouchableOpacity
|
|
style={{
|
|
flex: 1,
|
|
flexDirection: "row",
|
|
borderBottomColor: lineColor,
|
|
borderBottomWidth: 2,
|
|
position: "relative",
|
|
}}
|
|
activeOpacity={1}
|
|
onPress={() => {
|
|
const payload = {
|
|
currentStation: station,
|
|
navigate,
|
|
goTo: "menu",
|
|
onExit: () => SheetManager.hide("StationDetailView"),
|
|
};
|
|
//@ts-ignore
|
|
SheetManager.show("StationDetailView", { payload });
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
flex: 3,
|
|
flexDirection: "column",
|
|
alignContent: "center",
|
|
alignSelf: "center",
|
|
alignItems: "center",
|
|
height: "100%",
|
|
backgroundColor: colors.background,
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
backgroundColor: lineColor,
|
|
flexDirection: "row",
|
|
width: "100%",
|
|
alignContent: "center",
|
|
alignItems: "center",
|
|
height: 22,
|
|
overflow: "hidden",
|
|
paddingLeft: 5,
|
|
}}
|
|
>
|
|
<StationNumberMaker
|
|
currentStation={station}
|
|
singleSize={18}
|
|
useEach={true}
|
|
/>
|
|
<Text
|
|
style={{
|
|
fontSize: 14,
|
|
textAlignVertical: "center",
|
|
margin: 0,
|
|
padding: 0,
|
|
paddingLeft: 5,
|
|
flex: 1,
|
|
color: fixed.textOnPrimary,
|
|
}}
|
|
>
|
|
{station[0]?.Station_JP}
|
|
</Text>
|
|
<View
|
|
style={{
|
|
backgroundColor: colors.background,
|
|
width: 6,
|
|
borderLeftColor: lineColor,
|
|
borderTopColor: lineColor,
|
|
borderBottomColor: colors.background,
|
|
borderRightColor: colors.background,
|
|
borderBottomWidth: 18,
|
|
borderLeftWidth: 10,
|
|
borderRightWidth: 0,
|
|
borderTopWidth: 5,
|
|
height: 20,
|
|
}}
|
|
/>
|
|
</View>
|
|
<View
|
|
style={{
|
|
height: "100%",
|
|
backgroundColor: colors.background,
|
|
flex: 1,
|
|
}}
|
|
>
|
|
<Text style={{ fontSize: 18, color: colors.text }}>次の発車予定:</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View
|
|
style={{
|
|
flex: 5,
|
|
flexDirection: "column",
|
|
backgroundColor: colors.background,
|
|
borderTopWidth: 5,
|
|
borderTopColor: lineColor,
|
|
overflow: "hidden",
|
|
}}
|
|
>
|
|
{selectedTrain.length > 0 ? (
|
|
selectedTrain.map((d) => (
|
|
<FixedStationBoxEachTrain
|
|
d={d}
|
|
station={station[0]}
|
|
displaySize={fixedPositionSize}
|
|
key={d.train + "-fixedStationBox"}
|
|
/>
|
|
))
|
|
) : (
|
|
<View style={{ backgroundColor: colors.background, flex: 1 }}>
|
|
<Text style={{ fontSize: parseInt("11%"), color: colors.text }}>
|
|
当駅を発着する走行中の列車はありません。
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
borderTopColor: lineColor,
|
|
borderTopWidth: 2,
|
|
}}
|
|
pointerEvents="box-none"
|
|
>
|
|
<TouchableOpacity
|
|
style={{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
}}
|
|
onPress={() => {
|
|
setFixedPosition({ type: null, value: null });
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: lineColor,
|
|
paddingHorizontal: 5,
|
|
height: 26,
|
|
}}
|
|
>
|
|
{isGpsFollowing ? (
|
|
<Animated.View
|
|
style={[{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
}, pulseStyle]}
|
|
>
|
|
<Ionicons name="navigate" size={15} color={fixed.textOnPrimary} />
|
|
<Text style={{ color: fixed.textOnPrimary, fontSize: 15, paddingRight: 5, paddingLeft: 3 }}>
|
|
GPS追従中
|
|
</Text>
|
|
</Animated.View>
|
|
) : (
|
|
<>
|
|
<Ionicons name="lock-closed" size={15} color={fixed.textOnPrimary} />
|
|
<Text style={{ color: fixed.textOnPrimary, fontSize: 15, paddingRight: 5 }}>
|
|
駅位置ロック中
|
|
</Text>
|
|
</>
|
|
)}
|
|
<Ionicons name="close" size={15} color={fixed.textOnPrimary} />
|
|
</View>
|
|
|
|
<View
|
|
style={{
|
|
backgroundColor: "#0000",
|
|
width: 6,
|
|
borderLeftColor: lineColor,
|
|
borderTopColor: lineColor,
|
|
borderBottomColor: "#0000",
|
|
borderRightColor: "#0000",
|
|
borderBottomWidth: 26,
|
|
borderLeftWidth: 10,
|
|
borderRightWidth: 0,
|
|
borderTopWidth: 0,
|
|
height: 26,
|
|
}}
|
|
/>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
style={{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
}}
|
|
onPress={() => {
|
|
LayoutAnimation.configureNext({
|
|
duration: 500,
|
|
update: { type: "spring", springDamping: 0.7 },
|
|
});
|
|
if (fixedPositionSize === 226) {
|
|
setFixedPositionSize(mapSwitch == "true" ? 76 : 80);
|
|
} else {
|
|
setFixedPositionSize(226);
|
|
}
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
backgroundColor: "#0000",
|
|
width: 6,
|
|
borderLeftColor: "#0000",
|
|
borderTopColor: lineColor,
|
|
borderBottomColor: "#0000",
|
|
borderRightColor: lineColor,
|
|
borderBottomWidth: 26,
|
|
borderLeftWidth: 0,
|
|
borderRightWidth: 10,
|
|
borderTopWidth: 0,
|
|
height: 26,
|
|
}}
|
|
/>
|
|
<View
|
|
style={{
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
backgroundColor: lineColor,
|
|
paddingHorizontal: 5,
|
|
height: 26,
|
|
}}
|
|
pointerEvents="none"
|
|
>
|
|
<Ionicons
|
|
name={fixedPositionSize == 226 ? "chevron-up" : "chevron-down"}
|
|
size={15}
|
|
color={fixed.textOnPrimary}
|
|
/>
|
|
<Text
|
|
style={{
|
|
color: fixed.textOnPrimary,
|
|
paddingRight: 5,
|
|
backgroundColor: lineColor,
|
|
fontSize: 15,
|
|
}}
|
|
>
|
|
{fixedPositionSize == 226
|
|
? "時刻表を縮小する"
|
|
: "時刻表を展開する"}
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
const FixedStationBoxEachTrain = ({ d, station, displaySize }) => {
|
|
const { colors, isDark } = useThemeColors();
|
|
const { currentTrain } = useCurrentTrain();
|
|
const { stationList } = useStationList();
|
|
const { allCustomTrainData } = useAllTrainDiagram();
|
|
const currentTrainData = checkDuplicateTrainData(
|
|
currentTrain.filter((a) => a.num == d.train),
|
|
stationList
|
|
);
|
|
|
|
const trainDelayStatus = `${getTrainDelayStatus(
|
|
currentTrainData,
|
|
station.Station_JP
|
|
)}`;
|
|
const [train, setTrain] = useState<CustomTrainData>(
|
|
getCurrentTrainData(d.train, currentTrain, allCustomTrainData)
|
|
);
|
|
useEffect(() => {
|
|
setTrain(getCurrentTrainData(d.train, currentTrain, allCustomTrainData));
|
|
}, [currentTrain, d.train]);
|
|
const { name, color } = getTrainType({ type: train.type, whiteMode: !isDark });
|
|
return (
|
|
<View
|
|
style={{
|
|
backgroundColor: colors.background,
|
|
flexDirection: "row",
|
|
height: displaySize == 226 ? "7.5%" : "33%",
|
|
overflow: "visible",
|
|
}}
|
|
>
|
|
<Text style={{ fontSize: parseInt("11%"), flex: 3, color: colors.text }}>{d.time}</Text>
|
|
<Text style={{ fontSize: parseInt("11%"), flex: 4, color }}>{name}</Text>
|
|
<Text style={{ fontSize: parseInt("11%"), flex: 4, color: colors.text }}>
|
|
{d.lastStation}行
|
|
</Text>
|
|
<Text style={{ fontSize: parseInt("11%"), flex: 3, color: colors.text }}>
|
|
{trainDelayStatus}
|
|
</Text>
|
|
</View>
|
|
);
|
|
};
|