- getTrainTypeのcolorStringに競技の色、trainTypeStringに「急行」を追加し、typecheckエラーを解消 - WebView注入用のTRAIN_TYPE_CONFIGにEXPRESS(急行)とCompetition(競技)を追加 - TRAIN_TYPE_CONFIGがtrainTypeIDを網羅しているかsatisfiesで型検査 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01BDYYpzQGabzZcAgz6NPKmK
JR四国 列車位置情報アプリ
JR四国の列車リアルタイム位置情報を表示するReact Nativeアプリケーション(非公式)。
🚀 技術スタック
正確なバージョンは宣言値を
package.json、解決値をyarn.lockで確認してください。
- フレームワーク: Expo SDK 57 / React Native 0.86系 / React 19系(New Architecture)
- 言語: TypeScript / JavaScript
- ナビゲーション: React Navigation 7(bottom tabs + stack。Expo Routerは未使用)
- 状態管理: React Context API + カスタムフック(
stateBox/) - ストレージ: AsyncStorage、expo-sqlite
- 地図表示: WebView + JavaScript Injection、react-native-maps
- シート: react-native-actions-sheet、@gorhom/bottom-sheet
- 日付処理: dayjs
- アニメーション / ジェスチャー: react-native-reanimated、react-native-gesture-handler(v2系)
- 監視: Sentry
- ウィジェット: react-native-android-widget(Android)、
@bacons/apple-targetsによるWidgetKit拡張(iOS)
📦 プロジェクト構造
jrshikoku/
├── components/ # Reactコンポーネント
│ ├── Apps/ # 走行位置画面
│ │ └── FixedPositionBox/
│ │ └── hooks/ # カスタムフック
│ ├── AndroidWidget/ # Androidウィジェット
│ ├── ActionSheetComponents/
│ ├── atom/ # 基本UIコンポーネント
│ ├── Menu/ # メニュー関連
│ ├── Settings/ # 設定画面
│ └── TrainMenu/ # 列車メニュー
├── stateBox/ # グローバル状態管理(Context + hooks)
├── lib/ # ユーティリティライブラリ
│ ├── domain/ # 駅・列車identityのdomain型
│ ├── eachTrainInfoCoreLib/ # 列車情報処理
│ ├── observability/ # Sentry・テレメトリ
│ ├── theme/ # カラーテーマ
│ └── webview/ # WebView注入用データ
├── constants/ # 定数定義
│ ├── intervals.ts # 時間間隔定数
│ ├── api.ts # APIエンドポイント
│ ├── storage.ts # StorageKeys
│ └── index.ts
├── modules/ # ローカルExpoモジュール
│ ├── expo-felica-reader/ # FeliCa読み取り(iOS/Android)
│ └── expo-live-activity/ # Live Activities(iOS)
├── targets/widget/ # iOS WidgetKit拡張
├── types/ # TypeScript型定義
├── utils/ # ユーティリティ関数
│ ├── logger.ts # ロギング
│ └── seUtils.ts # SE判定処理
├── tests/ # pure関数のテスト(node:test)
├── docs/ # 設計・changelog・検証記録
│ └── architecture/ # domain契約・ADR
├── assets/ # 静的リソース
└── config/ # 設定ファイル
android/ と ios/ はprebuildで生成されるため、gitでは管理していません。
🛠️ セットアップ
必要環境
- Node.js(Expo SDK 57が対応するLTS。
yarn expo:doctorで確認) - Yarn(lockfileは
yarn.lockのみ) - EAS CLI(ビルドする場合)
インストールと起動
# 依存関係のインストール(postinstallでpatch-packageが実行されます)
yarn install
# 開発サーバー起動(dev client)
yarn start
# ネイティブビルドして実行
yarn ios
yarn android
# Web版のエクスポート
yarn web:export
検証
yarn typecheck # tsc --noEmit
yarn lint # expo lint
yarn test # tests/*.test.ts
yarn verify # 上記3つをまとめて実行
yarn expo:doctor # Expo依存関係の整合性チェック
依存ライブラリのライセンス一覧
dependencies を追加・更新したら、アプリ内の「オープンソースライセンス」画面で使う一覧を再生成してください。再生成を忘れると yarn test が失敗します。
yarn licenses:generate # assets/licenses/oss-licenses.json を更新
📝 開発ガイドライン
開発エージェント向けの詳細なルールは AGENTS.md、domain契約は docs/architecture/ を参照してください。
定数の使用
// ❌ Bad
setTimeout(update, 60000);
fetch('https://example.com/api');
// ✅ Good
import { INTERVALS, API_ENDPOINTS } from '@/constants';
setTimeout(update, INTERVALS.DELAY_UPDATE);
fetch(API_ENDPOINTS.DIAGRAM_TODAY);
ロギング
// ❌ Bad
console.log('Debug info', data);
console.error('Error occurred', error);
// ✅ Good
import { logger } from '@/utils';
logger.debug('Debug info', data); // 開発環境のみ
logger.error('Error occurred', error); // 本番環境でも出力
型定義
// ❌ Bad
type TrainData = any;
// ✅ Good
import type { TrainDataType } from '@/types';
const trainData: TrainDataType = {...};
ストレージアクセス
// ❌ Bad
AsyncStorage.getItem('bus_and_train');
// ✅ Good
import { STORAGE_KEYS } from '@/constants';
AsyncStorage.getItem(STORAGE_KEYS.BUS_AND_TRAIN);
🎯 主要機能
列車位置追跡
- リアルタイム列車位置表示
- 遅延情報の表示(遅延速報EX)
- 次駅・着駅の予測
- 列車追従・駅固定モード(iOSではLive Activitiesに対応)
- 走行位置の記録と再生
駅情報
- 時刻表表示
- 駅詳細情報
- お気に入り駅登録・並び替え
運行情報・通知
- JR四国の運行情報表示
- プッシュ通知(遅延速報EX、運行情報など)
- サウンドによる案内(β)
ICカード
- FeliCa対応ICカードの残高・履歴読み取り
カスタマイズ(設定画面)
- 起動時に表示する画面、レイアウト設定
- カラーテーマ(ライト / 端末依存 / ダーク)
- アイコン設定、ランチャーアイコン変更
- サウンド設定(β)、運行情報設定(β)
- 通知設定、情報ソース設定、接続ステータス
- ウィジェット設定(Android)
🔧 カスタムフック
FixedPositionBox用フック
useFixedTrainData: 列車データの取得と管理useStopStationList: 停車駅IDリストの生成useTrainCurrentPosition: 現在位置の計算useNextStationCalculator: 次駅と着駅の計算useTrainDataWithThrough: 通過駅を含む列車データ生成useDestinationStation: 行先駅データの管理
グローバル状態フック(stateBox/)
useAllTrainDiagram: 全列車ダイヤデータuseAreaInfo: 運行情報スナップショット(エリア情報)useBusAndTrainData: バス・列車データuseCurrentTrain: 現在の列車位置データuseDeviceOrientationChange: 画面の向きuseElesite: えれサイト運用データuseFavoriteStation: お気に入り駅useNotifications: プッシュ通知と通知タップ時のルーティングuseStationList: 全駅リストuseTopMenu: トップメニュー状態useTrainDelayData: 遅延情報useTrainMenu: 列車メニュー状態useUnyohub: 鉄道運用Hub運用データuseUserPosition: ユーザーの現在地
📊 リファクタリング実績
詳細はREFACTORING.mdを参照。
- 定数化: 50+ 箇所
- 型安全性: 22ファイル、46+ 箇所のany型削減
- ロギング: 10ファイルでlogger導入
- コード重複削減: 87%(seUtils)
- 大型コンポーネント分割: FixedTrainBox.tsx(356行抽出)
🚦 API エンドポイント
エンドポイントは constants/api.ts の API_ENDPOINTS に集約しています。一覧と各URLは同ファイルを参照してください。
// constants/api.ts(抜粋)
const BASE_URL = 'https://jr-shikoku-api-data-storage.haruk.in';
export const API_ENDPOINTS = {
DIAGRAM_TODAY: `${BASE_URL}/tmp/diagram-today.json`,
OPERATION_INFO: `${BASE_URL}/operation-info/jr-shikoku/latest.json`,
DELAY_INFO: `${BASE_URL}/derived/delays/latest.json`,
CURRENT_POSITIONS: `${BASE_URL}/tmp/currentPositions.json`,
// ...
};
データ取得の全体像は docs/data-fetch-map.md を参照してください。
🔗 外部からのアプリ起動(ディープリンク)
本アプリは複数の方法で外部から起動・画面遷移できます。
カスタムURLスキーム
スキーム jrshikoku:// を使って特定の画面を直接開くことができます。
| URL | 遷移先 |
|---|---|
jrshikoku://open/felica(felicahistorypage も可) |
FeliCa履歴ページ |
jrshikoku://open/traininfo |
遅延速報EX |
jrshikoku://open/operation |
運行情報 |
jrshikoku://open/settings |
設定 |
jrshikoku://open/topmenu |
トップメニュー |
jrshikoku://positions/apps |
走行位置 |
URLパターンの定義は lib/startupDeepLink.ts、遷移処理は lib/useStartupDeepLinkNavigation.ts の routeFromUrl で実装されています。
Androidウィジェット
ホーム画面に配置可能なウィジェットからアプリを起動できます。
| ウィジェット名 | 説明 |
|---|---|
JR_shikoku_train_info |
遅延速報EX |
JR_shikoku_apps_shortcut |
クイックアクセス(各機能へのショートカットタイル) |
JR_shikoku_felica_balance |
ICカード残高表示 |
JR_shikoku_info |
運行情報 |
ウィジェットのタップ時は jrshikoku:// スキームでアプリ内画面へ遷移します。
配置可能なウィジェットは app.json の react-native-android-widget プラグイン設定で登録し、描画は components/AndroidWidget/widget-task-handler.tsx で行います。
未公開: 怪レい列車ウィジェット(JR_shikoku_train_strange)
公開する予定はありますが、現在は未完成のため app.json に登録しておらず、ホーム画面には配置できません。
components/AndroidWidget/StrangeTrainWidget.tsxとwidget-task-handler.tsxに描画処理はありますが、表示内容は固定文の仮実装です。- ウィジェットに表示するデータの取得先(エンドポイント)がまだ用意されていません。
- タップ先は仮で
jrshikoku://open/traininfo(遅延速報EX)になっています。 - 公開時は、データ取得の実装、プレビュー画像(
assets/widgetResource/)の追加、app.jsonへの登録が必要です。登録はネイティブ設定の変更になるため、OTAではなく新しいビルドで配布する必要があります。
怪レい列車の通知(通知設定、通知アクション strange-train)は、ウィジェットとは別に実装されています。
iOSウィジェット(WidgetKit)
WidgetKit拡張として、次のホーム画面ウィジェットを提供しています。
- ショートカット
- ICカード残高
- 遅延速報
- 運行情報
Live Activitiesは、駅固定(StationLock)と列車追従(TrainFollow)に対応しています。
実装: targets/widget/
プッシュ通知
通知タップ時に対応する画面へ遷移します。
| アクション | 遷移先 |
|---|---|
delay-ex |
遅延速報EX |
strange-train |
走行位置(怪レい列車) |
information |
運行情報 |
実装: stateBox/useNotifications.tsx
NFC(FeliCa)
NFC-Fを利用した交通系ICカードの読み取りに、iOS・Androidとも対応しています。アプリ内からスキャンを開始する形式です。
実装: modules/expo-felica-reader/(7nohe/expo-felica-reader をもとに改変、MIT)
📱 ビルド
EASのprofileとchannelは eas.json で管理しています。主なprofileは次のとおりです。
| profile | 用途 |
|---|---|
development |
dev client(internal配布) |
preview |
内部配布 |
production<version> / beta<version> |
バージョン別の本番・ベータ(例: production7.2、beta7.2) |
eas build --platform android --profile <profile>
eas build --platform ios --profile <profile>
runtimeVersionの方針は docs/architecture/adr/001-runtime-version-policy.md を参照してください。
ネイティブ設定(app.json のプラグイン、permission、entitlement、ローカルモジュールなど)を変更した場合は、OTAではなく新しいビルドが必要です。
📄 ライセンス
本リポジトリは非公開です。無断での複製・再配布を禁止します(All Rights Reserved)。
- 同梱しているフォント・音声・ロゴなどの素材は、それぞれの権利者の規約に従います。
- 依存ライブラリのライセンスは、各パッケージの記載に従います。一覧はアプリの「設定 > オープンソースライセンス」で確認できます。
modules/expo-felica-reader/は、Daiki Urata氏の expo-felica-reader 0.1.0(MIT License)をもとに改変したものです。ライセンス表記はmodules/expo-felica-reader/LICENSEを参照してください。
本アプリはJR四国とは無関係の非公式アプリです。「JR四国」などの名称・商標は各権利者に帰属します。
👤 開発者
harukin/Xprocess