Files
jrshikoku/lib/menuStationSelectors.ts
harukin-expo-dev-env f210ca4d77 feat: Add loading states and map functionality to the menu
- 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.
2026-09-02 21:24:41 +09:00

129 lines
3.7 KiB
TypeScript

import type { OriginalStationList, StationProps } from "./CommonTypes";
import type { StationSource } from "../types";
export type MenuCoordinates = {
latitude: number;
longitude: number;
};
type NearbyStationGroupsArgs = {
coordinates: MenuCoordinates;
originalStationList: OriginalStationList;
lineNames: readonly string[];
touristSpotKey?: string;
};
type MenuStationGroupsArgs = {
stationSource: StationSource;
originalStationList: OriginalStationList;
nearbyPositionStations: StationProps[][];
favoriteStations: StationProps[][];
findStationsByName: (query: string) => StationProps[];
lineWebIds: Readonly<Record<string, string>>;
stationIds: Readonly<Record<string, string>>;
};
export const MENU_NEARBY_SEARCH_RADIUS = 0.055;
const distanceFrom = (
station: StationProps,
coordinates: MenuCoordinates,
): number => {
const latitudeDistance = Math.abs(station.lat - coordinates.latitude);
const longitudeDistance = Math.abs(station.lng - coordinates.longitude);
return Math.sqrt(
latitudeDistance * latitudeDistance +
longitudeDistance * longitudeDistance,
);
};
const sortNearbyStations = (
stations: StationProps[],
coordinates: MenuCoordinates,
): StationProps[] => {
return stations
.map((station) => ({
station,
distance: distanceFrom(station, coordinates),
}))
.filter(({ distance }) => distance < MENU_NEARBY_SEARCH_RADIUS)
.sort((a, b) => a.distance - b.distance)
.map(({ station }) => station);
};
export const getNearbyStationGroups = ({
coordinates,
originalStationList,
lineNames,
touristSpotKey = "観光スポット",
}: NearbyStationGroupsArgs): StationProps[][] => {
const nearbyStations = lineNames.flatMap((lineName) =>
sortNearbyStations(originalStationList[lineName] ?? [], coordinates),
);
nearbyStations.push(
...sortNearbyStations(
originalStationList[touristSpotKey] ?? [],
coordinates,
),
);
const groupsByStationName = new Map<string, StationProps[]>();
for (const station of nearbyStations) {
const group = groupsByStationName.get(station.Station_JP);
if (group) {
group.push(station);
} else {
groupsByStationName.set(station.Station_JP, [station]);
}
}
return Array.from(groupsByStationName.values()).sort(
(a, b) => distanceFrom(a[0], coordinates) - distanceFrom(b[0], coordinates),
);
};
export const getMenuStationGroups = ({
stationSource,
originalStationList,
nearbyPositionStations,
favoriteStations,
findStationsByName,
lineWebIds,
stationIds,
}: MenuStationGroupsArgs): StationProps[][] => {
if (stationSource.type === "position") {
return nearbyPositionStations.filter((station) => station !== undefined && station !== null);
}
if (stationSource.type === "favorite") {
return favoriteStations.filter((station) => station !== undefined && station !== null);
}
const { query, lineId } = stationSource;
if (!query && !lineId) return [];
if (!query) {
const groups: StationProps[][] = [];
for (const lineName of Object.keys(lineWebIds)) {
const lineIdForName = stationIds[lineWebIds[lineName]];
if (lineId !== lineIdForName) continue;
for (const station of originalStationList[lineName] ?? []) {
if (station.StationNumber) groups.push([station]);
}
}
return groups;
}
const groupsByStationName = new Map<string, StationProps[]>();
for (const station of findStationsByName(query)) {
const group = groupsByStationName.get(station.Station_JP);
if (group) {
group.push(station);
} else {
groupsByStationName.set(station.Station_JP, [station]);
}
}
return Array.from(groupsByStationName.values());
};