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/MenuPage.js b/MenuPage.js index dc198b5..de4f199 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,15 @@ 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"; 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 +48,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 +87,7 @@ export function MenuPage() { }); return unsubscribe; - }, [navigation]); + }, [navigation, mapHeight]); return ( } + children={() => ( + + )} /> { 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 +85,74 @@ 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 || ( + + )} + {!currentStation[0].StationMap || ( + + )} + {!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/Menu/Carousel/CarouselBox.tsx b/components/Menu/Carousel/CarouselBox.tsx new file mode 100644 index 0000000..950acec --- /dev/null +++ b/components/Menu/Carousel/CarouselBox.tsx @@ -0,0 +1,129 @@ +import Sign from "@/components/駅名表/Sign"; +import React, { useEffect, useRef, useState } from "react"; +import { AS } from "@/storageControl"; +import { useWindowDimensions, View, LayoutAnimation } 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, +}) => { + const carouselRef = useRef(null); + const { height, width } = useWindowDimensions(); + const [dotButton, setDotButton] = useState(false); + 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"); + }); + }, []); + useEffect(() => { + if (!carouselRef.current) return; + carouselRef?.current.scrollTo({ + count: listIndex - carouselRef.current.getCurrentIndex(), + animated: true, + }); + }, [listIndex]); + const RenderItem = ({ item, index }) => { + return ( + + + + + + ); + }; + return ( + + + + {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..11f13af --- /dev/null +++ b/components/Menu/Carousel/CarouselTypeChanger.tsx @@ -0,0 +1,142 @@ +import { AS } from "@/storageControl"; +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; +import React, { useEffect, useRef } from "react"; +import { View, TouchableOpacity, Text, LayoutAnimation } from "react-native"; +import Ionicons from "react-native-vector-icons/Ionicons"; + +export const CarouselTypeChanger = ({ + locationStatus, + position, + mapsRef, + stationListMode, + setStationListMode, + setSelectedCurrentStation, + mapMode, + setMapMode, +}) => { + const tabBarHeight = useBottomTabBarHeight(); + 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/JRSTraInfoBox.tsx b/components/Menu/JRSTraInfoBox.tsx new file mode 100644 index 0000000..b740f31 --- /dev/null +++ b/components/Menu/JRSTraInfoBox.tsx @@ -0,0 +1,111 @@ +import React from "react"; +import { + View, + Text, + TouchableOpacity, + ScrollView, + StyleProp, + ViewStyle, +} from "react-native"; +import { Ionicons } from "@expo/vector-icons"; +import { SheetManager } from "react-native-actions-sheet"; +import LottieView from "lottie-react-native"; +import { useTrainDelayData } from "@/stateBox/useTrainDelayData"; +import dayjs from "dayjs"; +export const JRSTraInfoBox = () => { + 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/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/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/発車時刻表/EachData.tsx b/components/発車時刻表/EachData.tsx index 84c47e4..664fd8e 100644 --- a/components/発車時刻表/EachData.tsx +++ b/components/発車時刻表/EachData.tsx @@ -16,6 +16,8 @@ import { TrainPosition } from "./LED_inside_Component/TrainPosition"; import { TrainPositionDataPush } from "./LED_inside_Component/TrainPositionDataPush"; import { TrainPositionDataDelete } from "./LED_inside_Component/TrainPositionDataDelete"; import { useStationList } from "../../stateBox/useStationList"; +import useInterval from "@/lib/useInterval"; +import dayjs from "dayjs"; type Props = { d: { @@ -117,26 +119,46 @@ 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..274b9c8 100644 --- a/components/発車時刻表/LED_Vision_Component/Header.js +++ b/components/発車時刻表/LED_Vision_Component/Header.js @@ -3,8 +3,8 @@ import { useCurrentTrain } from "../../../stateBox/useCurrentTrain"; import LottieView from "lottie-react-native"; import { Ionicons } from "@expo/vector-icons"; -export const Header = ({ getCurrentTrain }) => { - const { currentTrainLoading, setCurrentTrainLoading } = useCurrentTrain(); +export const Header = () => { + const { currentTrainLoading, setCurrentTrainLoading,getCurrentTrain } = useCurrentTrain(); return ( { 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/menu.js b/menu.js index 42a8592..73a2ade 100644 --- a/menu.js +++ b/menu.js @@ -1,71 +1,101 @@ import React, { useRef, useState, useEffect } from "react"; -import Carousel from "react-native-reanimated-carousel"; import { Platform, View, ScrollView, - Linking, - Text, - TouchableOpacity, + useWindowDimensions, 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 { 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(); +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 +108,98 @@ 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([]); useEffect(() => { - setAllStationData( - [currentStation, ...favoriteStation].filter((d) => d != undefined) - ); - }, [currentStation, favoriteStation]); + if (stationListMode == "position") { + setListUpStation(nearPositionStation.filter((d) => d != undefined)); + } else { + setListUpStation(favoriteStation.filter((d) => d != undefined)); + } + }, [nearPositionStation, favoriteStation, stationListMode]); 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[listIndex] == undefined) { + const count = listIndex - 1; + setMapMode(false); + setListIndex(count); } - }, [selectedCurrentStation, currentStation, allStationData]); + }, [listIndex, nearPositionStation, listUpStation]); 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) return; + mapsRef.current.animateToRegion(mapRegion, 1000); + }, [listIndex, nearPositionStation, listUpStation, mapsRef]); - 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 && ( - - { + console.log(e.nativeEvent.velocity); + 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] && ( - {}} + + {listUpStation.length != 0 && 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/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..97eac6a 100644 --- a/stateBox/useStationList.tsx +++ b/stateBox/useStationList.tsx @@ -10,13 +10,15 @@ import { lineList, getStationList } from "../lib/getStationList"; type initialStateType = { originalStationList: any[][]; setOriginalStationList: React.Dispatch>; - getStationData: (id: string) => void; + getStationDataFromName: (id: string) => any[]; + getStationDataFromId: (id: string) => any[]; stationList: any[]; }; const initialState = { originalStationList: [[]], setOriginalStationList: () => {}, - getStationData: () => {}, + getStationDataFromName: () => [], + getStationDataFromId: () => [], stationList: [], }; @@ -33,7 +35,18 @@ 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) => { @@ -48,19 +61,18 @@ export const StationListProvider: FC = ({ children }) => { 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) + setStationList(stationList); },[originalStationList]) 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/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} + + ); +};