437 lines
14 KiB
JavaScript
437 lines
14 KiB
JavaScript
import React, { useRef, useState, useEffect, useLayoutEffect } from "react";
|
|
import {
|
|
Platform,
|
|
View,
|
|
ScrollView,
|
|
useWindowDimensions,
|
|
LayoutAnimation,
|
|
Text,
|
|
TouchableOpacity,
|
|
} from "react-native";
|
|
import Constants from "expo-constants";
|
|
import {
|
|
configureReanimatedLogger,
|
|
ReanimatedLogLevel,
|
|
} from "react-native-reanimated";
|
|
import StatusbarDetect from "./StatusbarDetect";
|
|
|
|
import LED_vision from "./components/発車時刻表/LED_vidion";
|
|
import { TitleBar } from "./components/Menu/TitleBar";
|
|
import { FixedContentBottom } from "./components/Menu/FixedContentBottom";
|
|
|
|
import { lineList, stationIDPair } from "./lib/getStationList";
|
|
import { useFavoriteStation } from "./stateBox/useFavoriteStation";
|
|
import { useNavigation } from "@react-navigation/native";
|
|
import { useStationList } from "./stateBox/useStationList";
|
|
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 { lineList_LineWebID } from "./lib/getStationList";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
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, getStationDataFromNameBase } = useStationList();
|
|
const { height, width } = useWindowDimensions();
|
|
const { bottom, left, right, top } = useSafeAreaInsets();
|
|
const tabBarHeight = useBottomTabBarHeight();
|
|
const [stationListMode, setStationListMode] = useState(
|
|
/*<"position"|"favorite">*/ "position"
|
|
);
|
|
useEffect(() => {
|
|
AS.getItem("stationListMode")
|
|
.then((res) => setStationListMode(res))
|
|
.catch((e) => {
|
|
// AS.setItem("stationListMode", "position");
|
|
});
|
|
}, []);
|
|
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.055; //検索範囲
|
|
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);
|
|
};
|
|
let NearStation = selectLine.filter(
|
|
(d) =>
|
|
_calcDistance(d, {
|
|
lat: location.coords.latitude,
|
|
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 _stList = lineList
|
|
.map((d) => findStationEachLine(originalStationList[d]))
|
|
.filter((d) => d.length > 0)
|
|
.reduce((pre, current) => {
|
|
pre.push(...current);
|
|
return pre;
|
|
}, []);
|
|
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);
|
|
}
|
|
};
|
|
|
|
const [nearPositionStation, setNearPositionStation] = useState([]); //第三要素
|
|
|
|
const [listIndex, setListIndex] = useState(0);
|
|
|
|
const [listUpStation, setListUpStation] = useState([]);
|
|
const [isSearchMode, setisSearchMode] = useState(false);
|
|
const [input, setInput] = React.useState("");
|
|
useLayoutEffect(() => {
|
|
if (!!isSearchMode) {
|
|
const returnData = [];
|
|
if (!input || input == "") {
|
|
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]);
|
|
});
|
|
});
|
|
} else {
|
|
const hoge = getStationDataFromNameBase(input);
|
|
hoge.forEach((d, index, array) => {
|
|
const stationName = d.Station_JP;
|
|
if (returnData.findIndex((d1) => d1[0].Station_JP == stationName) != -1)
|
|
return;
|
|
returnData.push(array.filter((d2) => d2.Station_JP == stationName));
|
|
});
|
|
}
|
|
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(() => {
|
|
if (listUpStation.length == 0) {
|
|
setListIndex(0);
|
|
return;
|
|
}
|
|
if (listUpStation.length == 1) {
|
|
setListIndex(0);
|
|
return;
|
|
}
|
|
if (listUpStation[listIndex] == undefined) {
|
|
const count = listIndex - 1;
|
|
setMapMode(false);
|
|
setListIndex(count);
|
|
}
|
|
}, [listIndex, listUpStation, isSearchMode]);
|
|
useEffect(() => {
|
|
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,
|
|
};
|
|
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]);
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
height: "100%",
|
|
backgroundColor: "white",
|
|
paddingTop: Platform.OS == "ios" ? Constants.statusBarHeight : 0,
|
|
}}
|
|
>
|
|
<StatusbarDetect />
|
|
{!mapMode ? (
|
|
<TitleBar />
|
|
) : (
|
|
<></>
|
|
// <Text
|
|
// style={{
|
|
// fontSize: 30,
|
|
// color: "#0099CC",
|
|
// fontWeight: "bold",
|
|
// position: "absolute",
|
|
// top: 0,
|
|
// zIndex: 1000,
|
|
// fontStyle: "italic",
|
|
// }}
|
|
// >
|
|
// JRShikoku RailScope
|
|
// </Text>
|
|
)}
|
|
<ScrollView
|
|
ref={scrollRef}
|
|
snapToStart={false}
|
|
snapToEnd={false}
|
|
decelerationRate={"normal"}
|
|
snapToOffsets={[mapHeight - 80]}
|
|
// contentContainerStyle={{
|
|
// position: "relative",
|
|
// }}
|
|
onScrollBeginDrag={onScrollBeginDrag}
|
|
onScrollEndDrag={(e) => {
|
|
if (e.nativeEvent.contentOffset.y < mapHeight - 80) {
|
|
if (scrollStartPosition > e.nativeEvent.contentOffset.y) {
|
|
goToMap();
|
|
} else {
|
|
returnToTop();
|
|
}
|
|
}
|
|
}}
|
|
>
|
|
<MapView
|
|
ref={mapsRef}
|
|
style={{ width: "100%", height: mapMode ? MapFullHeight : mapHeight }}
|
|
showsUserLocation={true}
|
|
loadingEnabled={true}
|
|
showsMyLocationButton={false}
|
|
moveOnMarkerPress={false}
|
|
showsCompass={false}
|
|
//provider={PROVIDER_GOOGLE}
|
|
initialRegion={{
|
|
latitude: 33.774519,
|
|
longitude: 133.533306,
|
|
latitudeDelta: 1.8, //小さくなるほどズーム
|
|
longitudeDelta: 1.8,
|
|
}}
|
|
onTouchStart={() => {
|
|
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) => (
|
|
<Marker
|
|
key={index + StationNumber}
|
|
coordinate={{
|
|
latitude: parseFloat(lat),
|
|
longitude: parseFloat(lng),
|
|
}}
|
|
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();
|
|
}}
|
|
/>
|
|
))}
|
|
</MapView>
|
|
{!mapMode && (
|
|
<CarouselTypeChanger
|
|
{...{
|
|
locationStatus,
|
|
position,
|
|
stationListMode,
|
|
setStationListMode,
|
|
setSelectedCurrentStation: setListIndex,
|
|
mapMode,
|
|
setMapMode,
|
|
isSearchMode,
|
|
setisSearchMode,
|
|
input,
|
|
setInput,
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
{originalStationList.length != 0 && (
|
|
<>
|
|
<CarouselBox
|
|
{...{
|
|
originalStationList,
|
|
listUpStation,
|
|
nearPositionStation,
|
|
setListIndex,
|
|
listIndex,
|
|
navigate,
|
|
stationListMode,
|
|
isSearchMode,
|
|
}}
|
|
/>
|
|
{listUpStation[listIndex] && (
|
|
<LED_vision
|
|
station={listUpStation[listIndex]}
|
|
navigate={navigate}
|
|
openStationACFromEachTrainInfo={() => {}}
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
|
|
<TopMenuButton />
|
|
<JRSTraInfoBox />
|
|
<FixedContentBottom navigate={navigate} />
|
|
</ScrollView>
|
|
{mapMode && (
|
|
<CarouselTypeChanger
|
|
{...{
|
|
locationStatus,
|
|
position,
|
|
stationListMode,
|
|
setStationListMode,
|
|
setSelectedCurrentStation: setListIndex,
|
|
mapMode,
|
|
setMapMode,
|
|
isSearchMode,
|
|
setisSearchMode,
|
|
input,
|
|
setInput,
|
|
}}
|
|
/>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|