From 20317863fb734454d9b5b501a3f14fb076706580 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Wed, 25 Feb 2026 21:47:05 +0900 Subject: [PATCH 1/6] =?UTF-8?q?routeTypes=E3=82=AF=E3=82=A8=E3=83=AA?= =?UTF-8?q?=E3=81=B8=E3=81=AE=E7=A7=BB=E8=A1=8C=E3=81=A8=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=80=E3=83=ABUI=E3=83=96=E3=83=A9=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=E3=82=A2=E3=83=83=E3=83=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 経路検索をroutesからrouteTypesに変更し、駅データはlineGroupStationsでバッチ取得 - RouteInfoModalのデザイン改善: セクション分け、停車条件アイコン追加、フッター固定 - 各線の種別を会社単位でグルーピングし、trainTypeがnullの場合のフォールバック改善 - 路線カラードットがflex-wrap対応で多路線でも崩れないように修正 Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 18 +- components/RouteInfoModal.tsx | 282 +++++++++++++++++++++-------- graphql/queries.ts | 20 +- hooks/useFetchRoutes.ts | 57 +++++- hooks/useFetchStationsByGroupId.ts | 7 +- types/stationapi.ts | 9 +- utils/generateSWRKey.ts | 5 +- 7 files changed, 288 insertions(+), 110 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index ca1b594..805d523 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -10,7 +10,6 @@ import { ChevronRightIcon } from "@/components/icons/ChevronRight"; import { MenuIcon } from "@/components/icons/Menu"; import { MenuModal } from "@/components/MenuModal"; import { RouteInfoModal } from "@/components/RouteInfoModal"; -import type { Line, Route, Station } from "@/types/stationapi"; import { useFetchLineById } from "@/hooks/useFetchLineById"; import { useFetchLinesByName } from "@/hooks/useFetchLinesByName"; import { useFetchRoutes } from "@/hooks/useFetchRoutes"; @@ -18,6 +17,7 @@ import { useFetchStationsByGroupId } from "@/hooks/useFetchStationsByGroupId"; import { useFetchStationsByLineId } from "@/hooks/useFetchStationsByLineId"; import { useFetchStationsByName } from "@/hooks/useFetchStationsByName"; import { useParams } from "@/hooks/useParams"; +import type { Line, Route, Station } from "@/types/stationapi"; import { removeBrackets } from "@/utils/removeBracket"; type Inputs = { @@ -129,11 +129,11 @@ const SelectStationListBox = ({ textValue={value} >

{sta.name}

-
+
{sta.lines.map((line) => (
))} @@ -227,10 +227,10 @@ const LineListBox = ({ textValue={value} >

{l.nameShort}

-
+
@@ -317,7 +317,7 @@ const RoutesListBox = ({ )}

-
+
{Array.from( new Map( route.stops.map((stop) => [ @@ -336,7 +336,7 @@ const RoutesListBox = ({ .map((stop) => (
))} @@ -406,7 +406,7 @@ const StationListBox = ({ textValue={sta.id.toString()} >

{sta.name ?? ""}

-
+
{sta.lines .filter((stop, idx, arr) => { const lineColors = arr.map((l) => l?.color); @@ -418,7 +418,7 @@ const StationListBox = ({ .map((l) => (
))} diff --git a/components/RouteInfoModal.tsx b/components/RouteInfoModal.tsx index 61eb5ca..1402c85 100644 --- a/components/RouteInfoModal.tsx +++ b/components/RouteInfoModal.tsx @@ -7,7 +7,7 @@ import { ModalHeader, } from "@nextui-org/react"; import { useMemo } from "react"; -import { StopCondition, type Route, type TrainType } from "@/types/stationapi"; +import { type Route, StopCondition, type TrainType } from "@/types/stationapi"; import dropEitherJunctionStation from "@/utils/dropJunctionStation"; import { removeBrackets } from "@/utils/removeBracket"; import { CloseSmallRoundedIcon } from "./icons/CloseSmallRounded"; @@ -24,13 +24,91 @@ type Props = { onLaunchApp: () => void; }; +const StopIcon = ({ color }: { color: string }) => ( + + + +); + +const PassIcon = ({ color }: { color: string }) => ( + + + +); + +const PartialIcon = ({ color }: { color: string }) => ( + + + +); + +const WeekdayIcon = ({ color }: { color: string }) => ( + + + + 平 + + +); + +const HolidayIcon = ({ color }: { color: string }) => ( + + + + 休 + + +); + const STOP_CONDITIONS = [ - { id: StopCondition.All, text: "停車", color: "#000000" }, - { id: StopCondition.Not, text: "通過", color: "#99a1af" }, - { id: StopCondition.Partial, text: "一部通過", color: "#fcc800" }, - { id: StopCondition.PartialStop, text: "一部停車", color: "#fcc800" }, - { id: StopCondition.Weekday, text: "平日停車", color: "#51a2ff" }, - { id: StopCondition.Holiday, text: "休日停車", color: "#ff6467" }, + { id: StopCondition.All, text: "停車", color: "#000000", Icon: StopIcon }, + { id: StopCondition.Not, text: "通過", color: "#99a1af", Icon: PassIcon }, + { + id: StopCondition.Partial, + text: "一部通過", + color: "#fcc800", + Icon: PartialIcon, + }, + { + id: StopCondition.PartialStop, + text: "一部停車", + color: "#fcc800", + Icon: PartialIcon, + }, + { + id: StopCondition.Weekday, + text: "平日停車", + color: "#51a2ff", + Icon: WeekdayIcon, + }, + { + id: StopCondition.Holiday, + text: "休日停車", + color: "#ff6467", + Icon: HolidayIcon, + }, ] as const; export const RouteInfoModal = ({ @@ -40,95 +118,145 @@ export const RouteInfoModal = ({ route, onLaunchApp, }: Props) => { - const uniqueLineStops = useMemo( - () => - Array.from( - new Map(route?.stops.map((stop) => [stop.line?.id, stop])).values(), - ), - [route?.stops], - ); + const linesByCompany = useMemo(() => { + const uniqueLines = Array.from( + new Map(route?.stops.map((stop) => [stop.line?.id, stop])).values(), + ); + const companyMap = new Map< + number, + { lineNames: string[]; stop: (typeof uniqueLines)[number] } + >(); + for (const stop of uniqueLines) { + const companyId = stop.line?.company?.id; + if (companyId == null) continue; + const existing = companyMap.get(companyId); + if (existing) { + const name = stop.line?.nameShort; + if (name && !existing.lineNames.includes(name)) { + existing.lineNames.push(name); + } + if (!existing.stop.trainType && stop.trainType) { + existing.stop = stop; + } + } else { + companyMap.set(companyId, { + lineNames: stop.line?.nameShort ? [stop.line.nameShort] : [], + stop, + }); + } + } + return Array.from(companyMap.values()); + }, [route?.stops]); return ( - - + + {(onClose) => ( <> - -
-
- {modalContent.lineName} + +
+
+ {modalContent.lineName} {removeBrackets(modalContent.trainType?.name ?? "")}
-
- -

停車駅:

-
    - {dropEitherJunctionStation(route?.stops ?? []).flatMap( - (stop) => - stop.stopCondition === StopCondition.All ? ( -
  • - {stop.name} -
  • - ) : ( -
  • cnd.id === stop.stopCondition, - )?.color, - }} - > - {stop.name} -
  • - ), - )} -
- -
- {STOP_CONDITIONS.map((cnd) => ( -
+ +
+

停車駅

+
    + {dropEitherJunctionStation(route?.stops ?? []).flatMap( + (stop) => { + const cnd = STOP_CONDITIONS.find( + (c) => c.id === stop.stopCondition, + ); + return ( +
  • + {cnd && stop.stopCondition !== StopCondition.All && ( + + )} + {stop.name} +
  • + ); + }, + )} +
+
+ {STOP_CONDITIONS.map((cnd) => (
- {cnd.text} -
- ))} -
+ className="flex items-center gap-1 text-xs text-gray-600" + key={cnd.id} + > + + {cnd.text} +
+ ))} +
+ -

各線の種別:

-
- {uniqueLineStops.map((stop) => ( -

- {stop.line?.nameShort}: - +

+ 各線の種別 +

+
+ {linesByCompany.map(({ stop }) => ( +
- {removeBrackets( - stop.trainType?.name ?? "普通または各駅停車", - )} - -

- ))} -
+ + {stop.line?.company?.nameShort}線 + + + {removeBrackets( + stop.trainType?.name ?? "普通または各駅停車", + )} + +
+ ))} +
+
- - - diff --git a/graphql/queries.ts b/graphql/queries.ts index 4c6620d..28d435c 100644 --- a/graphql/queries.ts +++ b/graphql/queries.ts @@ -219,17 +219,21 @@ export const LINES_BY_NAME = gql` } `; -export const ROUTES = gql` - ${STATION_NESTED_FRAGMENT} - query Routes($fromStationGroupId: Int!, $toStationGroupId: Int!) { - routes(fromStationGroupId: $fromStationGroupId, toStationGroupId: $toStationGroupId) { - routes { +export const ROUTE_TYPES = gql` + query RouteTypes($fromStationGroupId: Int!, $toStationGroupId: Int!) { + routeTypes(fromStationGroupId: $fromStationGroupId, toStationGroupId: $toStationGroupId) { + trainTypes { id - stops { - ...StationNestedFields - } + groupId } nextPageToken } } `; + +export const buildLineGroupStationsQuery = (groupIds: number[]) => gql` + ${STATION_FRAGMENT} + query LineGroupStationsBatch { + ${groupIds.map((id) => `g_${id}: lineGroupStations(lineGroupId: ${id}) { ...StationFields }`).join("\n ")} + } +`; diff --git a/hooks/useFetchRoutes.ts b/hooks/useFetchRoutes.ts index cfb2021..d339c93 100644 --- a/hooks/useFetchRoutes.ts +++ b/hooks/useFetchRoutes.ts @@ -1,30 +1,69 @@ import useSWR from "swr"; import { graphqlClient } from "@/api/client"; -import { ROUTES } from "@/graphql/queries"; -import type { RoutesResponse } from "@/types/stationapi"; +import { buildLineGroupStationsQuery, ROUTE_TYPES } from "@/graphql/queries"; +import type { Route, RouteTypesResponse, Station } from "@/types/stationapi"; import { generateSWRKey } from "@/utils/generateSWRKey"; +const CHUNK_SIZE = 10; + +const fetchStationsByChunks = async ( + groupIds: number[], +): Promise> => { + const chunks: number[][] = []; + for (let i = 0; i < groupIds.length; i += CHUNK_SIZE) { + chunks.push(groupIds.slice(i, i + CHUNK_SIZE)); + } + + const results = await Promise.all( + chunks.map((chunkIds) => + graphqlClient.request>( + buildLineGroupStationsQuery(chunkIds), + ), + ), + ); + + const grouped = new Map(); + for (let i = 0; i < chunks.length; i++) { + for (const id of chunks[i]) { + grouped.set(id, results[i][`g_${id}`] ?? []); + } + } + + return grouped; +}; + export const useFetchRoutes = ( fromStationGroupId: number, toStationGroupId: number, ) => { const variables = { fromStationGroupId, toStationGroupId }; - const swrKey = generateSWRKey("routes", variables); + const swrKey = generateSWRKey("routeTypes", variables); const { data: routes, error, isLoading, - } = useSWR(swrKey, async () => { + } = useSWR(swrKey, async () => { if (isNaN(fromStationGroupId) || isNaN(toStationGroupId)) { return []; } - const res = await graphqlClient.request<{ routes: RoutesResponse }>( - ROUTES, - variables, - ); - return res.routes.routes; + const res = await graphqlClient.request<{ + routeTypes: RouteTypesResponse; + }>(ROUTE_TYPES, variables); + + const trainTypes = res.routeTypes.trainTypes; + if (trainTypes.length === 0) { + return []; + } + + const groupIds = trainTypes.map((tt) => tt.groupId); + const grouped = await fetchStationsByChunks(groupIds); + + return trainTypes.map((tt) => ({ + id: tt.groupId, + stops: grouped.get(tt.groupId) ?? [], + })); }); return { routes, error, isLoading }; diff --git a/hooks/useFetchStationsByGroupId.ts b/hooks/useFetchStationsByGroupId.ts index 6e6b413..6728c15 100644 --- a/hooks/useFetchStationsByGroupId.ts +++ b/hooks/useFetchStationsByGroupId.ts @@ -18,10 +18,9 @@ export const useFetchStationsByGroupId = (groupId: number) => { return []; } - const res = await graphqlClient.request<{ stationGroupStations: Station[] }>( - STATION_GROUP_STATIONS, - variables, - ); + const res = await graphqlClient.request<{ + stationGroupStations: Station[]; + }>(STATION_GROUP_STATIONS, variables); return res.stationGroupStations; }); diff --git a/types/stationapi.ts b/types/stationapi.ts index 5986dd2..5b14b6d 100644 --- a/types/stationapi.ts +++ b/types/stationapi.ts @@ -146,7 +146,12 @@ export interface Route { stops: Station[]; } -export interface RoutesResponse { - routes: Route[]; +export interface RouteTypeTrainType { + id: number; + groupId: number; +} + +export interface RouteTypesResponse { + trainTypes: RouteTypeTrainType[]; nextPageToken: string; } diff --git a/utils/generateSWRKey.ts b/utils/generateSWRKey.ts index b985fce..2d15978 100644 --- a/utils/generateSWRKey.ts +++ b/utils/generateSWRKey.ts @@ -1,4 +1,7 @@ -export const generateSWRKey = (method: string, request: Record) => +export const generateSWRKey = ( + method: string, + request: Record, +) => `${method}:${Object.entries(request) .map(([key, value]) => `${key}:${value}`) .join(":")}`; From eb13de4bd106600315e57bec4a1b5c0cf3b5c62e Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Wed, 25 Feb 2026 21:59:10 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=E7=B5=8C=E8=B7=AF=E6=A4=9C=E7=B4=A2?= =?UTF-8?q?=E7=B5=90=E6=9E=9C=E7=94=BB=E9=9D=A2=E3=81=AE=E3=82=B5=E3=83=96?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=88=E3=83=AB=E9=A7=85=E5=90=8D=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useFetchStationsByGroupIdからデータが取得できない場合に スケルトンが永続する問題を、routesデータからの フォールバック取得で解決。不要なFragmentも除去。 Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 76 +++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 805d523..dc7cd07 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -519,12 +519,10 @@ export default function Home() { const { stations: fromStationsByGroupId = [], error: fetchFromStationsByGroupIdError, - isLoading: isFromStationsByGroupIdLoading, } = useFetchStationsByGroupId(Number(params.get("fsid"))); const { stations: toStationsByGroupId = [], error: fetchToStationsByGroupIdError, - isLoading: isToStationsByGroupIdLoading, } = useFetchStationsByGroupId(Number(params.get("tsid"))); const debouncedToStationName = useDebounce(toStationName, DEBOUNCE_DELAY); @@ -659,6 +657,24 @@ export default function Home() { [selectedToStationId, toStationsByGroupId], ); + const fromStationDisplayName = useMemo(() => { + if (fromStation?.name) return fromStation.name; + const fromGroupId = Number(selectedFromStationId); + const stop = routes + ?.flatMap((r) => r.stops) + .find((s) => s.groupId === fromGroupId); + return stop ? `${stop.name}駅` : undefined; + }, [fromStation?.name, routes, selectedFromStationId]); + + const toStationDisplayName = useMemo(() => { + if (toStation?.name) return toStation.name; + const toGroupId = Number(selectedToStationId); + const stop = routes + ?.flatMap((r) => r.stops) + .find((s) => s.groupId === toGroupId); + return stop ? `${stop.name}駅` : undefined; + }, [toStation?.name, routes, selectedToStationId]); + const handleLaunchApp = useCallback(() => { const appScheme = devMode ? "trainlcd-canary://" : "trainlcd://"; @@ -768,22 +784,18 @@ export default function Home() {

{params.get("mode") !== "line" && - (isFromStationsLoading || - isToStationsLoading || - !fromStation || - !toStation) ? ( + !fromStationDisplayName && + !toStationDisplayName ? ( ) : null} {params.get("mode") !== "line" && - !isFromStationsLoading && - !isToStationsLoading && - fromStation && - toStation ? ( + fromStationDisplayName && + toStationDisplayName ? (

- {fromStation?.name} + {fromStationDisplayName}  -  - {toStation?.name} + {toStationDisplayName}

) : null} @@ -804,27 +816,25 @@ export default function Home() { ) : null} - <> - {selectedLineId ? ( - - ) : ( - - )} -

- TrainLCDアプリで利用可能なデータであるため、実際の情報とは異なる場合があります。 -

- + {selectedLineId ? ( + + ) : ( + + )} +

+ TrainLCDアプリで利用可能なデータであるため、実際の情報とは異なる場合があります。 +

)} From b01acd6af40b7ae211672231072f3ddf7170dd17 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Wed, 25 Feb 2026 22:00:56 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E7=A8=AE=E5=88=A5=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=82=E3=82=8A=E3=81=AE=E8=AA=A4=E5=88=A4=E5=AE=9A=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trainTypeが未設定(undefined)の停車駅を除外してから 種別の一意性を判定するよう変更。 Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index dc7cd07..955b802 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -260,8 +260,10 @@ const RoutesListBox = ({ const isHasTypeChange = useCallback( (routeId: number) => { const targetRoute = routes?.find((r) => r.id === routeId); - const typeIds = targetRoute?.stops.map((s) => s.trainType?.typeId); - return Array.from(new Set(typeIds)).length > 1; + const typeIds = targetRoute?.stops + .map((s) => s.trainType?.typeId) + .filter((id) => id != null); + return new Set(typeIds).size > 1; }, [routes], ); From 9d623538623c13f1ffc207ec6ccb9cb20ec64514 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Wed, 25 Feb 2026 22:04:19 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E3=83=87=E3=82=A3=E3=83=BC=E3=83=97?= =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E3=81=AEdirection=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 方向の0/1が逆だった問題を修正し、groupIdの参照元を undefinedになり得るfromStation/toStationから selectedFromStationId/selectedToStationIdに変更。 Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 955b802..666636c 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -684,13 +684,13 @@ export default function Home() { (stop) => stop.trainType?.groupId === Number(selectedRouteId), )?.trainType?.groupId; - const direction = - (route?.stops ?? []).findIndex( - (s) => s.groupId === fromStation?.groupId, - ) < - (route?.stops ?? []).findIndex((s) => s.groupId === toStation?.groupId) - ? 1 - : 0; + const fromIndex = (route?.stops ?? []).findIndex( + (s) => s.groupId === Number(selectedFromStationId), + ); + const toIndex = (route?.stops ?? []).findIndex( + (s) => s.groupId === Number(selectedToStationId), + ); + const direction = fromIndex < toIndex ? 0 : 1; const lineId = fromStop?.line?.id; From af29c2868aec68cda10deab44c88f32da9fe571e Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Wed, 25 Feb 2026 22:12:39 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E3=82=B5=E3=83=96=E3=82=BF=E3=82=A4?= =?UTF-8?q?=E3=83=88=E3=83=AB=E9=A7=85=E5=90=8D=E8=A1=A8=E7=A4=BA=E3=81=AE?= =?UTF-8?q?=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF=E3=81=A8=E7=89=87?= =?UTF-8?q?=E5=81=B4=E8=A1=A8=E7=A4=BA=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重複したフォールバックロジックをgetStationDisplayNameに統合し、 片方の駅名のみ取得できた場合にもサブタイトルを表示するよう修正。 Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 666636c..36ca603 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -659,23 +659,27 @@ export default function Home() { [selectedToStationId, toStationsByGroupId], ); - const fromStationDisplayName = useMemo(() => { - if (fromStation?.name) return fromStation.name; - const fromGroupId = Number(selectedFromStationId); - const stop = routes - ?.flatMap((r) => r.stops) - .find((s) => s.groupId === fromGroupId); - return stop ? `${stop.name}駅` : undefined; - }, [fromStation?.name, routes, selectedFromStationId]); - - const toStationDisplayName = useMemo(() => { - if (toStation?.name) return toStation.name; - const toGroupId = Number(selectedToStationId); - const stop = routes - ?.flatMap((r) => r.stops) - .find((s) => s.groupId === toGroupId); - return stop ? `${stop.name}駅` : undefined; - }, [toStation?.name, routes, selectedToStationId]); + const getStationDisplayName = useCallback( + (stationName: string | undefined, stationGroupId: string) => { + if (stationName) return stationName; + const groupId = Number(stationGroupId); + const stop = routes + ?.flatMap((r) => r.stops) + .find((s) => s.groupId === groupId); + return stop ? `${stop.name}駅` : undefined; + }, + [routes], + ); + + const fromStationDisplayName = useMemo( + () => getStationDisplayName(fromStation?.name, selectedFromStationId), + [getStationDisplayName, fromStation?.name, selectedFromStationId], + ); + + const toStationDisplayName = useMemo( + () => getStationDisplayName(toStation?.name, selectedToStationId), + [getStationDisplayName, toStation?.name, selectedToStationId], + ); const handleLaunchApp = useCallback(() => { const appScheme = devMode ? "trainlcd-canary://" : "trainlcd://"; @@ -792,12 +796,11 @@ export default function Home() { ) : null} {params.get("mode") !== "line" && - fromStationDisplayName && - toStationDisplayName ? ( + (fromStationDisplayName || toStationDisplayName) ? (

- {fromStationDisplayName} + {fromStationDisplayName ?? "..."}  -  - {toStationDisplayName} + {toStationDisplayName ?? "..."}

) : null} From 07ec2476481fcaba60c66aa536fc1257c0032f66 Mon Sep 17 00:00:00 2001 From: Tsubasa SEKIGUCHI Date: Wed, 25 Feb 2026 22:19:14 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E6=8C=87=E6=91=98=E3=81=AE=E5=AE=89=E5=85=A8=E6=80=A7=E3=83=BB?= =?UTF-8?q?=E5=9E=8B=E6=94=B9=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - typeIds filterにtype predicateを追加 - getStationDisplayNameの"駅"重複付与を防止 - handleLaunchAppのfindIndex -1ガードとsgid undefinedガードを追加 - handleLaunchAppのuseCallback依存配列にselectedFrom/ToStationIdを追加 Co-Authored-By: Claude Opus 4.6 --- app/page.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 36ca603..321fc7f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -262,7 +262,7 @@ const RoutesListBox = ({ const targetRoute = routes?.find((r) => r.id === routeId); const typeIds = targetRoute?.stops .map((s) => s.trainType?.typeId) - .filter((id) => id != null); + .filter((id): id is number => id != null); return new Set(typeIds).size > 1; }, [routes], @@ -666,7 +666,8 @@ export default function Home() { const stop = routes ?.flatMap((r) => r.stops) .find((s) => s.groupId === groupId); - return stop ? `${stop.name}駅` : undefined; + if (!stop) return undefined; + return stop.name.endsWith("駅") ? stop.name : `${stop.name}駅`; }, [routes], ); @@ -694,20 +695,23 @@ export default function Home() { const toIndex = (route?.stops ?? []).findIndex( (s) => s.groupId === Number(selectedToStationId), ); + if (fromIndex === -1 || toIndex === -1) return; const direction = fromIndex < toIndex ? 0 : 1; const lineId = fromStop?.line?.id; + const sgid = fromStation?.groupId; + if (!sgid) return; if (lineId && lineGroupId) { window.open( - `${appScheme}?sgid=${fromStation?.groupId}&lid=${lineId}&lgid=${lineGroupId}&dir=${direction}`, + `${appScheme}?sgid=${sgid}&lid=${lineId}&lgid=${lineGroupId}&dir=${direction}`, ); return; } if (lineId) { window.open( - `${appScheme}?sgid=${fromStation?.groupId}&lid=${lineId}&dir=${direction}`, + `${appScheme}?sgid=${sgid}&lid=${lineId}&dir=${direction}`, ); } }, [ @@ -715,8 +719,9 @@ export default function Home() { fromStation?.groupId, fromStop?.line?.id, route?.stops, + selectedFromStationId, + selectedToStationId, selectedRouteId, - toStation?.groupId, ]); const handleUpdateSearchMode = useCallback(