Files
jrshikoku/components/StationDiagram/StationDiagramView.tsx
2025-09-04 20:52:18 +00:00

455 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { FC, useEffect, useState } from "react";
import {
View,
Text,
ScrollView,
TextInput,
Keyboard,
KeyboardAvoidingView,
Platform,
TouchableOpacity,
LayoutAnimation,
} from "react-native";
import { useNavigation } from "@react-navigation/native";
import { BigButton } from "../atom/BigButton";
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
import { ListView } from "@/components/StationDiagram/ListView";
import dayjs from "dayjs";
import { ExGridView } from "./ExGridView";
import { Switch } from "react-native-elements";
import { customTrainDataDetector } from "../custom-train-data";
import { typeID } from "@/lib/getStringConfig";
import { colorString } from "@/lib/getTrainType";
type props = {
route: {
params: {
currentStation: {
Station_JP: string;
Station_EN: string;
StationName?: string;
MyStation?: string;
StationNumber: string;
DispNum?: string;
StationTimeTable: string;
StationMap?: string;
JrHpUrl?: string;
lat: number;
lng: number;
jslodApi: string;
}[];
};
};
};
export const StationDiagramView: FC<props> = ({ route }) => {
if (!route.params) {
return null;
}
const { currentStation } = route.params;
// 必要な情報:駅情報、全ダイヤ、カスタム列車情報
// 表示モード:縦並びリスト、横並びグリッド(時刻分割)、横並び単純左詰め
// フィルタリング:終点路線、種別、行先、関係停車駅
const { keyList, allTrainDiagram, allCustomTrainData } = useAllTrainDiagram();
const { navigate, addListener, goBack, canGoBack } = useNavigation();
const [keyBoardVisible, setKeyBoardVisible] = useState(false);
const [input, setInput] = useState("");
const [selectedTypeList, setSelectedTypeList] = useState<typeID[]>([
"Normal",
"OneMan",
"Rapid",
"OneManRapid",
"LTDEXP",
"NightLTDEXP",
]);
type hoge = {
trainNumber: string;
array: string;
name: string;
timeType: string;
time: string;
}[];
const [showTypeFiltering, setShowTypeFiltering] = useState(false);
const [showLastStop, setShowLastStop] = useState(false);
const [threw, setIsThrew] = useState(false);
const [currentStationDiagram, setCurrentStationDiagram] = useState<hoge>([]);
useEffect(() => {
if (allTrainDiagram && currentStation.length > 0) {
const stationName = currentStation[0].Station_JP;
let returnDataArray: hoge = [];
keyList
.filter((s) => {
const boolData = allTrainDiagram[s];
let isStop = false;
let isInput = false;
boolData.split("#").forEach((d) => {
const [station, type, time] = d.split(",");
if (station === stationName) isStop = true;
if (station === input && type && !type.includes("通"))
isInput = true;
});
if (input && input.length > 0) {
return isInput && isStop;
}
return isStop;
})
.forEach((d) => {
allTrainDiagram[d]
.split("#")
.filter((d) => {
const [station, type, time] = d.split(",");
return station === stationName;
})
.forEach((x) => {
const [name, timeType, time] = x.split(",");
if (!name || !timeType || !time) return;
const { img, trainName, type, trainNumDistance, infogram } =
customTrainDataDetector(d, allCustomTrainData);
const arrayData = {
trainNumber: d,
array: allTrainDiagram[d],
name,
timeType,
time,
};
// //条件によってフィルタリング
if (!threw && timeType && timeType.includes("通")) return;
if (!showLastStop && timeType && timeType.includes("着")) return;
if (
selectedTypeList.length > 0 &&
selectedTypeList.findIndex((item) => item === type) === -1
) {
if (
selectedTypeList.findIndex(
(item) => item === "Forwarding"
) !== -1
) {
console.log("回送");
console.log(d);
console.log(!d.match(/[A,B,R,H,E,T,L]/));
if (!d.match(/[A,B,R,H,E,T,L]/)) return;
}
else if (
selectedTypeList.findIndex((item) => item === "SPCL") !== -1
) {
if (!d.match(/9\d\d\d[D,M,S]/)) return;
}
else{ return; }
}
returnDataArray.push(arrayData);
});
});
setCurrentStationDiagram(
returnDataArray.sort((a, b) => {
const adjustTime = (t: string) => {
const [h, m] = t.split(":").map(Number);
// 4時未満は翌日の時刻とみなして+24時間
return h < 4
? dayjs().add(1, "day").hour(h).minute(m)
: dayjs().hour(h).minute(m);
};
const aa = adjustTime(a.time);
const bb = adjustTime(b.time);
const x = aa.isAfter(bb);
return x ? 1 : -1;
//return true;
})
);
}
}, [currentStation, showLastStop, threw, input, selectedTypeList]);
useEffect(() => {
const showSubscription = Keyboard.addListener("keyboardDidShow", () => {
setKeyBoardVisible(true);
});
const hideSubscription = Keyboard.addListener("keyboardDidHide", () => {
setKeyBoardVisible(false);
});
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
return (
<View style={{ height: "100%", backgroundColor: "#0099CC" }}>
<Text
style={{
textAlign: "center",
fontSize: 20,
color: "white",
fontWeight: "bold",
paddingVertical: 10,
}}
>
{currentStation[0].Station_JP}
</Text>
{/* <ListView data={currentStationDiagram} /> */}
<ExGridView data={currentStationDiagram} />
{/* <Text
style={{
backgroundColor: "white",
borderWidth: 1,
borderStyle: "solid",
}}
>
お気に入り登録した駅のうち、位置情報システムで移動可能な駅が表示されています。タップすることで位置情報システムの当該の駅に移動します。
</Text> */}
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={80}
enabled={Platform.OS === "ios"}
>
<ScrollView horizontal style={{ height: 35, flexDirection: "row" }}>
<TouchableOpacity
style={{
alignItems: "center",
marginHorizontal: 5,
backgroundColor: threw ? "white" : "#ffffff00",
alignSelf: "center",
borderColor: "white",
borderWidth: 1,
borderRadius: 100,
}}
onPress={() => {
setIsThrew(!threw);
}}
>
<Text
style={{
color: threw ? "#0099CC" : "white",
fontSize: 14,
margin: 5,
}}
>
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
alignItems: "center",
marginHorizontal: 5,
backgroundColor: showLastStop ? "white" : "#ffffff00",
alignSelf: "center",
borderColor: "white",
borderWidth: 1,
borderRadius: 100,
}}
onPress={() => {
setShowLastStop(!showLastStop);
}}
>
<Text
style={{
color: showLastStop ? "#0099CC" : "white",
fontSize: 14,
margin: 5,
}}
>
</Text>
</TouchableOpacity>
<View style={{ height: "auto", borderLeftWidth: 1, margin: 5, borderColor: "white" }} />
{showTypeFiltering ? (
<>
<TypeSelectorBox
selectedTypeList={selectedTypeList}
setSelectedTypeList={setSelectedTypeList}
typeName="普通"
typeID="Normal"
color="black"
relativeID={["OneMan"]}
/>
<TypeSelectorBox
selectedTypeList={selectedTypeList}
setSelectedTypeList={setSelectedTypeList}
typeName="快速"
typeID="Rapid"
color="#00b8d8cc"
relativeID={["OneManRapid"]}
/>
<TypeSelectorBox
selectedTypeList={selectedTypeList}
setSelectedTypeList={setSelectedTypeList}
typeName="特急"
typeID="LTDEXP"
color="red"
relativeID={["NightLTDEXP"]}
/>
<TypeSelectorBox
selectedTypeList={selectedTypeList}
setSelectedTypeList={setSelectedTypeList}
typeName="臨時"
typeID="SPCL"
color="#297bff"
relativeID={["SPCL_Normal", "SPCL_Rapid", "SPCL_EXP", "Party"]}
/>
<TypeSelectorBox
selectedTypeList={selectedTypeList}
setSelectedTypeList={setSelectedTypeList}
typeName="貨物"
typeID="Freight"
color="#00869ecc"
/>
<TypeSelectorBox
selectedTypeList={selectedTypeList}
setSelectedTypeList={setSelectedTypeList}
typeName="回送"
typeID="Forwarding"
color="#727272cc"
relativeID={["FreightForwarding"]}
/>
<TouchableOpacity
style={{
alignItems: "center",
marginHorizontal: 5,
backgroundColor: "#ffffff00",
alignSelf: "center",
borderColor: "white",
borderWidth: 1,
borderRadius: 100,
}}
onPress={() => {
LayoutAnimation.configureNext(
LayoutAnimation.Presets.easeInEaseOut
);
setShowTypeFiltering(false);
}}
>
<Text
style={{
color: "white",
fontSize: 14,
margin: 5,
}}
>
</Text>
</TouchableOpacity>
</>
) : (
<TouchableOpacity
style={{
alignItems: "center",
marginHorizontal: 5,
backgroundColor: "#ffffff00",
alignSelf: "center",
borderColor: "white",
borderWidth: 1,
borderRadius: 100,
}}
onPress={() => {
LayoutAnimation.configureNext(
LayoutAnimation.Presets.easeInEaseOut
);
setShowTypeFiltering(true);
}}
>
<Text
style={{
color: "white",
fontSize: 14,
margin: 5,
}}
>
</Text>
</TouchableOpacity>
)}
</ScrollView>
<View
style={{
height: 35,
margin: 5,
alignItems: "center",
backgroundColor: "#F4F4F4",
flexDirection: "row",
paddingLeft: 10,
paddingRight: 10,
borderRadius: 25,
borderColor: "#F4F4F4",
}}
>
<TextInput
placeholder="駅名を入力して停車駅でフィルタリングします。"
onFocus={() => setKeyBoardVisible(true)}
onEndEditing={() => {}}
onChange={(ret) => setInput(ret.nativeEvent.text)}
value={input}
style={{ flex: 1 }}
/>
</View>
</KeyboardAvoidingView>
{keyBoardVisible || (
<BigButton onPress={() => goBack()} string="閉じる" />
)}
</View>
);
};
export const TypeSelectorBox: FC<{
selectedTypeList: typeID[];
setSelectedTypeList: (list: typeID[]) => void;
typeName: string;
typeID: typeID;
color: colorString;
relativeID?: typeID[];
}> = (props) => {
const {
selectedTypeList,
setSelectedTypeList,
typeName,
typeID,
relativeID,
color
} = props;
return (
<TouchableOpacity
style={{
alignItems: "center",
marginHorizontal: 5,
opacity: selectedTypeList.findIndex((item) => item === typeID) !== -1 ? 1 : 0.8,
backgroundColor:
selectedTypeList.findIndex((item) => item === typeID) !== -1
? "white"
: color,
alignSelf: "center",
borderColor: color,
borderWidth: 1,
borderRadius: 100,
}}
onPress={() => {
if (selectedTypeList.findIndex((item) => item === typeID) === -1) {
setSelectedTypeList([
...selectedTypeList,
typeID,
...(relativeID ?? []),
]);
} else {
setSelectedTypeList(
selectedTypeList.filter(
(item) => item !== typeID && !relativeID?.includes(item)
)
);
}
}}
>
<Text
style={{
color:
selectedTypeList.findIndex((item) => item === typeID) !== -1
? color
: "white",
fontSize: 14,
margin: 5,
}}
>
{typeName}
</Text>
</TouchableOpacity>
);
};