diff --git a/App.js b/App.js index 9f27d9c..ddd4afe 100644 --- a/App.js +++ b/App.js @@ -17,6 +17,7 @@ import { DeviceOrientationChangeProvider } from "./stateBox/useDeviceOrientation import { TrainMenuProvider } from "./stateBox/useTrainMenu"; import { buildProvidersTree } from "./lib/providerTreeProvider"; import { StationListProvider } from "./stateBox/useStationList"; +import { NotificationProvider } from "./stateBox/useNotifications"; LogBox.ignoreLogs([ "ViewPropTypes will be removed", @@ -33,6 +34,7 @@ export default function App() { useEffect(() => UpdateAsync(), []); const ProviderTree = buildProvidersTree([ + NotificationProvider, StationListProvider, FavoriteStationProvider, TrainDelayDataProvider, diff --git a/Apps.js b/Apps.js index 5e8ebca..8f1a292 100644 --- a/Apps.js +++ b/Apps.js @@ -2,6 +2,7 @@ import React from "react"; import { NavigationContainer } from "@react-navigation/native"; import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; import { Platform } from "react-native"; +import { useFonts } from "expo-font"; import TNDView from "./ndView"; import { initIcon } from "./lib/initIcon"; import { Top } from "./Top"; @@ -24,11 +25,15 @@ export function AppContainer() { tabBarBadgeStyle: style, }, }); + const [fontLoaded, error] = useFonts({ + "JR-Nishi": require("./assets/fonts/jr-nishi.otf"), + "Zou": require("./assets/fonts/DelaGothicOne-Regular.ttf"), + }); return ( { + AS.getItem("startPage") + .then((res) => { + if (res == "true") navigation.navigate("login"); + }) + .catch((e) => { + //6.0以降false + AS.setItem("startPage", "true"); + }); + }, []); useEffect(() => { const unsubscribe = addListener("tabPress", (e) => { AS.getItem("favoriteStation") @@ -70,11 +80,7 @@ export function MenuPage() { options={{ ...optionData, gestureEnabled: false }} component={AllTrainDiagramView} /> - + ); } diff --git a/app.json b/app.json index 8ce15ed..fc58c9a 100644 --- a/app.json +++ b/app.json @@ -50,7 +50,7 @@ }, "assetBundlePatterns": ["**/*"], "ios": { - "buildNumber": "38", + "buildNumber": "39", "supportsTablet": false, "bundleIdentifier": "jrshikokuinfo.xprocess.hrkn", "config": { diff --git a/assets/configuration/showSetting0.jpg b/assets/configuration/showSetting0.jpg new file mode 100644 index 0000000..57bb439 Binary files /dev/null and b/assets/configuration/showSetting0.jpg differ diff --git a/assets/configuration/showSetting1.jpg b/assets/configuration/showSetting1.jpg new file mode 100644 index 0000000..dd9dbf6 Binary files /dev/null and b/assets/configuration/showSetting1.jpg differ diff --git a/assets/fonts/DelaGothicOne-Regular.ttf b/assets/fonts/DelaGothicOne-Regular.ttf new file mode 100644 index 0000000..475c4dd Binary files /dev/null and b/assets/fonts/DelaGothicOne-Regular.ttf differ diff --git a/assets/fonts/jr-nishi.otf b/assets/fonts/jr-nishi.otf new file mode 100644 index 0000000..e698259 Binary files /dev/null and b/assets/fonts/jr-nishi.otf differ diff --git a/components/ActionSheetComponents/EachTrainInfo/EachStopList.js b/components/ActionSheetComponents/EachTrainInfo/EachStopList.js index 9f3d451..3fa4f41 100644 --- a/components/ActionSheetComponents/EachTrainInfo/EachStopList.js +++ b/components/ActionSheetComponents/EachTrainInfo/EachStopList.js @@ -46,7 +46,10 @@ export const EachStopList = ({ .set("minute", parseInt(time.split(":")[1])) .add(isNaN(currentTrainData?.delay) ? 0 : currentTrainData.delay, "minute"); const timeString = se == "通過" ? "" : dates.format("HH:mm").split(":"); - + const onClickStateText = (string) => { + if (string != "通過") return; + alert("この駅は通過駅です"); + }; return ( @@ -140,6 +143,7 @@ export const EachStopList = ({ : "red", width: 60, }} + onPress={() => onClickStateText(se)} > {se == "通過" ? "レ" : `${timeString[0]}:${timeString[1]}`} diff --git a/components/ActionSheetComponents/EachTrainInfo/StateBox.tsx b/components/ActionSheetComponents/EachTrainInfo/StateBox.tsx index 41cf9cb..fa5f65c 100644 --- a/components/ActionSheetComponents/EachTrainInfo/StateBox.tsx +++ b/components/ActionSheetComponents/EachTrainInfo/StateBox.tsx @@ -6,30 +6,46 @@ type stateBox = { title: string; style?: ViewStyle; mode?: number; + endText?: string; }; -export const StateBox: FC = ({ text, title, style, mode }) => ( - - {title} - - - {text?.match("~") ? ( - <> - - {text.split("~")[0]} +export const StateBox: FC = (props) => { + const { text, title, style, mode, endText } = props; + return ( + + {title} + + + {text?.match("~") ? ( + <> + + {text.split("~")[0]} + + + {mode == 2 ? "→" : "↓"} + + + {text.split("~")[1]} + + + ) : ( + {text} + )} + + {endText && ( + + + {endText} - - {mode == 2 ? "→" : "↓"} - - - {text.split("~")[1]} - - - ) : ( - {text} + )} - -); + ); +}; const boxStyle: ViewStyle = { flex: 1, backgroundColor: "white", diff --git a/components/ActionSheetComponents/EachTrainInfo/TrainDataView.js b/components/ActionSheetComponents/EachTrainInfo/TrainDataView.js index 26d7334..dbdb86a 100644 --- a/components/ActionSheetComponents/EachTrainInfo/TrainDataView.js +++ b/components/ActionSheetComponents/EachTrainInfo/TrainDataView.js @@ -6,6 +6,12 @@ import { getStationList2 } from "../../../lib/getStationList2"; import { useCurrentTrain } from "../../../stateBox/useCurrentTrain"; import { SheetManager } from "react-native-actions-sheet"; import { trainPosition } from "../../../lib/trainPositionTextArray"; +import { TrainPositionDataPush } from "../../発車時刻表/LED_inside_Component/TrainPositionDataPush"; +import { getStationID } from "../../../lib/eachTrainInfoCoreLib/getStationData"; +import { useStationList } from "../../../stateBox/useStationList"; +import { lineList } from "../../../lib/getStationList"; + + export const TrainDataView = ({ currentTrainData, @@ -15,20 +21,59 @@ export const TrainDataView = ({ mode = 0, navigate, }) => { + + const { stationList } = useStationList(); + const { width, height } = useWindowDimensions(); const { isLandscape } = useDeviceOrientationChange(); const { inject } = useCurrentTrain(); const [mapsStationData, setMapsStationData] = useState(undefined); + + const [platformNumber, setPlatformNumber] = useState(); + const [platformDescription, setPlatformDescription] = useState(); + useEffect(() => { + //currentTrainData.Pos = "鴨川~端岡"; //test + if (!currentTrainData) return; + fetch( + `https://n8n.haruk.in/webhook/JR-shikoku-PosID?PosNum=${currentTrainData?.PosNum}&Line=${currentTrainData?.Line}` + ) + .then((res) => res.json()) + .then((data) => { + setPlatformNumber(data?.type == "Station" ? data?.platform : undefined); + setPlatformDescription( + data?.type == "Station" ? data?.description : undefined + ); + }); + }, [currentTrainData]); useEffect(() => { getStationList2().then(setMapsStationData); }, []); const onLine = !!currentPosition?.toString().length; const trainPositionText = (trainData) => { - const { isBetween, Pos } = trainPosition(trainData); - if (isBetween === true) return `${Pos.from}~${Pos.to}`; - else return Pos.Pos == "" ? "" : `${Pos.Pos}`; + const { isBetween, Pos: PosData } = trainPosition(trainData); + const { from, to, Pos } = PosData; + if (isBetween === true) return `${from}~${to}`; + if (Pos == "") return ""; + return `${Pos}${platformNumber ? ` ${platformNumber}番線` : ""}`; }; - return ( + const [dialog, setDialog] = useState(false); + const [deleteDialog, setDeleteDialog] = useState(false); + const [posInput, setPosInput] = useState(""); + const [descInput, setDescInput] = useState(""); + const [stationInput, setStationInput] = useState(""); + const [stationNumberInput, setStationNumberInput] = useState(""); + return (<> + { + const { isBetween, Pos } = trainPosition(currentTrainData); + if (isBetween === true) { + if (platformNumber == undefined && platformDescription == undefined) + return; + setStationInput(`${Pos.from}→${Pos.to}間`); + setStationNumberInput( + getStationID(currentTrainData?.Pos, stationList) + ); + setPosInput(platformNumber?.toString() || ""); + setDeleteDialog(true); + } else { + setStationInput(Pos.Pos); + setStationNumberInput( + getStationID(currentTrainData?.Pos, stationList) + ); + setDescInput(platformDescription || ""); + setPosInput(platformNumber?.toString() || ""); + setDialog(true); + } + }} onPress={() => { + if(!onLine) return; const test = []; Object.keys(mapsStationData).forEach((d) => { mapsStationData[d].forEach((x) => { @@ -67,6 +134,7 @@ export const TrainDataView = ({ mode={mode} title={`現在地 ${currentPosition?.toString()}${onLine ? "▶️" : ""}`} text={trainPositionText(currentTrainData)} + endText={platformDescription ? `${platformDescription}` : ""} style={ onLine ? { borderWidth: 1, borderColor: "red", borderStyle: "solid" } @@ -110,5 +178,6 @@ export const TrainDataView = ({ + ); }; diff --git a/components/ActionSheetComponents/EachTrainInfoCore.js b/components/ActionSheetComponents/EachTrainInfoCore.js index baae4cf..bc639e9 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore.js +++ b/components/ActionSheetComponents/EachTrainInfoCore.js @@ -12,7 +12,7 @@ import { import { SheetManager } from "react-native-actions-sheet"; import { AS } from "../../storageControl"; import trainList from "../../assets/originData/trainList"; -import { lineList, lineListPair } from "../../lib/getStationList"; +import { lineListPair } from "../../lib/getStationList"; import { useCurrentTrain } from "../../stateBox/useCurrentTrain"; import { checkDuplicateTrainData } from "../../lib/checkDuplicateTrainData"; import { getTrainType } from "../../lib/getTrainType"; @@ -46,7 +46,7 @@ export const EachTrainInfoCore = ({ }) => { // const [actionSheetHorizonalScroll, setActionSheetHorizonalScroll] = useState(false); const { currentTrain } = useCurrentTrain(); - const { originalStationList } = useStationList(); + const { originalStationList, stationList } = useStationList(); const { setTrainInfo } = useTrainMenu(); const [currentTrainData, setCurrentTrainData] = useState(); @@ -56,7 +56,8 @@ export const EachTrainInfoCore = ({ if (!currentTrain.length) return; setCurrentTrainData( checkDuplicateTrainData( - currentTrain.filter((d) => d.num == data.trainNum) + currentTrain.filter((d) => d.num == data.trainNum), + stationList ) ); }, [currentTrain, data.trainNum]); @@ -86,15 +87,6 @@ export const EachTrainInfoCore = ({ const [showThrew, setShowThrew] = useState(false); const [haveThrough, setHaveThrough] = useState(false); - const stationList = - originalStationList && - lineList.map((d) => - originalStationList[d].map((a) => ({ - StationNumber: a.StationNumber, - StationName: a.Station_JP, - })) - ); - // 使用例 const stopStationIDList = trainDataWidhThrough.map((i) => { const [station, se, time] = i.split(","); @@ -343,6 +335,7 @@ export const EachTrainInfoCore = ({ .then((d) => { if (d) setTrainPositionSwitch(d); }) + //6.0で変更 .catch(() => AS.setItem("trainPositionSwitch", "false")); }, []); diff --git a/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx b/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx index 45b0ab0..e4d8a51 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx +++ b/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx @@ -6,6 +6,7 @@ import { getType } from "../../../lib/eachTrainInfoCoreLib/getType"; import { migrateTrainName } from "../../../lib/eachTrainInfoCoreLib/migrateTrainName"; import { TrainIconStatus } from "./trainIconStatus"; import { TrainViewIcon } from "./trainViewIcon"; +import { OneManText } from "./HeaderTextParts/OneManText"; type Props = { data: { trainNum: string; limited: string }; @@ -16,10 +17,11 @@ type Props = { tailStation: { id: string }[]; navigate: any; from: string; + fontLoaded: boolean; }; const textConfig: TextStyle = { - fontSize: 20, + fontSize: 18, fontWeight: "bold", color: "white", }; @@ -34,36 +36,127 @@ export const HeaderText: FC = ({ navigate, from, }) => { - const trainName = useMemo(() => { - if (!data.limited) return ""; - const limitedArray = data.limited.split(":"); - const type = getType(limitedArray[0]); + const { limited, trainNum } = data; + // 貨物の判定 + const freightDetect = (num:string)=>{ + switch(num){ + case "71": + return "貨物 東京(タ)→高松(タ)"; + case "73": + case "75": + return "貨物 大阪(タ)→高松(タ)"; + case "3079": + return "貨物 高松(タ)→伊予三島"; + case "3071": + case "3077": + return "貨物 高松(タ)→新居浜"; + case "3073": + return "貨物 高松(タ)→松山貨物"; + case "70": + return "貨物 高松(タ)→東京(タ)"; + case "74": + case "76": + return "貨物 高松(タ)→大阪(タ)"; + case "3078": + return "貨物 伊予三島→高松(タ)"; + case "3070": + return "貨物 新居浜→高松(タ)"; + case "3076": + return "貨物 新居浜→高松(タ)"; + case "3072": + return "貨物 松山貨物→高松(タ)"; + case "9070": + return "貨物 臨時"; + default: + return false; + } + } + + // 列車名、種別、フォントの取得 + const [typeName, trainName, fontAvailable] = useMemo(() => { + if (!limited) return ""; + const limitedArray = limited.split(":"); + const [type, fontAvailable] = (() => { + const d = getType(limitedArray[0]); + switch (true) { + case !!d: + return [d, true]; + case !!trainNum.includes("T"): + return ["単機回送", false]; + case !!trainNum.includes("R"): + case !!trainNum.includes("E"): + case !!trainNum.includes("L"): + case !!trainNum.includes("A"): + case !!trainNum.includes("B"): + return ["回送", false]; + case !!trainNum.includes("H"): + return ["試運転", false]; + case !!trainNum.match("D"): + case !!trainNum.match("M"): + return ["普通", true]; + case !!freightDetect(trainNum): + return [freightDetect(trainNum), false]; + default: + return ["", false]; + } + })(); switch (true) { case !!limitedArray[1]: // 特急の場合は、列車名を取得 - return type + migrateTrainName(limitedArray[1]); + return [type, migrateTrainName(limitedArray[1]), fontAvailable]; case trainData.length == 0: // 特急以外の場合は、列車番号を取得 - return type; + + return [type, "", fontAvailable]; default: // 行先がある場合は、行先を取得 - return ( - type + + return [ + type, migrateTrainName( trainData[trainData.length - 1].split(",")[0] + "行き" - ) - ); + ), + fontAvailable, + ]; } - }, [data.limited, trainData]); + }, [limited, trainData]); + + // 1人運転の判定 + const isOneMan = useMemo(() => { + const OneManRegex = new RegExp(/^4[1-9]\d\d[DM]$/); + const OneManRegex2 = new RegExp(/^5[1-7]\d\d[DM]$/); + return !!( + OneManRegex.test(trainNum) || + OneManRegex2.test(trainNum) || + trainNum === "3621D" + ); + }, [trainNum]); + return ( - - {trainName} + + + + {typeName} + + {isOneMan && } + {trainName} + + {showHeadStation.map((d) => `${headStation[d].id} + `)} - {data.trainNum} + {trainNum} {showTailStation.map((d) => ` + ${tailStation[d].id}`)} diff --git a/components/ActionSheetComponents/EachTrainInfoCore/HeaderTextParts/OneManText.tsx b/components/ActionSheetComponents/EachTrainInfoCore/HeaderTextParts/OneManText.tsx new file mode 100644 index 0000000..9891dd8 --- /dev/null +++ b/components/ActionSheetComponents/EachTrainInfoCore/HeaderTextParts/OneManText.tsx @@ -0,0 +1,16 @@ +import React, { FC } from "react"; +import { Text, View } from "react-native"; +export const OneManText: FC = () => { + const styles = { + fontSize: 12, + margin: -2, + color: "white", + fontFamily: "Zou", + }; + return ( + + ワン + マン + + ); +}; diff --git a/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx b/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx index 2d01dfe..53573d8 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx +++ b/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx @@ -5,6 +5,7 @@ import dayjs from "dayjs"; import { useInterval } from "../../../lib/useInterval"; import { Icon } from "@expo/vector-icons/build/createIconSet"; import { SheetManager } from "react-native-actions-sheet"; +import { customTrainDataDetector } from "../../custom-train-data"; type GlyphNames = ComponentProps["name"]; @@ -20,8 +21,14 @@ type apt = { export const TrainIconStatus: FC = ({ data, navigate, from }) => { const [trainIcon, setTrainIcon] = useState(null); const [anpanmanStatus, setAnpanmanStatus] = useState(); + const [address, setAddress] = useState(""); useEffect(() => { if (!data.trainNum) return; + const { trainIcon, infoUrl } = customTrainDataDetector(data.trainNum); + if (trainIcon) setTrainIcon(trainIcon); + + if (infoUrl) setAddress(infoUrl); + switch (data.trainNum) { case "32D": case "36D": @@ -33,11 +40,6 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { case "45D": case "49D": case "57D": - setTrainIcon( - `https://n8n.haruk.in/webhook/dosan-anpanman-pictures.png?trainNum=${ - data.trainNum - }&day=${dayjs().format("yyyy-MM-DD")}` - ); fetch( `https://n8n.haruk.in/webhook/dosan-anpanman?trainNum=${ data.trainNum @@ -72,7 +74,7 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { { navigate("howto", { - info: "https://www.jr-eki.com/aptrain/naani/dosan/jikoku.html", + info: address, goTo: from == "LED" ? "menu" : from, }); SheetManager.hide("EachTrainInfo"); @@ -81,11 +83,15 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { {move ? ( ) : ( - + )} )} diff --git a/components/AllTrainDiagramView.js b/components/AllTrainDiagramView.js index 07169b7..17e0672 100644 --- a/components/AllTrainDiagramView.js +++ b/components/AllTrainDiagramView.js @@ -8,6 +8,8 @@ import { TextInput, Platform, Keyboard, + ScrollView, + Linking, } from "react-native"; import { useAllTrainDiagram } from "../stateBox/useAllTrainDiagram"; @@ -16,12 +18,26 @@ import { getTrainType } from "../lib/getTrainType"; import { SheetManager } from "react-native-actions-sheet"; import { useNavigation } from "@react-navigation/native"; import { BigButton } from "./atom/BigButton"; +import { Switch } from "react-native-elements"; export default function AllTrainDiagramView() { const { navigate } = useNavigation(); - const { keyList } = useAllTrainDiagram(); + const { keyList, allTrainDiagram } = useAllTrainDiagram(); const [input, setInput] = useState(""); // 文字入力 const [keyBoardVisible, setKeyBoardVisible] = useState(false); - + const [useStationName, setUseStationName] = useState(false); + const [useRegex, setUseRegex] = useState(false); + const regexTextStyle = { + color: "white", + fontSize: 20, + margin: 3, + padding: 3, + }; + const regexTextButtonStyle = { + ...regexTextStyle, + borderWidth: 1, + borderColor: "white", + borderRadius: 3, + }; useEffect(() => { const showSubscription = Keyboard.addListener("keyboardDidShow", () => { setKeyBoardVisible(true); @@ -59,7 +75,23 @@ export default function AllTrainDiagramView() { d.includes(input))} + data={keyList?.filter((d) => { + if (useStationName) { + const ls = input.split(",").map((stationName) => { + return allTrainDiagram[d].includes(stationName); + }); + return !ls.includes(false); + } + if (useRegex) { + try { + const regex = new RegExp(input); + return regex.test(d); + } catch (e) { + return false; + } + } + return d.includes(input); + })} renderItem={({ item }) => } keyExtractor={(item) => item} //initialNumToRender={100} @@ -69,6 +101,87 @@ export default function AllTrainDiagramView() { keyboardVerticalOffset={80} enabled={Platform.OS === "ios"} > + + { + setUseRegex(!useRegex); + setUseStationName(false); + }} + color="red" + style={{ margin: 5 }} + /> + + 正規表現を使用 + + { + setUseRegex(false); + setUseStationName(!useStationName); + }} + color="red" + style={{ margin: 5 }} + /> + + 駅名で検索 + + + + 正規表現のサンプル: + setInput("D")}> + 気動車を選択 + + setInput("3\\d\\d\\dM")} + > + マリンライナーを選択 + + setInput("[4,5]\\d\\d\\d[D,M]")} + > + ワンマン列車を選択 + + setInput("^\\d?\\dM")} + > + しおかぜを選択 + + setInput("^\\d?[0,2,4,6,8]D")} + > + 下り南風を選択 + + setInput("^([\\d])+\\1")} + > + 数字が二桁揃っている列車を選択 + + + Linking.openURL( + "https://qiita.com/tossh/items/635aea9a529b9deb3038" + ) + } + > + 参考資料(Qiita) + + { const { navigate } = useNavigation(); const { favoriteStation } = useFavoriteStation(); const { isLandscape } = useDeviceOrientationChange(); - const { originalStationList } = useStationList(); + const { originalStationList, stationList } = useStationList(); const { setSelectedLine, mapsStationData: stationData, @@ -96,7 +96,8 @@ export const AppsWebView = ({ openStationACFromEachTrainInfo }) => { trainNum, limited, trainData: checkDuplicateTrainData( - currentTrain.filter((a) => a.num == trainNum) + currentTrain.filter((a) => a.num == trainNum), + stationList ), }); //遅延情報は未実装 if (isLandscape) return; diff --git a/components/Settings/NotificationSettings.js b/components/Settings/NotificationSettings.js new file mode 100644 index 0000000..f0c5352 --- /dev/null +++ b/components/Settings/NotificationSettings.js @@ -0,0 +1,149 @@ +import React, { useEffect, useState } from "react"; +import { View, Text, TouchableOpacity, ScrollView,Clipboard } from "react-native"; + +import { CheckBox } from "react-native-elements"; +import { AS } from "../../storageControl"; +import { useNotification } from "../../stateBox/useNotifications"; + +export const NotificationSettings = ({ navigate }) => { + const { expoPushToken } = useNotification(); + const [traInfoEX, setTraInfoEX] = useState(false); + const [informations, setInformations] = useState(false); + const [strangeTrain, setStrangeTrain] = useState(false); + useEffect(() => { + AS.getItem("traInfoEX").then(setTraInfoEX); + AS.getItem("informations").then(setInformations); + AS.getItem("strangeTrain").then(setStrangeTrain); + }, []); + + return ( + + + + { + navigate("settingTopPage"); + }} + style={{ + flexDirection: "column", + flex: 1, + }} + > + + + < 設定/送信 + + + + + + + 通知設定(β) + + + { + fetch( + "https://n8n.haruk.in/webhook/jr-shikoku-notification-configurations", + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + token: expoPushToken, + traInfoEX, + informations, + strangeTrain, + }), + } + ).then(() => { + Promise.all([ + AS.setItem("traInfoEX", traInfoEX.toString()), + AS.setItem("informations", informations.toString()), + AS.setItem("strangeTrain", strangeTrain.toString()), + ]).then(()=>alert("通知の設定を保存、登録しました")); + }); + }} + style={{ + flexDirection: "column", + flex: 1, + }} + > + + + 登録実行 + + + + + + + + + + { + Clipboard.setString(expoPushToken); + }}> + 通知を受け取りたい項目を選択してください。チェックボックスを選び、右上の「登録実行」を押すと設定が反映され、通知が届くようになります。 + + + + ); +}; + +const SimpleSwitch = ({ bool, setBool, str }) => ( + + setBool(bool == "true" ? "false" : "true")} + containerStyle={{ + flex: 1, + backgroundColor: "#00000000", + borderColor: "white", + alignContent: "center", + }} + textStyle={{ fontSize: 20, fontWeight: "normal" }} + title={str} + /> + +); diff --git a/components/Settings/SettingTopPage.js b/components/Settings/SettingTopPage.js index 3716164..783b275 100644 --- a/components/Settings/SettingTopPage.js +++ b/components/Settings/SettingTopPage.js @@ -11,11 +11,20 @@ import { import * as Updates from "expo-updates"; import { useWindowDimensions } from "react-native"; import { ListItem } from "native-base"; +import { SwitchArea } from "../atom/SwitchArea"; +import { useNotification } from "../../stateBox/useNotifications"; -const versionCode = "5.5.1"; +const versionCode = "5.6"; -export const SettingTopPage = ({ navigate, testNFC, updateAndReload }) => { +export const SettingTopPage = ({ + navigate, + testNFC, + startPage, + setStartPage, + updateAndReload, +}) => { const { width } = useWindowDimensions(); + const { expoPushToken } = useNotification(); return ( @@ -100,6 +109,15 @@ export const SettingTopPage = ({ navigate, testNFC, updateAndReload }) => { + navigate("FavoriteSettings")} @@ -128,6 +146,35 @@ export const SettingTopPage = ({ navigate, testNFC, updateAndReload }) => { {">"} + navigate("NotificationSettings")} + disabled={expoPushToken == ""} + > + + 通知設定{expoPushToken == "" ? "(通知設定をオンにしてください)" : "(β)"} + + + + {">"} + + navigate("LayoutSettings")} diff --git a/components/Settings/settings.js b/components/Settings/settings.js index 5d3be45..d375630 100644 --- a/components/Settings/settings.js +++ b/components/Settings/settings.js @@ -22,6 +22,7 @@ import { SettingTopPage } from "./SettingTopPage"; import { LayoutSettings } from "./LayoutSettings"; import { FavoriteSettings } from "./FavoriteSettings"; import { WidgetSettings } from "./WidgetSettings"; +import { NotificationSettings } from "./NotificationSettings"; const Stack = createStackNavigator(); export default function Setting(props) { @@ -35,6 +36,7 @@ export default function Setting(props) { const [trainMenu, setTrainMenu] = useState(false); const [trainPosition, setTrainPosition] = useState(false); const [headerSize, setHeaderSize] = useState("default"); + const [startPage, setStartPage] = useState(false); useLayoutEffect(() => { AS.getItem("iconSwitch").then(setIconSetting); AS.getItem("mapSwitch").then(setMapSwitch); @@ -43,6 +45,7 @@ export default function Setting(props) { AS.getItem("trainSwitch").then(setTrainMenu); AS.getItem("trainPositionSwitch").then(setTrainPosition); AS.getItem("headerSize").then(setHeaderSize); + AS.getItem("startPage").then(setStartPage); }, []); const testNFC = async () => { const result = await ExpoFelicaReader.scan(); @@ -57,6 +60,7 @@ export default function Setting(props) { AS.setItem("trainSwitch", trainMenu.toString()), AS.setItem("trainPositionSwitch", trainPosition.toString()), AS.setItem("headerSize", headerSize), + AS.setItem("startPage", startPage.toString()), ]).then(() => Updates.reloadAsync()); }; return ( @@ -75,19 +79,9 @@ export default function Setting(props) { )} @@ -125,6 +119,23 @@ export default function Setting(props) { /> )} + + {(props) => ( + + )} + { switch (TrainNumber) { //しおかぜメイン @@ -22,7 +23,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "http://www.trainfrontview.net/b/s8000nr.png", + trainIcon: "https://storage.haruk.in/s8000nr.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/shiokaze.html", trainNumDistance: 0, info: "いしづちと併結 / 8000系で運転", }; @@ -30,19 +33,22 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "http://www.trainfrontview.net/b/s8000nr.png", + trainIcon: "https://storage.haruk.in/s8000nr.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/shiokaze.html", trainNumDistance: 0, info: "8000系で運転", }; //8000 アンパン + case "10M": case "22M": case "9M": - case "10M": case "21M": return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "http://www.trainfrontview.net/f/s8000ap.png", + trainIcon: "https://storage.haruk.in/s8000ap.png", + infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 0, info: "いしづちと併結 / アンパンマン列車で運転", }; @@ -58,7 +64,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "http://www.trainfrontview.net/b/s8600.png", + trainIcon: "https://storage.haruk.in/s8600.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/shiokaze.html", trainNumDistance: 0, info: "いしづちと併結 / 8600系で運転", }; @@ -86,7 +94,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "http://www.trainfrontview.net/b/s8000no.png", + trainIcon: "https://storage.haruk.in/s8000no.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 1000, info: "しおかぜと併結 / 8000系で運転", }; @@ -99,7 +109,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "http://www.trainfrontview.net/f/s8000ap.png", + trainIcon: "https://storage.haruk.in/s8000ap.png", + infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 1000, info: "しおかぜと併結 / アンパンマン列車で運転", }; @@ -116,7 +127,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "http://www.trainfrontview.net/b/s8600_isz.png", + trainIcon: "https://storage.haruk.in/s8600_isz.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 1000, info: "しおかぜと併結 / 8600系で運転", }; @@ -127,7 +140,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "モーニングEXP高松", - trainIcon: "http://www.trainfrontview.net/b/s8000no.png", + trainIcon: "https://storage.haruk.in/s8000nr.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/morning.html", trainNumDistance: null, info: "8000系で運転", }; @@ -136,7 +151,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "モーニングEXP松山", - trainIcon: "http://www.trainfrontview.net/b/s8600_isz.png", + trainIcon: "https://storage.haruk.in/s8600_isz.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/morning.html", trainNumDistance: null, info: "8600系で運転", }; @@ -147,7 +164,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "http://www.trainfrontview.net/f/s8000ap.png", + trainIcon: "https://storage.haruk.in/s8000ap.png", + infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 940, info: "アンパンマン列車で運転", }; @@ -158,7 +176,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "http://www.trainfrontview.net/b/s8600_isz.png", + trainIcon: "https://storage.haruk.in/s8600_isz.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 940, info: "8600系で運転", }; @@ -171,7 +191,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "南風", - trainIcon: "http://www.trainfrontview.net/b/s2700.png", + trainIcon: "https://storage.haruk.in/s2700.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/nanpu.html", trainNumDistance: 30, info: "しまんとと併結 / 2700系で運転", }; @@ -180,7 +201,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "南風", - trainIcon: "http://www.trainfrontview.net/b/s2700.png", + trainIcon: "https://storage.haruk.in/s2700.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/nanpu.html", trainNumDistance: 30, info: "うずしおと併結 / 2700系で運転", }; @@ -199,7 +221,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "南風", - trainIcon: "http://www.trainfrontview.net/b/s2700.png", + trainIcon: "https://storage.haruk.in/s2700.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/nanpu.html", trainNumDistance: 30, info: "2700系で運転", }; @@ -210,22 +233,30 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "南風", - trainIcon: "http://www.trainfrontview.net/f/s2700apr.png", + trainIcon: `https://n8n.haruk.in/webhook/dosan-anpanman-pictures.png?trainNum=${TrainNumber}&day=${dayjs().format( + "yyyy-MM-DD" + )}`, + infoUrl: "https://www.jr-eki.com/aptrain/naani/dosan/train.html", trainNumDistance: 30, info: "うずしおと連結 / アンパンマン列車で運転", }; case "32D": + case "36D": case "44D": case "48D": case "56D": case "33D": + case "37D": case "45D": case "49D": case "57D": return { type: "LTDEXP", trainName: "南風", - trainIcon: "http://www.trainfrontview.net/f/s2700apr.png", + trainIcon: `https://n8n.haruk.in/webhook/dosan-anpanman-pictures.png?trainNum=${TrainNumber}&day=${dayjs().format( + "yyyy-MM-DD" + )}`, + infoUrl: "https://www.jr-eki.com/aptrain/naani/dosan/train.html", trainNumDistance: 30, info: "アンパンマン列車で運転", }; @@ -239,7 +270,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "http://www.trainfrontview.net/b/s2700_uzu.png", + trainIcon: "https://storage.haruk.in/s2700_uzu.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 5000, info: "南風と併結 / 高松-宇多津間進行方向逆転 / 2700系で運転", }; @@ -255,7 +288,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "http://www.trainfrontview.net/b/s2700_uzu.png", + trainIcon: "https://storage.haruk.in/s2700_uzu.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "2700系で運転", }; @@ -273,7 +308,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "http://www.trainfrontview.net/b/s2700_uzu.png", + trainIcon: "https://storage.haruk.in/s2700_uzu.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "2700系で運転", }; @@ -292,7 +329,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "http://www.trainfrontview.net/b/s2600.png", + trainIcon: "https://storage.haruk.in/s2600.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "2600系で運転", }; @@ -303,7 +342,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "http://www.trainfrontview.net/b/s185tu_uzu.png", + trainIcon: "https://storage.haruk.in/s185tu_uzu.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "キハ185系で運転", }; @@ -380,7 +421,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "Rapid", trainName: "マリンライナー", - trainIcon: "http://www.trainfrontview.net/b/s5001.png", + trainIcon: "https://storage.haruk.in/s5001.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", trainNumDistance: 3100, info: "", }; @@ -392,7 +434,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "Rapid", trainName: "マリンライナー", - trainIcon: "http://www.trainfrontview.net/b/s5001k.png", + trainIcon: "https://storage.haruk.in/s5001k.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", trainNumDistance: 3100, info: "", }; @@ -403,7 +446,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "NightLTDEXP", trainName: "サンライズ瀬戸", - trainIcon: "http://www.trainfrontview.net/b/w285.png", + trainIcon: "https://storage.haruk.in/w285.png", + infoUrl: + "https://www.jr-odekake.net/train/sunriseseto_izumo/index.html", trainNumDistance: null, info: "", }; @@ -412,7 +457,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "NightLTDEXP", trainName: "サンライズ瀬戸", - trainIcon: "http://www.trainfrontview.net/b/w285.png", + trainIcon: "https://storage.haruk.in/w285.png", + infoUrl: + "https://www.jr-odekake.net/train/sunriseseto_izumo/index.html", trainNumDistance: null, info: "琴平延長運転日", }; @@ -446,7 +493,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "宇和海", - trainIcon: "http://www.trainfrontview.net/b/s2000_uwa.png", + trainIcon: "https://storage.haruk.in/s2000_uwa.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/uwakai.html", trainNumDistance: 1050, info: "2000系で運転", }; @@ -462,7 +510,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "宇和海", - trainIcon: "http://www.trainfrontview.net/f/s2002a.png", + trainIcon: "https://storage.haruk.in/s2002a.png", + infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 1050, info: "アンパン列車で運転", }; @@ -474,7 +523,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しまんと", - trainIcon: "http://www.trainfrontview.net/b/s2700_smn.png", + trainIcon: "https://storage.haruk.in/s2700_smn.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/shimanto.html", trainNumDistance: 2000, info: "2700系で運転", }; @@ -485,7 +536,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しまんと", - trainIcon: "http://www.trainfrontview.net/b/s2700_smn.png", + trainIcon: "https://storage.haruk.in/s2000_smn.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/shimanto.html", trainNumDistance: 2000, info: "南風と併結 / 2700系で運転", }; @@ -502,7 +555,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "あしずり", - trainIcon: "http://www.trainfrontview.net/b/s2000_asi.png", + trainIcon: "https://storage.haruk.in/s2000_asi.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/ashizuri.html", trainNumDistance: 2070, info: "2000系で運転", }; @@ -518,7 +573,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "あしずり", - trainIcon: "http://www.trainfrontview.net/b/s2700_asi.png", + trainIcon: "https://storage.haruk.in/s2700_asi.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/ashizuri.html", trainNumDistance: 2070, info: "2700系で運転", }; @@ -538,7 +595,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "剣山", - trainIcon: "http://www.trainfrontview.net/b/s185tu.png", + trainIcon: "https://storage.haruk.in/s185tu.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/tsurugisan.html", trainNumDistance: 4000, info: "キハ185系で運転", }; @@ -549,7 +608,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "むろと", - trainIcon: "http://www.trainfrontview.net/b/s185_mrt.png", + trainIcon: "https://storage.haruk.in/s185_mrt.png", + infoUrl: "http://jr-shikoku.co.jp/01_trainbus/vehicle-info/muroto.html", trainNumDistance: 5050, info: "キハ185系で運転", }; @@ -560,7 +620,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "よしのがわトロッコ", - trainIcon: "http://www.trainfrontview.net/f/s185to_ai.png", + trainIcon: "https://storage.haruk.in/s185to_ai.png", + infoUrl: + "http://jr-shikoku.co.jp/01_trainbus/event_train/yoshino_torokko.html", trainNumDistance: null, info: "", }; @@ -574,7 +636,9 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "アンパンマントロッコ", - trainIcon: "http://www.trainfrontview.net/f/s32to4.png", + trainIcon: "https://storage.haruk.in/s32to4.png", + infoUrl: + "https://www.jr-eki.com/aptrain/naani/torokko_seto/jikoku.html", trainNumDistance: null, info: "", }; @@ -582,12 +646,21 @@ export const customTrainDataDetector = (TrainNumber: string) => { //伊予灘ものがたり case "8091D": case "8093D": + return { + type: "LTDEXP", + trainName: "伊予灘ものがたり", + trainIcon: "https://storage.haruk.in/s185iyor.png", + infoUrl: "https://iyonadamonogatari.com/", + trainNumDistance: null, + info: "", + }; case "8092D": case "8094D": return { type: "LTDEXP", trainName: "伊予灘ものがたり", - trainIcon: "http://www.trainfrontview.net/b/s185iyoy.png", + trainIcon: "https://storage.haruk.in/s185iyoy.png", + infoUrl: "https://iyonadamonogatari.com/", trainNumDistance: null, info: "", }; @@ -598,7 +671,8 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "四国まんなか千年ものがたり", - trainIcon: "http://www.trainfrontview.net/b/s185mm1.png", + trainIcon: "https://storage.haruk.in/s185mm1.png", + infoUrl: "https://www.jr-shikoku.co.jp/sennenmonogatari/", trainNumDistance: null, info: "", }; @@ -611,19 +685,35 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "時代の夜明けのものがたり", - trainIcon: "http://www.trainfrontview.net/b/s185ym1.png", + trainIcon: "https://storage.haruk.in/s185ym1.png", + infoUrl: "https://www.jr-shikoku.co.jp/yoakenomonogatari/index.html", trainNumDistance: null, info: "", }; default: - return { - type: "Normal", - trainName: "", - trainIcon: null, - trainNumDistance: null, - info: null, - }; + if ( + new RegExp(/^4[1-9]\d\d[DM]$/).test(TrainNumber) || + new RegExp(/^5[1-7]\d\d[DM]$/).test(TrainNumber) || + TrainNumber === "3621D" + ) + return { + type: "OneMan", + trainName: "", + trainIcon: null, + infoUrl: null, + trainNumDistance: null, + info: null, + }; + else + return { + type: "Normal", + trainName: "", + trainIcon: null, + infoUrl: null, + trainNumDistance: null, + info: null, + }; break; } }; diff --git a/components/trainMenu.js b/components/trainMenu.js index ea00fac..ef5f5d2 100644 --- a/components/trainMenu.js +++ b/components/trainMenu.js @@ -82,7 +82,7 @@ export default function TrainMenu({ style }) { d={d} navigate={navigate} webview={webview} - key={index + indexBase} + key={D.StationNumber+d} /> ))} diff --git a/components/発車時刻表/EachData.tsx b/components/発車時刻表/EachData.tsx index 275358e..84c47e4 100644 --- a/components/発車時刻表/EachData.tsx +++ b/components/発車時刻表/EachData.tsx @@ -1,5 +1,6 @@ import React, { FC, useEffect, useState } from "react"; -import { Linking, TouchableOpacity } from "react-native"; +import { Linking, TouchableOpacity, Text } from "react-native"; +import { Dialog, Button, Input } from "react-native-elements"; import { checkDuplicateTrainData } from "../../lib/checkDuplicateTrainData"; import { getTrainDelayStatus } from "../../lib/getTrainDelayStatus"; import { getTrainType } from "../../lib/getTrainType"; @@ -11,7 +12,10 @@ import { LastStation } from "./LED_inside_Component/LastStation"; import { StatusAndDelay } from "./LED_inside_Component/StatusAndDelay"; import { TrainName } from "./LED_inside_Component/TrainName"; import { customTrainDataDetector } from "../custom-train-data"; -import { trainDataType, trainPosition } from "../../lib/trainPositionTextArray"; +import { TrainPosition } from "./LED_inside_Component/TrainPosition"; +import { TrainPositionDataPush } from "./LED_inside_Component/TrainPositionDataPush"; +import { TrainPositionDataDelete } from "./LED_inside_Component/TrainPositionDataDelete"; +import { useStationList } from "../../stateBox/useStationList"; type Props = { d: { @@ -23,6 +27,7 @@ type Props = { trainDescriptionSwitch: boolean; station: { Station_JP: string; + StationNumber: string; }; navigate: (screen: string, data?: any) => void; openStationACFromEachTrainInfo: (station: string) => void; @@ -37,6 +42,7 @@ export const EachData: FC = (props) => { openStationACFromEachTrainInfo, } = props; const { currentTrain } = useCurrentTrain(); + const { stationList } = useStationList(); const openTrainInfo = (d: { train: string; lastStation: string; @@ -67,21 +73,26 @@ export const EachData: FC = (props) => { const getTrainDataFromCurrentTrain = (trainNum: string) => { const customTrainData = customTrainDataDetector(d.train); - if (customTrainData.type != "Normal") return customTrainData; - const currentTrainData = currentTrain.filter((a) => a.num == trainNum); - if (currentTrainData.length == 0) return customTrainData; - else if (currentTrainData[0].Type.includes("rapid:")) { - const typeText = currentTrainData[0].Type.split(":"); - const returnData = { - type: "Rapid", - trainName: typeText[1].replace("\r", ""), - trainIcon: null, - trainNumDistance: null, - info: "", - }; - return returnData; + switch (customTrainData.type) { + case "Normal": + case "OneMan": + const currentTrainData = currentTrain.filter((a) => a.num == trainNum); + if (currentTrainData.length == 0) return customTrainData; + else if (currentTrainData[0].Type.includes("rapid:")) { + const typeText = currentTrainData[0].Type.split(":"); + const returnData = { + type: "Rapid", + trainName: typeText[1].replace("\r", ""), + trainIcon: null, + trainNumDistance: null, + info: "", + }; + return returnData; + } + return customTrainData; + default: + return customTrainData; } - return customTrainData; }; const [train, setTrain] = useState(getTrainDataFromCurrentTrain(d.train)); useEffect(() => { @@ -89,35 +100,44 @@ export const EachData: FC = (props) => { }, [currentTrain, d.train, trainDescriptionSwitch]); // 土讃線複数存在対策 const currentTrainData = checkDuplicateTrainData( - currentTrain.filter((a) => a.num == d.train) + currentTrain.filter((a) => a.num == d.train), + stationList ); const trainDelayStatus = `${getTrainDelayStatus( currentTrainData, station.Station_JP )}`; - const trainPositionText = (trainData: trainDataType) => { - const { isBetween, Pos } = trainPosition(trainData); - if (isBetween === true) return `現在地:${Pos.from}→${Pos.to}間を走行中`; - else return Pos.Pos == "" ? "" : `現在地:${Pos.Pos}`; - }; const [platformNumber, setPlatformNumber] = useState(); const [platformDescription, setPlatformDescription] = useState(); - useEffect(() => { - fetch( - `https://n8n.haruk.in/webhook/JR-shikoku-PosID?PosNum=${currentTrainData?.PosNum}&Line=${currentTrainData?.Line}` - ) - .then((res) => res.json()) - .then((data) => { - setPlatformNumber(data?.type == "Station" ? data?.platform : undefined); - setPlatformDescription( - data?.type == "Station" ? data?.description : undefined - ); - }); - }, [currentTrainData, currentTrain]); + const [dialog, setDialog] = useState(false); + const [deleteDialog, setDeleteDialog] = useState(false); + const [posInput, setPosInput] = useState(""); + const [descInput, setDescInput] = useState(""); + const [stationInput, setStationInput] = useState(""); + const [stationNumberInput, setStationNumberInput] = useState(""); return ( <> + + = (props) => { {trainDescriptionSwitch && ( - { - Linking.openURL( - "https://nexcloud.haruk.in/apps/forms/s/TEkBQW5WLcYjLyAzGxncQLtw" - ); - }} + )} {trainDescriptionSwitch && !!train.info && ( diff --git a/components/発車時刻表/LED_inside_Component/TrainPosition.tsx b/components/発車時刻表/LED_inside_Component/TrainPosition.tsx new file mode 100644 index 0000000..e53e8ef --- /dev/null +++ b/components/発車時刻表/LED_inside_Component/TrainPosition.tsx @@ -0,0 +1,120 @@ +import React, { FC, useEffect } from "react"; +import { Text, TextStyle, View, TouchableOpacity } from "react-native"; +import { useStationList } from "../../../stateBox/useStationList"; +import { + trainDataType, + trainPosition, +} from "../../../lib/trainPositionTextArray"; +import { lineList } from "../../../lib/getStationList"; +import { getStationID } from "../../../lib/eachTrainInfoCoreLib/getStationData"; +import { useCurrentTrain } from "../../../stateBox/useCurrentTrain"; + +const descriptionStyle: TextStyle = { + fontSize: parseInt("16%"), + fontWeight: "bold", +}; + +type Props = { + numberOfLines?: number; + trainIDSwitch: boolean; + currentTrainData: trainDataType; + setStationInput: (station: string) => void; + setStationNumberInput: (station: string) => void; + setDescInput: (desc: string) => void; + setPosInput: (pos: string) => void; + setDialog: (dialog: boolean) => void; + setDeleteDialog: (dialog: boolean) => void; + platformDescription: string; + platformNumber: number; + setPlatformDescription: (desc: string) => void; + setPlatformNumber: (num: number) => void; +}; + +export const TrainPosition: FC = ({ + numberOfLines = 0, + trainIDSwitch, + currentTrainData, + setStationInput, + setStationNumberInput, + setDescInput, + setPosInput, + setDialog, + setDeleteDialog, + setPlatformDescription, + setPlatformNumber, + platformDescription, + platformNumber, +}) => { + const { currentTrain } = useCurrentTrain(); + const { stationList } = useStationList(); + + useEffect(() => { + fetch( + `https://n8n.haruk.in/webhook/JR-shikoku-PosID?PosNum=${currentTrainData?.PosNum}&Line=${currentTrainData?.Line}` + ) + .then((res) => res.json()) + .then((data: { type: string; platform: number; description: string }) => { + setPlatformNumber(data?.type == "Station" ? data?.platform : undefined); + setPlatformDescription( + data?.type == "Station" ? data?.description : undefined + ); + }); + }, [currentTrainData, currentTrain]); + + const trainPositionText = (trainData: trainDataType) => { + const { isBetween, Pos } = trainPosition(trainData); + if (isBetween === true) return `現在地:${Pos.from}→${Pos.to}間を走行中`; + else return Pos.Pos == "" ? "" : `現在地:${Pos.Pos}`; + }; + + return ( + { + const { isBetween, Pos } = trainPosition(currentTrainData); + if (isBetween === true) { + if (platformNumber == undefined && platformDescription == undefined) + return; + setStationInput(`${Pos.from}→${Pos.to}間`); + setStationNumberInput( + getStationID(currentTrainData?.Pos, stationList) + ); + setPosInput(platformNumber?.toString() || ""); + setDeleteDialog(true); + } else { + setStationInput(Pos.Pos); + setStationNumberInput( + getStationID(currentTrainData?.Pos, stationList) + ); + setDescInput(platformDescription || ""); + setPosInput(platformNumber?.toString() || ""); + setDialog(true); + } + }} + > + + + {`${ + trainIDSwitch + ? currentTrainData?.PosNum + currentTrainData?.Line + : trainPositionText(currentTrainData) + } ${platformNumber ? platformNumber + "番線" : ""} ${ + platformDescription ? "(" + platformDescription + ")" : "" + }`} + + + + ); +}; diff --git a/components/発車時刻表/LED_inside_Component/TrainPositionDataDelete.tsx b/components/発車時刻表/LED_inside_Component/TrainPositionDataDelete.tsx new file mode 100644 index 0000000..b266d6c --- /dev/null +++ b/components/発車時刻表/LED_inside_Component/TrainPositionDataDelete.tsx @@ -0,0 +1,46 @@ +import React, { FC } from "react"; +import { Text } from "react-native"; +import { Dialog, Button } from "react-native-elements"; +import { trainDataType } from "../../../lib/trainPositionTextArray"; +type Props = { + dialog: boolean; + setDialog: (dialog: boolean) => void; + currentTrainData: trainDataType; + stationInput: string; + stationNumberInput: string; +}; +export const TrainPositionDataDelete: FC = ({ + dialog, + setDialog, + currentTrainData, + stationInput, + stationNumberInput, +}) => { + const sendPlatformData = () => { + fetch(`https://n8n.haruk.in/webhook/JR-shikoku-PosID`, { + method: "DELETE", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + PosId: currentTrainData?.PosNum, + lineName: currentTrainData?.Line, + }), + }) + .then(() => { + alert("位置情報データ削除要求を送信しました。"); + setDialog(false); + }) + .catch(() => { + alert("位置情報データ削除要求の送信に失敗しました。"); + }); + }; + return ( + setDialog(false)}> + 以下のデータを誤情報として削除要求を出しますか? + 路線: {currentTrainData?.Line} + 地点ID: {currentTrainData?.PosNum} + 駅名: {stationInput} + 駅ナンバー: {stationNumberInput} + + ); +}; diff --git a/components/発車時刻表/LED_inside_Component/TrainPositionDataPush.tsx b/components/発車時刻表/LED_inside_Component/TrainPositionDataPush.tsx new file mode 100644 index 0000000..7bf8be2 --- /dev/null +++ b/components/発車時刻表/LED_inside_Component/TrainPositionDataPush.tsx @@ -0,0 +1,77 @@ +import React, { FC, useState } from "react"; +import { Text } from "react-native"; +import { Dialog, Input, Button } from "react-native-elements"; +import { trainDataType } from "../../../lib/trainPositionTextArray"; +import { useCurrentTrain } from "../../../stateBox/useCurrentTrain"; +type Props = { + dialog: boolean; + setDialog: (dialog: boolean) => void; + currentTrainData: trainDataType; + stationInput: string; + stationNumberInput: string; + posInput: string; + descInput: string; + setPosInput: (pos: string) => void; + setDescInput: (desc: string) => void; + station: { + Station_JP: string; + StationNumber: string; + }; +}; +export const TrainPositionDataPush: FC = ({ + dialog, + setDialog, + currentTrainData, + stationInput, + stationNumberInput, + posInput, + descInput, + setPosInput, + setDescInput, + station, +}) => { + const sendPlatformData = () => { + fetch(`https://n8n.haruk.in/webhook/JR-shikoku-PosID`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + PosId: currentTrainData?.PosNum, + lineName: currentTrainData?.Line, + PlatformNum: parseInt(posInput), + Description: descInput, + StationName: station.Station_JP, + StationId: station.StationNumber, + }), + }) + .then(() => { + alert("位置情報データを送信しました。"); + setDialog(false); + setPosInput(""); + setDescInput(""); + }) + .catch(() => { + alert("位置情報データの送信に失敗しました。"); + }); + }; + return ( + setDialog(false)}> + 路線: {currentTrainData?.Line} + 地点ID: {currentTrainData?.PosNum} + 駅名: {stationInput} + 駅ナンバー: {stationNumberInput} + + + + ); +}; diff --git a/components/発車時刻表/LED_vidion.js b/components/発車時刻表/LED_vidion.js index 51fa6e3..eca24ec 100644 --- a/components/発車時刻表/LED_vidion.js +++ b/components/発車時刻表/LED_vidion.js @@ -206,6 +206,7 @@ export default function LED_vision(props) { openStationACFromEachTrainInfo, }} station={station[0]} + key={d.train} /> ))} {areaString != "" && isInfoArea && ( diff --git a/config/newsUpdate.ts b/config/newsUpdate.ts index 69f97eb..c7c7b82 100644 --- a/config/newsUpdate.ts +++ b/config/newsUpdate.ts @@ -1 +1 @@ -export const news = "2024-11-22"; +export const news = "2025-01-09"; diff --git a/howto.js b/howto.js index 7bf9dd2..514594b 100644 --- a/howto.js +++ b/howto.js @@ -2,11 +2,17 @@ import React from "react"; import { View } from "react-native"; import { WebView } from "react-native-webview"; import { BigButton } from "./components/atom/BigButton"; +import { useNavigation } from "@react-navigation/native"; export default ({ navigation: { navigate }, route }) => { const { info, goTo, useShow } = route.params; + const { goBack } = useNavigation(); const onExit = () => { - navigate(goTo || "Apps"); - useShow && useShow(); + if(goTo != "NearTrainDiagramView") { + navigate(goTo || "Apps"); + useShow && useShow(); + } else { + goBack(); + } }; return ( diff --git a/lib/checkDuplicateTrainData.ts b/lib/checkDuplicateTrainData.ts index 881db65..3e2e3b5 100644 --- a/lib/checkDuplicateTrainData.ts +++ b/lib/checkDuplicateTrainData.ts @@ -1,8 +1,71 @@ import { trainDataType } from "./trainPositionTextArray"; +import { getStationID } from "./eachTrainInfoCoreLib/getStationData"; +import { stationIDPair } from "../lib/getStationList2"; -export const checkDuplicateTrainData = (currentTrainArray: trainDataType[]) => { - const notNyujoData = currentTrainArray.filter((d) => d.delay !== "入線"); +export const checkDuplicateTrainData = ( + currentTrainArray: trainDataType[], + stationList: any[] +) => { + const notSameLineData = checkSameTrain(currentTrainArray, stationList); + const notNyujoData = notSameLineData.filter((d) => d.delay !== "入線"); if (currentTrainArray.length == 1) return currentTrainArray[0]; if (notNyujoData.length == 0) return currentTrainArray[0]; else return notNyujoData[0]; }; + +// 二つのデータを比較して、正しい路線を反映しているデータだけを返す関数 +const checkSameTrain = ( + currentTrainArray: trainDataType[], + stationList: any[] +) => { + const trueLineData = currentTrainArray + .map((d) => { + if (d.Pos.match("~")) { + const [topST, downST] = d.Pos.replace("(下り)", "") + .replace("(上り)", "") + .split("~"); + const stopStationList = [topST, downST].map((i) => { + return stationList.map((a) => a.filter((d) => d.StationName == i)); + }); + const x = stopStationList.map((i, index, array) => { + if (index == array.length - 1) return; + + const firstItem = array[index]; + const secondItem = array[index + 1]; + let betweenStationLine = ""; + let baseStationNumberFirst = ""; + let baseStationNumberSecond = ""; + Object.keys(stationIDPair).forEach((d, index2, array) => { + if (!d) return; + const haveFirst = firstItem[index2]; + const haveSecond = secondItem[index2]; + if (haveFirst.length && haveSecond.length) { + betweenStationLine = d; + baseStationNumberFirst = haveFirst[0].StationNumber; + baseStationNumberSecond = haveSecond[0].StationNumber; + } + }); + + return betweenStationLine; + }); + return x; + } else { + const ST = d.Pos.replace("(下り)", "").replace("(上り)", ""); + const stopStationList = stationList.map((a) => + a.filter((d) => d.StationName == ST) + ); + + const string = Object.keys(stationIDPair).map((d, index2, array) => { + if (!d) return; + if (!stopStationList[index2].length) return; + return d; + }); + return string; + } + }) + .map((x) => x.filter((d) => d != undefined)); + const returnData = currentTrainArray.filter((d, index) => { + return d.Line == trueLineData[index][0]; + }); + return returnData; +}; diff --git a/lib/getTrainType.ts b/lib/getTrainType.ts index e63cec2..279fd15 100644 --- a/lib/getTrainType.ts +++ b/lib/getTrainType.ts @@ -12,6 +12,7 @@ type trainTypeString = | "寝台特急" | "臨時" | "普通列車" + | "普通列車(ワンマン)" | "その他"; type trainTypeDataString = "rapid" | "express" | "normal"; type getTrainType = (d: nameString) => { @@ -29,6 +30,8 @@ export const getTrainType: getTrainType = (nameString) => { return { color: "red", name: "寝台特急", data: "express" }; case "SPCL": return { color: "blue", name: "臨時", data: "normal" }; + case "OneMan": + return { color: "white", name: "普通列車(ワンマン)", data: "normal" }; case "Normal": return { color: "white", name: "普通列車", data: "normal" }; default: diff --git a/menu.js b/menu.js index 260d4a8..12b9d36 100644 --- a/menu.js +++ b/menu.js @@ -362,10 +362,10 @@ const JRSTraInfoBox = () => { /> ) : delayData ? ( - delayData.map((d) => { + delayData.map((d,index,array) => { let data = d.split(" "); return ( - + {data[0].replace("\n", "")} diff --git a/stateBox/useNotifications.tsx b/stateBox/useNotifications.tsx new file mode 100644 index 0000000..bb3ec4c --- /dev/null +++ b/stateBox/useNotifications.tsx @@ -0,0 +1,143 @@ +import React, { + createContext, + useContext, + useState, + useEffect, + FC, + useRef, +} from "react"; +// 6.0でライブラリ変更 +import { Platform, Clipboard } from "react-native"; +// 6.0でライブラリ更新、tsの型定義が変わった +import * as Notifications from "expo-notifications"; +import * as Device from "expo-device"; +import Constants from "expo-constants"; +type initialStateType = { + expoPushToken: string; +}; +const initialState = { + expoPushToken: "", +}; + +Notifications.setNotificationHandler({ + handleNotification: async () => ({ + shouldShowAlert: true, + shouldPlaySound: true, + shouldSetBadge: true, + }), +}); + +function handleRegistrationError(errorMessage: string) { + console.log(errorMessage); + //throw new Error(errorMessage); +} + +async function registerForPushNotificationsAsync() { + if (Platform.OS === "android") { + Notifications.setNotificationChannelAsync("default", { + name: "default", + importance: Notifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: "#FF231F7C", + }); + Notifications.setNotificationChannelAsync("運行情報", { + name: "運行情報", + importance: Notifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: "#FF231F7C", + }); + Notifications.setNotificationChannelAsync("遅延速報EX", { + name: "遅延速報EX", + importance: Notifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: "#FF231F7C", + }); + Notifications.setNotificationChannelAsync("怪レい列車BOT", { + name: "怪レい列車BOT", + importance: Notifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: "#FF231F7C", + }); + } + + if (Device.isDevice) { + const { status: existingStatus } = + await Notifications.getPermissionsAsync(); + let finalStatus = existingStatus; + if (existingStatus !== "granted") { + const { status } = await Notifications.requestPermissionsAsync(); + finalStatus = status; + } + if (finalStatus !== "granted") { + handleRegistrationError( + "Permission not granted to get push token for push notification!" + ); + return; + } + const projectId = + Constants?.expoConfig?.extra?.eas?.projectId ?? + Constants?.easConfig?.projectId; + if (!projectId) { + handleRegistrationError("Project ID not found"); + } + try { + const pushTokenString = ( + await Notifications.getExpoPushTokenAsync({ + projectId, + }) + ).data; + console.log(pushTokenString); + return pushTokenString; + } catch (e) { + handleRegistrationError(`${e}`); + } + } else { + handleRegistrationError("Must use physical device for push notifications"); + } +} +const NotificationContext = createContext(initialState); +type Props = { + children: React.ReactNode; +}; +export const useNotification = () => { + return useContext(NotificationContext); +}; + +export const NotificationProvider: FC = ({ children }) => { + const [expoPushToken, setExpoPushToken] = useState(""); + const [notification, setNotification] = useState< + Notifications.Notification | undefined + >(undefined); + const notificationListener = useRef(); + const responseListener = useRef(); + + useEffect(() => { + registerForPushNotificationsAsync() + .then((token) => setExpoPushToken(token ?? "")) + .catch((error) => setExpoPushToken(`${error}`)); + + notificationListener.current = + Notifications.addNotificationReceivedListener((notification) => { + setNotification(notification); + }); + + responseListener.current = + Notifications.addNotificationResponseReceivedListener((response) => { + console.log(response); + }); + + return () => { + notificationListener.current && + Notifications.removeNotificationSubscription( + notificationListener.current + ); + responseListener.current && + Notifications.removeNotificationSubscription(responseListener.current); + }; + }, []); + return ( + + {children} + + ); +}; diff --git a/stateBox/useStationList.tsx b/stateBox/useStationList.tsx index d947796..c064c12 100644 --- a/stateBox/useStationList.tsx +++ b/stateBox/useStationList.tsx @@ -6,16 +6,19 @@ import React, { FC, } from "react"; import { getStationList } from "../lib/getStationList"; +import { lineList } from "../lib/getStationList"; type initialStateType = { - originalStationList: any[]; + originalStationList: any[][]; setOriginalStationList: React.Dispatch>; getStationData: (id: string) => void; + stationList: any[]; }; const initialState = { - originalStationList: [], + originalStationList: [[]], setOriginalStationList: () => {}, getStationData: () => {}, + stationList: [], }; const StationListContext = createContext(initialState); @@ -42,10 +45,23 @@ export const StationListProvider: FC = ({ children }) => { }); return returnArray; }; - + const [stationList, setStationList] = useState([[]]); + useEffect(()=>{ + if(originalStationList.length === 0) return; + const stationList = + originalStationList && + lineList.map((d) => + originalStationList[d].map((a) => ({ + StationNumber: a.StationNumber, + StationName: a.Station_JP, + })) + ); + setStationList(stationList) + },[originalStationList]) + return ( {children} diff --git a/stateBox/useTrainMenu.js b/stateBox/useTrainMenu.js index eabd0ec..4db725b 100644 --- a/stateBox/useTrainMenu.js +++ b/stateBox/useTrainMenu.js @@ -72,6 +72,7 @@ export const TrainMenuProvider = ({ children }) => { //列車アイコンスイッチ ASCore({ k: "iconSwitch", s: setIconSetting, d: "true", u: true }); //地図スイッチ + //6.0.0以降true ASCore({ k: "mapSwitch", s: setMapSwitch, d: "false", u: true }); //駅メニュースイッチ ASCore({ k: "stationSwitch", s: setStationMenu, d: "true", u: true });