diff --git a/App.js b/App.js index ded91f4..4fec0f5 100644 --- a/App.js +++ b/App.js @@ -18,6 +18,7 @@ import { TrainMenuProvider } from "./stateBox/useTrainMenu"; import { buildProvidersTree } from "./lib/providerTreeProvider"; import { StationListProvider } from "./stateBox/useStationList"; import { NotificationProvider } from "./stateBox/useNotifications"; +import { UserPositionProvider } from "./stateBox/useUserPosition"; LogBox.ignoreLogs([ "ViewPropTypes will be removed", @@ -36,6 +37,7 @@ export default function App() { const ProviderTree = buildProvidersTree([ AllTrainDiagramProvider, NotificationProvider, + UserPositionProvider, StationListProvider, FavoriteStationProvider, TrainDelayDataProvider, diff --git a/GeneralWebView.tsx b/GeneralWebView.tsx new file mode 100644 index 0000000..dbc47ea --- /dev/null +++ b/GeneralWebView.tsx @@ -0,0 +1,30 @@ +import React, { CSSProperties } from "react"; +import { View, ViewProps } from "react-native"; +import { WebView } from "react-native-webview"; +import { BigButton } from "./components/atom/BigButton"; +import { useNavigation } from "@react-navigation/native"; +export default ({ route }) => { + if (!route.params) { + return null; + } + const { uri, useExitButton = true } = route.params; + const { goBack } = useNavigation(); + return ( + + { + const { data } = event.nativeEvent; + const {type} = JSON.parse(data); + if (type === "windowClose") return goBack(); + }} + /> + {useExitButton && } + + ); +}; +const styles: ViewProps["style"] = { + height: "100%", + backgroundColor: "#0099CC", +}; diff --git a/MenuPage.js b/MenuPage.js index dc198b5..6f00ec3 100644 --- a/MenuPage.js +++ b/MenuPage.js @@ -1,5 +1,10 @@ -import React, { useEffect } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { createStackNavigator } from "@react-navigation/stack"; +import { useWindowDimensions, Platform } from "react-native"; +import Constants from "expo-constants"; + +import { Dimensions, StatusBar } from "react-native"; + import { SheetManager } from "react-native-actions-sheet"; import { AS } from "./storageControl"; import TrainBase from "./components/trainbaseview"; @@ -10,14 +15,16 @@ import Setting from "./components/Settings/settings"; import { useFavoriteStation } from "./stateBox/useFavoriteStation"; import { optionData } from "./lib/stackOption"; import AllTrainDiagramView from "./components/AllTrainDiagramView"; -import { useCurrentTrain } from "./stateBox/useCurrentTrain"; import { useNavigation } from "@react-navigation/native"; import { news } from "./config/newsUpdate"; +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; +import GeneralWebView from "./GeneralWebView"; const Stack = createStackNavigator(); export function MenuPage() { const { favoriteStation, setFavoriteStation } = useFavoriteStation(); - const { getCurrentTrain } = useCurrentTrain(); + const { height, width } = useWindowDimensions(); + const tabBarHeight = useBottomTabBarHeight(); const navigation = useNavigation(); const { addListener } = navigation; useEffect(() => { @@ -42,8 +49,34 @@ export function MenuPage() { }) .catch((error) => console.error("Error fetching icon setting:", error)); }, []); + + const scrollRef = useRef(null); + const [mapMode, setMapMode] = useState(false); + const [mapHeight, setMapHeight] = useState(0); + useEffect(() => { + const MapHeight = + height - + tabBarHeight + + (Platform.OS == "android" ? Constants.statusBarHeight : 0) - + 100 - + ((((width / 100) * 80) / 20) * 9 + 10 + 30); + setMapHeight(MapHeight); + }, [height, tabBarHeight, width]); + const [MapFullHeight, setMapFullHeight] = useState(0); + useEffect(() => { + const MapFullHeight = + height - + tabBarHeight + + (Platform.OS == "android" ? Constants.statusBarHeight : 0); + setMapFullHeight(MapFullHeight); + }, [height, tabBarHeight, width]); useEffect(() => { const unsubscribe = addListener("tabPress", (e) => { + scrollRef.current.scrollTo({ + y: mapHeight - 80, + animated: true, + }); + setMapMode(false); AS.getItem("favoriteStation") .then((d) => { const returnData = JSON.parse(d); @@ -55,7 +88,7 @@ export function MenuPage() { }); return unsubscribe; - }, [navigation]); + }, [navigation, mapHeight]); return ( } + children={() => ( + + )} /> + ); } diff --git a/Top.js b/Top.js index 4e547b8..5f25b74 100644 --- a/Top.js +++ b/Top.js @@ -13,6 +13,7 @@ import { useTrainMenu } from "./stateBox/useTrainMenu"; import { AS } from "./storageControl"; import { news } from "./config/newsUpdate"; import { Linking, Platform } from "react-native"; +import GeneralWebView from "./GeneralWebView"; const Stack = createStackNavigator(); export const Top = () => { const { webview } = useCurrentTrain(); @@ -64,6 +65,7 @@ export const Top = () => { component={TrainBase} /> + { stickyHeaderIndices={[1]} scrollEventThrottle={16} onScroll={(d) => { - console.log(d.nativeEvent.contentOffset.y); + // Handle scroll events }} > diff --git a/components/ActionSheetComponents/EachTrainInfoCore.js b/components/ActionSheetComponents/EachTrainInfoCore.js index c419bb7..23a15cb 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore.js +++ b/components/ActionSheetComponents/EachTrainInfoCore.js @@ -44,7 +44,8 @@ export const EachTrainInfoCore = ({ }) => { const { currentTrain } = useCurrentTrain(); const { originalStationList, stationList } = useStationList(); - const { allTrainDiagram: trainList } = useAllTrainDiagram(); + const { allTrainDiagram: trainList, allCustonTrainData } = + useAllTrainDiagram(); const { setTrainInfo } = useTrainMenu(); const [currentTrainData, setCurrentTrainData] = useState(); @@ -170,12 +171,12 @@ export const EachTrainInfoCore = ({ const position = points.findIndex((d) => d == true); let isThrew = false; if (position == -1) return () => {}; + setShowThrew(true); if (trainDataWidhThrough[position].split(",")[1] == "通過") { LayoutAnimation.configureNext({ duration: 400, update: { type: "easeInEaseOut", springDamping: 0.6 }, }); - setShowThrew(true); isThrew = true; } if (position < 5) { @@ -322,7 +323,7 @@ export const EachTrainInfoCore = ({ }, []); const openTrainInfo = (d) => { - const train = customTrainDataDetector(d); + const train = customTrainDataDetector(d, allCustonTrainData); let TrainNumber = ""; if (train.trainNumDistance != undefined) { const timeInfo = diff --git a/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx b/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx index 33037ef..79b6195 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx +++ b/components/ActionSheetComponents/EachTrainInfoCore/HeaderText.tsx @@ -1,14 +1,15 @@ import React, { CSSProperties, FC, useEffect, useMemo, useState } from "react"; -import { Text, View, LayoutAnimation, TextStyle } from "react-native"; -import { Ionicons } from "@expo/vector-icons"; +import { Text, View, TextStyle, TouchableOpacity } from "react-native"; import { SheetManager } from "react-native-actions-sheet"; -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"; import { customTrainDataDetector } from "@/components/custom-train-data"; import { InfogramText } from "@/components/ActionSheetComponents/EachTrainInfoCore/HeaderTextParts/InfogramText"; +import { useTrainMenu } from "@/stateBox/useTrainMenu"; +import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram"; +import { useNotification } from "@/stateBox/useNotifications"; type Props = { data: { trainNum: string; limited: string }; @@ -38,14 +39,21 @@ export const HeaderText: FC = ({ tailStation, navigate, from, - scrollHandlers + scrollHandlers, }) => { const { limited, trainNum } = data; + const { updatePermission } = useTrainMenu(); + const { allCustonTrainData } = useAllTrainDiagram(); + const { expoPushToken } = useNotification(); + // 列車名、種別、フォントの取得 const [typeName, trainName, fontAvailable, isOneMan, infogram] = useMemo(() => { - const customTrainData = customTrainDataDetector(trainNum); + const customTrainData = customTrainDataDetector( + trainNum, + allCustonTrainData + ); const [type, fontAvailable, isOneMan] = (() => { switch (customTrainData.type) { case "LTDEXP": @@ -118,10 +126,27 @@ export const HeaderText: FC = ({ }, [trainData]); return ( - scrollHandlers.ref.current?.scrollTo({ y: 0, animated: true })}> + + scrollHandlers.ref.current?.scrollTo({ y: 0, animated: true }) + } + > - { + navigate("generalWebView", { + uri: + "https://jr-shikoku-data-post-system.pages.dev?trainNum=" + + trainNum + + "&token=" + + expoPushToken, + useExitButton: false, + }); + SheetManager.hide("EachTrainInfo"); + }} + disabled={!updatePermission} > = ({ {isOneMan && } {trainName} - + diff --git a/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx b/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx index ce910e1..ed642e9 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx +++ b/components/ActionSheetComponents/EachTrainInfoCore/trainIconStatus.tsx @@ -6,6 +6,7 @@ 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"; +import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram"; type GlyphNames = ComponentProps["name"]; @@ -22,11 +23,14 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { const [trainIcon, setTrainIcon] = useState(null); const [anpanmanStatus, setAnpanmanStatus] = useState(); const [address, setAddress] = useState(""); + const { allCustonTrainData } = useAllTrainDiagram(); useEffect(() => { if (!data.trainNum) return; - const { trainIcon, infoUrl } = customTrainDataDetector(data.trainNum); - if (trainIcon) setTrainIcon(trainIcon); - + const { img, infoUrl } = customTrainDataDetector( + data.trainNum, + allCustonTrainData + ); + if (img) setTrainIcon(img); if (infoUrl) setAddress(infoUrl); switch (data.trainNum) { @@ -47,7 +51,6 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { ) .then((d) => d.json()) .then((d) => { - console.log(d); if (d.trainStatus == "〇") { //setAnpanmanStatus({name:"checkmark-circle-outline",color:"blue"}); } else if (d.trainStatus == "▲") { @@ -57,31 +60,30 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { } }); break; - case "2074D": - case "2076D": - case "2080D": - case "2082D": - case "2071D": - case "2073D": - case "2079D": - case "2081D": - fetch( - `https://n8n.haruk.in/webhook/dosan-anpanman-first?trainNum=${ - data.trainNum - }&month=${dayjs().format("M")}&day=${dayjs().format("D")}` - ) - .then((d) => d.json()) - .then((d) => { - console.log(d); - if (d.trainStatus == "〇") { - //setAnpanmanStatus({name:"checkmark-circle-outline",color:"blue"}); - } else if (d.trainStatus == "▲") { - setAnpanmanStatus({ name: "warning-outline", color: "yellow" }); - } else if (d.trainStatus == "×") { - //setAnpanmanStatus({ name: "close-circle-outline", color: "red" }); - } - }); - break; + case "2074D": + case "2076D": + case "2080D": + case "2082D": + case "2071D": + case "2073D": + case "2079D": + case "2081D": + fetch( + `https://n8n.haruk.in/webhook/dosan-anpanman-first?trainNum=${ + data.trainNum + }&month=${dayjs().format("M")}&day=${dayjs().format("D")}` + ) + .then((d) => d.json()) + .then((d) => { + if (d.trainStatus == "〇") { + //setAnpanmanStatus({name:"checkmark-circle-outline",color:"blue"}); + } else if (d.trainStatus == "▲") { + setAnpanmanStatus({ name: "warning-outline", color: "yellow" }); + } else if (d.trainStatus == "×") { + //setAnpanmanStatus({ name: "close-circle-outline", color: "red" }); + } + }); + break; } }, [data.trainNum]); const [move, setMove] = useState(true); @@ -104,6 +106,7 @@ export const TrainIconStatus: FC = ({ data, navigate, from }) => { }); SheetManager.hide("EachTrainInfo"); }} + disabled={!address} > {move ? ( { + const actionSheetRef = useRef(null); + const insets = useSafeAreaInsets(); + + return ( + } + ref={actionSheetRef} + isModal={Platform.OS == "ios"} + containerStyle={ + Platform.OS == "android" + ? { + paddingBottom: insets.bottom, + } + : {} + } + useBottomSafeAreaPadding={Platform.OS == "android"} + > + + + + + + + + + JR四国公式SNS一族 + + + + {[ + { + url: "https://twitter.com/jr_shikoku_info", + name: "JR四国列車運行情報", + }, + + { + url: "https://twitter.com/JRshikoku_eigyo", + name: "JR四国営業部【公式】", + }, + { + url: "https://twitter.com/JRshikoku_tokyo", + name: "JR四国 東京営業情報【公式】", + }, + { + url: "https://twitter.com/JRshikoku_osaka", + name: "JR四国 大阪営業部【公式】", + }, + { + url: "https://twitter.com/jrs_matsuyama", + name: "JR四国 松山駅 【公式】", + }, + { + url: "https://twitter.com/jrshikoku_kochi", + name: "JR四国 高知駅【公式】", + }, + { + url: "https://twitter.com/jr_tokust", + name: "JR四国 徳島駅【公式】", + }, + { + url: "https://twitter.com/jrshikoku_uwjm", + name: "JR四国 宇和島駅【公式】", + }, + { + url: "https://twitter.com/jrshikoku_wkama", + name: "JR四国 ワープ高松支店【公式】", + }, + { + url: "https://twitter.com/JRshikoku_wkoch", + name: "JR四国 ワープ高知支店【公式】", + }, + { + url: "https://twitter.com/Yoakemonogatari", + name: "志国土佐 時代の夜明けのものがたり【公式】", + }, + { + url: "https://twitter.com/Smile_Eki_Chan", + name: "すまいるえきちゃん♡JR四国【公式】", + }, + { + url: "https://twitter.com/sper_ponchan", + name: "しこくたぬきのぽんちゃん 【四国家サポーターズクラブ】", + }, + ].map((d) => ( + Linking.openURL(d.url)} + key={d.url} + friction={90} // + tension={100} // These props are passed to the parent component (here TouchableScale) + activeScale={0.95} // + Component={TouchableScale} + > + + {d.name} + + + + ))} + + + + ); +}; diff --git a/components/ActionSheetComponents/SpecialTrainInfo.tsx b/components/ActionSheetComponents/SpecialTrainInfo.tsx new file mode 100644 index 0000000..590f0d9 --- /dev/null +++ b/components/ActionSheetComponents/SpecialTrainInfo.tsx @@ -0,0 +1,51 @@ +import React, { useRef } from "react"; +import { View, Platform } from "react-native"; +import ActionSheet from "react-native-actions-sheet"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; + +import { SpecialTrainInfoBox } from "../Menu/SpecialTrainInfoBox"; +export const SpecialTrainInfo = ({ payload }) => { + const { navigate } = payload; + const actionSheetRef = useRef(null); + const insets = useSafeAreaInsets(); + + return ( + } + ref={actionSheetRef} + isModal={Platform.OS == "ios"} + containerStyle={ + Platform.OS == "android" + ? { + paddingBottom: insets.bottom, + } + : {} + } + useBottomSafeAreaPadding={Platform.OS == "android"} + > + + + + + + + + ); +}; diff --git a/components/ActionSheetComponents/StationDeteilView.js b/components/ActionSheetComponents/StationDeteilView.js index 48d67a3..3cfffd2 100644 --- a/components/ActionSheetComponents/StationDeteilView.js +++ b/components/ActionSheetComponents/StationDeteilView.js @@ -2,19 +2,14 @@ import React, { useState, useEffect } from "react"; import { View, Linking, - Text, - TouchableOpacity, BackHandler, Platform, + useWindowDimensions, } from "react-native"; -import AutoHeightImage from "react-native-auto-height-image"; -import { FontAwesome, Foundation, Ionicons } from "@expo/vector-icons"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import ActionSheet, { SheetManager } from "react-native-actions-sheet"; import Sign from "../../components/駅名表/Sign"; -import { TicketBox } from "../atom/TicketBox"; -import { widthPercentageToDP as wp } from "react-native-responsive-screen"; import { getPDFViewURL } from "../../lib/getPdfViewURL"; import { useBusAndTrainData } from "../../stateBox/useBusAndTrainData"; import { AS } from "../../storageControl"; @@ -23,17 +18,19 @@ import { TrainBusButton } from "./StationDeteilView/TrainBusButton"; import { 駅構内図 } from "./StationDeteilView/StationInsideMapButton"; import { WebSiteButton } from "./StationDeteilView/WebSiteButton"; import { StationTimeTableButton } from "./StationDeteilView/StationTimeTableButton"; +import { StationTrainPositionButton } from "./StationDeteilView/StationTrainPositionButton"; export const StationDeteilView = (props) => { if (!props.payload) return <>; const { currentStation, navigate, onExit, goTo, useShow } = props.payload; + const { width } = useWindowDimensions(); const { busAndTrainData } = useBusAndTrainData(); const [trainBus, setTrainBus] = useState(); useEffect(() => { if (!currentStation) return () => {}; - const data = busAndTrainData.filter((d) => - d.name === currentStation[0].Station_JP + const data = busAndTrainData.filter( + (d) => d.name === currentStation[0].Station_JP ); if (data.length == 0) { setTrainBus(); @@ -89,73 +86,84 @@ export const StationDeteilView = (props) => { {currentStation && ( - - { - usePDFView == "true" - ? Linking.openURL(currentStation[0].StationTimeTable) - : navigate("howto", { - info, + <> + + { + usePDFView == "true" + ? Linking.openURL(currentStation[0].StationTimeTable) + : navigate("howto", { + info, + goTo, + useShow, + }); + onExit(); + }} + oLP={() => + Linking.openURL(currentStation[0].StationTimeTable) + } + /> + + + + {currentStation[0].JrHpUrl && + currentStation[0].StationNumber != "M12" && ( + <駅構内図 //児島例外/ + navigate={navigate} + goTo={goTo} + useShow={useShow} + address={currentStation[0].JrHpUrl} + onExit={onExit} + /> + )} + + + {!currentStation[0].JrHpUrl || ( + + )} + {!currentStation[0].StationTimeTable || ( + + )} + + {!trainBus || ( + { + navigate("howto", { + info: trainBus.address, goTo, useShow, }); - onExit(); - }} - oLP={() => Linking.openURL(currentStation[0].StationTimeTable)} - /> - - )} - {currentStation && - currentStation[0].JrHpUrl && - currentStation[0].StationNumber != "M12" && ( - <駅構内図 //児島例外/ - navigate={navigate} - goTo={goTo} - useShow={useShow} - address={currentStation[0].JrHpUrl} - onExit={onExit} - /> - )} - {currentStation && ( - - {!currentStation[0].JrHpUrl || ( - - )} - {!currentStation[0].StationTimeTable || ( - - )} - {!currentStation[0].StationMap || ( - - )} - {!trainBus || ( - { - navigate("howto", { - info: trainBus.address, - goTo, - useShow, - }); - onExit(); - }} - /> - )} - + onExit(); + }} + /> + )} + + )} diff --git a/components/ActionSheetComponents/StationDeteilView/StationInsideMapButton.tsx b/components/ActionSheetComponents/StationDeteilView/StationInsideMapButton.tsx index 19cc47a..6b68b45 100644 --- a/components/ActionSheetComponents/StationDeteilView/StationInsideMapButton.tsx +++ b/components/ActionSheetComponents/StationDeteilView/StationInsideMapButton.tsx @@ -19,6 +19,7 @@ export const 駅構内図:FC = (props) => { alignContent: "center", alignItems: "center", margin: 2, + flex: 1, }} onPress={() => { navigate("howto", { info, goTo, useShow }); diff --git a/components/ActionSheetComponents/StationDeteilView/StationTrainPositionButton.tsx b/components/ActionSheetComponents/StationDeteilView/StationTrainPositionButton.tsx new file mode 100644 index 0000000..dcc0a84 --- /dev/null +++ b/components/ActionSheetComponents/StationDeteilView/StationTrainPositionButton.tsx @@ -0,0 +1,54 @@ +import { FC } from "react"; +import { TouchableOpacity, View, Text, Linking } from "react-native"; +import { useStationList } from "@/stateBox/useStationList"; +import { useCurrentTrain } from "@/stateBox/useCurrentTrain"; +import AntDesign from "react-native-vector-icons/AntDesign"; +type Props = { + stationNumber: string; + onExit: () => void; + navigate?: (screen: string, params: { screen: string }) => void; +}; +export const StationTrainPositionButton: FC = (props) => { + const { stationNumber, onExit, navigate } = props; + const { + inject, + } = useCurrentTrain(); + const { getInjectJavascriptAddress } = useStationList(); + return ( + { + navigate("positions", { screen: "Apps" }); + const script = getInjectJavascriptAddress(stationNumber); + inject(script); + onExit(); + }} + > + + + + 走行位置に移動 + + + + ); +}; diff --git a/components/ActionSheetComponents/sheets.ts b/components/ActionSheetComponents/sheets.ts index 2f4ba52..71d9912 100644 --- a/components/ActionSheetComponents/sheets.ts +++ b/components/ActionSheetComponents/sheets.ts @@ -4,11 +4,15 @@ import { JRSTraInfo } from "./JRSTraInfo"; import { StationDeteilView } from "./StationDeteilView"; import { TrainMenuLineSelector } from "./TrainMenuLineSelector"; import { TrainIconUpdate } from "./TrainIconUpdate"; +import { SpecialTrainInfo } from "./SpecialTrainInfo"; +import { Social } from "./SocialMenu"; registerSheet("EachTrainInfo", EachTrainInfo); registerSheet("JRSTraInfo", JRSTraInfo); registerSheet("StationDetailView", StationDeteilView); registerSheet("TrainMenuLineSelector", TrainMenuLineSelector); registerSheet("TrainIconUpdate", TrainIconUpdate); +registerSheet("SpecialTrainInfo", SpecialTrainInfo); +registerSheet("Social", Social); export {}; diff --git a/components/AllTrainDiagramView.js b/components/AllTrainDiagramView.js index a9ddd98..491f9cc 100644 --- a/components/AllTrainDiagramView.js +++ b/components/AllTrainDiagramView.js @@ -21,7 +21,7 @@ import { BigButton } from "./atom/BigButton"; import { Switch } from "react-native-elements"; export default function AllTrainDiagramView() { const { goBack, navigate } = useNavigation(); - const { keyList, allTrainDiagram } = useAllTrainDiagram(); + const { keyList, allTrainDiagram, allCustonTrainData } = useAllTrainDiagram(); const [input, setInput] = useState(""); // 文字入力 const [keyBoardVisible, setKeyBoardVisible] = useState(false); const [useStationName, setUseStationName] = useState(false); @@ -53,7 +53,7 @@ export default function AllTrainDiagramView() { }, []); const openTrainInfo = (d) => { - const train = customTrainDataDetector(d); + const train = customTrainDataDetector(d, allCustonTrainData); let TrainNumber = ""; if (train.trainNumDistance != undefined) { const timeInfo = @@ -74,6 +74,7 @@ export default function AllTrainDiagramView() { return ( { if (useStationName) { @@ -93,6 +94,14 @@ export default function AllTrainDiagramView() { return d.includes(input); })} renderItem={({ item }) => } + ListEmptyComponent={ + + + 検索結果がありません。 + + + } + keyExtractor={(item) => item} //initialNumToRender={100} /> diff --git a/components/Apps/WebView.jsx b/components/Apps/WebView.jsx index 28e19dc..9cd2c11 100644 --- a/components/Apps/WebView.jsx +++ b/components/Apps/WebView.jsx @@ -2,7 +2,13 @@ import React from "react"; import { Platform, LayoutAnimation } from "react-native"; import { WebView } from "react-native-webview"; -import { lineList, stationNamePair } from "../../lib/getStationList"; +import { + lineList, + lineList_LineWebID, + lineListPair, + stationIDPair, + stationNamePair, +} from "../../lib/getStationList"; import { checkDuplicateTrainData } from "../../lib/checkDuplicateTrainData"; import { useFavoriteStation } from "../../stateBox/useFavoriteStation"; import { useCurrentTrain } from "../../stateBox/useCurrentTrain"; @@ -17,7 +23,7 @@ export const AppsWebView = ({ openStationACFromEachTrainInfo }) => { const { navigate } = useNavigation(); const { favoriteStation } = useFavoriteStation(); const { isLandscape } = useDeviceOrientationChange(); - const { originalStationList, stationList } = useStationList(); + const { originalStationList, stationList, getInjectJavascriptAddress } = useStationList(); const { setSelectedLine, mapsStationData: stationData, @@ -129,20 +135,11 @@ export const AppsWebView = ({ openStationACFromEachTrainInfo }) => { const onLoadEnd = () => { if (once) return () => {}; if (!stationData) return () => {}; + if (!originalStationList) return () => {}; if (favoriteStation.length < 1) return () => {}; - const getStationLine = (now) => { - const returnData = Object.keys(stationData).filter((d) => { - const cache = stationData[d].findIndex( - (data) => data.Station_JP == now.Station_JP - ); - return cache != -1; - }); - return returnData[0]; - }; - const lineName = getStationLine(favoriteStation[0][0]); - webview.current?.injectJavaScript( - `MoveDisplayStation('${lineName}_${favoriteStation[0][0].MyStation}_${favoriteStation[0][0].Station_JP}')` - ); + const string = getInjectJavascriptAddress(favoriteStation[0][0].StationNumber); + if (!string) return () => {}; + webview?.current.injectJavaScript(string); once = true; }; diff --git a/components/FavoriteList.tsx b/components/FavoriteList.tsx index 814da0f..55b3ef9 100644 --- a/components/FavoriteList.tsx +++ b/components/FavoriteList.tsx @@ -8,11 +8,13 @@ import { useNavigation } from "@react-navigation/native"; import { useTrainMenu } from "../stateBox/useTrainMenu"; import { FavoriteListItem } from "./atom/FavoriteListItem"; import { BigButton } from "./atom/BigButton"; +import { useStationList } from "@/stateBox/useStationList"; export const FavoriteList: FC = () => { const { favoriteStation } = useFavoriteStation(); const { webview } = useCurrentTrain(); const { navigate, addListener, goBack, canGoBack } = useNavigation(); const { mapsStationData: stationData } = useTrainMenu(); + const { getInjectJavascriptAddress } = useStationList(); useEffect(() => { const unsubscribe = addListener("tabPress", goToTrainMenu); @@ -38,27 +40,16 @@ export const FavoriteList: FC = () => { {favoriteStation - .filter((d) => d[0].StationMap) .map((currentStation) => { return ( { - const getStationLine = (now) => { - const returnData = Object.keys(stationData).filter((d) => { - const cache = stationData[d].findIndex( - (data) => data.Station_JP == now.Station_JP - ); - return cache != -1; - }); - return returnData[0]; - }; - const lineName = getStationLine(currentStation[0]); - - webview.current?.injectJavaScript( - `MoveDisplayStation('${lineName}_${currentStation[0].MyStation}_${currentStation[0].Station_JP}'); - document.getElementById("disp").insertAdjacentHTML("afterbegin", "
");` + const scriptString = getInjectJavascriptAddress( + currentStation[0].StationNumber ); + if (!scriptString) return; + webview.current?.injectJavaScript(scriptString); goBack(); if (canGoBack()) goBack(); }} diff --git a/components/Menu/Carousel/CarouselBox.tsx b/components/Menu/Carousel/CarouselBox.tsx new file mode 100644 index 0000000..0b048fc --- /dev/null +++ b/components/Menu/Carousel/CarouselBox.tsx @@ -0,0 +1,186 @@ +import Sign from "@/components/駅名表/Sign"; +import React, { useEffect, useRef, useState } from "react"; +import { AS } from "@/storageControl"; +import { + useWindowDimensions, + View, + LayoutAnimation, + TouchableOpacity, + Text, + ScrollView, +} from "react-native"; +import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel"; +import { SheetManager } from "react-native-actions-sheet"; +import { StationNumber } from "../StationPagination"; +import { SimpleDot } from "../SimpleDot"; +export const CarouselBox = ({ + originalStationList, + listUpStation, + nearPositionStation, + setListIndex, + listIndex, + navigate, + stationListMode, + isSearchMode +}) => { + const carouselRef = useRef(null); + const { height, width } = useWindowDimensions(); + const [dotButton, setDotButton] = useState(false); + const carouselBadgeScrollViewRef = useRef(null); + + useEffect(() => { + if (!carouselBadgeScrollViewRef.current) return; + const dotSize = dotButton ? 28 : 24; + const scrollToIndex = dotSize * listIndex - width / 2 + dotSize - 5; + carouselBadgeScrollViewRef.current.scrollTo({ + x: scrollToIndex, + animated: true, + }); + }, [listIndex, dotButton, width, carouselBadgeScrollViewRef]); + const oPSign = () => { + const payload = { + currentStation: listUpStation[listIndex], + navigate, + goTo: "menu", + //@ts-ignore + useShow: () => SheetManager.show("StationDetailView", { payload }), + onExit: () => SheetManager.hide("StationDetailView"), + }; + //@ts-ignore + SheetManager.show("StationDetailView", { payload }); + }; + const oLPSign = () => { + LayoutAnimation.configureNext({ + duration: 600, + update: { type: "spring", springDamping: 0.5 }, + }); + AS.setItem( + "CarouselSettings/activeDotSettings", + !dotButton ? "true" : "false" + ); + setDotButton(!dotButton); + }; + + useEffect(() => { + AS.getItem("CarouselSettings/activeDotSettings").then((data) => { + setDotButton(data === "true"); + }); + }, []); + const RenderItem = ({ item, index }) => { + return ( + + + {item[0].StationNumber != "null" ? ( + + ) : ( + + + + {!!isSearchMode ? "路線検索モードです。上に並んでいる路線を選んでみましょう!" :stationListMode == "position" + ? "現在地の近くに駅がありません。" + : "お気に入りリストがありません。お気に入りの駅を追加しよう!"} + + + + )} + + + ); + }; + return ( + + 0 + ? listUpStation + : [[{ StationNumber: "null" }]] + } + height={(((width / 100) * 80) / 20) * 9 + 10} + pagingEnabled={true} + snapEnabled={true} + loop={false} + width={width} + style={{ width: width, alignContent: "center" }} + mode="parallax" + modeConfig={{ + parallaxScrollingScale: 1, + parallaxScrollingOffset: 100, + parallaxAdjacentItemScale: 0.8, + }} + scrollAnimationDuration={600} + onSnapToItem={setListIndex} + renderItem={RenderItem} + overscrollEnabled={false} + defaultIndex={listIndex >= listUpStation.length ? 0 : listIndex} + /> + { + // ScrollViewのrefを保存 + if (scrollViewRef) { + carouselBadgeScrollViewRef.current = scrollViewRef; + } + }} + > + {originalStationList && + listUpStation.map((d, index) => { + const active = index == listIndex; + const numberKey = d[0].StationNumber + index; + return dotButton ? ( + setListIndex(index)} + currentStation={d} + active={active} + key={numberKey} + /> + ) : ( + setListIndex(index)} + active={active} + key={numberKey} + /> + ); + })} + + + ); +}; diff --git a/components/Menu/Carousel/CarouselTypeChanger.tsx b/components/Menu/Carousel/CarouselTypeChanger.tsx new file mode 100644 index 0000000..57266ae --- /dev/null +++ b/components/Menu/Carousel/CarouselTypeChanger.tsx @@ -0,0 +1,174 @@ +import { AS } from "@/storageControl"; +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; +import React, { useEffect, useRef } from "react"; +import { useWindowDimensions } from "react-native"; +import { + View, + TouchableOpacity, + Text, + LayoutAnimation, + KeyboardAvoidingView, + Platform, +} from "react-native"; +import Ionicons from "react-native-vector-icons/Ionicons"; +import { SearchUnitBox } from "@/components/Menu/RailScope/SearchUnitBox"; + +export const CarouselTypeChanger = ({ + locationStatus, + position, + mapsRef, + scrollRef, + stationListMode, + setStationListMode, + setSelectedCurrentStation, + mapMode, + setMapMode, + isSearchMode, + setisSearchMode, +}) => { + const tabBarHeight = useBottomTabBarHeight(); + const { height, width } = useWindowDimensions(); + const returnToDefaultMode = () => { + LayoutAnimation.configureNext({ + duration: 300, + create: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + update: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + }); + setMapMode(false); + }; + return ( + + + { + if (!position) return; + returnToDefaultMode(); + setStationListMode("position"); + AS.setItem("stationListMode", "position"); + setSelectedCurrentStation(0); + }} + onPress={() => { + if (!position) return; + returnToDefaultMode(); + setStationListMode("position"); + AS.setItem("stationListMode", "position"); + setSelectedCurrentStation(0); + }} + > + + + 現在地基準 + + + returnToDefaultMode()} + > + + + { + returnToDefaultMode(); + // お気に入りリスト更新 + setStationListMode("favorite"); + AS.setItem("stationListMode", "favorite"); + setSelectedCurrentStation(0); + }} + onPress={() => { + returnToDefaultMode(); + // お気に入りリスト更新 + setStationListMode("favorite"); + AS.setItem("stationListMode", "favorite"); + setSelectedCurrentStation(0); + }} + > + + + お気に入りリスト + + + + ); +}; diff --git a/components/Menu/FixedContentBottom.js b/components/Menu/FixedContentBottom.js index 2ae1aaf..de78d59 100644 --- a/components/Menu/FixedContentBottom.js +++ b/components/Menu/FixedContentBottom.js @@ -6,10 +6,12 @@ import { MaterialCommunityIcons, } from "@expo/vector-icons"; import { ListItem } from "@rneui/themed"; -import TouchableScale from 'react-native-touchable-scale'; +import TouchableScale from "react-native-touchable-scale"; import Icon from "react-native-vector-icons/Entypo"; import { TextBox } from "../atom/TextBox"; import { TicketBox } from "../atom/TicketBox"; +import { SpecialTrainInfoBox } from "./SpecialTrainInfoBox"; +import { SheetManager } from "react-native-actions-sheet"; export const FixedContentBottom = (props) => { return ( @@ -54,7 +56,9 @@ export const FixedContentBottom = (props) => { backgroundColor="red" flex={1} onPressButton={() => - Linking.openURL("https://xprocess.haruk.in/JR-shikoku-Apps-Common/2025-update-status") + Linking.openURL( + "https://xprocess.haruk.in/JR-shikoku-Apps-Common/2025-update-status" + ) } > @@ -82,7 +86,9 @@ export const FixedContentBottom = (props) => { backgroundColor="#0099CC" flex={1} onPressButton={() => - Linking.openURL("https://www.jr-shikoku.co.jp/sp/index.html#menu-box") + SheetManager.show("SpecialTrainInfo", { + payload: { navigate: props.navigate }, + }) } > @@ -179,105 +185,20 @@ export const FixedContentBottom = (props) => { (通話料がかかります) - + SheetManager.show("Social") + } > - - - - JR四国公式Twitter一族 - - - - {[ - { - url: "https://twitter.com/jr_shikoku_info", - name: "JR四国列車運行情報", - }, - - { - url: "https://twitter.com/JRshikoku_eigyo", - name: "JR四国営業部【公式】", - }, - { - url: "https://twitter.com/JRshikoku_tokyo", - name: "JR四国 東京営業情報【公式】", - }, - { - url: "https://twitter.com/JRshikoku_osaka", - name: "JR四国 大阪営業部【公式】", - }, - { - url: "https://twitter.com/jrs_matsuyama", - name: "JR四国 松山駅 【公式】", - }, - { - url: "https://twitter.com/jrshikoku_kochi", - name: "JR四国 高知駅【公式】", - }, - { - url: "https://twitter.com/jr_tokust", - name: "JR四国 徳島駅【公式】", - }, - { - url: "https://twitter.com/jrshikoku_uwjm", - name: "JR四国 宇和島駅【公式】", - }, - { - url: "https://twitter.com/jrshikoku_wkama", - name: "JR四国 ワープ高松支店【公式】", - }, - { - url: "https://twitter.com/JRshikoku_wkoch", - name: "JR四国 ワープ高知支店【公式】", - }, - { - url: "https://twitter.com/Yoakemonogatari", - name: "志国土佐 時代の夜明けのものがたり【公式】", - }, - { - url: "https://twitter.com/Smile_Eki_Chan", - name: "すまいるえきちゃん♡JR四国【公式】", - }, - { - url: "https://twitter.com/sper_ponchan", - name: "しこくたぬきのぽんちゃん 【四国家サポーターズクラブ】", - }, - ].map((d) => ( - Linking.openURL(d.url)} - key={d.url}friction={90} // - tension={100} // These props are passed to the parent component (here TouchableScale) - activeScale={0.95} // - Component={TouchableScale} - > - - {d.name} - - - - ))} - - + + ソーシャルメディア + + + JR四国のSNS一覧です。 + + 上級者向け機能 { + const { getTime, delayData, loadingDelayData, setLoadingDelayData } = + useTrainDelayData(); + const styles: { [key: string]: StyleProp } = { + touch: { + backgroundColor: "#0099CC", + borderRadius: 5, + margin: 10, + borderColor: "black", + borderWidth: 2, + overflow: "hidden", + }, + scroll: { + backgroundColor: "#0099CC", + borderRadius: 5, + maxHeight: 300, + }, + bottom: { + position: "absolute", + top: 250, + alignItems: "center", + width: "100%", + height: 50, + backgroundColor: "#007FCC88", + }, + box: { + padding: 10, + backgroundColor: "white", + borderBottomLeftRadius: 5, + borderBottomRightRadius: 5, + }, + }; + return ( + SheetManager.show("JRSTraInfo")} + style={styles.touch} + > + + + + 列車遅延速報EX + + + + {getTime ? dayjs(getTime).format("HH:mm") : NaN} + + setLoadingDelayData(true)} + /> + + + {loadingDelayData ? ( + + + + ) : delayData ? ( + delayData.map((d, index, array) => { + let data = d.split(" "); + return ( + + + {data[0].replace("\n", "")} + + {data[1]} + {data[3]} + + ); + }) + ) : ( + 現在、5分以上の遅れはありません。 + )} + + + + + + 詳細を見る + + + + + ); +}; diff --git a/components/Menu/RailScope/SearchUnitBox.tsx b/components/Menu/RailScope/SearchUnitBox.tsx new file mode 100644 index 0000000..9d0c1f2 --- /dev/null +++ b/components/Menu/RailScope/SearchUnitBox.tsx @@ -0,0 +1,114 @@ +import React from "react"; +import { + TouchableOpacity, + Text, + View, + LayoutAnimation, + TextInput, + KeyboardAvoidingView, + Platform, +} from "react-native"; +import Ionicons from "react-native-vector-icons/Ionicons"; +import { useWindowDimensions } from "react-native"; +import lineColorList from "@/assets/originData/lineColorList"; +import { lineList_LineWebID, stationIDPair } from "@/lib/getStationList"; +export const SearchUnitBox = ({ isSearchMode, setisSearchMode }) => { + const { height, width } = useWindowDimensions(); + return ( + <> + { + LayoutAnimation.configureNext({ + duration: 100, + update: { type: "easeInEaseOut", springDamping: 0.6 }, + }); + setisSearchMode(true); + }} + > + {!isSearchMode && } + {!!isSearchMode && ( + + + + { + LayoutAnimation.configureNext({ + duration: 100, + update: { type: "easeInEaseOut", springDamping: 0.6 }, + }); + setisSearchMode(false); + }}> + + + {Object.keys(lineList_LineWebID).map((d) => ( + { + const id = stationIDPair[lineList_LineWebID[d]]; + const s = isSearchMode == id ? undefined : id; + if (!s) return; + setisSearchMode(s); + }} + key={stationIDPair[lineList_LineWebID[d]]} + > + + {stationIDPair[lineList_LineWebID[d]]} + + + ))} + {/* + setKeyBoardVisible(true)} + onEndEditing={() => {}} + //onChange={(ret) => setInput(ret.nativeEvent.text)} + //value={input} + style={{ flex: 1 }} + /> + */} + + + )} + + + ); +}; diff --git a/components/Menu/SpecialTrainInfoBox.tsx b/components/Menu/SpecialTrainInfoBox.tsx new file mode 100644 index 0000000..c24bb95 --- /dev/null +++ b/components/Menu/SpecialTrainInfoBox.tsx @@ -0,0 +1,66 @@ +import { FC, useEffect, useLayoutEffect, useState } from "react"; +import { View, Text, TouchableOpacity } from "react-native"; +import { getPDFViewURL } from "@/lib/getPdfViewURL"; +import { ScrollView, SheetManager } from "react-native-actions-sheet"; + +type props = { + navigate: (screen: string, params?: object) => void; +}; +export const SpecialTrainInfoBox: FC = ({ navigate }) => { + const [specialData, setSpecialData] = useState([]); + useLayoutEffect(() => { + fetch("https://n8n.haruk.in/webhook/sptrainfo") + .then((res) => res.json()) + .then((data) => setSpecialData(data.data)) + .catch((err) => console.log(err)); + }, []); + return ( + + + + 臨時列車情報 + + + + {specialData.map((d) => ( + { + navigate("howto", { + info: getPDFViewURL("https://www.jr-shikoku.co.jp" + d.address), + goTo: "menu", + }); + SheetManager.hide("SpecialTrainInfo"); + }} + onLongPress={() => alert(d.description)} + key={d.address} + style={{ + padding: 10, + borderBottomWidth: 1, + borderBottomColor: "#ccc", + flexDirection: "row", + alignItems: "center", + }} + > + {d.text} + + ))} + + + ); +}; diff --git a/components/Menu/TitleBar.js b/components/Menu/TitleBar.js index e8672a6..ad05cb5 100644 --- a/components/Menu/TitleBar.js +++ b/components/Menu/TitleBar.js @@ -1,18 +1,25 @@ -import { View, TouchableOpacity, Linking } from "react-native"; -import AutoHeightImage from "react-native-auto-height-image"; -import { widthPercentageToDP as wp } from "react-native-responsive-screen"; +import { View, TouchableOpacity, Linking,Platform, Image, useWindowDimensions } from "react-native"; +import Constants from "expo-constants"; + export const TitleBar = () => { + const { width } = useWindowDimensions(); return ( - + Linking.openURL("https://www.jr-shikoku.co.jp")} > - + ); diff --git a/components/Menu/TopMenuButton.tsx b/components/Menu/TopMenuButton.tsx new file mode 100644 index 0000000..d4950db --- /dev/null +++ b/components/Menu/TopMenuButton.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { Linking, View } from "react-native"; +import { UsefulBox } from "@/components/TrainMenu/UsefulBox"; +import MaterialCommunityIcons from "@expo/vector-icons/build/MaterialCommunityIcons"; +export const TopMenuButton = () => { + const buttonList:{ + backgroundColor: string; + icon: keyof typeof MaterialCommunityIcons.glyphMap; + onPress: () => void; + title: string; + }[] = [ + { + backgroundColor: "#F89038", + icon: "train-car", + onPress: () => + Linking.openURL("https://www.jr-shikoku.co.jp/01_trainbus/sp/"), + title: "駅・鉄道情報", + }, + { + backgroundColor: "#EA4752", + icon: "google-spreadsheet", + onPress: () => + Linking.openURL( + "https://www.jr-shikoku.co.jp/01_trainbus/jikoku/sp/#mainprice-box" + ), + title: "運賃表", + }, + { + backgroundColor: "#91C31F", + icon: "clipboard-list-outline", + onPress: () => Linking.openURL("https://www.jr-shikoku.co.jp/e5489/"), + title: "予約", + }, + ]; + + + return ( + + {buttonList.map((d, index) => ( + + {d.title} + + ))} + + ); +}; \ No newline at end of file diff --git a/components/Settings/FavoliteSettings/FavoiliteSettingsItem.js b/components/Settings/FavoliteSettings/FavoiliteSettingsItem.js index a5f926c..cdc84f8 100644 --- a/components/Settings/FavoliteSettings/FavoiliteSettingsItem.js +++ b/components/Settings/FavoliteSettings/FavoiliteSettingsItem.js @@ -2,44 +2,18 @@ import React, { useEffect, useState } from "react"; import Icon from "react-native-vector-icons/Entypo"; import { View, Text, TouchableOpacity, LayoutAnimation } from "react-native"; import lineColorList from "../../../assets/originData/lineColorList"; +import Ionicons from "react-native-vector-icons/Ionicons"; import { AS } from "../../../storageControl"; -export const FavoriteSettingsItem = ({ - currentStation, - setFavoriteStation, - index, - array, -}) => { +export const FavoriteSettingsItem = ({ currentStation }) => { const lineIDs = []; const EachIDs = []; - console.log(currentStation); currentStation.forEach((d) => { if (!d.StationNumber) return; const textArray = d.StationNumber.split(""); lineIDs.push(textArray.filter((s) => "A" < s && s < "Z").join("")); EachIDs.push(textArray.filter((s) => "0" <= s && s <= "9").join("")); }); - const [head, setHead] = useState(false); - const [tail, setTail] = useState(false); - useEffect(() => { - switch (true) { - case array.length == 1: - setHead(true); - setTail(true); - break; - case index == 0: - setHead(true); - setTail(false); - break; - case index == array.length - 1: - setHead(false); - setTail(true); - break; - default: - setHead(false); - setTail(false); - } - }, [array]); return ( @@ -47,7 +21,7 @@ export const FavoriteSettingsItem = ({ style={{ width: 35, position: "relative", - marginHorizontal: 15, + marginHorizontal: 10, flexDirection: "row", height: "101%", }} @@ -91,43 +65,15 @@ export const FavoriteSettingsItem = ({ > {currentStation[0].Station_JP} - { - console.log("up"); - LayoutAnimation.configureNext( - LayoutAnimation.Presets.easeInEaseOut - ); - const removedStation = [...array].filter((d, i) => { - if (i == index) return false; - return true; - }); - removedStation.splice(index - 1, 0, currentStation); - setFavoriteStation(removedStation); - - AS.setItem("favoriteStation", JSON.stringify(removedStation)); - }} - > - {head ? null : } - - { - console.log("down"); - LayoutAnimation.configureNext( - LayoutAnimation.Presets.easeInEaseOut - ); - const removedStation = [...array].filter((d, i) => { - if (i == index) return false; - return true; - }); - removedStation.splice(index + 1, 0, currentStation); - setFavoriteStation(removedStation); - AS.setItem("favoriteStation", JSON.stringify(removedStation)); - }} - > - {tail ? null : } - + + + ); diff --git a/components/Settings/FavoriteSettings.js b/components/Settings/FavoriteSettings.js index 9a08d43..99ed924 100644 --- a/components/Settings/FavoriteSettings.js +++ b/components/Settings/FavoriteSettings.js @@ -1,31 +1,58 @@ -import React from "react"; -import { View, Text, TouchableOpacity, ScrollView } from "react-native"; +import React, { useCallback } from "react"; +import { View, Text, StyleSheet } from "react-native"; +import Animated, { useAnimatedRef } from "react-native-reanimated"; import { useNavigation } from "@react-navigation/native"; +import Sortable from "react-native-sortables"; import { useFavoriteStation } from "../../stateBox/useFavoriteStation"; -import { CheckBox } from "react-native-elements"; import { FavoriteSettingsItem } from "./FavoliteSettings/FavoiliteSettingsItem"; import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem"; +import { AS } from "@/storageControl"; export const FavoriteSettings = () => { const { favoriteStation, setFavoriteStation } = useFavoriteStation(); + const scrollableRef = useAnimatedRef(); const { goBack } = useNavigation(); + const renderItem = useCallback((props) => { + const { item, index } = props; + return ( + + ); + }, []); return ( - - {favoriteStation.map((currentStation, index, array) => ( - - ))} - + + { + const newFavoriteStation = newOrder.indexToKey.map( + (item, index, array) => { + let returnData = []; + favoriteStation.forEach((station) => { + if (station[0].StationNumber === item) returnData = station; + }); + return returnData; + } + ); + setFavoriteStation(newFavoriteStation); + AS.setItem("favoriteStation", JSON.stringify(newFavoriteStation)); + }} + keyExtractor={(item) => item[0].StationNumber} + /> + { ); }; -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} - /> - -); +const styles = StyleSheet.create({ + card: { + alignItems: "center", + backgroundColor: "#36877F", + borderRadius: 10, + height: 100, + justifyContent: "center", + }, + contentContainer: { + padding: 10, + }, + text: { + color: "white", + fontWeight: "bold", + }, +}); diff --git a/components/Settings/LayoutSettings.js b/components/Settings/LayoutSettings.js index 4020857..07e1780 100644 --- a/components/Settings/LayoutSettings.js +++ b/components/Settings/LayoutSettings.js @@ -18,6 +18,8 @@ export const LayoutSettings = ({ setUsePDFView, trainMenu, setTrainMenu, + uiSetting, + setUiSetting, trainPosition, setTrainPosition, headerSize, @@ -38,6 +40,17 @@ export const LayoutSettings = ({ falseText={"本家\n(文字アイコン)"} trueText={"オリジナル\n(車種アイコン)"} /> + { AS.getItem("iconSwitch").then(setIconSetting); AS.getItem("mapSwitch").then(setMapSwitch); @@ -48,6 +49,7 @@ export default function Setting(props) { AS.getItem("trainPositionSwitch").then(setTrainPosition); AS.getItem("headerSize").then(setHeaderSize); AS.getItem("startPage").then(setStartPage); + AS.getItem("uiSetting").then(setUiSetting); }, []); const testNFC = async () => { //const result = await ExpoFelicaReader.scan(); @@ -63,6 +65,7 @@ export default function Setting(props) { AS.setItem("trainPositionSwitch", trainPosition.toString()), AS.setItem("headerSize", headerSize), AS.setItem("startPage", startPage.toString()), + AS.setItem("uiSetting", uiSetting), ]).then(() => Updates.reloadAsync()); }; return ( @@ -114,6 +117,8 @@ export default function Setting(props) { setTrainMenu={setTrainMenu} trainPosition={trainPosition} setTrainPosition={setTrainPosition} + uiSetting={uiSetting} + setUiSetting={setUiSetting} testNFC={testNFC} updateAndReload={updateAndReload} headerSize={headerSize} diff --git a/components/TrainMenu/MapPin.tsx b/components/TrainMenu/MapPin.tsx index f679e6b..5d4eef2 100644 --- a/components/TrainMenu/MapPin.tsx +++ b/components/TrainMenu/MapPin.tsx @@ -1,6 +1,7 @@ import React, { FC } from "react"; import { Marker } from "react-native-maps"; import { useNavigation } from "@react-navigation/native"; +import { useStationList } from "@/stateBox/useStationList"; type Props = { index: number; indexBase: number; @@ -13,7 +14,8 @@ type Props = { export const MapPin: FC = (props) => { const { index, indexBase, latlng, D, d, navigate, webview } = props; - const {goBack} = useNavigation(); + const { goBack } = useNavigation(); + const { getInjectJavascriptAddress } = useStationList(); return ( = (props) => { longitude: parseFloat(latlng[1]), }} onPress={() => { - webview.current?.injectJavaScript( - `MoveDisplayStation('${d}_${D.MyStation}_${D.Station_JP}'); - document.getElementById("disp").insertAdjacentHTML("afterbegin", "
");` - ); + const address = getInjectJavascriptAddress(D.StationNumber); + if (!address) return; + webview.current?.injectJavaScript(address); if (navigate) goBack(); }} image={require("../../assets/reccha-small.png")} diff --git a/components/atom/SwitchArea.js b/components/atom/SwitchArea.js index 245a708..74e39d0 100644 --- a/components/atom/SwitchArea.js +++ b/components/atom/SwitchArea.js @@ -8,6 +8,8 @@ export const SwitchArea = ({ trueImage, falseText, trueText, + falseValue = false, + trueValue = true, children, }) => { return ( @@ -37,7 +39,7 @@ export const SwitchArea = ({ bool={bool} setBool={setBool} color="red" - value={false} + value={falseValue} image={falseImage} subText={falseText} /> @@ -45,7 +47,7 @@ export const SwitchArea = ({ bool={bool} setBool={setBool} color="red" - value={true} + value={trueValue} image={trueImage} subText={trueText} /> diff --git a/components/atom/UsefulBox.js b/components/atom/UsefulBox.js deleted file mode 100644 index afab4cd..0000000 --- a/components/atom/UsefulBox.js +++ /dev/null @@ -1,22 +0,0 @@ -import { TouchableOpacity, Text } from "react-native"; -import { MaterialCommunityIcons } from "@expo/vector-icons"; -export const UsefulBox = (props) => { - const { icon, backgroundColor, flex, onPressButton, children } = props; - return ( - - - - {children} - - - ); -}; diff --git a/components/custom-train-data.ts b/components/custom-train-data.ts index 3cf8526..abe330c 100644 --- a/components/custom-train-data.ts +++ b/components/custom-train-data.ts @@ -1,5 +1,19 @@ import dayjs from "dayjs"; -export const customTrainDataDetector = (TrainNumber: string) => { +export const customTrainDataDetector = ( + TrainNumber: string, + allCustonTrainData?: any[] +) => { + if (allCustonTrainData && allCustonTrainData.length > 0) { + const customTrain = allCustonTrainData.find( + (train) => train.TrainNumber === TrainNumber + ); + if (customTrain) { + return customTrain; + } + } + const trainGetText = `?trainNum=${TrainNumber}&month=${dayjs().format( + "M" + )}&day=${dayjs().format("D")}`; switch (TrainNumber) { //しおかぜメイン //8000 ノーマル @@ -23,23 +37,23 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "https://storage.haruk.in/s8000nr.png", + img: "https://storage.haruk.in/s8000nr.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/shiokaze.html", trainNumDistance: 0, info: "いしづちと併結 / 8000系で運転", - infogram: "G" + infogram: "G", }; case "2M": return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "https://storage.haruk.in/s8000nr.png", + img: "https://storage.haruk.in/s8000nr.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/shiokaze.html", trainNumDistance: 0, info: "8000系で運転", - infogram: "G" + infogram: "G", }; //8000 アンパン case "10M": @@ -49,11 +63,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: `https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=${TrainNumber}`, + img: `https://n8n.haruk.in/webhook/anpanman-pictures.png${trainGetText}`, infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 0, info: "いしづちと併結 / アンパンマン列車で運転", - infogram: "G" + infogram: "G", }; //8600 case "8M": @@ -67,12 +81,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しおかぜ", - trainIcon: "https://storage.haruk.in/s8600.png", + img: "https://storage.haruk.in/s8600.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/shiokaze.html", trainNumDistance: 0, info: "いしづちと併結 / 8600系で運転", - infogram: "G" + infogram: "G", }; //いしづちメイン @@ -98,12 +112,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "https://storage.haruk.in/s8000no.png", + img: "https://storage.haruk.in/s8000no.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 1000, info: "しおかぜと併結 / 8000系で運転", - infogram: "" + infogram: "", }; //8000 アンパン @@ -114,11 +128,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: `https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=${TrainNumber}`, + img: `https://n8n.haruk.in/webhook/anpanman-pictures.png${trainGetText}`, infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 1000, info: "しおかぜと併結 / アンパンマン列車で運転", - infogram: "" + infogram: "", }; //8600 @@ -133,12 +147,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "https://storage.haruk.in/s8600_isz.png", + img: "https://storage.haruk.in/s8600_isz.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 1000, info: "しおかぜと併結 / 8600系で運転", - infogram: "" + infogram: "", }; //MEXP @@ -147,24 +161,24 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "モーニングEXP高松", - trainIcon: "https://storage.haruk.in/s8000nr.png", + img: "https://storage.haruk.in/s8000nr.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/morning.html", trainNumDistance: null, info: "8000系で運転", - infogram: "G" + infogram: "G", }; //8600 case "1091M": return { type: "LTDEXP", trainName: "モーニングEXP松山", - trainIcon: "https://storage.haruk.in/s8600_isz.png", + img: "https://storage.haruk.in/s8600_isz.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/morning.html", trainNumDistance: null, info: "8600系で運転", - infogram: "G" + infogram: "G", }; //三桁いしづち //8000 アンパン @@ -173,11 +187,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: `https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=${TrainNumber}`, + img: `https://n8n.haruk.in/webhook/anpanman-pictures.png${trainGetText}`, infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 940, info: "アンパンマン列車で運転", - infogram: "G" + infogram: "G", }; //8600 case "1043M": @@ -185,23 +199,23 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "https://storage.haruk.in/s8600_isz.png", + img: "https://storage.haruk.in/s8600_isz.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 940, info: "8600系で運転", - infogram: "" + infogram: "", }; case "1046M": return { type: "LTDEXP", trainName: "いしづち", - trainIcon: "https://storage.haruk.in/s8600_isz.png", + img: "https://storage.haruk.in/s8600_isz.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/ishizuchi.html", trainNumDistance: 940, info: "8600系で運転", - infogram: "G" + infogram: "G", }; //南風 2700ノーマル @@ -226,11 +240,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "南風", - trainIcon: "https://storage.haruk.in/s2700.png", - infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/nanpu.html", + img: "https://storage.haruk.in/s2700.png", + infoUrl: + "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/nanpu.html", trainNumDistance: 30, info: "2700系で運転", - infogram: "G" + infogram: "G", }; //2700アンパン @@ -247,11 +262,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "南風", - trainIcon: `https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=${TrainNumber}`, + img: `https://n8n.haruk.in/webhook/anpanman-pictures.png${trainGetText}`, infoUrl: "https://www.jr-eki.com/aptrain/naani/dosan/train.html", trainNumDistance: 30, info: "アンパンマン列車で運転", - infogram: "G" + infogram: "G", }; //うずしお @@ -272,12 +287,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "https://storage.haruk.in/s2700_uzu.png", + img: "https://storage.haruk.in/s2700_uzu.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "2700系で運転", - infogram: "" + infogram: "", }; //2700 二両編成 @@ -294,12 +309,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "https://storage.haruk.in/s2700_uzu.png", + img: "https://storage.haruk.in/s2700_uzu.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "2700系で運転", - infogram: "" + infogram: "", }; //2600 @@ -316,15 +331,14 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "うずしお", - trainIcon: "https://storage.haruk.in/s2600.png", + img: "https://storage.haruk.in/s2600.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/uzushio.html", trainNumDistance: 3000, info: "2600系で運転", - infogram: "" + infogram: "", }; - //マリンライナー case "3104M": case "3106M": @@ -397,11 +411,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "Rapid", trainName: "マリンライナー", - trainIcon: "https://storage.haruk.in/s5001.png", - infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", + img: "https://storage.haruk.in/s5001.png", + infoUrl: + "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", trainNumDistance: 3100, info: "", - infogram: "G" + infogram: "G", }; case "3102M": case "3101M": @@ -411,22 +426,23 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "Rapid", trainName: "マリンライナー", - trainIcon: "https://storage.haruk.in/s5001k.png", - infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", + img: "https://storage.haruk.in/s5001k.png", + infoUrl: + "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", trainNumDistance: 3100, info: "", - infogram: "" + infogram: "", }; //下りサンポート case "1219M": return { type: "Normal", trainName: "南風リレー", - trainIcon: "", + img: "", infoUrl: "", trainNumDistance: null, info: " 土曜・休日は多度津-琴平間運休", - infogram: "" + infogram: "", }; case "111M": @@ -442,11 +458,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "Rapid", trainName: "サンポート南風リレー", - trainIcon: null, + img: null, infoUrl: null, trainNumDistance: null, info: null, - infogram: "" + infogram: "", }; case "5109M": case "5135M": @@ -454,32 +470,32 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "OneManRapid", trainName: "サンポート南風リレー", - trainIcon: null, + img: null, infoUrl: null, trainNumDistance: null, info: null, - infogram: "" + infogram: "", }; case "137M": return { type: "Rapid", trainName: "サンポート", - trainIcon: null, + img: null, infoUrl: null, trainNumDistance: null, info: "土曜・休日運休", - infogram: "" + infogram: "", }; //上りサンポート case "116M": return { type: "Normal", trainName: "南風リレー", - trainIcon: null, + img: null, infoUrl: null, trainNumDistance: null, info: null, - infogram: "" + infogram: "", }; case "130M": @@ -493,11 +509,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "Rapid", trainName: "サンポート南風リレー", - trainIcon: "", + img: "", infoUrl: "", trainNumDistance: null, info: "", - infogram: "" + infogram: "", }; case "5118M": case "5120M": @@ -507,11 +523,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "OneManRapid", trainName: "サンポート南風リレー", - trainIcon: null, + img: null, infoUrl: null, trainNumDistance: null, info: null, - infogram: "" + infogram: "", }; //サンライズ瀬戸 @@ -520,24 +536,24 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "NightLTDEXP", trainName: "サンライズ瀬戸", - trainIcon: "https://storage.haruk.in/w285.png", + img: "https://storage.haruk.in/w285.png", infoUrl: "https://www.jr-odekake.net/train/sunriseseto_izumo/index.html", trainNumDistance: null, info: "", - infogram: "ブ" + infogram: "ブ", }; case "8041M": //琴平延長高松迄 case "8031M": //琴平延長高松以降 return { type: "NightLTDEXP", trainName: "サンライズ瀬戸", - trainIcon: "https://storage.haruk.in/w285.png", + img: "https://storage.haruk.in/w285.png", infoUrl: "https://www.jr-odekake.net/train/sunriseseto_izumo/index.html", trainNumDistance: null, info: "琴平延長運転日", - infogram: "ブ" + infogram: "ブ", }; //宇和海 @@ -571,11 +587,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "宇和海", - trainIcon: "https://storage.haruk.in/s2000_uwa.png", - infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/uwakai.html", + img: "https://storage.haruk.in/s2000_uwa.png", + infoUrl: + "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/uwakai.html", trainNumDistance: 1050, info: "2000系で運転", - infogram: "" + infogram: "", }; //2000 アンパン込み case "1058D": @@ -587,11 +604,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "宇和海", - trainIcon: `https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=${TrainNumber}`, + img: `https://n8n.haruk.in/webhook/anpanman-pictures.png${trainGetText}`, infoUrl: "https://www.jr-eki.com/aptrain/naani/yosan/train.html", trainNumDistance: 1050, info: "アンパン列車で運転", - infogram: "" + infogram: "", }; //しまんと case "2002D": @@ -601,12 +618,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "しまんと", - trainIcon: "https://storage.haruk.in/s2700_smn.png", + img: "https://storage.haruk.in/s2700_smn.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/shimanto.html", trainNumDistance: 2000, info: "2700系で運転", - infogram: "" + infogram: "", }; //あしずり 2000 @@ -621,12 +638,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "あしずり", - trainIcon: `https://n8n.haruk.in/webhook/anpanman-pictures.png?trainNum=${TrainNumber}`, + img: `https://n8n.haruk.in/webhook/anpanman-pictures.png${trainGetText}`, infoUrl: "https://www.jr-eki.com/aptrain/naani/first-generation/jikoku.html", trainNumDistance: 2070, info: "2000系で運転", - infogram: "" + infogram: "", }; //あしずり 2700 @@ -637,24 +654,24 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "あしずり", - trainIcon: "https://storage.haruk.in/s2700_asi.png", + img: "https://storage.haruk.in/s2700_asi.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/ashizuri.html", trainNumDistance: 2070, info: "2700系で運転", - infogram: "" + infogram: "", }; case "2072D": case "2083D": return { type: "LTDEXP", trainName: "あしずり", - trainIcon: "https://storage.haruk.in/s2700_asi.png", + img: "https://storage.haruk.in/s2700_asi.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/ashizuri.html", trainNumDistance: 2070, info: "2700系で運転", - infogram: "G" + infogram: "G", }; //剣山 @@ -668,12 +685,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "剣山", - trainIcon: "https://storage.haruk.in/s185tu.png", + img: "https://storage.haruk.in/s185tu.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/tsurugisan.html", trainNumDistance: 4000, info: "キハ185系で運転", - infogram: "" + infogram: "", }; //よしのがわトロッコ @@ -682,12 +699,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "よしのがわトロッコ", - trainIcon: "https://storage.haruk.in/s185to_ai.png", + img: "https://storage.haruk.in/s185to_ai.png", infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/event_train/yoshino_torokko.html", trainNumDistance: null, info: "", - infogram: "" + infogram: "", }; //岡山高松アントロ @@ -699,12 +716,12 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "アンパンマントロッコ", - trainIcon: "https://storage.haruk.in/s32to4.png", + img: "https://storage.haruk.in/s32to4.png", infoUrl: "https://www.jr-eki.com/aptrain/naani/torokko_seto/jikoku.html", trainNumDistance: null, info: "", - infogram: "G" + infogram: "G", }; //伊予灘ものがたり @@ -713,22 +730,22 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "伊予灘ものがたり", - trainIcon: "https://storage.haruk.in/s185iyor.png", + img: "https://storage.haruk.in/s185iyor.png", infoUrl: "https://iyonadamonogatari.com/", trainNumDistance: null, info: "", - infogram: "G" + infogram: "G", }; case "8092D": case "8094D": return { type: "LTDEXP", trainName: "伊予灘ものがたり", - trainIcon: "https://storage.haruk.in/s185iyoy.png", + img: "https://storage.haruk.in/s185iyoy.png", infoUrl: "https://iyonadamonogatari.com/", trainNumDistance: null, info: "", - infogram: "G" + infogram: "G", }; //千年ものがたり @@ -737,11 +754,11 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "四国まんなか千年ものがたり", - trainIcon: "https://storage.haruk.in/s185mm1.png", + img: "https://storage.haruk.in/s185mm1.png", infoUrl: "https://www.jr-shikoku.co.jp/sennenmonogatari/", trainNumDistance: null, info: "", - infogram: "G" + infogram: "G", }; //夜明けものがたり @@ -752,67 +769,67 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "LTDEXP", trainName: "時代の夜明けのものがたり", - trainIcon: "https://storage.haruk.in/s185ym1.png", + img: "https://storage.haruk.in/s185ym1.png", infoUrl: "https://www.jr-shikoku.co.jp/yoakenomonogatari/index.html", trainNumDistance: null, info: "", - infogram: "G" + infogram: "G", }; case "9174M": return { type: "SPCL_Rapid", trainName: "マリンライナー94号", - trainIcon: "https://storage.haruk.in/s5001.png", - infoUrl: "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", + img: "https://storage.haruk.in/s5001.png", + infoUrl: + "https://www.jr-shikoku.co.jp/01_trainbus/vehicle-info/marine.html", trainNumDistance: null, info: "臨時列車 4/12,13,19のみ運転", - infogram: "G" + infogram: "G", }; case "9395D": return { type: "SPCL_Normal", trainName: "", - trainIcon: "https://storage.haruk.in/s1500.png", + img: "https://storage.haruk.in/s1500.png", infoUrl: null, trainNumDistance: null, info: "臨時列車 4/12,13,19のみ運転", - infogram: "" + infogram: "", + }; + case "9662D": + case "9665D": + return { + type: "SPCL_Normal", + trainName: "れんげ号", + img: "", + infoUrl: null, + trainNumDistance: null, + info: "臨時列車 4/29のみ運転", + infogram: "", + }; + case "9664D": + case "9663D": + return { + type: "SPCL_Normal", + trainName: "わらぐろ号", + img: "", + infoUrl: null, + trainNumDistance: null, + info: "臨時列車 4/29のみ運転", + infogram: "", }; - case "9662D": - case "9665D": - return { - type: "SPCL_Normal", - trainName: "れんげ号", - trainIcon: "", - infoUrl: null, - trainNumDistance: null, - info: "臨時列車 4/29のみ運転", - infogram: "" - }; - case "9664D": - case "9663D": - return { - type: "SPCL_Normal", - trainName: "わらぐろ号", - trainIcon: "", - infoUrl: null, - trainNumDistance: null, - info: "臨時列車 4/29のみ運転", - infogram: "" - }; default: - if(getJRF(TrainNumber) !== null){ + if (getJRF(TrainNumber) !== null) { return { type: "Freight", trainName: getJRF(TrainNumber), - trainIcon: "https://storage.haruk.in/ef210a.png", + img: "https://storage.haruk.in/ef210a.png", infoUrl: null, trainNumDistance: null, info: "", infogram: "", }; - } - else if ( + } else if ( new RegExp(/^4[1-9]\d\d[DM]$/).test(TrainNumber) || new RegExp(/^5[1-7]\d\d[DM]$/).test(TrainNumber) || new RegExp(/^3[2-9]\d\d[DM]$/).test(TrainNumber) @@ -820,38 +837,38 @@ export const customTrainDataDetector = (TrainNumber: string) => { return { type: "OneMan", trainName: "", - trainIcon: null, + img: null, infoUrl: null, trainNumDistance: null, info: null, - infogram: "" + infogram: "", }; else if ( new RegExp(/^[1-9]\d\d[DM]$/).test(TrainNumber) || new RegExp(/^1[26]\d\d[DM]$/).test(TrainNumber) || new RegExp(/^58\d\d[DM]$/).test(TrainNumber) || new RegExp(/^6\d\d\d[DM]$/).test(TrainNumber) - ){ - return { - type: "Normal", - trainName: "", - trainIcon: null, - infoUrl: null, - trainNumDistance: null, - info: null, - infogram: "" - }; - } else { - return { - type: "Other", - trainName: "", - trainIcon: null, - infoUrl: null, - trainNumDistance: null, - info: null, - infogram: "" - }; - } + ) { + return { + type: "Normal", + trainName: "", + img: null, + infoUrl: null, + trainNumDistance: null, + info: null, + infogram: "", + }; + } else { + return { + type: "Other", + trainName: "", + img: null, + infoUrl: null, + trainNumDistance: null, + info: null, + infogram: "", + }; + } } }; export const getJRF = (num: string) => { diff --git a/components/trainMenu.js b/components/trainMenu.js index ad3ad94..cd416f1 100644 --- a/components/trainMenu.js +++ b/components/trainMenu.js @@ -4,12 +4,13 @@ import MapView from "react-native-maps"; import { useCurrentTrain } from "../stateBox/useCurrentTrain"; import { useNavigation } from "@react-navigation/native"; import lineColorList from "../assets/originData/lineColorList"; -import { lineListPair, stationIDPair } from "../lib/getStationList"; +import { lineList_LineWebID, lineListPair, stationIDPair } from "../lib/getStationList"; import { SheetManager } from "react-native-actions-sheet"; import { useTrainMenu } from "../stateBox/useTrainMenu"; import { MapPin } from "./TrainMenu/MapPin"; import { UsefulBox } from "./TrainMenu/UsefulBox"; import { MapsButton } from "./TrainMenu/MapsButton"; +import { useStationList } from "@/stateBox/useStationList"; export default function TrainMenu({ style }) { const { webview } = useCurrentTrain(); const mapRef = useRef(); @@ -20,31 +21,28 @@ export default function TrainMenu({ style }) { setSelectedLine, mapsStationData: stationData, } = useTrainMenu(); + const { originalStationList } = useStationList(); useEffect(() => { const stationPinData = []; - Object.keys(stationData).forEach((d, indexBase) => { - stationData[d].forEach((D, index) => { - if (!D.StationMap) return null; - if (selectedLine && selectedLine != d) return; - const latlng = D.StationMap.replace( - "https://www.google.co.jp/maps/place/", - "" - ).split(","); + Object.keys(lineList_LineWebID).forEach((d, indexBase) => { + originalStationList[d].forEach((D, index) => { + if (selectedLine && selectedLine != lineList_LineWebID[d]) return; + const latlng = [D.lat,D.lng]; if (latlng.length == 0) return null; stationPinData.push({ D, d, latlng, indexBase: 0, index }); }); }); setStationPin(stationPinData); - }, [stationData, selectedLine]); + }, [originalStationList, selectedLine]); useLayoutEffect(() => { - mapRef.current.fitToCoordinates( + mapRef?.current.fitToCoordinates( stationPin.map(({ latlng }) => ({ latitude: parseFloat(latlng[0]), longitude: parseFloat(latlng[1]), })), { edgePadding: { top: 80, bottom: 120, left: 50, right: 50 } } // Add margin values here ); - }, [stationPin]); + }, [stationPin,mapRef]); return ( ))} @@ -163,6 +161,7 @@ export default function TrainMenu({ style }) { }); }); }} + key={stationIDPair[d]} > = (props) => { } = props; const { currentTrain } = useCurrentTrain(); const { stationList } = useStationList(); + const { allCustonTrainData } = useAllTrainDiagram(); const openTrainInfo = (d: { train: string; lastStation: string; @@ -72,7 +76,10 @@ export const EachData: FC = (props) => { }; const getTrainDataFromCurrentTrain = (trainNum: string) => { - const customTrainData = customTrainDataDetector(d.train); + const customTrainData = customTrainDataDetector( + d.train, + allCustonTrainData + ); switch (customTrainData.type) { case "Normal": case "OneMan": @@ -117,26 +124,47 @@ export const EachData: FC = (props) => { const [descInput, setDescInput] = useState(""); const [stationInput, setStationInput] = useState(""); const [stationNumberInput, setStationNumberInput] = useState(""); + + const [isShow, setIsShow] = useState(true); + const [isDepartureNow, setIsDepartureNow] = useState(false); + useEffect(() => { + const currentTime = dayjs(); + const trainTime = currentTime + .set("hour", parseInt(d.time.split(":")[0])) + .set("minute", parseInt(d.time.split(":")[1])); + const diff = trainTime.diff(currentTime, "minute"); + if (diff < 2) setIsDepartureNow(true); + else setIsDepartureNow(false); + return () => { + setIsDepartureNow(false); + setIsShow(true); + }; + }, [d.time, currentTrainData]); + useInterval(() => { + if (isDepartureNow) { + setIsShow(!isShow); + } + }, 800); return ( <> = (props) => { marginHorizontal: "3%", backgroundColor: "#000", flexDirection: "row", + opacity: isShow ? 1 : 0.5, }} onPress={() => openTrainInfo(d)} + key={d.train + "-eachData"} > = (props) => { + {!!isDepartureNow && ( + + )} {trainDescriptionSwitch && ( = (props) => { /> )} {trainDescriptionSwitch && !!train.info && ( - + )} ); diff --git a/components/発車時刻表/LED_Vision_Component/Header.js b/components/発車時刻表/LED_Vision_Component/Header.js index 392ed13..5fc696e 100644 --- a/components/発車時刻表/LED_Vision_Component/Header.js +++ b/components/発車時刻表/LED_Vision_Component/Header.js @@ -1,48 +1,58 @@ import { View, Text } from "react-native"; import { useCurrentTrain } from "../../../stateBox/useCurrentTrain"; import LottieView from "lottie-react-native"; -import { Ionicons } from "@expo/vector-icons"; +import { Ionicons, AntDesign } from "@expo/vector-icons"; +import { useStationList } from "@/stateBox/useStationList"; +import { useNavigation } from "@react-navigation/native"; -export const Header = ({ getCurrentTrain }) => { - const { currentTrainLoading, setCurrentTrainLoading } = useCurrentTrain(); - return ( - - - - - 次の列車 - - Next Train - - - {currentTrainLoading == "loading" ? ( - - ) : currentTrainLoading == "error" ? ( - { - setCurrentTrainLoading("loading"); - getCurrentTrain(); - }} - /> - ) : null} - +export const Header = ({ station }) => { + const { + currentTrainLoading, + setCurrentTrainLoading, + getCurrentTrain, + inject, + } = useCurrentTrain(); + const { getInjectJavascriptAddress } = useStationList(); + const { navigate } = useNavigation(); + return ( + + - ); - }; \ No newline at end of file + + + 次の列車 + + Next Train + + + {currentTrainLoading == "loading" ? ( + + ) : currentTrainLoading == "error" ? ( + { + setCurrentTrainLoading("loading"); + getCurrentTrain(); + }} + /> + ) : null} + + + ); +}; diff --git a/components/発車時刻表/LED_vidion.js b/components/発車時刻表/LED_vidion.js index 6eefb49..96d297b 100644 --- a/components/発車時刻表/LED_vidion.js +++ b/components/発車時刻表/LED_vidion.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; -import { View } from "react-native"; -import { widthPercentageToDP as wp } from "react-native-responsive-screen"; +import { View, useWindowDimensions } from "react-native"; +import dayjs from "dayjs"; import { useInterval } from "../../lib/useInterval"; import { objectIsEmpty } from "../../lib/objectIsEmpty"; import { useCurrentTrain } from "../../stateBox/useCurrentTrain"; @@ -10,8 +10,8 @@ import { Footer } from "./LED_Vision_Component/Footer"; import { Header } from "./LED_Vision_Component/Header"; import { Description } from "./LED_inside_Component/Description"; import { EachData } from "./EachData"; - - +import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram"; +import { trainPosition } from "@/lib/trainPositionTextArray"; /** * @@ -43,13 +43,7 @@ import { EachData } from "./EachData"; * 9062D 四国まんなか千年ものがたり(臨時?) */ export default function LED_vision(props) { - const { - station, - trainDiagram, - getCurrentTrain, - navigate, - openStationACFromEachTrainInfo, - } = props; + const { station, navigate, openStationACFromEachTrainInfo } = props; const { currentTrain } = useCurrentTrain(); const [stationDiagram, setStationDiagram] = useState({}); //当該駅の全時刻表 const [finalSwitch, setFinalSwitch] = useState(false); @@ -57,36 +51,40 @@ export default function LED_vision(props) { const [trainDescriptionSwitch, setTrainDescriptionSwitch] = useState(false); const [isInfoArea, setIsInfoArea] = useState(false); const { areaInfo, areaStationID } = useAreaInfo(); + const { allTrainDiagram } = useAllTrainDiagram(); useEffect(() => { AS.getItem("LEDSettings/trainIDSwitch").then((data) => { setTrainIDSwitch(data === "true"); }); AS.getItem("LEDSettings/trainDescriptionSwitch").then((data) => { - setTrainDescriptionSwitch(data == "true"); + setTrainDescriptionSwitch(data === "true"); }); AS.getItem("LEDSettings/finalSwitch").then((data) => { - setFinalSwitch(data == "true"); + setFinalSwitch(data === "true"); }); }, []); useEffect(() => { // 現在の駅に停車するダイヤを作成する副作用[列車ダイヤと現在駅情報] - if (!trainDiagram) { + if (!allTrainDiagram) { setStationDiagram({}); return; } let returnData = {}; - Object.keys(trainDiagram).forEach((key) => { - if (trainDiagram[key].match(station[0].Station_JP + ",")) { - returnData[key] = trainDiagram[key]; + 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))); - }, [trainDiagram, station]); + }, [allTrainDiagram, station]); - const [trainTimeAndNumber, setTrainTimeAndNumber] = useState(null); + /* +{lastStation: "当駅止", time: "12:34", train: "1234M"} + */ + const [trainTimeAndNumber, setTrainTimeAndNumber] = useState([]); useEffect(() => { //現在の駅に停車する列車から時刻を切り出してLEDベースにフォーマット @@ -100,34 +98,36 @@ export default function LED_vision(props) { if (!trainTimeAndNumber) return () => {}; if (!currentTrain) return () => {}; const data = trainTimeAndNumber - .filter((d) => currentTrain.map((m) => m.num).includes(d.train)) + .filter((d) => currentTrain.map((m) => m.num).includes(d.train)) //現在の列車に絞る[ToDo] .filter(timeFiltering) .filter((d) => !!finalSwitch || d.lastStation != "当駅止"); setSelectedTrain(data); }, [trainTimeAndNumber, currentTrain, finalSwitch]); const getTime = (stationDiagram, station) => { - const returnData = Object.keys(stationDiagram).map((trainNum) => { - let trainData = {}; - stationDiagram[trainNum].split("#").forEach((data) => { - if (data.match("着")) { - trainData.lastStation = data.split(",着,")[0]; - } - if (data.split(",")[0] === station.Station_JP) { - if (data.match(",発,")) { - trainData.time = data.split(",発,")[1]; - } else if(data.match(",着,")){ - trainData.time = data.split(",着,")[1]; - trainData.lastStation = "当駅止"; + const returnData = Object.keys(stationDiagram) + .map((trainNum) => { + let trainData = {}; + stationDiagram[trainNum].split("#").forEach((data) => { + if (data.match("着")) { + trainData.lastStation = data.split(",着,")[0]; } - } - }); - return { - train: trainNum, - time: trainData.time, - lastStation: trainData.lastStation, - }; - }).filter((d) => d.time); + if (data.split(",")[0] === station.Station_JP) { + if (data.match(",発,")) { + trainData.time = data.split(",発,")[1]; + } else if (data.match(",着,")) { + trainData.time = data.split(",着,")[1]; + trainData.lastStation = "当駅止"; + } + } + }); + return { + train: trainNum, + time: trainData.time, + lastStation: trainData.lastStation, + }; + }) + .filter((d) => d.time); return returnData.sort((a, b) => { switch (true) { case parseInt(a.time.split(":")[0]) < parseInt(b.time.split(":")[0]): @@ -145,16 +145,40 @@ export default function LED_vision(props) { }; const timeFiltering = (d) => { - const date = new Date(); - const newDate = new Date(); - let data = d.time.split(":"); - let delay = isNaN(currentTrain.filter((t) => t.num == d.train)[0].delay) - ? 0 - : currentTrain.filter((t) => t.num == d.train)[0].delay; - date.setHours(parseInt(data[0])); - date.setMinutes(parseInt(data[1]) + parseInt(delay)); - return !(newDate > date); + const baseTime = 2; + + if (currentTrain.filter((t) => t.num == d.train).length == 0) { + const date = dayjs(); + const trainTime = date + .hour(parseInt(d.time.split(":")[0])) + .minute(parseInt(d.time.split(":")[1])); + + if (date.isAfter(trainTime)) { + return false; + } else if (trainTime.diff(date) < baseTime * 60 * 60 * 1000) { + return true; + } + return false; + } else { + const Pos = trainPosition( + currentTrain.filter((t) => t.num == d.train)[0] + ); + const nextPos = Pos.isBetween ? Pos.Pos.to : Pos.Pos.Pos; + const PrePos = Pos.isBetween ? Pos.Pos.from : ""; + if (station[0].Station_JP == nextPos) { + if(d.lastStation != "当駅止") return true; + } else if (station[0].Station_JP == PrePos) { + return false; + } + const date = dayjs(); + let [h, m] = d.time.split(":"); + let delay = isNaN(currentTrain.filter((t) => t.num == d.train)[0].delay) + ? 0 + : currentTrain.filter((t) => t.num == d.train)[0].delay; + const db = date.hour(parseInt(h)).minute(parseInt(m) + parseInt(delay)); + return !date.isAfter(db); + } }; const [areaString, setAreaString] = useState(""); @@ -186,18 +210,19 @@ export default function LED_vision(props) { } setAreaStringLength(areaInfo.length); }, [areaInfo]); - + const { width } = useWindowDimensions(); + const adjustedWidth = width * 0.98; return ( -
+
{selectedTrain.map((d) => ( { + const 下枠フレーム: ViewStyle = { + flex: 1, + flexDirection: "row", + alignContent: "center", + alignItems: "center", + }; + return ( @@ -49,12 +62,40 @@ type FCimport = { children: string; }; const BottomSideArrow: FC = ({ isMatsuyama, children }) => { + const 下枠左右マーク: TextStyle = { + fontWeight: "bold", + fontSize: parseInt("20%"), + color: "white", + paddingHorizontal: 5, + textAlignVertical: "center", + }; return !isMatsuyama && {children}; }; const BottomStationNumberView: FC = ({ isMatsuyama, children }) => { + const { width } = useWindowDimensions(); const lineID = children.slice(0, 1); const lineName = children.slice(1); + const 下枠駅ナンバー: ViewStyle = { + alignContent: "center", + alignItems: "center", + width: width * 0.08, + height: width * 0.08, + margin: width * 0.01, + backgroundColor: "white", + borderWidth: parseInt("3%"), + borderRadius: parseInt("100%"), + }; + const 下枠駅ナンバーB: ViewStyle = { + alignContent: "center", + alignItems: "center", + width: width * 0.07, + height: width * 0.07, + margin: width * 0.02, + backgroundColor: "white", + borderWidth: parseInt("3%"), + borderRadius: parseInt("100%"), + }; return ( = ({ isMatsuyama, children }) => { ); }; - -const 下枠フレーム: ViewStyle = { - flex: 1, - flexDirection: "row", - alignContent: "center", - alignItems: "center", -}; -const 下枠左右マーク: TextStyle = { - fontWeight: "bold", - fontSize: parseInt("20%"), - color: "white", - paddingHorizontal: 5, - textAlignVertical: "center", -}; -const 下枠駅ナンバー: ViewStyle = { - alignContent: "center", - alignItems: "center", - width: wp("8%"), - height: wp("8%"), - margin: wp("1%"), - backgroundColor: "white", - borderWidth: parseInt("3%"), - borderRadius: parseInt("100%"), -}; -const 下枠駅ナンバーB: ViewStyle = { - alignContent: "center", - alignItems: "center", - width: wp("7%"), - height: wp("7%"), - margin: wp("2%"), - backgroundColor: "white", - borderWidth: parseInt("3%"), - borderRadius: parseInt("100%"), -}; diff --git a/components/駅名表/Sign.js b/components/駅名表/Sign.js index 7cd1723..f7a3929 100644 --- a/components/駅名表/Sign.js +++ b/components/駅名表/Sign.js @@ -1,6 +1,10 @@ import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; -import { View, Text, TouchableOpacity } from "react-native"; -import { widthPercentageToDP as wp } from "react-native-responsive-screen"; +import { + View, + Text, + TouchableOpacity, + useWindowDimensions, +} from "react-native"; import { MaterialCommunityIcons } from "@expo/vector-icons"; import LottieView from "lottie-react-native"; import { useInterval } from "../../lib/useInterval"; @@ -15,7 +19,14 @@ import { AddressText } from "./AddressText"; import { useStationList } from "../../stateBox/useStationList"; export default function Sign(props) { - const { currentStation, oP, oLP, isCurrentStation = false } = props; + const { oP, oLP, isCurrentStation = false, stationID } = props; + + const { width, height } = useWindowDimensions(); + const { getStationDataFromId } = useStationList(); + if (!stationID) { + return <>; + } + const [currentStationData] = useState(getStationDataFromId(stationID)); const { favoriteStation, setFavoriteStation } = useFavoriteStation(); const [nexPrePosition, setNexPrePosition] = useState(0); const { originalStationList } = useStationList(); @@ -26,48 +37,40 @@ export default function Sign(props) { useLayoutEffect(() => { const isFavorite = favoriteStation.filter((d) => { const compare = JSON.stringify(d); - const current = JSON.stringify(currentStation); - if (compare === current) { - return true; - } else { - return false; - } + const current = JSON.stringify(currentStationData); + return compare === current; }); setTestButtonStatus(isFavorite.length == 0 ? false : true); - }, [favoriteStation, currentStation]); + }, [favoriteStation, currentStationData]); useEffect(() => { const isFavorite = favoriteStation.filter((d) => { const compare = JSON.stringify(d); - const current = JSON.stringify(currentStation); - if (compare === current) { - return true; - } else { - return false; - } + const current = JSON.stringify(currentStationData); + return compare === current; }); setTestButtonStatus(isFavorite.length == 0 ? false : true); - }, [favoriteStation, currentStation]); + }, [favoriteStation, currentStationData]); useInterval(() => { - if (currentStation.length == 1) { + if (currentStationData.length == 1) { setNexPrePosition(0); return () => {}; } setNexPrePosition( - nexPrePosition + 1 == currentStation.length ? 0 : nexPrePosition + 1 + nexPrePosition + 1 == currentStationData.length ? 0 : nexPrePosition + 1 ); }, 2000); useEffect(() => { setNexPrePosition(0); - getPreNextStation(currentStation[0]); - if (currentStation.length == 1) return () => {}; - getPreNextStation(currentStation[1]); - }, [currentStation]); + getPreNextStation(currentStationData[0]); + if (currentStationData.length == 1) return () => {}; + getPreNextStation(currentStationData[1]); + }, [currentStationData]); useEffect(() => { - if (!currentStation[nexPrePosition]) return () => {}; - getPreNextStation(currentStation[nexPrePosition]); + if (!currentStationData[nexPrePosition]) return () => {}; + getPreNextStation(currentStationData[nexPrePosition]); }, [nexPrePosition]); const getPreNextStation = (now) => { const lineList = [ @@ -101,25 +104,81 @@ export default function Sign(props) { if (returnData[1]) setNexStation(returnData[1]); } }; - const isMatsuyama = currentStation[0].StationNumber == "Y55"; + const isMatsuyama = currentStationData[0].StationNumber == "Y55"; //const isMatsuyama = true; const favoliteChanger = () => { if (testButtonStatus) { const otherData = favoriteStation.filter((d) => { const compare = JSON.stringify(d); - const current = JSON.stringify(currentStation); + const current = JSON.stringify(currentStationData); return compare !== current; }); AS.setItem("favoriteStation", JSON.stringify(otherData)); setFavoriteStation(otherData); } else { let ret = favoriteStation; - ret.push(currentStation); + ret.push(currentStationData); AS.setItem("favoriteStation", JSON.stringify(ret)); setFavoriteStation(ret); } setTestButtonStatus(!testButtonStatus); }; + + const styleSheet = { + 外枠: { + width: width * 0.8, + height: ((width * 0.8) / 20) * 9, + borderColor: "#0099CC", + borderWidth: 1, + backgroundColor: "white", + }, + 外枠B: { + width: width * 0.8, + height: ((width * 0.8) / 20) * 9, + borderWidth: 0, + }, + 下帯: { + position: "absolute", + bottom: "8%", + left: "0%", + width: "100%", + height: "27%", + backgroundColor: "#0099CC", + }, + 下帯B: { + position: "absolute", + bottom: "0%", + left: "0%", + width: "100%", + height: "26%", + backgroundColor: "#454545", + }, + JRStyle: { + position: "absolute", + top: "2%", + left: "2%", + fontWeight: "bold", + fontSize: parseInt("25%"), + color: "#0099CC", + }, + 下帯内容: { + position: "absolute", + bottom: "8%", + height: "27%", + width: "100%", + alignItems: "center", + flexDirection: "column", + }, + 下帯内容B: { + position: "absolute", + bottom: "0%", + height: "26%", + width: "100%", + alignItems: "center", + flexDirection: "column", + }, + }; + return ( )} - - + + {isCurrentStation ? ( - + ); } - -const styleSheet = { - 外枠: { - width: wp("80%"), - height: (wp("80%") / 20) * 9, - borderColor: "#0099CC", - borderWidth: 1, - backgroundColor: "white", - }, - 外枠B: { - width: wp("80%"), - height: (wp("80%") / 20) * 9, - borderWidth: 0, - }, - 下帯: { - position: "absolute", - bottom: "8%", - left: "0%", - width: "100%", - height: "27%", - backgroundColor: "#0099CC", - }, - 下帯B: { - position: "absolute", - bottom: "0%", - left: "0%", - width: "100%", - height: "26%", - backgroundColor: "#454545", - }, - JRStyle: { - position: "absolute", - top: "2%", - left: "2%", - fontWeight: "bold", - fontSize: parseInt("25%"), - color: "#0099CC", - }, - 下帯内容: { - position: "absolute", - bottom: "8%", - height: "27%", - width: "100%", - alignItems: "center", - flexDirection: "column", - }, - 下帯内容B: { - position: "absolute", - bottom: "0%", - height: "26%", - width: "100%", - alignItems: "center", - flexDirection: "column", - }, -}; diff --git a/components/駅名表/StationNumberMaker.tsx b/components/駅名表/StationNumberMaker.tsx index dbeef0e..bd38e40 100644 --- a/components/駅名表/StationNumberMaker.tsx +++ b/components/駅名表/StationNumberMaker.tsx @@ -1,10 +1,11 @@ import React from "react"; import { Text, View } from "react-native"; -import { widthPercentageToDP as wp } from "react-native-responsive-screen"; +import { useWindowDimensions } from "react-native"; import lineColorList from "../../assets/originData/lineColorList"; export const StationNumberMaker = (props) => { const { currentStation, isMatsuyama } = props; + const { width } = useWindowDimensions(); const getTop = (array: number[], index: number) => { if (array.length == 1) return 20; else if (index == 0) return 5; @@ -24,8 +25,8 @@ export const StationNumberMaker = (props) => { alignItems: "center", top: `${getTop(array, index)}%`, right: "10%", - width: wp("10%"), - height: wp("10%"), + width: (width / 100 * 10), + height: (width / 100 * 10), borderColor: lineColorList[lineID], backgroundColor: "white", borderWidth: parseInt("3%"), diff --git a/config/newsUpdate.ts b/config/newsUpdate.ts index 04902c9..c3e61f4 100644 --- a/config/newsUpdate.ts +++ b/config/newsUpdate.ts @@ -1 +1 @@ -export const news = "2025-03-06"; +export const news = "2025-07-09"; diff --git a/lib/getStationList.ts b/lib/getStationList.ts index 1a851f3..45d3e17 100644 --- a/lib/getStationList.ts +++ b/lib/getStationList.ts @@ -40,6 +40,17 @@ export const lineListPair = { N: "鳴門線(池谷-鳴門間)[N]", M: "瀬戸大橋線(児島-宇多津間)[M]", }; +export const lineList_LineWebID = { + "予讃線(高松-松山間)[Y]" : "yosan", + "予讃線(松山-宇和島間)[U]" : "uwajima", + "予讃線/愛ある伊予灘線(向井原-伊予大洲間)[S]" : "uwajima2", + "土讃線(多度津-高知間)[D]" : "dosan", + "土讃線(高知-窪川間)[K]" : "dosan2", + "高徳線(高松-徳島間)[T]" : "koutoku", + "徳島線(徳島-阿波池田間)[B]" : "tokushima", + "鳴門線(池谷-鳴門間)[N]" : "naruto", + "瀬戸大橋線(児島-宇多津間)[M]" : "seto", +}; export const getStationList2 = async () => { return { yosan, @@ -167,6 +178,11 @@ export const getStationList = async () => { 予讃線, stationList["日英対応表"] ); + stationList["予讃線(松山-宇和島間)[U]"] = addStationPosition( + concatBetweenStations(stationList["予讃線(松山-宇和島間)[U]"]), + 内子線, + stationList["日英対応表"] + ); stationList["予讃線/愛ある伊予灘線(向井原-伊予大洲間)[S]"] = addStationPosition( concatBetweenStations( @@ -195,15 +211,11 @@ export const getStationList = async () => { 鳴門線, stationList["日英対応表"] ); - const tokushimaCurrent = addStationPosition( + stationList["徳島線(徳島-阿波池田間)[B]"] = addStationPosition( concatBetweenStations(stationList["徳島線(徳島-阿波池田間)[B]"]), 徳島線, stationList["日英対応表"] ); - stationList["徳島線(徳島-阿波池田間)[B]"] = [ - tokushimaCurrent[tokushimaCurrent.length - 1], - ...tokushimaCurrent, - ]; stationList["徳島線(徳島-阿波池田間)[B]"].pop(); stationList["瀬戸大橋線(児島-宇多津間)[M]"] = [ { diff --git a/lib/getTrainType.ts b/lib/getTrainType.ts index 5dda2d3..151630c 100644 --- a/lib/getTrainType.ts +++ b/lib/getTrainType.ts @@ -5,7 +5,7 @@ type nameString = | "SPCL" | "Normal" | string; -type colorString = "aqua" | "red" | "#297bff" | "white"; +type colorString = "aqua" | "red" | "#297bff" | "white" | "pink"; type trainTypeString = | "快速" | "特急" @@ -28,7 +28,7 @@ export const getTrainType: getTrainType = (nameString) => { case "LTDEXP": return { color: "red", name: "特急", data: "express" }; case "NightLTDEXP": - return { color: "red", name: "寝台特急", data: "express" }; + return { color: "pink", name: "寝台特急", data: "express" }; case "SPCL": case "SPCL_Rapid": case "SPCL_EXP": diff --git a/lib/webViewInjectjavascript.ts b/lib/webViewInjectjavascript.ts index 492d280..c0484d7 100644 --- a/lib/webViewInjectjavascript.ts +++ b/lib/webViewInjectjavascript.ts @@ -2,13 +2,15 @@ type InjectJavascriptData = ( a: string, b: string, c: string, - d: string + d: string, + e: string ) => string; export const injectJavascriptData: InjectJavascriptData = ( mapSwitch, iconSetting, stationMenu, - trainMenu + trainMenu, + uiSetting ) => { // 一番上のメニュー非表示 地図スイッチによって切り替え const topMenu = @@ -28,10 +30,29 @@ export const injectJavascriptData: InjectJavascriptData = ( // 上部ヘッダーの取り扱い、自動再読み込み、setStringsの実行 const bootData = ` + let stationList = {}; + fetch("https://n8n.haruk.in/webhook/jr-shikoku-station-list").then((response) => response.json()).then((data) => { + stationList = data; + }); let trainDataList = []; fetch("https://n8n.haruk.in/webhook/jr-shikoku-position-custom-datalist").then((response) => response.json()).then((data) => { trainDataList = data[0].data; }).then(()=>setReload()); + let trainDiagramData2 = {}; + fetch("https://n8n.haruk.in/webhook/JR-shikoku-diagram-migrate-original") + .then((response) => response.json()) + .then((res)=>res.data) + .then((res) => { + const data = {}; + res.forEach((d) => { + const keys = Object.keys(d); + data[keys] = d[keys]; + }); + return data; + }) + .then((data) => { + trainDiagramData2 = data; + }); let probremsData = []; fetch("https://n8n.haruk.in/webhook/jrshikoku-position-problems").then((response) => response.json()).then((data) => { probremsData = data.data; @@ -52,7 +73,7 @@ export const injectJavascriptData: InjectJavascriptData = ( const trainIcon = ` const setStationIcon = (setIconElem,img,hasProblem) =>{ const position = setIconElem.getAttribute("style").includes("left"); - setIconElem.insertAdjacentHTML('beforebegin', ""); + setIconElem.insertAdjacentHTML('beforebegin', ""); setIconElem.remove(); } @@ -998,20 +1019,54 @@ export const injectJavascriptData: InjectJavascriptData = ( `; const normal_train_name = ` + const getJRF = num =>{ + 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 undefined; + } + } const nameReplace = (列車名データ,列番データ,行き先情報,hasProblem) =>{ let isWanman = false; let trainName = ""; + let trainType = ""; + let trainTypeColor = "black"; let viaData = ""; let ToData = ""; let TrainNumber = 列番データ; try{ - const diagram = trainTimeInfo[列番データ]; + const diagram = trainDiagramData2[列番データ] || trainTimeInfo[列番データ]; if(diagram){ const diagramData = diagram.split("#"); ToData = diagramData[diagramData.length - 2].split(",")[0]; } - }catch(e){} if(列車名データ.split(":")[1]){ const textBase = 列車名データ.split(":")[1].replace("\\r",""); @@ -1039,42 +1094,20 @@ export const injectJavascriptData: InjectJavascriptData = ( else if(列番データ.indexOf("R") != -1){ trainName = "回送"; } + else if(列番データ.indexOf("E") != -1){ + trainName = "回送"; + } + else if(列番データ.indexOf("A") != -1){ + trainName = "回送"; + } + else if(列番データ.indexOf("B") != -1){ + trainName = "回送"; + } else if(列番データ.indexOf("S") != -1){ trainName = "臨時列車"; } - const getJRF = num =>{ - 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 "臨時貨物"; - } - } let JRF = true; const JRFTemp = getJRF(列番データ); @@ -1231,10 +1264,49 @@ export const injectJavascriptData: InjectJavascriptData = ( getThrew(列番データ); if(trainDataList.find(e => e.id === 列番データ) !== undefined){ const data = trainDataList.find(e => e.id === 列番データ); - //{id,isWanman,trainName,viaData,ToData} + //{id,isWanman,trainName,viaData,ToData,TrainNumber,JRF,type,infoUrl,trainNumDistance,info,infogram} + trainType = (()=>{ + switch(data.type){ + case "Normal": + trainTypeColor = "black"; + return "普通"; + case "OneMan": + trainTypeColor = "black"; + return "普通"; + case "Freight": + trainTypeColor = "black"; + return "貨物"; + case "SPCL_Normal": + trainTypeColor = "#297bff"; + return "臨時"; + case "SPCL_Rapid": + trainTypeColor = "#297bff"; + return "臨時快速"; + case "SPCL_LTDEXP": + trainTypeColor = "#297bff"; + return "臨時特急"; + case "LTDEXP": + trainTypeColor = "red"; + return "特急"; + case "NightLTDEXP": + trainTypeColor = "rgb(211, 0, 176)"; + return "寝台特急"; + case "OneManRapid": + trainTypeColor = "rgba(0, 140, 255, 1)"; + return "快速"; + case "Rapid": + trainTypeColor = "rgba(0, 140, 255, 1)"; + return "快速"; + default: + return ""; + } + })(); isWanman = data.isWanman; if(data.trainName != ""){ trainName = data.trainName; + if(data.trainNumDistance != null){ + trainName += parseInt(列番データ.replace("M", "").replace("D", "")) - data.trainNumDistance+"号"; + } } if(data.viaData != ""){ viaData = data.viaData; @@ -1247,18 +1319,167 @@ export const injectJavascriptData: InjectJavascriptData = ( const returnText1 = (isWanman ? "ワンマン " : "") + trainName + viaData; const returnText2 = (ToData ? ToData+"行 " : ToData)+ TrainNumber; 行き先情報.innerText = ""; - 行き先情報.insertAdjacentHTML('beforebegin', "

" + returnText1 + "

"); - 行き先情報.insertAdjacentHTML('beforebegin', "

" + (ToData ? ToData + "行 " : ToData) + "

" + TrainNumber + (JRF ? "":"レ") + "

"); - 行き先情報.insertAdjacentHTML('beforebegin', "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); - } + ${uiSetting === "tokyo" ? ` + let stationIDs = []; + let stationLines = []; + Object.keys(stationList).forEach((key) => { + const data = stationList[key].find(e => e.Station_JP === ToData )?.StationNumber; + if(data){ + stationIDs.push(data); + stationLines.push(key); + } + }); + let getColors = []; + if(stationLines.length === 0){ + getColors = ["rgba(97, 96, 96, 0.81)"]; + }else{ + getColors = stationLines.map(e => GetLineBarColor(e)); + } + let yosan2Color = undefined; + if(viaData == "(内子経由)"){ + yosan2Color ="#F5AC13"; + } + else if(viaData == "(海経由)"){ + yosan2Color = "#9AA7D7"; + } + + const gradient = getColors.length > 1 ? "linear-gradient(130deg, " + getColors[0] + " 0%, "+ getColors[0]+"50%, "+ getColors[1]+"50%, " + getColors[1] + " 100%)" : getColors[0]; + 行き先情報.insertAdjacentHTML('beforebegin', "

" + TrainNumber + (JRF ? "":"レ") + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (isWanman ? "ワンマン " : "") + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + viaData + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + trainName + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (ToData ? ToData + "行" : ToData) + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + trainType + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); + `: ` + 行き先情報.insertAdjacentHTML('beforebegin', "

" + returnText1 + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (ToData ? ToData + "行 " : ToData) + "

" + TrainNumber + (JRF ? "":"レ") + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); + `} + } `; const textInsert = ` + + +const setNewTrainItem = (element,hasProblem)=>{ + var 列番データ = element.getAttribute('offclick').split('"')[1]; + const JRFTemp = getJRF(列番データ); + if(element.getAttribute('offclick').includes("express")){ + element.style.borderColor = 'rgba(255, 0, 0, 1)'; + }else if(element.getAttribute('offclick').includes("rapid")){ + element.style.borderColor = 'rgba(0, 140, 255, 1)'; + }else if(JRFTemp){ + element.style.borderColor = 'rgba(0, 134, 158, 0.8)'; + }else{ + element.style.borderColor = 'white'; + } + element.style.borderWidth = '2px'; + element.style.borderStyle = 'solid'; + element.style.borderRadius = '10%'; + switch(true){ + case 列番データ.indexOf("H") != -1: + case 列番データ.indexOf("R") != -1: + case 列番データ.indexOf("E") != -1: + case 列番データ.indexOf("A") != -1: + case 列番データ.indexOf("B") != -1: + case !!JRFTemp: + element.style.backgroundColor = 'rgba(199, 199, 199, 0.8)'; + break; + default: + element.style.backgroundColor = 'rgba(255, 255, 255, 0.8)'; + break; + } + if(hasProblem){ + element.style.boxShadow = '0 0 10px rgba(255, 0, 0, 0.9)'; + }else{ + element.style.boxShadow = '0 0 4px rgba(0, 0, 0, 0.2)'; + } + element.style.margin = '2px'; + element.style.display = 'flex'; + element.style.alignItems = 'center'; + element.style.justifyContent = 'center'; + element.style.width = '4.5em'; + element.style.minHeight = '80px'; + element.style.height = '100%'; + element.getElementsByTagName("img")[0].style.float = 'unset'; + element.style.webkitTapHighlightColor = 'rgba(0, 0, 0, 0)'; + element.style.transition = 'transform 0.1s ease-in-out'; + element.addEventListener('touchstart', () => element.style.transform = 'scale(0.8)'); + element.addEventListener('touchend', () => element.style.transform = 'scale(1)'); + if(element.getAttribute("style").includes("left")){ + // borderを使って五角形を生成 下り + element.style.borderRadius = '10% 10% 40% 40%'; + element.style.flexDirection = 'column-reverse'; + } + else if(element.getAttribute("style").includes("right")){ + // borderを使って五角形を生成 上り + element.style.borderRadius = '40% 40% 10% 10%'; + element.style.flexDirection = 'column'; + } + +} //列番付与 const setStrings = () =>{ try { const elements = document.querySelectorAll('#disp > div > div > div[onclick]'); + const setNewTrainItemUI = ()=>{ + const elementBaseBase = document.querySelectorAll('[id^="stationBlock"]'); + elementBaseBase.forEach(e=>{ //それぞれの駅ブロック横一列 + e.style.display = 'flex'; + e.style.height = "unset"; + e.style.flexDirection = 'row'; + //e.style.alignItems = "left"; + e.style.justifyContent = 'center'; + const x = e.querySelectorAll(':scope > div');//配下のdiv要素を選択 + //x[0] 登りブロック x[2] 下りブロック x[1] 駅ブロック + const aaa = (x2,pos) => { + x2.style.display = 'flex'; + x2.style.flexDirection = 'row'; + if(pos == "right"){ + x2.style.alignItems = 'flex-start'; + x2.style.justifyContent = 'flex-start'; + }else if(pos == "left"){ + x2.style.alignItems = 'flex-end'; + x2.style.justifyContent = 'flex-end'; + } + x2.style.flexWrap = 'wrap'; + x2.style.width = '100%'; + x2.style.height = "100%"; + + } + const aaa2 = (x2) => { + x2.style.display = 'flex'; + x2.style.flexDirection = 'row'; + x2.style.alignItems = 'center'; + x2.style.justifyContent = 'center'; + x2.style.flexWrap = 'wrap'; + x2.style.width = '100%'; + x2.style.height = "unset"; + const x3 = x2.querySelectorAll(":scope > div"); + x3.forEach(i=>{ + i.style.position = "unset"; + i.style.display = "flex"; + i.style.flexDirection = "column"; + i.style.flex = "1"; + i.style.backgroundColor = "#00000000"; + i.querySelectorAll(":scope > *").forEach(j=>{ + j.style.width = "100%"; + j.style.textAlign = "center"; + j.style.margin = "5px"; + j.style.padding = "5px"; + }); + }); + } + aaa(x[0],"left"); + aaa2(x[1]); + aaa(x[2],"right"); + + }); + } + + ${uiSetting === "tokyo" ? `setNewTrainItemUI();`: ``} for (let element of elements) { if(element.getAttribute('offclick')){ continue; } element.setAttribute('offclick',element.getAttribute('onclick')) @@ -1266,7 +1487,7 @@ const setStrings = () =>{ var 行き先情報 = element.getElementsByTagName("p")[0]; var 列番データ = element.getAttribute('offclick').split('"')[1]; var 列車名データ = element.getAttribute('offclick').split('"')[3]; - const trainData = trainPositionDatas.find(e => e.TrainNum == 列番データ); + const trainData = trainPositionDatas.filter(e=>!(e.Pos && e.Pos.includes("予告窓"))).find(e => e.TrainNum == 列番データ); const hasProblem = probremsData.find((e)=>{ return e.TrainNum == trainData.TrainNum && e.Pos == trainData.Pos; }); @@ -1289,6 +1510,7 @@ const setStrings = () =>{ } ` : ""} nameReplace(列車名データ,列番データ,行き先情報,hasProblem); + ${uiSetting === "tokyo" ? `setNewTrainItem(element,hasProblem);`: ``} } try{ for(let d of document.getElementById('disp').childNodes){ diff --git a/menu.js b/menu.js index 42a8592..bc51c70 100644 --- a/menu.js +++ b/menu.js @@ -1,71 +1,106 @@ -import React, { useRef, useState, useEffect } from "react"; -import Carousel from "react-native-reanimated-carousel"; +import React, { useRef, useState, useEffect, useLayoutEffect } from "react"; import { Platform, View, ScrollView, - Linking, + useWindowDimensions, + LayoutAnimation, Text, TouchableOpacity, - LayoutAnimation, - Dimensions, } from "react-native"; import Constants from "expo-constants"; -import * as Location from "expo-location"; +import { + configureReanimatedLogger, + ReanimatedLogLevel, +} from "react-native-reanimated"; import StatusbarDetect from "./StatusbarDetect"; -import { widthPercentageToDP as wp } from "react-native-responsive-screen"; -import { Ionicons } from "@expo/vector-icons"; -import LottieView from "lottie-react-native"; -import { parseAllTrainDiagram } from "./lib/parseAllTrainDiagram"; import LED_vision from "./components/発車時刻表/LED_vidion"; -import Sign from "./components/駅名表/Sign"; import { TitleBar } from "./components/Menu/TitleBar"; import { FixedContentBottom } from "./components/Menu/FixedContentBottom"; -import { UsefulBox } from "./components/atom/UsefulBox"; -import { lineList } from "./lib/getStationList"; -import useInterval from "./lib/useInterval"; -import { HeaderConfig } from "./lib/HeaderConfig"; +import { lineList, stationIDPair } from "./lib/getStationList"; import { useFavoriteStation } from "./stateBox/useFavoriteStation"; -import { SheetManager } from "react-native-actions-sheet"; -import { useTrainDelayData } from "./stateBox/useTrainDelayData"; import { useNavigation } from "@react-navigation/native"; import { useStationList } from "./stateBox/useStationList"; -import { StationNumber } from "./components/Menu/StationPagination"; -import lineColorList from "./assets/originData/lineColorList"; +import { TopMenuButton } from "@/components/Menu/TopMenuButton"; +import { JRSTraInfoBox } from "@/components/Menu/JRSTraInfoBox"; +import MapView, { Marker } from "react-native-maps"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; +import { CarouselBox } from "./components/Menu/Carousel/CarouselBox"; +import { CarouselTypeChanger } from "./components/Menu/Carousel/CarouselTypeChanger"; +import { useUserPosition } from "./stateBox/useUserPosition"; import { AS } from "./storageControl"; -import { SimpleDot } from "./components/Menu/SimpleDot"; -import { useAllTrainDiagram } from "./stateBox/useAllTrainDiagram"; - -export default function Menu({ getCurrentTrain }) { - const { navigate } = useNavigation(); +import { lineList_LineWebID } from "./lib/getStationList"; +import { Ionicons } from "@expo/vector-icons"; +import { SearchUnitBox } from "./components/Menu/RailScope/SearchUnitBox"; +configureReanimatedLogger({ + level: ReanimatedLogLevel.error, // Set the log level to error + strict: true, // Reanimated runs in strict mode by default +}); +export default function Menu(props) { + const { scrollRef, mapHeight, MapFullHeight, mapMode, setMapMode } = props; + const { navigate, addListener, isFocused } = useNavigation(); const { favoriteStation } = useFavoriteStation(); const { originalStationList } = useStationList(); - - //位置情報 - const [locationStatus, setLocationStatus] = useState(null); + const { height, width } = useWindowDimensions(); + const { bottom, left, right, top } = useSafeAreaInsets(); + const tabBarHeight = useBottomTabBarHeight(); + const [stationListMode, setStationListMode] = useState( + /*<"position"|"favorite">*/ "position" + ); useEffect(() => { - if (Platform.OS == "web") return; - Location.requestForegroundPermissionsAsync().then((data) => { - setLocationStatus( - Platform.OS == "ios" - ? data.status == "granted" - : data.android.accuracy == "fine" - ); - }); + AS.getItem("stationListMode") + .then((res) => setStationListMode(res)) + .catch((e) => { + // AS.setItem("stationListMode", "position"); + }); }, []); - - const getCurrentPosition = () => { - if (!locationStatus) return () => {}; - Location.getCurrentPositionAsync({}).then((location) => - makeCurrentStation(location) - ); + const mapsRef = useRef(null); + const returnToTop = (bool = true) => { + scrollRef?.current.scrollTo({ + y: mapHeight > 80 ? mapHeight - 80 : 0, + animated: bool, + }); }; + const goToMap = () => { + scrollRef?.current.scrollTo({ + y: 0, + animated: true, + }); + }; + useEffect(() => { + setTimeout(() => { + returnToTop(false); + }, 10); + }, [mapHeight]); + const [scrollStartPosition, setScrollStartPosition] = useState(0); + const onScrollBeginDrag = (e) => { + LayoutAnimation.configureNext({ + duration: 300, + create: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + update: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + }); + setScrollStartPosition(e.nativeEvent.contentOffset.y); + setMapMode(false); + }; + //現在地基準の駅名標リストアップ機能 + const { position, locationStatus } = useUserPosition(); + useEffect(() => { + if (!position) return () => {}; + makeCurrentStation(position); + }, [position, stationListMode]); const makeCurrentStation = (location) => { if (!originalStationList) return () => {}; const findStationEachLine = (selectLine) => { - const searchArea = 0.002; + const searchArea = 0.055; //検索範囲 const _calcDistance = (from, to) => { let lat = Math.abs(from.lat - to.lat); let lng = Math.abs(from.lng - to.lng); @@ -78,94 +113,135 @@ export default function Menu({ getCurrentTrain }) { lng: location.coords.longitude, }) < searchArea ); + //NearStationを距離の近い順にソート + NearStation.sort((a, b) => { + return ( + _calcDistance(a, { + lat: location.coords.latitude, + lng: location.coords.longitude, + }) - + _calcDistance(b, { + lat: location.coords.latitude, + lng: location.coords.longitude, + }) + ); + }); return NearStation; }; - let returnDataBase = lineList + let _stList = lineList .map((d) => findStationEachLine(originalStationList[d])) .filter((d) => d.length > 0) .reduce((pre, current) => { pre.push(...current); return pre; }, []); - if (returnDataBase.length) { - let currentStation = currentStation == undefined ? [] : currentStation; - if (currentStation.toString() != returnDataBase.toString()) { - setCurrentStation(returnDataBase); - } - } else { - setCurrentStation(undefined); + if (_stList.length == 0) setNearPositionStation([]); + else { + let returnData = []; + _stList.forEach((d, index, array) => { + const stationName = d.Station_JP; + if (returnData.findIndex((d) => d[0].Station_JP == stationName) != -1) + return; + returnData.push(array.filter((d2) => d2.Station_JP == stationName)); + }); + //returnDataを距離の近い順にソート + returnData.sort((a, b) => { + const _calcDistance = (from, to) => { + let lat = Math.abs(from.lat - to.lat); + let lng = Math.abs(from.lng - to.lng); + return Math.sqrt(lat * lat + lng * lng); + }; + return ( + _calcDistance(a[0], { + lat: location.coords.latitude, + lng: location.coords.longitude, + }) - + _calcDistance(b[0], { + lat: location.coords.latitude, + lng: location.coords.longitude, + }) + ); + }); + setNearPositionStation(returnData); } }; - useEffect(getCurrentPosition, [locationStatus]); - useInterval(getCurrentPosition, 5000); + const [nearPositionStation, setNearPositionStation] = useState([]); //第三要素 - const [currentStation, setCurrentStation] = useState(undefined); //第三要素 + const [listIndex, setListIndex] = useState(0); - const carouselRef = useRef(); - const [selectedCurrentStation, setSelectedCurrentStation] = useState(0); - - const [allStationData, setAllStationData] = useState([]); + const [listUpStation, setListUpStation] = useState([]); + const [isSearchMode, setisSearchMode] = useState(false); + useLayoutEffect(() => { + if (!!isSearchMode) { + const returnData = []; + Object.keys(lineList_LineWebID).forEach((d, indexBase) => { + originalStationList[d].forEach((D, index) => { + if ( + isSearchMode && + isSearchMode != stationIDPair[lineList_LineWebID[d]] + ) + return; + const latlng = [D.lat, D.lng]; + if (latlng.length == 0) return null; + if (D.StationNumber == undefined) { + return null; + } + returnData.push([D]); + }); + }); + if (JSON.stringify(returnData) == JSON.stringify(listUpStation)) return; + setListUpStation(returnData); + } else if (stationListMode == "position") { + const returnData = nearPositionStation.filter((d) => d != undefined); + if (JSON.stringify(returnData) == JSON.stringify(listUpStation)) return; + setListUpStation(returnData); + } else { + const returnData = favoriteStation.filter((d) => d != undefined); + if (JSON.stringify(returnData) == JSON.stringify(listUpStation)) return; + setListUpStation(returnData); + } + }, [nearPositionStation, favoriteStation, stationListMode, isSearchMode]); useEffect(() => { - setAllStationData( - [currentStation, ...favoriteStation].filter((d) => d != undefined) - ); - }, [currentStation, favoriteStation]); - useEffect(() => { - if (allStationData.length == 0) { - setSelectedCurrentStation(0); + if (listUpStation.length == 0) { + setListIndex(0); return; } - if (allStationData[selectedCurrentStation] == undefined) { - const count = selectedCurrentStation - 1; - setSelectedCurrentStation(count); + if (listUpStation.length == 1) { + setListIndex(0); + return; } - }, [selectedCurrentStation, currentStation, allStationData]); + if (listUpStation[listIndex] == undefined) { + const count = listIndex - 1; + setMapMode(false); + setListIndex(count); + } + }, [listIndex, listUpStation, isSearchMode]); useEffect(() => { - if (!carouselRef.current) return; - carouselRef?.current.scrollTo({ - count: selectedCurrentStation - carouselRef.current.getCurrentIndex(), - animated: true, - }); - }, [selectedCurrentStation]); - - //全列車ダイヤリストを作成するuseEffect - const { allTrainDiagram:trainDiagram} = useAllTrainDiagram(); - - const oPSign = () => { - const payload = { - currentStation: - originalStationList && - allStationData.length != 0 && - allStationData[selectedCurrentStation], - navigate: navigate, - goTo: "menu", - useShow: () => SheetManager.show("StationDetailView", { payload }), - onExit: () => SheetManager.hide("StationDetailView"), + if (originalStationList == undefined) return; + if (listUpStation.length == 0) return; + if (listUpStation[listIndex] == undefined) return; + const { lat, lng } = listUpStation[listIndex][0]; + const mapRegion = { + latitude: lat, + longitude: lng, + latitudeDelta: 0.05, + longitudeDelta: 0.05, }; - SheetManager.show("StationDetailView", { payload }); - }; + if (mapMode) { + mapsRef?.current.fitToCoordinates( + listUpStation.map((d) => ({ + latitude: parseFloat(d[0].lat), + longitude: parseFloat(d[0].lng), + })), + { edgePadding: { top: 80, bottom: 120, left: 50, right: 50 } } // Add margin values here + ); + } else { + mapsRef.current.animateToRegion(mapRegion, 1000); + } + }, [listIndex, listUpStation]); - const [dotButton, setDotButton] = useState(false); - - useEffect(() => { - AS.getItem("CarouselSettings/activeDotSettings").then((data) => { - setDotButton(data === "true"); - }); - }, []); - const oLPSign = () => { - LayoutAnimation.configureNext({ - duration: 600, - update: { type: "spring", springDamping: 0.5 }, - }); - AS.setItem( - "CarouselSettings/activeDotSettings", - !dotButton ? "true" : "false" - ); - setDotButton(!dotButton); - }; - const width = Dimensions.get("window").width; return ( - - - - {originalStationList.length != 0 && allStationData.length != 0 && ( - - + ) : ( + <> + // + // JRShikoku RailScope + // + )} + { + if (e.nativeEvent.contentOffset.y < mapHeight - 80) { + if (scrollStartPosition > e.nativeEvent.contentOffset.y) { + goToMap(); + } else { + returnToTop(); + } + } + }} + > + { + LayoutAnimation.configureNext({ + duration: 300, + create: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + update: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + }); + setMapMode(true); + goToMap(); + }} + > + {listUpStation.map(([{ lat, lng, StationNumber }], index) => ( + { - return ( - - - - - - ); + image={require("@/assets/reccha-small.png")} + onPress={() => { + setMapMode(false); + setListIndex(index); + if (mapsRef.current) { + mapsRef.current.animateToRegion( + { + latitude: parseFloat(lat), + longitude: parseFloat(lng), + latitudeDelta: 0.05, + longitudeDelta: 0.05, + }, + 1000 + ); + } + LayoutAnimation.configureNext({ + duration: 300, + create: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + update: { + type: LayoutAnimation.Types.easeInEaseOut, + property: LayoutAnimation.Properties.opacity, + }, + }); + returnToTop(); }} /> - - {originalStationList && - allStationData.map((d, index) => { - const active = index == selectedCurrentStation; - const numberIndex = d[0].StationNumber; - if (dotButton) { - return ( - setSelectedCurrentStation(index)} - currentStation={d} - active={active} - index={numberIndex} - /> - ); - } else { - return ( - setSelectedCurrentStation(index)} - active={active} - index={numberIndex} - /> - ); - } - })} - - + ))} + + {!mapMode && ( + )} - {allStationData.length != 0 && - originalStationList.length != 0 && - allStationData[selectedCurrentStation] && ( - {}} + + {originalStationList.length != 0 && ( + <> + - )} + {listUpStation[listIndex] && ( + {}} + /> + )} + + )} + + + {mapMode && ( + + )} ); } - -const TopMenuButton = () => { - const buttonList = [ - { - backgroundColor: "#F89038", - icon: "train-car", - onPress: () => - Linking.openURL("https://www.jr-shikoku.co.jp/01_trainbus/sp/"), - title: "駅・鉄道情報", - }, - { - backgroundColor: "#EA4752", - icon: "google-spreadsheet", - onPress: () => - Linking.openURL( - "https://www.jr-shikoku.co.jp/01_trainbus/jikoku/sp/#mainprice-box" - ), - title: "運賃表", - }, - { - backgroundColor: "#91C31F", - icon: "clipboard-list-outline", - onPress: () => Linking.openURL("https://www.jr-shikoku.co.jp/e5489/"), - title: "予約", - }, - ]; - return ( - - {buttonList.map((d, index) => ( - - {d.title} - - ))} - - ); -}; - -const JRSTraInfoBox = () => { - const { getTime, delayData, loadingDelayData, setLoadingDelayData } = - useTrainDelayData(); - const styles = { - touch: { - backgroundColor: "#0099CC", - borderRadius: 5, - margin: 10, - borderColor: "black", - borderWidth: 2, - overflow: "hidden", - }, - scroll: { - backgroundColor: "#0099CC", - borderRadius: 5, - maxHeight: 300, - }, - bottom: { - position: "absolute", - top: 250, - alignItems: "center", - width: "100%", - height: 50, - backgroundColor: "#007FCC88", - }, - box: { - padding: 10, - backgroundColor: "white", - borderBottomLeftRadius: 5, - borderBottomRightRadius: 5, - }, - }; - return ( - SheetManager.show("JRSTraInfo")} - style={styles.touch} - > - - - - 列車遅延速報EX - - - - {getTime - ? getTime.toLocaleTimeString("ja-JP").split(":")[0] + - ":" + - getTime.toLocaleTimeString("ja-JP").split(":")[1] - : NaN} - - { - setLoadingDelayData(true); - }} - /> - - - {loadingDelayData ? ( - - - - ) : delayData ? ( - delayData.map((d, index, array) => { - let data = d.split(" "); - return ( - - - {data[0].replace("\n", "")} - - {data[1]} - {data[3]} - - ); - }) - ) : ( - 現在、5分以上の遅れはありません。 - )} - - - - - - 詳細を見る - - - - - ); -}; diff --git a/stateBox/useAllTrainDiagram.js b/stateBox/useAllTrainDiagram.js index 4f8b589..2ed4173 100644 --- a/stateBox/useAllTrainDiagram.js +++ b/stateBox/useAllTrainDiagram.js @@ -4,6 +4,7 @@ import React, { createContext, useContext, useEffect, useState } from "react"; const initialState = { allTrainDiagram: undefined, setAllTrainDiagram: () => {}, + allCustonTrainData: [], }; const AllTrainDiagramContext = createContext(initialState); @@ -12,6 +13,7 @@ export const useAllTrainDiagram = () => useContext(AllTrainDiagramContext); export const AllTrainDiagramProvider = ({ children }) => { const [allTrainDiagram, setAllTrainDiagram] = useState(trainList); + const [allCustonTrainData, setAllCustonTrainData] = useState([]); // カスタム列車データ const [keyList, setKeyList] = useState(); // 第二要素 useEffect( () => allTrainDiagram && setKeyList(Object.keys(allTrainDiagram)), @@ -41,10 +43,26 @@ export const AllTrainDiagramProvider = ({ children }) => { }); }); }, []); + useEffect(() => { + // カスタム列車データの取得 + fetch("https://n8n.haruk.in/webhook/jr-shikoku-position-custom-datalist") + .then((res) => res.json()) + .then((res) => { + setAllCustonTrainData(res[0].data); + }) + .catch(() => { + alert("カスタム列車データの取得に失敗しました。"); + }); + }, []); return ( {children} diff --git a/stateBox/useCurrentTrain.js b/stateBox/useCurrentTrain.js index e49290a..15f6b44 100644 --- a/stateBox/useCurrentTrain.js +++ b/stateBox/useCurrentTrain.js @@ -15,6 +15,7 @@ const initialState = { currentTrainLoading: "loading", setCurrentTrainLoading: () => {}, getCurrentTrain: () => {}, + inject: (i) => {}, }; const CurrentTrainContext = createContext(initialState); diff --git a/stateBox/useFavoriteStation.tsx b/stateBox/useFavoriteStation.tsx index dbc09d2..d4f5893 100644 --- a/stateBox/useFavoriteStation.tsx +++ b/stateBox/useFavoriteStation.tsx @@ -29,10 +29,10 @@ type Props = { }; export const FavoriteStationProvider:FC = ({ children }) => { const [favoriteStation, setFavoriteStation] = useState([]); - const { getStationData } = useStationList(); + const { getStationDataFromName } = useStationList(); const lodAddMigration = () => { const migration = favoriteStation.map((d) => { - return getStationData(d[0].Station_JP); + return getStationDataFromName(d[0].Station_JP); }); setFavoriteStation(migration); }; diff --git a/stateBox/useStationList.tsx b/stateBox/useStationList.tsx index f718064..7073735 100644 --- a/stateBox/useStationList.tsx +++ b/stateBox/useStationList.tsx @@ -5,19 +5,27 @@ import React, { useEffect, FC, } from "react"; -import { lineList, getStationList } from "../lib/getStationList"; +import { + lineList, + getStationList, + lineList_LineWebID, +} from "../lib/getStationList"; type initialStateType = { originalStationList: any[][]; setOriginalStationList: React.Dispatch>; - getStationData: (id: string) => void; + getStationDataFromName: (id: string) => any[]; + getStationDataFromId: (id: string) => any[]; stationList: any[]; + getInjectJavascriptAddress: (StationNumber: string) => string; }; const initialState = { originalStationList: [[]], setOriginalStationList: () => {}, - getStationData: () => {}, + getStationDataFromName: () => [], + getStationDataFromId: () => [], stationList: [], + getInjectJavascriptAddress: (StationNumber: string) => "", }; const StationListContext = createContext(initialState); @@ -33,7 +41,21 @@ export const StationListProvider: FC = ({ children }) => { useEffect(() => { getStationList().then(setOriginalStationList); }, []); - const getStationData: (name: string) => void = (name) => { + const getStationDataFromId: (id: string) => any[] = (id) => { + let returnArray = []; + Object.keys(originalStationList).forEach((key) => { + originalStationList[key].forEach((station) => { + if (station.StationNumber === id) { + returnArray = [ + ...returnArray, + ...getStationDataFromName(station.Station_JP), + ]; + } + }); + }); + return returnArray; + }; + const getStationDataFromName: (name: string) => any[] = (name) => { const returnArray = []; Object.keys(originalStationList).forEach((key) => { originalStationList[key].forEach((station) => { @@ -45,22 +67,55 @@ 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]) - + useEffect(() => { + if (originalStationList.length === 0) return; + const stationList = lineList.map((d) => + originalStationList[d].map((a) => ({ + StationNumber: a.StationNumber, + StationName: a.Station_JP, + })) + ); + setStationList(stationList); + }, [originalStationList]); + + const getInjectJavascriptAddress = (StationNumber: string) => { + const bootStationList = []; + Object.keys(originalStationList).forEach((d) => { + let findNearStations = false; + originalStationList[d].forEach((x) => { + let lineName = lineList_LineWebID[d]; + if (findNearStations) { + if (x.MyStation) { + bootStationList.push({ line: lineName, station: x }); + findNearStations = false; + } + return; + } + if (x.StationNumber == StationNumber) { + if (!x.MyStation) findNearStations = true; + else bootStationList.push({ line: lineName, station: x }); + } + }); + if (StationNumber == "M12") { + bootStationList.push({ + line: "seto", + station: { Station_JP: "児島", MyStation: "0" }, + }); + } + }); + return `MoveDisplayStation('${bootStationList[0].line}_${bootStationList[0].station.MyStation}_${bootStationList[0].station.Station_JP}');document.getElementById("disp").insertAdjacentHTML("afterbegin", "
");`; + }; + return ( {children} diff --git a/stateBox/useTopMenu.tsx b/stateBox/useTopMenu.tsx new file mode 100644 index 0000000..b5b35c0 --- /dev/null +++ b/stateBox/useTopMenu.tsx @@ -0,0 +1,68 @@ +import React, { + createContext, + useContext, + useState, + useEffect, + FC, +} from "react"; +import { lineList, getStationList } from "../lib/getStationList"; + +type initialStateType = { + originalStationList: any[][]; + setOriginalStationList: React.Dispatch>; + getStationData: (id: string) => void; + stationList: any[]; +}; +const initialState = { + originalStationList: [[]], + setOriginalStationList: () => {}, + getStationData: () => {}, + stationList: [], +}; + +const TopMenuContext = createContext(initialState); +type Props = { + children: React.ReactNode; +}; +export const useTopMenu = () => { + return useContext(TopMenuContext); +}; + +export const TopMenuProvider: FC = ({ children }) => { + const [originalStationList, setOriginalStationList] = useState([]); + useEffect(() => { + getStationList().then(setOriginalStationList); + }, []); + const getStationData: (name: string) => void = (name) => { + const returnArray = []; + Object.keys(originalStationList).forEach((key) => { + originalStationList[key].forEach((station) => { + if (station.Station_JP === name) { + if (!!station.jslodApi) returnArray.push(station); + } + }); + }); + 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/useTrainDelayData.js b/stateBox/useTrainDelayData.js index 28e977f..56087e3 100644 --- a/stateBox/useTrainDelayData.js +++ b/stateBox/useTrainDelayData.js @@ -3,7 +3,7 @@ const initialState = { getTime: new Date(), setGetTime: () => {}, loadingDelayData: true, - setLoadingDelayData: () => {}, + setLoadingDelayData: (loading) => {}, delayData: undefined, setDelayData: () => {}, }; diff --git a/stateBox/useTrainMenu.js b/stateBox/useTrainMenu.js index fca06d7..f169fc0 100644 --- a/stateBox/useTrainMenu.js +++ b/stateBox/useTrainMenu.js @@ -5,6 +5,8 @@ import { ASCore } from "../storageControl"; import { getStationList2 } from "../lib/getStationList"; import { injectJavascriptData } from "../lib/webViewInjectjavascript"; +import { useNotification } from "../stateBox/useNotifications"; + const initialState = { selectedLine: undefined, setSelectedLine: () => {}, @@ -16,6 +18,8 @@ const initialState = { setMapSwitch: () => {}, stationMenu: undefined, setStationMenu: () => {}, + uiSetting: undefined, + setUiSetting: () => {}, LoadError: false, setLoadError: () => {}, trainInfo: { @@ -26,6 +30,8 @@ const initialState = { setTrainInfo: () => {}, trainMenu: "true", setTrainMenu: () => {}, + updatePermission : false, + setUpdatePermission : () => {}, injectJavascript: "", }; @@ -36,6 +42,7 @@ export const useTrainMenu = () => { }; export const TrainMenuProvider = ({ children }) => { + const { expoPushToken } = useNotification(); const [selectedLine, setSelectedLine] = useState(undefined); const [mapsStationData, setMapsStationData] = useState(undefined); useEffect(() => { @@ -48,6 +55,19 @@ export const TrainMenuProvider = ({ children }) => { const [stationMenu, setStationMenu] = useState(undefined); const [LoadError, setLoadError] = useState(false); + //更新権限所有確認 + const [updatePermission, setUpdatePermission] = useState(false); + useEffect(()=>{ + fetch("https://n8n.haruk.in/webhook/data-edit-permission?token=" + expoPushToken).then((res)=>res.json()) + .then((res)=>{ + if(res.data == true){ + setUpdatePermission(true); + }else{ + setUpdatePermission(false); + } + }) + }, [expoPushToken]) + //列車情報表示関連 const [trainInfo, setTrainInfo] = useState({ trainNum: undefined, @@ -58,12 +78,16 @@ export const TrainMenuProvider = ({ children }) => { //駅情報画面用 const [trainMenu, setTrainMenu] = useState("true"); + //GUIデザインベース + const [uiSetting, setUiSetting] = useState("tokyo"); + //地図表示テキスト const injectJavascript = injectJavascriptData( mapSwitch, iconSetting, stationMenu, - trainMenu + trainMenu, + uiSetting ); useEffect(() => { @@ -75,6 +99,8 @@ export const TrainMenuProvider = ({ children }) => { ASCore({ k: "stationSwitch", s: setStationMenu, d: "true", u: true }); //列車メニュースイッチ ASCore({ k: "trainSwitch", s: setTrainMenu, d: "true", u: true }); + //GUIデザインベーススイッチ + ASCore({ k: "uiSetting", s: setUiSetting, d: "tokyo", u: true }); }, []); return ( @@ -90,12 +116,16 @@ export const TrainMenuProvider = ({ children }) => { setMapSwitch, stationMenu, setStationMenu, + uiSetting, + setUiSetting, LoadError, setLoadError, trainInfo, setTrainInfo, trainMenu, setTrainMenu, + updatePermission, + setUpdatePermission, injectJavascript, }} > diff --git a/stateBox/useUserPosition.tsx b/stateBox/useUserPosition.tsx new file mode 100644 index 0000000..2b4cb83 --- /dev/null +++ b/stateBox/useUserPosition.tsx @@ -0,0 +1,76 @@ +import React, { + createContext, + useContext, + useState, + useEffect, + FC, +} from "react"; +import { + LocationObject, + requestForegroundPermissionsAsync, + getCurrentPositionAsync, +} from "expo-location"; +import { Platform } from "react-native"; +import useInterval from "@/lib/useInterval"; + +type initialStateType = { + position: LocationObject | undefined; + getCurrentPosition: () => void; + locationStatus: boolean | null; + getLocationPermission: () => void; +}; +const initialState = { + position: undefined, + getCurrentPosition: () => {}, + locationStatus: null, + getLocationPermission: () => {}, +}; + +const UserPositionContext = createContext(initialState); +type Props = { + children: React.ReactNode; +}; +export const useUserPosition = () => { + return useContext(UserPositionContext); +}; + +export const UserPositionProvider: FC = ({ children }) => { + //位置情報 + const [locationStatus, setLocationStatus] = useState(null); + const [position, setPosition] = useState( + undefined + ); + + const getLocationPermission = async () => { + return requestForegroundPermissionsAsync().then((data) => { + setLocationStatus( + Platform.OS == "ios" + ? data.status == "granted" + : data.android.accuracy == "fine" + ); + }); + }; + const getCurrentPosition = () => { + if (!locationStatus) return () => {}; + getCurrentPositionAsync({}).then((location) => setPosition(location)); + }; + useEffect(() => { + if (Platform.OS == "web") return; + getLocationPermission(); + }, []); + useEffect(getCurrentPosition, [locationStatus]); + useInterval(getCurrentPosition, 5000); + + return ( + + {children} + + ); +};