- Implemented StationListLoadingState component for loading messages. - Created MenuLED component to display LED information with loading animations. - Developed MenuMap component for interactive map features, including user location markers and station markers. - Introduced LEDFrame component for consistent styling of LED displays. - Enhanced LED_vision component to support embedded rendering and content readiness. - Added menuStationSelectors for managing nearby and favorite station groups. - Updated Menu component to integrate new loading states and map functionalities. - Added tests for menuStationSelectors to ensure correct grouping and selection logic.
213 lines
5.7 KiB
TypeScript
213 lines
5.7 KiB
TypeScript
import React, { useEffect, useRef } from "react";
|
|
import { Image, LayoutAnimation, Platform, View } from "react-native";
|
|
import type { LocationObject } from "expo-location";
|
|
import MapView, { Marker } from "react-native-maps";
|
|
import type { StationProps } from "@/lib/CommonTypes";
|
|
|
|
type MenuMapProps = {
|
|
mapHeight: number;
|
|
mapFullHeight: number;
|
|
mapMode: boolean;
|
|
backgroundColor: string;
|
|
isDark: boolean;
|
|
isHeavyContentReady: boolean;
|
|
listIndex: number;
|
|
listUpStation: StationProps[][];
|
|
position: LocationObject | undefined;
|
|
mapPaddingTop: number;
|
|
mapPaddingBottom: number;
|
|
setMapMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
setListIndex: (index: number) => void;
|
|
onGoToMap: () => void;
|
|
onReturnToTop: () => void;
|
|
};
|
|
|
|
const MAP_PIN_SIZE = Platform.OS === "android" ? 32 : 36;
|
|
const ANDROID_USER_LOCATION_MARKER_SIZE = 18;
|
|
const ANDROID_USER_LOCATION_INNER_SIZE = 8;
|
|
|
|
const configureMapLayoutAnimation = () => {
|
|
LayoutAnimation.configureNext({
|
|
duration: 300,
|
|
create: {
|
|
type: LayoutAnimation.Types.easeInEaseOut,
|
|
property: LayoutAnimation.Properties.opacity,
|
|
},
|
|
update: {
|
|
type: LayoutAnimation.Types.easeInEaseOut,
|
|
property: LayoutAnimation.Properties.opacity,
|
|
},
|
|
});
|
|
};
|
|
|
|
const MenuMapComponent = ({
|
|
mapHeight,
|
|
mapFullHeight,
|
|
mapMode,
|
|
backgroundColor,
|
|
isDark,
|
|
isHeavyContentReady,
|
|
listIndex,
|
|
listUpStation,
|
|
position,
|
|
mapPaddingTop,
|
|
mapPaddingBottom,
|
|
setMapMode,
|
|
setListIndex,
|
|
onGoToMap,
|
|
onReturnToTop,
|
|
}: MenuMapProps) => {
|
|
const mapsRef = useRef(null);
|
|
|
|
useEffect(() => {
|
|
if (!isHeavyContentReady) return;
|
|
if (!mapsRef.current) 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((stationGroup) => ({
|
|
latitude: stationGroup[0].lat,
|
|
longitude: stationGroup[0].lng,
|
|
})),
|
|
{
|
|
edgePadding: {
|
|
top: mapPaddingTop,
|
|
bottom: mapPaddingBottom,
|
|
left: 50,
|
|
right: 50,
|
|
},
|
|
},
|
|
);
|
|
} else {
|
|
mapsRef.current.animateToRegion(mapRegion, 1000);
|
|
}
|
|
// mapMode と padding の変更時には、既存挙動どおり個別追従しない
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isHeavyContentReady, listIndex, listUpStation]);
|
|
|
|
const handleMapTouch = () => {
|
|
configureMapLayoutAnimation();
|
|
setMapMode(true);
|
|
onGoToMap();
|
|
};
|
|
|
|
const handleMarkerPress = (
|
|
station: { lat: number; lng: number },
|
|
index: number,
|
|
) => {
|
|
setMapMode(false);
|
|
setListIndex(index);
|
|
if (mapsRef.current) {
|
|
mapsRef.current.animateToRegion(
|
|
{
|
|
latitude: station.lat,
|
|
longitude: station.lng,
|
|
latitudeDelta: 0.05,
|
|
longitudeDelta: 0.05,
|
|
},
|
|
1000,
|
|
);
|
|
}
|
|
configureMapLayoutAnimation();
|
|
onReturnToTop();
|
|
};
|
|
|
|
if (!isHeavyContentReady) {
|
|
return (
|
|
<View
|
|
style={{
|
|
width: "100%",
|
|
height: mapMode ? mapFullHeight : mapHeight,
|
|
backgroundColor,
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<MapView
|
|
ref={mapsRef}
|
|
style={{ width: "100%", height: mapMode ? mapFullHeight : mapHeight }}
|
|
showsUserLocation={Platform.OS !== "android"}
|
|
loadingEnabled={true}
|
|
showsMyLocationButton={false}
|
|
moveOnMarkerPress={false}
|
|
showsCompass={false}
|
|
userInterfaceStyle={isDark ? "dark" : "light"}
|
|
//provider={PROVIDER_GOOGLE}
|
|
initialRegion={{
|
|
latitude: 33.774519,
|
|
longitude: 133.533306,
|
|
latitudeDelta: 1.8,
|
|
longitudeDelta: 1.8,
|
|
}}
|
|
onTouchStart={handleMapTouch}
|
|
>
|
|
{Platform.OS === "android" && position ? (
|
|
<Marker
|
|
key="android-user-location"
|
|
coordinate={{
|
|
latitude: position.coords.latitude,
|
|
longitude: position.coords.longitude,
|
|
}}
|
|
anchor={{ x: 0.5, y: 0.5 }}
|
|
tracksViewChanges={false}
|
|
zIndex={999}
|
|
>
|
|
<View
|
|
style={{
|
|
width: ANDROID_USER_LOCATION_MARKER_SIZE,
|
|
height: ANDROID_USER_LOCATION_MARKER_SIZE,
|
|
borderRadius: ANDROID_USER_LOCATION_MARKER_SIZE / 2,
|
|
backgroundColor: "rgba(10, 132, 255, 0.24)",
|
|
borderWidth: 1,
|
|
borderColor: "rgba(10, 132, 255, 0.42)",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<View
|
|
style={{
|
|
width: ANDROID_USER_LOCATION_INNER_SIZE,
|
|
height: ANDROID_USER_LOCATION_INNER_SIZE,
|
|
borderRadius: ANDROID_USER_LOCATION_INNER_SIZE / 2,
|
|
backgroundColor: "#0A84FF",
|
|
borderWidth: 1,
|
|
borderColor: "#ffffff",
|
|
}}
|
|
/>
|
|
</View>
|
|
</Marker>
|
|
) : null}
|
|
{listUpStation.map(([{ lat, lng, StationNumber }], index) => (
|
|
<Marker
|
|
key={index + StationNumber}
|
|
coordinate={{ latitude: lat, longitude: lng }}
|
|
anchor={{ x: 0.5, y: 1 }}
|
|
tracksViewChanges={false}
|
|
onPress={() => handleMarkerPress({ lat, lng }, index)}
|
|
>
|
|
<Image
|
|
source={require("@/assets/reccha-small.png")}
|
|
style={{ width: MAP_PIN_SIZE, height: MAP_PIN_SIZE }}
|
|
resizeMode="contain"
|
|
/>
|
|
</Marker>
|
|
))}
|
|
</MapView>
|
|
);
|
|
};
|
|
|
|
export const MenuMap = React.memo(MenuMapComponent);
|
|
MenuMap.displayName = "MenuMap";
|