269 Commits

Author SHA1 Message Date
harukin-expo-dev-env
131bd30984 ウィジェットのテーマカラーを追加し、各ウィジェットでの色の適用を改善 2026-03-31 14:59:47 +00:00
harukin-expo-dev-env
af4e0d9438 SettingTopPageとSoundSettingsのリファクタリング、不要なコードの削除とオーディオプレイヤー機能の追加 2026-03-31 11:17:19 +00:00
harukin-expo-dev-env
ec9b6dd1bc アイコン設定画面のリファクタリングとローディングアニメーションの追加 2026-03-31 06:56:35 +00:00
harukin-expo-dev-env
e1c12c9dab アプリアイコンシステムガイドを追加し、アイコンの追加手順と規格を明確化 2026-03-31 03:32:49 +00:00
harukinMBP
ad75dfe27c iOSウィジェットにURLを追加し、ユーザーがトレイン情報と運行情報にアクセスできるようにしました 2026-03-31 12:10:07 +09:00
harukin-expo-dev-env
9c926362a9 アイコンのIDの大文字小文字を統一し、可読性を向上 2026-03-31 02:20:44 +00:00
harukin-expo-dev-env
0a1940f781 テキストスタイルにテーマカラーを適用し、可読性を向上 2026-03-31 02:15:14 +00:00
harukin-expo-dev-env
f937bcc22d Add new PNG icons for various assets
- Introduced multiple new PNG icons in the assets/icons directory, including:
  - s40.png
  - s54nany1.png
  - s54nany2.png
  - s54s.png
  - s54to0ys.png
  - s6000f.png
  - s7001.png
  - s8001nr.png
  - s9000.png
  - tosa9640ht.png
  - tosa9640jbl.png
  - tosa9640mo1.png
  - tosa9640mo2.png
  - tosa9640tyb.png
  - tosa9640tyg.png
  - w141jg.png
  - w40.png
  - w741.png

These icons are essential for enhancing the visual representation of the application.
2026-03-31 01:50:19 +00:00
harukin-expo-dev-env
69f2d38a0a useThemeColorsを使用してダークモードの判定を統一し、コードの可読性を向上 2026-03-31 00:21:38 +00:00
harukin-expo-dev-env
1d9a1d593b useThemeColorsからisDarkを追加し、ユーザーインターフェーススタイルを動的に設定 2026-03-31 00:01:07 +00:00
harukin-expo-dev-env
b3d7ba448d WebViewコンポーネントにcontentMode="mobile"を追加し、表示を最適化 2026-03-30 23:45:31 +00:00
harukin-expo-dev-env
3cb14405f6 画面遷移アニメーション完了後に重たい計算を実行するためのフラグを追加し、ローディングアニメーションを実装 2026-03-30 12:51:12 +00:00
harukin-expo-dev-env
33defa1182 TextInputコンポーネントのプレースホルダーのテキストカラーをcolors.textTertiaryに変更 2026-03-30 11:07:23 +00:00
harukin-expo-dev-env
1084b6b299 各コンポーネントのアニメーション最適化とリファクタリング: useSharedValueとuseAnimatedStyleを導入し、パフォーマンスを向上 2026-03-30 10:11:14 +00:00
harukin-expo-dev-env
fbb8580d28 ActionSheetアニメーション阻害の修正: LayoutAnimation削除とブリンクアニメーション最適化
- ActionSheet内外のLayoutAnimation.configureNext/easeInEaseOutを削除
  (グローバルにレイアウト更新を乗っ取り、シートの開閉アニメーションと競合するため)
- trainIconStatus: reanimated withRepeat→useInterval+setStateに変更
  (UIスレッドの無限アニメーションがActionSheetスプリングと競合するため)
- EachTrainInfo: Rules of Hooks違反修正(useRef/useSheetMaxHeightを条件分岐前に移動)
- useAutoScroll: InteractionManager.runAfterInteractionsでシート展開完了後に実行

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 10:09:45 +00:00
harukin-expo-dev-env
5db74714db TrainDataSources: 列番上書き時にヘッダーでくるくるアニメーション表示 2026-03-30 10:00:20 +00:00
harukin-expo-dev-env
5a106a3ab7 アイコンのリファクタリング: hub_logo.pngとicon_2048.webpを削除し、elesite_logo.pngとunyohub_logo.webpを追加 2026-03-30 07:21:07 +00:00
harukin-expo-dev-env
0ef169518a HeaderText: useMemoフックにtrainNumとallCustomTrainDataを追加して依存関係を最適化
TrainIconStatus: アンパンマンステータスAPIのエンドポイント判定を追加
webViewInjectjavascript: 新しい列車アイテムの最大幅を設定
2026-03-30 05:54:25 +00:00
harukin-expo-dev-env
9d76264a28 各コンポーネントでのuseMemoフックの追加とデータ処理の最適化 2026-03-30 04:00:25 +00:00
harukin-expo-dev-env
1181e488c1 Merge commit 'bbfd82aaea9fa2460e97ac8eee4d9beb7c9f8034' into develop 2026-03-30 02:27:24 +00:00
harukin-expo-dev-env
bbfd82aaea EachTrainInfo: 内部ScrollViewのmaxHeightを80%→70%に変更 2026-03-30 02:18:10 +00:00
harukin-expo-dev-env
bd10290bb3 ActionSheet: スマホのmaxHeightを90%→70%に変更 2026-03-30 02:12:07 +00:00
harukin-expo-dev-env
63431adab1 ActionSheet: スマホのみmaxHeight(90%)制限、タブレットは無制限
- useSheetMaxHeight() hook追加(短辺<600dpでスマホ判定)
- 全8つのActionSheetにmaxHeight適用
- タブレット/DeXでは制限なし
2026-03-30 02:03:57 +00:00
harukin-expo-dev-env
393bcc4df3 DeX: Dimensions.getモンキーパッチ + transform scaleで低密度ディスプレイ対応
- Dimensions.get('window')を1/1.3に縮小パッチ
- useWindowDimensions()も自動的に小さい値を返す
- DensityScaleWrapperで1.3倍transform scaleで拡大
- コンポーネントは872x531dpとしてレイアウト→実画面1133x690に拡大
2026-03-29 23:31:26 +00:00
harukin-expo-dev-env
ec53d4fa2a DeX: DensityScaleWrapperを一旦削除、正常表示に戻す 2026-03-29 17:32:17 +00:00
harukin-expo-dev-env
dd62ad8f73 DeX: スケール値を1.8→1.3に低減、レイアウト崩れ修正 2026-03-29 17:29:06 +00:00
harukin-expo-dev-env
25e13b9f41 DeX: DensityScaleWrapperでtransform scaleアプローチに切替、babel plugin wrapStyleForDensityを除去 2026-03-29 17:25:54 +00:00
harukin-expo-dev-env
b9f8ed1ea8 test: compensation倍率を大幅増加(2.625/PR, max4.0)で効果確認テスト 2026-03-29 17:16:21 +00:00
harukin-expo-dev-env
2b4b237d1a fix: babelプラグインでText/TextInputのstyleをglobal.__scaleTextStyleでラップ
- Text.renderが存在しない(関数コンポーネント)ためモンキーパッチ不可
- babel pluginでビルド時に全Text/TextInputのstyleプロップをラップ
- ランタイムのglobal.__scaleTextStyleがrender毎にPR動的チェック
- PR<2.0: fontSize * min(2.5, 2.0/PR)で拡大
- PR>=2.0: 変更なし(identity)
2026-03-29 17:10:59 +00:00
harukin-expo-dev-env
a69b59ed84 debug: Text構造の診断Alert追加(render存在確認) 2026-03-29 17:02:05 +00:00
harukin-expo-dev-env
49c69ffe53 fix: PixelRatioをrender時に動的チェック(起動時はPhone PR、DeX移動後にPR低下するため) 2026-03-29 16:59:00 +00:00
harukin-expo-dev-env
040ce9bce1 fix: Text.renderモンキーパッチで全テキストを低密度ディスプレイで自動拡大
- utils/scaleTextForDensity.ts: PR<2.0のとき全TextのfontSizeを2.0/PR倍(max2.5x)
- App.tsx: 起動時にscaleTextForDensityをインポート
- responsive.ts: fontScaleをidentityに戻す(二重スケーリング防止)
- 178箇所のハードコードfontSizeも個別修正不要で一括対応
2026-03-29 16:56:17 +00:00
harukin-expo-dev-env
06b2e97392 fix: DeX(PR=0.756)実測値に基づく密度補償 fontScale=2.0/PR(max 2.5x)
- DeX実測: Win=1133x690, PR=0.756, FS=0.8, Scr=731x411
- fontScale: 2.0/0.756 = 2.64 → cap 2.5x(テキスト2.5倍拡大)
- moderateScale: 拡大なし(レイアウト寸法保護)
- デバッグAlert/DebugMetrics除去
2026-03-29 16:46:01 +00:00
harukin-expo-dev-env
b8481e985e debug: Alert.alertでネイティブダイアログ表示(密度非依存の確実な方法) 2026-03-29 16:39:49 +00:00
harukin-expo-dev-env
146602caa1 debug: DebugMetricsをScrollView外に移動(常に画面上部に表示) 2026-03-29 16:35:50 +00:00
harukin-expo-dev-env
c07f8f1677 debug: デバッグパネルを赤背景+fontSize40で無条件表示(DeX値確認用) 2026-03-29 16:32:35 +00:00
harukin-expo-dev-env
8a318475bf fix: 密度補償を大幅に縮小 - fontScaleのみ1.3x、moderateScaleは拡大なし
- moderateScaleがwidth/height/borderRadiusにも使われておりレイアウト崩壊の原因
- fontScale: 低密度時1.3倍(テキスト可読性のみ改善)
- moderateScale: 常に原寸(レイアウト保護)
- デバッグパネル: 低密度時fontSize固定24
2026-03-29 16:28:10 +00:00
harukin-expo-dev-env
f5abf0d85a fix: DeX低密度ディスプレイでフォント/アイコンを拡大する密度補償を追加
- PixelRatio < 2.0 のとき densityCompensation = 2.0/PR で拡大(最大2.5倍)
- fontScale/moderateScale が密度補償倍率を適用
- デバッグパネルも密度に応じてfontSizeを自動拡大
- Phone窓モード(PR=2.01)では変化なし、DeX(PR≈1.0)では約2倍に拡大
2026-03-29 16:21:25 +00:00
harukin-expo-dev-env
e373ffdb76 fix: DeXで文字の縮小を停止、デバッグパネルのフォントサイズを拡大
- fontScale/moderateScale を恒等関数に変更(低密度ディスプレイでの縮小を停止)
- verticalScale のみレイアウト高さ圧縮に使用
- デバッグパネルのフォントサイズを10→16に拡大(DeXでも読めるように)
- デバッグ情報を簡潔化(Win/Scr/LowDensity表示)
2026-03-29 16:14:34 +00:00
harukin-expo-dev-env
61074d0bfe debug: DeX環境調査用のメトリクス表示を追加(一時的)
useWindowDimensions, Dimensions(window/screen), PixelRatio, FontScaleの
実際の値をメイン画面に表示してDeXでの挙動を特定する
2026-03-29 16:03:33 +00:00
harukin-expo-dev-env
9a03143853 fix: レスポンシブスケーリングを大幅強化 - fontScale 0.4→0.8、moderateScale 0.5→0.8
- responsive.ts: スケーリングファクターを大幅強化(fontScale 0.8, moderateScale 0.8)
- initIcon: タブバーアイコンsize 30→moderateScale(30)
- SimpleDot: ドットサイズ20/14→moderateScale
- StationPagination: 全寸法(28/24/18/22/8/6)をmoderateScale/fontScale化
- CarouselBox: fontSize 20/14→fontScale
- MenuPage: マップオフセット100/80/10/30→verticalScale
- menu: スクロールオフセット/snapToOffsets→verticalScale
2026-03-29 15:52:29 +00:00
harukin-expo-dev-env
3ca109edee feat: メイン画面のレスポンシブ対応 - Menu/atom/TrainMenuコンポーネントにスケーリング適用
- TitleBar: ヘッダー高さをverticalScaleに
- UsefulBox: アイコン50→moderateScale、フォント16→fontScale
- TicketBox: フォント18→fontScale
- TextBox: minHeight 70→verticalScale
- FixedContentBottom: 全fontSize(20/18)→fontScale、全アイコンsize(50/40)→moderateScale
- JRSTraInfoBox: 全fontSize(30/20/18)、アイコン、maxHeight、ローディングサイズをスケーリング
- SpecialTrainInfoBox: fontSize(30/20)→fontScale
- CarouselTypeChanger: height 40→verticalScale
2026-03-29 15:41:06 +00:00
harukin-expo-dev-env
5420531c64 feat: Samsung DeXレスポンシブ対応 - lib/responsive.ts追加、主要コンポーネントにスケーリング適用
- useResponsive()フックを作成(fontScale/verticalScale/moderateScale)
- ベースデザイン: iPhone 14 Pro (393x852)、スケールダウンのみ(最大1.0)
- DynamicHeaderScrollView: ヘッダー高さにverticalScale適用
- EachTrainInfoCore: maxHeight緩和(70→80%)、ハンドラーサイズスケーリング
- StateBox/PositionBox/ScrollStickyContent/HeaderText: fontScale適用
- DataConnectedButton/EachStopList: fontScale+moderateScale適用
- JRSTraInfo/StationDeteilView: ハンドラー+フォントスケーリング
- ReloadButton/MapsButton/NewMenu: ボタンサイズ+フォントスケーリング
- useDeviceOrientationChange: 全Android端末でisLandscape=false
2026-03-29 15:23:28 +00:00
harukin-expo-dev-env
8116ecd197 fix: タブレットの判定ロジックを追加し、サイドパネルレイアウトの使用を制御 2026-03-29 14:27:10 +00:00
harukin-expo-dev-env
1ecd67d8c5 fix: iPad ActionSheet/isLandscape対応 - isModalをiPad無効化、maxHeight追加、isLandscape常時false 2026-03-29 14:09:19 +00:00
harukin-expo-dev-env
6dd24b36ec fix: iPadでのヘッダー表示問題を修正 (ScrollView→View, width: 100%) 2026-03-29 14:07:07 +00:00
harukin-expo-dev-env
84b043ff5f fix: FelicaBalanceWidgetの表示をiPhoneのみに制限し、ダーク/ライトモードに対応した色設定を追加 2026-03-29 11:55:12 +00:00
harukin-expo-dev-env
5cf864b9ab fix: サウンド設定のJavaScriptインジェクションを遅延させ、列車番号のオーバーライドを追加 2026-03-29 11:42:09 +00:00
harukin-expo-dev-env
43758aa781 fix: Unyohub列番の上書き処理を追加し、運用Hub情報の取得を改善 2026-03-29 11:38:18 +00:00
harukin-expo-dev-env
684a184d40 fix: サウンド設定とレイアウト設定のナビゲーションを修正し、視認性向上のためのセクションヘッダーを追加 2026-03-28 04:07:46 +00:00
harukin-expo-dev-env
5e66fab175 feat: カラーテーマ設定とサウンド設定機能を追加し、外部起動方法をREADMEに記載 2026-03-26 13:55:24 +00:00
harukin-expo-dev-env
e1293d2500 fix: UIの色設定をダークモードに対応させ、視認性を向上 2026-03-26 08:56:31 +00:00
harukin-expo-dev-env
8ce0244c4b fix: 駅固定通知の出発済み列車フィルタと棒線駅接近判定を修正
- バックグラウンドで出発済み列車が消えないバグを修正
  (LiveActivityForegroundService: scheduledMinutes+delay<nowMinutes で除外)
- parseTimeToMinutes() ヘルパーを Kotlin に追加
- UI側も遅延加算した発車時刻で出発済み判定するよう統一
  (StationDiagramView: getDelayMinutes + dayjs.add)
- between.ts の BetweenStation を使った接近セクション判定を実装
  (棒線駅で列車がセクション手前に居る場合は未到達扱いで保持)
- StationTrainInfo に sectionStation フィールドを追加
  (prevStop/nextStop を廃止し API Pos と直接照合可能な文字列に変更)
- docs/station-lock-dual-impl.md 作成(KotlinとTS間のデュアル実装ガイド)
2026-03-26 03:00:59 +00:00
harukin-expo-dev-env
50e514543b Refactor code structure for improved readability and maintainability 2026-03-25 15:09:56 +00:00
harukin-expo-dev-env
e93cc93b77 Merge commit 'bf5be9bd57360e98b4279c4b4630c2a49ecc8062' into develop 2026-03-25 14:06:43 +00:00
harukin-expo-dev-env
bf5be9bd57 feat: implement sound announcement feature for delayed trains with settings 2026-03-25 14:05:59 +00:00
harukin-expo-dev-env
9e45d592b1 refactor: improve audio source handling for platform compatibility 2026-03-25 04:07:28 +00:00
harukin-expo-dev-env
834ad2bd41 feat: implement elesite data source integration and permission handling 2026-03-25 01:56:12 +00:00
harukin-expo-dev-env
83ca18f2c7 feat: add detailed changelog for version 7.0beta with feature enhancements and improvements 2026-03-25 01:44:54 +00:00
harukin-expo-dev-env
baacfd5855 refactor: remove LiveActivityButton from EachTrainInfoCore component 2026-03-25 01:30:57 +00:00
harukinMBP
066317bbc8 feat: add SpotSign component for tourist attractions 2026-03-25 10:22:12 +09:00
harukinMBP
0bcb03f833 refactor: remove live notification functionality from FixedStation and FixedTrain components 2026-03-25 02:07:43 +09:00
harukinMBP
9037d21237 fix: TypeScript build errors (143 to 47) 2026-03-25 01:44:29 +09:00
harukin-expo-dev-env
4789543573 Merge origin/develop into develop and resolve conflicts 2026-03-24 16:11:08 +00:00
harukin-expo-dev-env
5345dca95f Merge branch 'feature/fix-some' into develop 2026-03-24 16:07:30 +00:00
harukin-expo-dev-env
18f11c88b1 WidgetSettingsコンポーネントを削除し、SettingTopPageのウィジェット設定リンクをAndroidプラットフォームに限定 2026-03-24 16:03:23 +00:00
harukinMBP
cee238d060 ShortcutWidgetのタイル表示を改善し、遅延情報と運行情報を取得する機能を追加 2026-03-25 00:34:34 +09:00
harukin-expo-dev-env
dad462ff45 ListViewとExGridSimpleViewに次の列車の時間帯へ自動スクロール機能を追加 2026-03-24 14:12:35 +00:00
harukin-expo-dev-env
26cde03d3e AllTrainDiagramViewのmeasure対象を外側コンテナに変更
検索エリア自体ではなく最外側Viewをmeasure対象にすることで、
iOS上でキーボード頭に入力ブロックが隠れる問題を修正。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 10:11:31 +00:00
harukin-expo-dev-env
0230b56f8b StationDiagramViewの外側Viewをmeasure対象に変更
fallback計算ではなく、外側コンテナのmeasure()で
キーボード上端までの正確なオフセットを算出するように修正。
検索バーがキーボードに埋もれる問題を解消。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 10:04:35 +00:00
harukin-expo-dev-env
a2912d77ae キーボード回避ロジックをuseKeyboardAvoid hookに共通化
3コンポーネントに重複していたキーボード処理を lib/useKeyboardAvoid.ts に集約:
- Androidの偽イベント(height<100)ガード+キャッシュ
- hide→show高速切替のデバウンス(100ms)
- Android measure()の150ms遅延
- LayoutAnimation easeInEaseOut

対象: AllTrainDiagramView, SearchUnitBox, StationDiagramView

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 09:56:51 +00:00
harukin-expo-dev-env
e80eeae211 StationDiagramViewにキーボード高さキャッシュ・デバウンス・ガードを適用
- height<100の偽イベントを無視し、lastValidKbでキャッシュ利用
- hideデバウンス100msで素早い閉じ→開きに対応
- Android 150ms遅延でmeasure安定化
- LayoutAnimationをeaseInEaseOutに統一

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 09:50:17 +00:00
harukin-expo-dev-env
33410fcd61 fix: キーボード表示時の測定処理をリファクタリングし、タイマー管理を追加 2026-03-24 09:02:18 +00:00
harukin-expo-dev-env
ff25363600 fix: キーボード表示時のアニメーションの持続時間を短縮し、レイアウト調整を改善 2026-03-24 08:58:45 +00:00
harukin-expo-dev-env
36cd7448a5 fix: 検索ユニットボックスでキーボード表示時の位置調整を改善し、親コンテナの参照を追加 2026-03-24 08:51:09 +00:00
harukin-expo-dev-env
4a0e252366 fix: SearchUnitBoxに親コンテナの参照を追加し、キーボード表示時の位置調整を改善 2026-03-24 08:43:04 +00:00
harukin-expo-dev-env
1c96776f56 fix: アイコンシェア機能の options 引数ミスを修正
shareAsync の第2引数が (options = {...}) という代入式になっており
グローバル変数への代入になっていた。正しいオブジェクトリテラルに修正。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 08:32:44 +00:00
harukin-expo-dev-env
53b95a91d5 fix: アイコンプレビューが表示されないバグを修正
Image コンポーネントに padding: 30 を直接設定していたため、
RN 0.76+ の新アーキテクチャでコンテンツ領域がゼロになり画像が非表示になっていた。
padding を持つ wrapper View に移動し、Image サイズを 50x50 から 80x80 に拡大。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 08:26:40 +00:00
harukin-expo-dev-env
82fe8d1244 fix: タブのスタック状態を管理し、追加画面の表示状態を更新 2026-03-24 06:46:11 +00:00
harukin-expo-dev-env
80eeb51134 fix: 各コンポーネントのスタイルとナビゲーションを改善し、パフォーマンスを向上 2026-03-24 06:39:37 +00:00
harukin-expo-dev-env
2d96bdcad9 fix: Felicaウィジェットにスキャンタイムスタンプを追加 2026-03-24 04:37:56 +00:00
harukin-expo-dev-env
59653bbc16 fix: ダークモードに対応し、背景色を動的に変更 2026-03-24 04:37:46 +00:00
harukin-expo-dev-env
f34d06192b fix: アプリ起動時の意図しない自動画面遷移バグを修正
- useNotifications: getLastNotificationResponseAsync の処理済み通知IDを
  AsyncStorage に永続化し、アプリ再起動後に同じ通知で再ナビゲーションするバグを修正
- App.tsx: getInitialURL で返るディープリンクURLを永続化し、Android の
  singleTask モードで古いURLが再処理されるバグを修正

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 04:07:54 +00:00
harukin-expo-dev-env
16bf96faf7 fix: 不要なコードを削除し、URL処理を簡素化 2026-03-24 04:02:58 +00:00
harukin-expo-dev-env
3925370b97 fix: 与島PAの座標を正確な位置に修正
Wikipedia掲載の公式座標を使用
北緯34.389472度 東経133.816444度(香川県坂出市与島町587番地)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 03:24:11 +00:00
harukin-expo-dev-env
dcd8de06f8 fix: 与島PAの座標を修正(本島→与島)
34.377355, 133.773046(本島町)→ 34.360016, 133.784882(与島PA)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 03:13:52 +00:00
harukin-expo-dev-env
1d57f2a5c6 fix: 通知折りたたみ時も走行区間を表示
- contentTextにbody全行を全角スペース区切りで表示(折りたたみ時も見える)
- pollTrainPositionの変化なしスキップを除去(常に通知を更新)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 02:54:24 +00:00
harukin-expo-dev-env
d3e4b173c7 fix: StationNumber: null のスポットによるクラッシュを修正
- CarouselBox: key と keyExtractor を null セーフに
- StationPagination: data が空の場合の early return と optional chaining
- FixedStationBox / FixedTrainBox / ListViewItem: StationNumber?.slice() に修正

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 02:11:17 +00:00
harukin-expo-dev-env
5202f35702 feat: 与島(観光スポット)をトップメニューに追加
- assets/originData/spots.ts: 与島PAデータを新規作成(isSpot: true, StationNumber: null)
- lib/CommonTypes.ts: StationProps に isSpot フラグを追加
- lib/getStationList.ts: 観光スポットキーとして stationList に追加
- stateBox/useStationList.tsx: StationNumber: null でも名前検索が通るよう修正、getInjectJavascriptAddress で路線外エントリをスキップ
- menu.tsx: 位置情報検索に観光スポットを追加
- components/観光スポット看板/SpotSign.tsx: テーマパーク風の観光スポット看板コンポーネントを新規作成
- components/Menu/Carousel/CarouselBox.tsx: isSpot フラグで SpotSign に切り替え
- components/Menu/Carousel/GridMiniSign.tsx: isSpot 対応・ドット除去表示
- components/StationDiagram/SearchBox/SearchInputSuggestBox.tsx: ドット除去表示
- components/StationDiagram/StationDiagramView.tsx: スポットの「駅」表記を除去

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 01:58:16 +00:00
harukin-expo-dev-env
dc3d250466 fix: 次駅表示をDirection非依存に修正(JS/Kotlin両方)
- JS側: currentPosition[0]ではなくstopStationIDList上のmax(idx0,idx1)で進行方向の駅を判定
- Kotlin側: pollRunnable復活、allStationsのダイヤ順でmaxOf(idx0,idx1)で向かう駅を判定
- Kotlin at-station: 停車中は現在駅を表示(JS側と統一)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-24 01:39:31 +00:00
harukin-expo-dev-env
3f6b3cfcfb fix: API呼び出しにタイムアウトを追加し、エラーハンドリングを改善 2026-03-24 00:19:19 +00:00
harukin-expo-dev-env
2cd5142262 fix: バックグラウンドでのデータ取得を継続し、列車追跡の終了条件をフォアグラウンドに依存させるよう修正 2026-03-23 15:52:23 +00:00
harukin-expo-dev-env
4b518b848e fix: FelicaQuickAccessWidgetとFelicaBalanceWidgetのUIを改善し、背景デザインを最適化 2026-03-23 15:14:08 +00:00
harukin-expo-dev-env
fb89a2b334 fix: Live Activity関連の不要なコードを削除し、トレイン情報の構築を最適化 2026-03-23 14:54:56 +00:00
harukin-expo-dev-env
a66af59438 fix: Live Activityの定期的な更新を追加し、selectedTrainの再計算を60秒ごとに実施 2026-03-23 11:45:08 +00:00
harukin-expo-dev-env
04d0f50b99 fix: Live Activityの自動開始条件をselectedTrainに依存させるように修正 2026-03-23 11:33:36 +00:00
harukin-expo-dev-env
f4b86f4e77 fix: FelicaQuickAccessWidgetおよびShortcutWidgetのUIを改善し、バランスウィジェットのデザインを更新 2026-03-23 11:26:17 +00:00
harukin-expo-dev-env
91470b5db8 fix: LiveActivityButtonを削除し、FixedStationおよびFixedTrainコンポーネントでの自動Live Activity開始を実装 2026-03-23 11:25:29 +00:00
harukin-expo-dev-env
ffcc6ff660 fix: 列車通知機能に路線色を追加し、進捗スタイルのセグメントに対応 2026-03-23 10:55:14 +00:00
harukin-expo-dev-env
681b12b622 fix: 通知機能のエラーハンドリングを改善し、Androidの通知権限を要求する処理を追加 2026-03-23 09:37:15 +00:00
harukin-expo-dev-env
13f2c4de7a feat: 駅固定モード バックグラウンド更新 + 通知書式改善
- 駅固定モードでもForeground Serviceのバックグラウンドポーリングを有効化
- pollStationTrains(): APIから遅延情報を取得し通知を自動更新
- テキスト書式を「00:00 特急 ○○号 ○○行 定刻」に変更
- StationTrainInfo に typeColor フィールド追加
- StationLockNotificationBuilder: 種別名にBackgroundColorSpan適用
- ExpoLiveActivityModule: 駅固定もMap引数方式に変更
- trainsJson/stationName をServiceに保存してバックグラウンドで使用

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 06:31:05 +00:00
harukin-expo-dev-env
f19600a3af fix: ProgressStyle Point位置計算を修正
- Point(0) は @IntRange(from=1) 違反 → coerceIn(1, max) で修正
- Segment length を均等固定値(100)にして丸め誤差を排除
- progressMax = numSegments * 100 で座標系を統一
- Point position = stationIndex * 100 で全駅等分に忠実に配置
- デバッグログにpointPositionsを追加(Metroコンソールで確認可能)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 06:21:26 +00:00
harukin-expo-dev-env
9a567d2486 debug: ProgressStyle にログ追加 + dimColor alpha を100に増加
- buildProgressStyle にデバッグログ追加(total, stops, curIdx, progressValue)
- build() にデータ到着ログ追加(allStations.size, isStop count)
- dimColor alpha を60→100に上げて未通過Pointの視認性向上
- mapIndexedNotNull → forEachIndexed + mutableList に変更(明示的)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 06:09:47 +00:00
harukin-expo-dev-env
9271629aa9 fix: ProgressStyle復元 + 進捗位置更新の修正
- ProgressStyle (Point/Segment) を復元して描画
- 停車駅のみPointを配置(通過駅はセグメントのみ)
- currentStationIndex の部分一致フォールバック追加
- currentPosition 依存を除去し train.Pos から直接計算
- bodyフォーマッタで currentStation の '~' 区間を正しく分離

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 05:44:30 +00:00
harukin-expo-dev-env
d79f5a07f8 feat: TrainDataSourcesコンポーネントにスクリーンショット共有機能を追加 2026-03-23 05:14:05 +00:00
harukin-expo-dev-env
86a4428861 feat: 通知進捗バー再設計 - 全駅対応の進捗表示
- 進捗バー: 小●=通過駅, 大●=停車駅, 🚃=現在地
- タイトル: ○○号 ○○行き
- サブタイトル: 次は ●● / △△~△△間走行中
- StationEntry (name, isStop) で全駅リスト(通過含む)を通知に渡す
- TrainFollowNotificationBuilder: allStationsJson対応のProgressStyle
- LiveActivityForegroundService: バックグラウンドポーリングでも全駅インデックス計算
- FixedTrainBox/LiveActivityButton: allStations, currentStationIndex を計算・送信

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-23 05:01:55 +00:00
harukin-expo-dev-env
960acdbb3f refactor: update ShortcutWidget layout and component structure for improved readability and maintainability 2026-03-23 04:40:02 +00:00
harukin-expo-dev-env
86123ecb81 feat: implement train tracking notifications with background polling and update UI components 2026-03-23 04:33:58 +00:00
harukin-expo-dev-env
1d14bcf91a feat: add iOS Live Activity APNs Push Notification implementation details 2026-03-23 01:33:12 +00:00
harukin-expo-dev-env
c7b1501475 fix: correct scroll reference in useAutoScroll hook 2026-03-23 01:32:54 +00:00
harukin-expo-dev-env
814de31418 feat: add date formatting and stale check for Unyohub entries in TrainDataSources 2026-03-23 01:10:59 +00:00
harukin-expo-dev-env
ecc9ee313e feat: update navigation handling and widget click actions for improved user experience 2026-03-22 23:23:32 +00:00
harukin-expo-dev-env
0a2333a201 feat: add Android notification permission handling and improve error logging for live notifications 2026-03-22 23:06:56 +00:00
harukin-expo-dev-env
b1a8a4c98f feat: update iOS build number to 59 in app.json 2026-03-22 17:11:15 +00:00
harukin-expo-dev-env
0d45732d66 feat: update iOS build number to 59 in app.json 2026-03-22 17:11:15 +00:00
harukin-expo-dev-env
50fd2ece64 Merge commit 'f972d2b719eb9d26a738761800bc1901c573c800' into develop 2026-03-22 17:10:00 +00:00
harukin-expo-dev-env
38171574e8 Merge commit 'ce4fb4d1dabf8c795518af66ca4b3326b63cb355' into develop 2026-03-22 17:10:00 +00:00
harukin-expo-dev-env
f972d2b719 Merge commit 'deb24caaa2472ac2ec7715c96f3d0b69ebb96cde' into feature/migration-expo-upgrade 2026-03-22 17:09:51 +00:00
harukin-expo-dev-env
ce4fb4d1da Merge commit '39a5b33e7748a6f6240225a98e09435e4cb4f469' into feature/migration-expo-upgrade 2026-03-22 17:09:51 +00:00
harukin-expo-dev-env
deb24caaa2 feat: add station progress tracking and notification updates for train follow feature 2026-03-22 17:09:19 +00:00
harukin-expo-dev-env
39a5b33e77 feat: add station progress tracking and notification updates for train follow feature 2026-03-22 17:09:19 +00:00
harukinMBP
5515f42415 feat: add Live Activity button for train tracking and update iOS deployment target 2026-03-23 02:07:24 +09:00
harukinMBP
6829744fa4 feat: add Live Activity button for train tracking and update iOS deployment target 2026-03-23 02:07:24 +09:00
harukin-expo-dev-env
fbfb83fa34 feat: add live activity notifications for train tracking and station locking
- Implemented ExpoLiveActivity module for Android to manage live notifications.
- Added foreground service for train tracking and station locking notifications.
- Updated app permissions to include POST_NOTIFICATIONS.
- Enhanced FixedStation and FixedTrain components to support live notifications.
- Introduced new notification builders for train and station activities.
- Updated useCurrentTrain and useNotifications hooks to manage live notification state.
- Added notification channel for live tracking in Android.
2026-03-22 16:15:48 +00:00
harukin-expo-dev-env
777b5c8acb feat: add live activity notifications for train tracking and station locking
- Implemented ExpoLiveActivity module for Android to manage live notifications.
- Added foreground service for train tracking and station locking notifications.
- Updated app permissions to include POST_NOTIFICATIONS.
- Enhanced FixedStation and FixedTrain components to support live notifications.
- Introduced new notification builders for train and station activities.
- Updated useCurrentTrain and useNotifications hooks to manage live notification state.
- Added notification channel for live tracking in Android.
2026-03-22 16:15:48 +00:00
harukinMBP
8eb49f57d6 fix: update activity state handling to use ActivityContent with nil staleDate 2026-03-22 23:09:13 +09:00
harukinMBP
30e4e9780a fix: update activity state handling to use ActivityContent with nil staleDate 2026-03-22 23:09:13 +09:00
harukinMBP
c5d4dc3b65 feat: add automatic Live Activity updates for train position and station lock in TrainDataView and StationDiagramView 2026-03-22 23:03:07 +09:00
harukinMBP
d9574f991d feat: add automatic Live Activity updates for train position and station lock in TrainDataView and StationDiagramView 2026-03-22 23:03:07 +09:00
harukinMBP
7f2480bc01 feat: add Live Activity support for station locking in StationDiagramView 2026-03-22 23:01:00 +09:00
harukinMBP
a665bf3a74 feat: add Live Activity support for station locking in StationDiagramView 2026-03-22 23:01:00 +09:00
harukinMBP
b8372e5087 feat: add hooks for managing Live Activities for station locking and train following 2026-03-22 22:51:58 +09:00
harukinMBP
2cdcb5176b feat: add hooks for managing Live Activities for station locking and train following 2026-03-22 22:51:58 +09:00
harukinMBP
75c07f013d feat: add Live Activities support for train tracking and station locking 2026-03-22 22:49:05 +09:00
harukinMBP
44cb462595 feat: add Live Activities support for train tracking and station locking 2026-03-22 22:49:05 +09:00
harukin-expo-dev-env
46bfea4e13 feat: add androidGoogleMapsApiKey to app.json configuration 2026-03-22 13:17:53 +00:00
harukin-expo-dev-env
9bf7a735c1 feat: add androidGoogleMapsApiKey to app.json configuration 2026-03-22 13:17:53 +00:00
harukin-expo-dev-env
5a2dc8c6a8 feat: add appleTeamId to iOS configuration in app.json 2026-03-22 11:48:59 +00:00
harukin-expo-dev-env
bdffce9e6a feat: add appleTeamId to iOS configuration in app.json 2026-03-22 11:48:59 +00:00
harukin-expo-dev-env
06650d014a feat(widget): add Shortcut, Delay Info, and Felica Balance widgets
- Implemented ShortcutWidget for quick access to app features with customizable shortcuts.
- Added DelayInfoWidget to display train delay information fetched from a remote endpoint.
- Created FelicaBalanceWidget to show the balance of Felica-compatible IC cards.
- Introduced OperationInfoWidget for displaying train operation status.
- Set up shared data handling for Felica snapshots between the main app and widget.
- Configured widget assets and entitlements for proper functionality.
- Updated Info.plist and expo-target.config.js for widget deployment.
2026-03-22 11:45:58 +00:00
harukin-expo-dev-env
d4ad8c005e feat(widget): add Shortcut, Delay Info, and Felica Balance widgets
- Implemented ShortcutWidget for quick access to app features with customizable shortcuts.
- Added DelayInfoWidget to display train delay information fetched from a remote endpoint.
- Created FelicaBalanceWidget to show the balance of Felica-compatible IC cards.
- Introduced OperationInfoWidget for displaying train operation status.
- Set up shared data handling for Felica snapshots between the main app and widget.
- Configured widget assets and entitlements for proper functionality.
- Updated Info.plist and expo-target.config.js for widget deployment.
2026-03-22 11:45:58 +00:00
harukin-expo-dev-env
45feeece58 feat: FeliCa対応の可用性チェック機能を追加 2026-03-22 10:13:36 +00:00
harukin-expo-dev-env
0d0b82eee1 feat: FeliCa対応の可用性チェック機能を追加 2026-03-22 10:13:36 +00:00
harukinMBP
385c2d8b86 Update app.json build number to 58 and refine NFC error handling in ExpoFelicaReaderModule 2026-03-22 18:56:13 +09:00
harukinMBP
3e26463354 Update app.json build number to 58 and refine NFC error handling in ExpoFelicaReaderModule 2026-03-22 18:56:13 +09:00
harukinMBP
41cc70086a Implement NFC scanning functionality in ExpoFelicaReader module 2026-03-21 22:04:55 +09:00
harukinMBP
cefca15de9 Implement NFC scanning functionality in ExpoFelicaReader module 2026-03-21 22:04:55 +09:00
harukinMBP
98c71127aa Fix .gitignore: re-include modules/**/ios/ for EAS build, remove debug code 2026-03-21 20:13:34 +09:00
harukinMBP
62c84f153e Fix .gitignore: re-include modules/**/ios/ for EAS build, remove debug code 2026-03-21 20:13:34 +09:00
harukinMBP
d049d3b07d Debug: patch resolveCommand.js and apple.js with logging, add pod fallback 2026-03-21 20:03:40 +09:00
harukinMBP
83741e32fc Debug: patch resolveCommand.js and apple.js with logging, add pod fallback 2026-03-21 20:03:40 +09:00
harukinMBP
8484f15092 Enhanced autolinking debug: use external JS file 2026-03-21 19:46:25 +09:00
harukinMBP
f262226d4c Enhanced autolinking debug: use external JS file 2026-03-21 19:46:25 +09:00
harukinMBP
1afa5e4377 Add Podfile debug plugin to trace autolinking resolve 2026-03-21 19:33:22 +09:00
harukinMBP
032bcf127e Add Podfile debug plugin to trace autolinking resolve 2026-03-21 19:33:22 +09:00
harukinMBP
9f6d86b8b6 Fix ExpoFelicaReader module: restore full definition, update diagnostics 2026-03-21 17:54:54 +09:00
harukinMBP
787718c36a Fix ExpoFelicaReader module: restore full definition, update diagnostics 2026-03-21 17:54:54 +09:00
harukinMBP
72e7d63bd7 Add expo-felica-reader to dependencies for EAS build autolinking 2026-03-21 17:54:39 +09:00
harukinMBP
5457ced33d Add expo-felica-reader to dependencies for EAS build autolinking 2026-03-21 17:54:39 +09:00
harukin-expo-dev-env
b243439e78 Refactor code structure for improved readability and maintainability 2026-03-20 07:14:58 +00:00
harukin-expo-dev-env
91cad9c2c8 Refactor code structure for improved readability and maintainability 2026-03-20 07:14:58 +00:00
harukin-expo-dev-env
65f3b2a877 fix: ダークモード - 設定リスト・お気に入り並び替え・SNSリストの表示修正
- SettingTopPage: SettingList の containerStyle を直接設定して背景色を適用
- FavoiliteSettingsItem: 駅名テキストと並び替えアイコンに color: colors.text を追加
- SocialMenu: SNS一覧の ListItem に containerStyle と ListItem.Title の色を追加

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 08:46:14 +00:00
harukin-expo-dev-env
11cd8e0f40 fix: ダークモード - 設定リスト・お気に入り並び替え・SNSリストの表示修正
- SettingTopPage: SettingList の containerStyle を直接設定して背景色を適用
- FavoiliteSettingsItem: 駅名テキストと並び替えアイコンに color: colors.text を追加
- SocialMenu: SNS一覧の ListItem に containerStyle と ListItem.Title の色を追加

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-19 08:46:14 +00:00
harukin-expo-dev-env
925d162f26 feat: iPad サポートを有効化
app.json の supportsTablet を true に変更

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 08:44:36 +00:00
harukin-expo-dev-env
7c84b037ac feat: iPad サポートを有効化
app.json の supportsTablet を true に変更

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 08:44:36 +00:00
harukin-expo-dev-env
731fe504c6 perf: dev client で expo-updates をスキップ
__DEV__ 時は checkForUpdateAsync() が不要なため早期リターン

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 08:38:10 +00:00
harukin-expo-dev-env
676460353f perf: dev client で expo-updates をスキップ
__DEV__ 時は checkForUpdateAsync() が不要なため早期リターン

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 08:38:10 +00:00
harukin-expo-dev-env
942ec395f1 perf: metro transformer に experimentalImportSupport を復元
lazyImports: true と組み合わせて動作するオプション。
resolver への誤設定は除外し transformer のみに適用。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 07:42:55 +00:00
harukin-expo-dev-env
3f1da7272f perf: metro transformer に experimentalImportSupport を復元
lazyImports: true と組み合わせて動作するオプション。
resolver への誤設定は除外し transformer のみに適用。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 07:42:55 +00:00
harukin-expo-dev-env
306cf6882e chore: 不要なビルドファイル build-1773815539430.apk を削除 2026-03-18 07:12:45 +00:00
harukin-expo-dev-env
ea261f4bbb chore: 不要なビルドファイル build-1773815539430.apk を削除 2026-03-18 07:12:45 +00:00
harukin-expo-dev-env
468bb4633a chore: metro.config.js から deprecated な experimentalImportSupport を削除
SDK 55 の Metro では不要。resolver への設定は誤り。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 07:11:15 +00:00
harukin-expo-dev-env
e032dc9d70 chore: metro.config.js から deprecated な experimentalImportSupport を削除
SDK 55 の Metro では不要。resolver への設定は誤り。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 07:11:15 +00:00
harukin-expo-dev-env
2827fce560 fix: react-native-android-widget を 0.20.1 にアップグレード
RN 0.83 で CSSBackgroundDrawable が削除されたため
0.15.1 → 0.20.1 で対応

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 06:26:14 +00:00
harukin-expo-dev-env
bf4a59149a upgrade: Expo SDK 54 → 55
- app.json: newArchEnabled を削除(SDK 55 でオプション廃止)
- expo: ^54.0.0 → ^55.0.0
- react-native: 0.81.5 → 0.83.2
- react: 19.1.0 → 19.2.0
- react-dom: 19.1.0 → 19.2.0
- babel-preset-expo: ~54.0.10 → ~55.0.8
- resolutions を 19.2.0 に更新
- android/ios ネイティブディレクトリを SDK 55 で再生成

expo-doctor: 17/17 checks passed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-18 05:56:24 +00:00
harukin-expo-dev-env
cf611c6c8d feat: 新アーキテクチャへの移行準備と依存関係の更新
- app.json: newArchEnabledをtrueに設定し、kotlinVersionを2.1.20に更新
- babel.config.js: lazyImportsを有効化し、react-native-reanimated/pluginを追加
- metro.config.js: experimentalImportSupportを有効化
- package.json: react-native-reanimated-carouselを4.0.3に更新
- 新しいプラグインwith-android-local-propertiesを追加
- 新しいドキュメントnew-architecture-migration-plan.mdを作成
- その他のファイルでの軽微な修正
2026-03-18 05:43:21 +00:00
harukin-expo-dev-env
b7a09eda6e feat: Expo SDK 53 → 54 upgrade (React Native 0.81.5)
- expo: ^53.0.0 → ^54.0.0 (54.0.33)
- react-native: 0.79.6 → 0.81.5
- react: 19.0.0 → 19.1.0
- babel-preset-expo: ~13.0.0 → ~54.0.10
- @expo/vector-icons: ^14.0.2 → ^15.0.3
- react-native-reanimated: ~3.17.4 → ~4.1.1
- react-native-worklets: 新規追加 (reanimated v4 必須 peer dep)
- expo-system-ui: 新規追加 (userInterfaceStyle 対応)

変更内容:
- app.json: newArchEnabled を expo-build-properties から expo root へ移動
- babel.config.js: React Compiler を明示的に無効化
- UpdateAsync.ts: expo-updates reloadAsync API 変更に対応
- TopMenuButton.tsx: @expo/vector-icons deep path import を named import に変更
- trainIconStatus.tsx: 未使用の Icon deep path import を削除
- package.json: @types/react ~19.0.10 → ~19.1.4、doctor exclude 追加
2026-03-18 01:51:44 +00:00
harukin-expo-dev-env
10df37d0a2 feat: Expo SDK 52→53 upgrade + full dark mode support
- Upgrade Expo SDK 52→53 (React 18→19, RN 0.76→0.79)
- Remove deprecated packages (native-base, react-native-elements)
- Migrate to @rneui/themed 5.0.0 + modular vector icons
- Fix breaking changes: defaultProps, BackHandler, notifications, key props
- Add Babel plugin for font scaling (replaces Text.defaultProps)
- Configure expo-font for native font preloading
- Add complete dark mode theme system (lib/theme/)
  - AppThemeProvider + useThemeColors hook
  - Light/dark/fixed color token definitions
  - Migrate ~60 files across all screens to use theme colors
- Set userInterfaceStyle to "automatic" for system dark mode
2026-03-17 22:19:46 +00:00
harukin-expo-dev-env
58b2049b24 Merge commit '29bc89f1836ea3a5ec7092ed78c60d01a89569ed' into develop 2026-03-17 10:22:49 +00:00
harukin-expo-dev-env
29bc89f183 feat: Felicaウィジェット ディープリンク対応 + 通知タップルーティング\n\n- FelicaQuickAccessWidget: OPEN_URI で jrshikoku://open/felica へディープリンク\n- App.tsx: ディープリンクハンドラ (routeFromUrl → FelicaHistoryPage)\n- Apps.tsx: linking config + rootNavigationRef 接続\n- lib/rootNavigation.ts: グローバルナビゲーション ref 追加\n- useNotifications.tsx: 通知タップ時のルーティング (遅延速報EX/怪レい列車/運行情報)\n- docs/widget-overview.md: ウィジェット機能の概要ドキュメント" 2026-03-17 10:00:21 +00:00
harukin-expo-dev-env
adfe69b72f feat: expo-audioによる音声再生機能を追加
- expo-audio依存を追加、app.jsonにプラグイン設定
- 設定画面ヘッダー画像タップで音声再生
- expo-assetでOTA対応のアセット解決 + file:// prefix除去(SDK52バグ回避)
- 音声ファイルはmp3のみ保持(wav/m4a/ogg削除)
- SDK53以降でinterruptionMode: duckOthers対応予定
2026-03-17 09:43:54 +00:00
harukin-expo-dev-env
f387479ff7 feat(Felica): implement Felica quick access widget and update snapshot handling 2026-03-17 06:17:14 +00:00
harukin-expo-dev-env
684aaeb92f fix(webViewInjectjavascript): improve operation list fetching logic to handle null data 2026-03-17 02:33:52 +00:00
harukin-expo-dev-env
0a8d5ca2b6 feat(FixedContentBottom): add buttons for ダイヤグラフ and 運用チャート with navigation 2026-03-17 02:32:21 +00:00
harukin-expo-dev-env
48b38a2fa3 fix(TrainDataSources): update URL opening logic and hide sheet on link click
fix(ListViewItem): ensure source index wraps around for vehicle sources display
2026-03-15 08:28:47 +00:00
harukin-expo-dev-env
aeb043cac5 fix(ListViewItem): update dependency array in useEffect for source index and fade animation 2026-03-15 08:07:24 +00:00
harukin-expo-dev-env
2c6ceb73d8 fix(ScrollingDescription): round width values for text and container layout 2026-03-15 07:57:15 +00:00
harukin-expo-dev-env
f214f45fef feat(felica): add vehicle source display and selection modal in StationDiagramView and ListView components 2026-03-15 06:11:48 +00:00
harukin-expo-dev-env
616846e1cd feat(felica): enhance history row with balance calculation and long press copy functionality 2026-03-15 03:41:30 +00:00
harukin-expo-dev-env
be88a46df1 feat(felica): update build and version codes, enhance Felica history page with card type display and scanning functionality 2026-03-15 03:01:07 +00:00
harukin-expo-dev-env
7386ec09fc feat(felica): update station label handling for non-transit process types 2026-03-14 17:23:24 +00:00
harukin-expo-dev-env
a068dabc75 Merge commit '8fda56793adb6378e5b253738e6b79e848ca8ec1' into develop 2026-03-14 13:57:37 +00:00
harukin-expo-dev-env
8fda56793a Merge commit '83c7dbde630da01d608558c687ca178bbae5e020' into feature/felica-example 2026-03-14 13:55:27 +00:00
harukin-expo-dev-env
83c7dbde63 Merge commit '676fbf7b646a0ffde785f0bec47f9a3b582b4df3' into develop 2026-03-14 13:55:18 +00:00
harukin-expo-dev-env
676fbf7b64 feat: add nearest station tracking and display functionality 2026-03-14 13:55:05 +00:00
harukin-expo-dev-env
8bc726628a feat(felica): add station name lookup from FeliCa history
- Add regionCode (byte[15]) to history entry in Android/iOS native code
- areaCode = regionCode >> 6 determines the transit area (0-3)
- stationId = (areaCode<<16) | (lineCode<<8) | stationCode
- Add lib/felicaStationMap.ts with 5900+ station entries from
  metrodroid/felica_stations.db3 (GPL-3.0)
- FelicaHistoryPage now shows station names instead of raw L/S codes
- Falls back to raw code format if station is not in the database
2026-03-14 13:21:47 +00:00
harukin-expo-dev-env
ee22d21862 fix: FeliCa履歴サービスコードを0x090Dから0x090Fに修正、バイトレイアウト修正
- 履歴サービスコード修正 (Android/iOS): 0x090D → 0x090F
  - 0x090F が交通系IC共通の利用履歴サービスコード(tattn/NFCReader等で確認)
  - 0x090D は存在しないサービスで常に空配列が返っていた
- parseHistoryBlock バイトレイアウト修正 (Android/iOS):
  - [6] → 入場路線コード(旧: 入場時刻30分単位)
  - [7] → 入場駅コード  (旧: 出場時刻30分単位)
  - [8] → 出場路線コード(旧: 残高低位)
  - [9] → 出場駅コード  (旧: 残高高位)
  - [10-11] → 残高LE    (rolex: 路線コード)
  - [13] → 会社コード   (旧: byte[14])
- FelicaHistoryEntry から entryHour/entryMinute/exitHour/exitMinute 削除
- FelicaHistoryPage UI から時刻表示を削除、路線+駅コード表示に更新
- PiTaPa は systemCode 0003 (交通系IC共通) を実装しているため追加設定不要
2026-03-14 11:27:51 +00:00
harukin-expo-dev-env
3894694c9b fix: podspecのpackage.jsonパスとsource_filesを修正、.gitignoreのios除外を修正 2026-03-14 09:56:12 +00:00
harukin-expo-dev-env
ec4db3de9b feat: implement FeliCa history page and update navigation 2026-03-14 08:54:35 +00:00
harukin-expo-dev-env
6a66429431 feat: add FeliCa transaction history retrieval and update data structures 2026-03-14 08:53:23 +00:00
harukin-expo-dev-env
c2d3645b86 fix: update service code for FeliCa balance retrieval 2026-03-13 16:43:08 +00:00
harukin-expo-dev-env
ea94e4cf0d fix: update FeliCa NFC reader configuration and correct balance parsing comments 2026-03-13 01:54:06 +00:00
harukin-expo-dev-env
4a7e481bfd test 2026-03-12 14:39:57 +00:00
harukin-expo-dev-env
720123b1e5 Merge commit 'faf452166c8e89cba5405917fbf6a2c949a042ec' into feature/felica-example 2026-03-12 12:49:13 +00:00
harukin-expo-dev-env
faf452166c Merge commit '0eb7d70caaedb72fc13fca23eff49113a518e3de' into develop 2026-03-12 10:04:13 +00:00
harukin-expo-dev-env
0eb7d70caa fix: remove redundant train number checks in getInfluencedTrainData function 2026-03-12 10:04:06 +00:00
harukin-expo-dev-env
4296eada04 Merge commit '983d48a1fe4a7538f47f1acd47bab1216ab6de5f' into develop 2026-03-11 08:41:54 +00:00
harukin-expo-dev-env
983d48a1fe fix: update cache expiration times for various data types 2026-03-11 08:41:47 +00:00
harukin-expo-dev-env
556f98faac Merge commit '08e052f291055ba2a6d5498a416cf9e3a7ff708a' into develop 2026-03-11 08:41:24 +00:00
harukin-expo-dev-env
08e052f291 feat: add GridMiniSign component and integrate it into SortGridCard 2026-03-11 08:41:14 +00:00
harukin-expo-dev-env
ab92cc7a85 feat: unify station source management and enhance carousel UI modes 2026-03-10 15:08:05 +00:00
harukin-expo-dev-env
d50d77aa44 Merge commit '3ea83008466a5402733d435f7b644061c6768c0f' into develop 2026-03-10 13:37:36 +00:00
harukin-expo-dev-env
3ea8300846 Merge commit 'beb9b21e1cc8ab58e57d7fd43826a7875458f5bd' into feature/elesite-next 2026-03-10 08:50:32 +00:00
harukin-expo-dev-env
beb9b21e1c Merge commit '191dd76627eff79bfd78f238239c211301898b47' into develop 2026-03-10 08:50:24 +00:00
harukin-expo-dev-env
7d7b1849dd feat: add last reported timestamp and update TrainDataSources for elesite integration 2026-03-10 02:56:23 +00:00
harukin-expo-dev-env
30d1111768 feat: enhance HeaderText component with elesite integration and improve layout handling 2026-03-10 01:17:01 +00:00
harukin-expo-dev-env
cc15e6a1ee feat: update elesite integration to prioritize non-empty formation units and improve sorting logic 2026-03-08 08:52:58 +00:00
harukin-expo-dev-env
66650764df feat: add elesite integration and configuration settings
- Introduced useElesite hook for managing elesite data and settings.
- Added elesite logo asset.
- Updated types to include elesite data structures.
- Enhanced TrainMenuProvider to manage elesite usage settings.
- Implemented data fetching and caching for elesite information.
- Added utility functions to retrieve train information from elesite data.
2026-03-07 16:24:07 +00:00
harukin-expo-dev-env
191dd76627 Merge commit 'affe907dfdb24ece50180ecd8bff5515011cdc22' into feature/station-card-sortable 2026-03-07 04:38:01 +00:00
harukin-expo-dev-env
affe907dfd Merge commit '50822c6c7464c7071a828d510293b4aae9c4e86c' into develop 2026-03-07 04:37:54 +00:00
harukin-expo-dev-env
50822c6c74 Merge commit '9cc7b0d4afc2efad226eb044d06d07f4beb53226' into patch/6.x 2026-03-07 04:37:44 +00:00
harukin-expo-dev-env
2142d90141 カルーセルにソート機能を追加し、グリッド表示を実装。ソートモード中の動作を改善し、アニメーションを追加。 2026-03-07 04:37:35 +00:00
harukin-expo-dev-env
9cc7b0d4af 遅延による日付の調整ロジックを追加 2026-03-07 04:37:23 +00:00
harukin-expo-dev-env
0917bc0a74 Merge commit '0f52441b17081cd78dd706c84b66f0e40cf5fd2f' into develop 2026-03-06 15:47:15 +00:00
harukin-expo-dev-env
0f52441b17 LEDの情報の移動速度が爆速になってた致命的なバグを修正 2026-03-06 15:03:29 +00:00
harukin-expo-dev-env
bf27904d7c Merge commit '3a182d4650453d9a84227b83333901d71787a55c' into develop 2026-03-06 14:52:53 +00:00
harukin-expo-dev-env
3a182d4650 6.2.1.1 2026-03-06 14:52:44 +00:00
harukin-expo-dev-env
a16588c70f Merge commit 'cf1b2f763e0143dd40c00dd340e3c224db92ad65' into patch/6.x 2026-03-06 14:52:09 +00:00
harukin-expo-dev-env
cf1b2f763e Merge commit '381873b9261a472c41f25099bbf43665fa0e7240' into develop 2026-03-06 14:51:54 +00:00
harukin-expo-dev-env
381873b926 列車情報詳細のタイプタグに色とフォントスタイルを追加し、スクロール説明コンポーネントのスクロール速度を改善 2026-03-06 14:51:43 +00:00
harukin-expo-dev-env
9c14a871e8 運用情報のソートロジックを改善し、train_idsおよびrelated_train_idsに基づいてunit_idsを収集する機能を追加 2026-03-06 14:03:11 +00:00
harukin-expo-dev-env
d7f227d5e5 運用Hubの列車データ取得ロジックを改善し、方向に基づいて運用番号を正しい順序でソートする機能を追加 2026-03-06 10:37:00 +00:00
harukin-expo-dev-env
c1accbb204 鉄道運用Hubのデータ表示を改善し、進行方向バナーと基準方向ラベルを追加。アニメーションで路線を切り替える機能を実装。 2026-03-06 09:03:49 +00:00
harukin-expo-dev-env
ac2548e7b6 運用Hub情報の判定ロジックを改善し、車番が空でないエントリのみを考慮するように修正 2026-03-06 07:24:58 +00:00
harukin-expo-dev-env
87f1cf2b1e DataSourceAccordionCardコンポーネントを追加し、鉄道運用Hubのデータソース設定を改善 2026-03-04 14:55:18 +00:00
harukin-expo-dev-env
c49aeeb331 HUBロゴのSVGおよびPNGファイルを追加し、TrainSourcesPanelおよびTrainDataSourcesコンポーネントでの使用を更新 2026-03-04 14:54:52 +00:00
harukin-expo-dev-env
506dc7157e データ編集権限の取得URLを更新し、レスポンスからの権限情報の取得方法を修正 2026-03-04 08:59:09 +00:00
harukin-expo-dev-env
66f5744d51 鉄道運用Hubに関連するコンポーネントの名称を変更し、カスタム列車データの処理を追加 2026-03-04 07:43:49 +00:00
harukin-expo-dev-env
d4a9c4d7d8 データソース設定コンポーネントに戻るボタンの位置を設定し、条件付きレンダリングの構造を修正 2026-03-03 10:43:33 +00:00
harukin-expo-dev-env
f2d0b060b6 情報ソース設定へのアクセス権限管理機能を追加し、データソースの利用権限を実装 2026-03-03 10:37:18 +00:00
harukin-expo-dev-env
38191be0d3 UnyohubのON/OFF管理機能を追加し、追加ソースがオフの場合の挙動を修正 2026-03-03 09:22:03 +00:00
harukin-expo-dev-env
df2e4145a2 運用情報ソースの表示パネルを追加し、運用hubデータの取得機能を実装 2026-03-03 09:18:36 +00:00
harukin-expo-dev-env
d6ab19d4b1 Merge commit '7b7ec45bfa657c67bd11ccb73c6299e71b1cee6d' into develop 2026-03-03 06:58:29 +00:00
harukin-expo-dev-env
7b7ec45bfa 停止中の点滅アニメーションを動的に注入する機能を追加 2026-03-03 02:52:25 +00:00
harukin-expo-dev-env
29941f515f 暫定コミット 2026-03-03 02:16:37 +00:00
harukin-expo-dev-env
625ee1d786 FeliCaリーダー機能を実装し、残高情報を取得するためのインターフェースを追加 2026-03-02 13:34:11 +00:00
harukin-expo-dev-env
a9bb366308 Merge commit '657ee7494bd3109f05ae73c5e8824e12820ecb9c' into develop 2026-03-02 13:33:16 +00:00
harukin-expo-dev-env
657ee7494b ScrollingDescriptionコンポーネントを追加し、テキストをスクロール表示する機能を実装 2026-03-02 12:51:10 +00:00
harukin-expo-dev-env
b60a43f25c setReloadの呼び出しをsetTimeoutで遅延させ、スクリプト実行の完了後に処理を行うように変更 2026-03-02 12:50:56 +00:00
harukin-expo-dev-env
7004eeefad Add station data, train icon mapping, and train type configuration
- Introduced `stationData.ts` to store station information including names, numbers, and features.
- Created `trainIconMap.ts` for mapping train numbers to their respective image URLs, including dynamic URLs for special trains.
- Added `trainTypeConfig.ts` to define display settings for various train types, including colors and labels for different categories.
2026-03-02 12:38:43 +00:00
harukin-expo-dev-env
413ef4acb3 不要なコメントを削除し、列番データの処理を簡素化 2026-03-02 09:37:04 +00:00
harukin-expo-dev-env
7f3a1493ef InjectJavascriptOptionsインターフェースを追加し、injectJavascriptData関数の引数をオブジェクト形式に変更 2026-03-02 09:06:25 +00:00
harukin-expo-dev-env
8e64932a01 useIntervalとwebViewInjectjavascriptでのデータ取得処理を最適化し、localStorageキャッシュを実装。バックグラウンド復帰時にデータを即時再取得する機能を追加。 2026-03-02 05:59:01 +00:00
harukin-expo-dev-env
9036e7a8c1 古いWebViewの互換性向上のため、onclick属性の処理を改善し、イベントの伝播を制御。PopUpMenuとShowTrainTimeInfo関数をObject.definePropertyでロック。 2026-03-02 04:05:48 +00:00
harukin-expo-dev-env
1bf4a6991d Font Awesomeの依存を削除し、インラインSVGに置き換え。全WebView対応のバッジ表示を実装。 2026-03-02 03:40:09 +00:00
harukin-expo-dev-env
03b9080c06 Font Awesomeの非同期読み込みを追加し、lodash依存を削除。軽量な変更検出ユーティリティを実装し、データ取得処理を最適化。 2026-03-02 03:27:22 +00:00
harukin-expo-dev-env
4952e32e65 アイコンの反転表示対応 2026-02-20 17:06:01 +00:00
harukin-expo-dev-env
ff46c6ac8f 各コンポーネントでキャッシュバスティングを実装し、運用hubデータの取得時にタイムスタンプを追加。列車情報の表示をスクロール可能な形式に変更。 2026-02-13 15:49:04 +00:00
harukin-expo-dev-env
70bbb4ed5a Merge commit '0a4c61071dfe53f8669e724d193d5a815b0a2959' into feature/add-train-hub-connection 2026-02-09 04:06:43 +00:00
harukin-expo-dev-env
0a4c61071d Merge commit '7a58a9524a60ad90011596e4c7ba73edab88a093' into develop 2026-02-09 04:06:35 +00:00
harukin-expo-dev-env
7a58a9524a Merge commit '6bcb3fcaf10c324eea7db03c42241ef5c2613294' into patch/6.x 2026-02-09 04:06:28 +00:00
harukin-expo-dev-env
6bcb3fcaf1 運休表示のテキストの簡略化 2026-02-09 04:06:12 +00:00
harukin-expo-dev-env
d921d7f8b6 行き先名の取得ロジックを修正し、表示を列車名から行き先名に変更 2026-02-09 03:55:03 +00:00
harukin-expo-dev-env
0a677c908d 列車運用hubの設定を追加し、データ取得ロジックを実装 2026-02-09 03:42:30 +00:00
harukin-expo-dev-env
a42c0871bd unyohub連携仮作成 2026-02-07 17:19:16 +00:00
harukin-expo-dev-env
4eea97ed1f LEDの行き先表示に運休表示を追加 2026-02-07 12:29:19 +00:00
harukin-expo-dev-env
5b0de88218 時刻表テキストの結合条件の整理 2026-02-07 12:28:39 +00:00
harukin-expo-dev-env
765b0d72b7 Merge commit '09c00202247c0c97f1d8c324c2cc49214eee1393' into develop 2026-02-07 09:01:56 +00:00
harukin-expo-dev-env
0e9b049707 Merge commit '935b63f6cee9d84530a5e7f6e8f55bcc90f8a168' into develop 2026-02-01 13:18:43 +00:00
299 changed files with 24727 additions and 7602 deletions

10
.gitignore vendored
View File

@@ -4,7 +4,7 @@ node_modules/**/*
.pnp.js
# Expo
.expo/*
.expo/
.expo-shared
# Build outputs
@@ -52,3 +52,11 @@ Thumbs.db
*.log
*.tmp
.cache/
android/
!modules/**/android/
ios/
!modules/**/ios/
*.ipa
*.apk
*.aab

141
App.tsx
View File

@@ -1,5 +1,10 @@
import React, { useEffect } from "react";
import { Platform, UIManager } from "react-native";
import {
IS_LOW_DENSITY,
DEX_SCALE,
useRealWindowDimensions,
} from "./utils/dexDimensionOverride";
import { Linking, Platform, UIManager, View } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import "./utils/disableFontScaling"; // グローバルなフォントスケーリング無効化
import { AppContainer } from "./Apps";
@@ -10,7 +15,7 @@ import { CurrentTrainProvider } from "./stateBox/useCurrentTrain";
import { AreaInfoProvider } from "./stateBox/useAreaInfo";
import { BusAndTrainDataProvider } from "./stateBox/useBusAndTrainData";
import { AllTrainDiagramProvider } from "./stateBox/useAllTrainDiagram";
import { SheetProvider } from "react-native-actions-sheet";
import { SheetProvider, SheetManager } from "react-native-actions-sheet";
import "./components/ActionSheetComponents/sheets";
import { TrainDelayDataProvider } from "./stateBox/useTrainDelayData";
import { SafeAreaProvider } from "react-native-safe-area-context";
@@ -20,6 +25,9 @@ import { buildProvidersTree } from "./lib/providerTreeProvider";
import { StationListProvider } from "./stateBox/useStationList";
import { NotificationProvider } from "./stateBox/useNotifications";
import { UserPositionProvider } from "./stateBox/useUserPosition";
import { rootNavigationRef } from "./lib/rootNavigation";
import { AppThemeProvider } from "./lib/theme";
import StatusbarDetect from "./StatusbarDetect";
LogBox.ignoreLogs([
"ViewPropTypes will be removed",
@@ -37,6 +45,85 @@ export default function App() {
UpdateAsync();
}, []);
useEffect(() => {
const openFelicaPage = (retryCount = 0) => {
if (!rootNavigationRef.isReady()) {
if (retryCount < 8) {
setTimeout(() => openFelicaPage(retryCount + 1), 250);
}
return;
}
rootNavigationRef.navigate("topMenu", {
screen: "setting",
params: {
screen: "FelicaHistoryPage",
},
} as any);
};
const navigateWhenReady = (
callback: () => void,
url: string,
retryCount = 0
) => {
if (!rootNavigationRef.isReady()) {
if (retryCount < 8) {
setTimeout(() => navigateWhenReady(callback, url, retryCount + 1), 250);
}
return;
}
callback();
};
const routeFromUrl = (url: string, retryCount = 0) => {
const normalized = (url || "").toLowerCase();
if (!normalized) return;
if (
normalized.includes("felicahistorypage") ||
normalized.includes("open/felica")
) {
navigateWhenReady(() => openFelicaPage(), url, retryCount);
} else if (normalized.includes("open/traininfo")) {
navigateWhenReady(() => {
rootNavigationRef.navigate("topMenu", { screen: "menu" } as any);
setTimeout(() => {
SheetManager.show("JRSTraInfo");
}, 450);
}, url, retryCount);
} else if (normalized.includes("open/operation")) {
navigateWhenReady(() => {
rootNavigationRef.navigate("information" as any);
}, url, retryCount);
} else if (normalized.includes("open/settings")) {
navigateWhenReady(() => {
rootNavigationRef.navigate("topMenu", {
screen: "setting",
} as any);
}, url, retryCount);
} else if (normalized.includes("open/topmenu")) {
navigateWhenReady(() => {
rootNavigationRef.navigate("topMenu", {
screen: "menu",
} as any);
}, url, retryCount);
}
};
Linking.getInitialURL().then((url) => {
if (url) routeFromUrl(url);
});
const sub = Linking.addEventListener("url", ({ url }) => {
routeFromUrl(url);
});
return () => {
sub.remove();
};
}, []);
const ProviderTree = buildProvidersTree([
AllTrainDiagramProvider,
NotificationProvider,
@@ -51,14 +138,46 @@ export default function App() {
SheetProvider,
]);
return (
<DeviceOrientationChangeProvider>
<SafeAreaProvider>
<GestureHandlerRootView style={{ flex: 1 }}>
<ProviderTree>
<AppContainer />
</ProviderTree>
</GestureHandlerRootView>
</SafeAreaProvider>
</DeviceOrientationChangeProvider>
<AppThemeProvider>
<DeviceOrientationChangeProvider>
<SafeAreaProvider>
<StatusbarDetect />
<DensityScaleWrapper>
<GestureHandlerRootView style={{ flex: 1 }}>
<ProviderTree>
<AppContainer />
</ProviderTree>
</GestureHandlerRootView>
</DensityScaleWrapper>
</SafeAreaProvider>
</DeviceOrientationChangeProvider>
</AppThemeProvider>
);
}
/**
* 低密度ディスプレイDeX等で全体を transform scale で拡大。
* Dimensions.get をパッチ済みなので、コンポーネントは内部サイズに合わせてレイアウトする。
*/
function DensityScaleWrapper({ children }: { children: React.ReactNode }) {
const { width, height } = useRealWindowDimensions();
if (!IS_LOW_DENSITY) {
return <>{children}</>;
}
return (
<View style={{ width, height, overflow: "hidden" }}>
<View
style={{
width: width / DEX_SCALE,
height: height / DEX_SCALE,
transform: [{ scale: DEX_SCALE }],
transformOrigin: "top left",
}}
>
{children}
</View>
</View>
);
}

127
Apps.tsx
View File

@@ -1,14 +1,22 @@
import React from "react";
import { NavigationContainer, NavigationContainerRef } from "@react-navigation/native";
import { NavigationContainer, DarkTheme, DefaultTheme } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Platform } from "react-native";
import { Animated, Platform, ActivityIndicator, View, StyleSheet } from "react-native";
import { useNavigationState } from "@react-navigation/native";
import { useFonts } from "expo-font";
import { LinearGradient } from "expo-linear-gradient";
import TNDView from "./ndView";
import { initIcon } from "./lib/initIcon";
import { Top } from "./Top";
import { MenuPage } from "./MenuPage";
import { useAreaInfo } from "./stateBox/useAreaInfo";
import { useTrainMenu } from "./stateBox/useTrainMenu";
import lineColorList from "./assets/originData/lineColorList";
import { stationIDPair } from "./lib/getStationList";
import "./components/ActionSheetComponents/sheets";
import { rootNavigationRef } from "./lib/rootNavigation";
import { fixedColors } from "./lib/theme/colors";
import { useThemeColors } from "./lib/theme";
type RootTabParamList = {
positions: undefined;
@@ -25,10 +33,60 @@ type TabProps = {
isInfo?: boolean;
};
const Tab = createBottomTabNavigator<RootTabParamList>();
export function AppContainer() {
const Tab = createBottomTabNavigator<RootTabParamList>();
const { areaInfo, areaIconBadgeText, isInfo } = useAreaInfo();
const navigationRef = React.useRef<NavigationContainerRef<RootTabParamList>>(null);
const { selectedLine } = useTrainMenu();
const [isExtraWindowOpen, setIsExtraWindowOpen] = React.useState(false);
// フェードアニメーション用 (0=通常, 1=追加ウィンドウ青)
const fadeAnim = React.useRef(new Animated.Value(0)).current;
React.useEffect(() => {
Animated.timing(fadeAnim, {
toValue: isExtraWindowOpen ? 1 : 0,
duration: 300,
useNativeDriver: false,
}).start();
}, [isExtraWindowOpen]);
const lineColor = selectedLine && stationIDPair[selectedLine]
? lineColorList[stationIDPair[selectedLine]]
: null;
const darkenHex = (hex: string, factor: number) => {
const h = hex.replace("#", "");
const r = Math.round(parseInt(h.slice(0, 2), 16) * factor);
const g = Math.round(parseInt(h.slice(2, 4), 16) * factor);
const b = Math.round(parseInt(h.slice(4, 6), 16) * factor);
return `#${[r, g, b].map((v) => Math.min(255, v).toString(16).padStart(2, "0")).join("")}`;
};
const { isDark } = useThemeColors();
const lineColorDark = lineColor ? darkenHex(lineColor, 0.78) : null;
const linking = {
prefixes: ["jrshikoku://"],
config: {
screens: {
positions: {
screens: {
Apps: "positions/apps",
},
},
topMenu: {
screens: {
menu: "topMenu/menu",
setting: {
screens: {
settingTopPage: "topMenu/setting",
FelicaHistoryPage: "topMenu/setting/FelicaHistoryPage",
},
},
},
},
information: "information",
},
},
};
const getTabProps = (
name: keyof RootTabParamList,
@@ -53,19 +111,68 @@ export function AppContainer() {
"JNR-font": require("./assets/fonts/JNRfont_pict.ttf"),
"DiaPro": require("./assets/fonts/DiaPro-Regular.otf"),
});
if (!fontLoaded) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator size="large" />
</View>
);
}
return (
<NavigationContainer ref={navigationRef}>
<NavigationContainer
ref={rootNavigationRef}
linking={linking}
theme={isDark ? DarkTheme : DefaultTheme}
onStateChange={(state) => {
const hasExtra = state?.routes?.some((r) => (r.state?.index ?? 0) > 0) ?? false;
setIsExtraWindowOpen(hasExtra);
}}
>
{/* @ts-expect-error - Tab.Navigator type definition issue */}
<Tab.Navigator
initialRouteName="topMenu"
screenOptions={{
lazy: false,
tabBarHideOnKeyboard: Platform.OS === "android",
animation: "shift",
screenOptions={({ route }) => {
const showGradient = route.name === "positions" && !!lineColor && !!lineColorDark;
const defaultBg = isDark ? "#1c1c1e" : "white";
const defaultActive = isDark ? "#ffffff" : "#007AFF";
const defaultInactive = isDark ? "#8e8e93" : "#8e8e93";
return {
lazy: false,
tabBarHideOnKeyboard: Platform.OS === "android",
animation: Platform.OS === "ios" ? "none" : "shift",
sceneContainerStyle: { backgroundColor: defaultBg },
tabBarActiveTintColor: (showGradient || isExtraWindowOpen) ? "white" : defaultActive,
tabBarInactiveTintColor: (showGradient || isExtraWindowOpen) ? "rgba(255,255,255,0.75)" : defaultInactive,
tabBarStyle: { backgroundColor: "transparent" },
tabBarBackground: () => (
<View style={{ flex: 1 }}>
{/* 路線カラー or デフォルト背景 */}
{showGradient ? (
<LinearGradient
colors={[lineColor!, lineColorDark!]}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
style={{ ...StyleSheet.absoluteFillObject }}
/>
) : (
<View style={{ ...StyleSheet.absoluteFillObject, backgroundColor: defaultBg }} />
)}
{/* 追加ウィンドウ時の青グラデーション(フェードイン/アウト) */}
<Animated.View style={{ ...StyleSheet.absoluteFillObject, opacity: fadeAnim }}>
<LinearGradient
colors={[fixedColors.primary, fixedColors.primaryDark]}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
style={{ flex: 1 }}
/>
</Animated.View>
</View>
),
};
}}
>
<Tab.Screen
{...getTabProps("positions", "走行位置", "barchart", "AntDesign")}
{...getTabProps("positions", "走行位置", "bar-chart", "AntDesign")}
component={Top}
/>
<Tab.Screen

View File

@@ -1,14 +1,16 @@
import React, { CSSProperties } from "react";
import { BackHandler, View, ViewProps } from "react-native";
import { Alert, BackHandler, View, ViewProps } from "react-native";
import { WebView } from "react-native-webview";
import { BigButton } from "./components/atom/BigButton";
import { useFocusEffect, useNavigation } from "@react-navigation/native";
import { useThemeColors } from "@/lib/theme";
export default ({ route }) => {
if (!route.params) {
return null;
}
const { uri, useExitButton = true } = route.params;
const { goBack } = useNavigation();
const { fixed } = useThemeColors();
const webViewRef = React.useRef<WebView>(null);
const [canGoBack, setCanGoBack] = React.useState(false);
@@ -23,17 +25,26 @@ export default ({ route }) => {
return true;
};
BackHandler.addEventListener("hardwareBackPress", onHardwareBack);
return () => BackHandler.removeEventListener("hardwareBackPress", onHardwareBack);
const subscription = BackHandler.addEventListener("hardwareBackPress", onHardwareBack);
return () => subscription.remove();
}, [canGoBack, goBack])
);
return (
<View style={styles}>
<View style={{ height: "100%", backgroundColor: fixed.primary }}>
<WebView
source={{ uri }}
contentMode="mobile"
allowsBackForwardNavigationGestures
ref={webViewRef}
onNavigationStateChange={(navState) => setCanGoBack(navState.canGoBack)}
onNavigationStateChange={(navState) => {
setCanGoBack(navState.canGoBack);
if (navState.url === "https://unyohub.2pd.jp/integration/succeeded.php") {
goBack();
Alert.alert("鉄道運用HUBへの投稿完了", "運用HUBからのこのアプリへのデータ反映には暫く時間がかかりますので、しばらくお待ちください。", [
{ text: "完了" },
]);
}
}}
onMessage={(event) => {
const { data } = event.nativeEvent;
const { type } = JSON.parse(data);
@@ -45,7 +56,3 @@ export default ({ route }) => {
</View>
);
};
const styles: ViewProps["style"] = {
height: "100%",
backgroundColor: "#0099CC",
};

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useRef, useState } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useWindowDimensions, Platform } from "react-native";
import { useWindowDimensions, Platform, useColorScheme } from "react-native";
import Constants from "expo-constants";
import { useResponsive } from "@/lib/responsive";
import { Dimensions, StatusBar } from "react-native";
@@ -27,9 +28,12 @@ const Stack = createStackNavigator();
export function MenuPage() {
const { favoriteStation, setFavoriteStation } = useFavoriteStation();
const { height, width } = useWindowDimensions();
const { verticalScale } = useResponsive();
const tabBarHeight = useBottomTabBarHeight();
const navigation = useNavigation<any>();
const { addListener } = navigation;
const isDark = useColorScheme() === "dark";
const bgColor = isDark ? "#1c1c1e" : "#ffffff";
useEffect(() => {
AS.getItem(STORAGE_KEYS.START_PAGE)
.then((res) => {
@@ -56,15 +60,21 @@ export function MenuPage() {
const scrollRef = useRef(null);
const [mapMode, setMapMode] = useState(false);
const [mapHeight, setMapHeight] = useState(0);
const mapHeightRef = useRef(0);
const favoriteStationRef = useRef(favoriteStation);
useEffect(() => {
const MapHeight =
height -
tabBarHeight +
(Platform.OS == "android" ? Constants.statusBarHeight : 0) -
100 -
((((width / 100) * 80) / 20) * 9 + 10 + 30);
verticalScale(100) -
((((width / 100) * 80) / 20) * 9 + verticalScale(10) + verticalScale(30));
setMapHeight(MapHeight);
mapHeightRef.current = MapHeight;
}, [height, tabBarHeight, width]);
useEffect(() => {
favoriteStationRef.current = favoriteStation;
}, [favoriteStation]);
const [MapFullHeight, setMapFullHeight] = useState(0);
useEffect(() => {
const MapFullHeight =
@@ -75,15 +85,15 @@ export function MenuPage() {
}, [height, tabBarHeight, width]);
useEffect(() => {
const unsubscribe = addListener("tabPress", (e) => {
scrollRef.current.scrollTo({
y: mapHeight - 80,
scrollRef.current?.scrollTo({
y: mapHeightRef.current - verticalScale(80),
animated: true,
});
setMapMode(false);
AS.getItem(STORAGE_KEYS.FAVORITE_STATION)
.then((d) => {
const returnData = JSON.parse(d);
if (favoriteStation.toString() != d) {
if (favoriteStationRef.current.toString() != d) {
setFavoriteStation(returnData);
}
})
@@ -95,9 +105,12 @@ export function MenuPage() {
});
return unsubscribe;
}, [navigation, mapHeight, favoriteStation, setFavoriteStation]);
}, [navigation]);
return (
<Stack.Navigator id={null}>
<Stack.Navigator
id={null}
screenOptions={{ cardStyle: { backgroundColor: bgColor } }}
>
<Stack.Screen
name="menu"
options={{

View File

@@ -175,7 +175,63 @@ export const API_ENDPOINTS = {
};
```
## 📱 ビルド
## <EFBFBD> 外部からのアプリ起動(ディープリンク)
本アプリは複数の方法で外部から起動・画面遷移できます。
### カスタムURLスキーム
スキーム `jrshikoku://` を使って特定の画面を直接開くことができます。
| URL | 遷移先 |
|---|---|
| `jrshikoku://open/felica` | FeliCa履歴ページ |
| `jrshikoku://open/traininfo` | 遅延速報EX |
| `jrshikoku://open/operation` | 運行情報 |
| `jrshikoku://open/settings` | 設定 |
| `jrshikoku://open/topmenu` | トップメニュー |
| `jrshikoku://positions/apps` | 走行位置 |
URLの処理は `App.tsx``routeFromUrl` で実装されています。
### Androidウィジェット
ホーム画面に配置可能なウィジェットからアプリを起動できます。
| ウィジェット名 | 説明 |
|---|---|
| `JR_shikoku_train_info` | 遅延速報EX |
| `JR_shikoku_apps_shortcut` | クイックアクセス(各機能へのショートカットタイル) |
| `JR_shikoku_felica_balance` | ICカード残高表示 |
| `JR_shikoku_info` | 運行情報 |
| `JR_shikoku_train_strange` | 怪レい列車 |
ウィジェットのタップ時は `jrshikoku://` スキームでアプリ内画面へ遷移します。
実装: `components/AndroidWidget/widget-task-handler.tsx`
### iOSウィジェットWidgetKit
WidgetKit拡張としてホーム画面ウィジェットを提供しています。Live Activities にも対応しています。
設定: `targets/widget/Info.plist`
### プッシュ通知
通知タップ時に対応する画面へ遷移します。
| アクション | 遷移先 |
|---|---|
| `delay-ex` | 遅延速報EX |
| `strange-train` | 走行位置(怪レい列車) |
| `information` | 運行情報 |
実装: `stateBox/useNotifications.tsx`
### NFCFeliCa
NFC-Fを利用した交通系ICカードの読み取りに対応していますAndroid。アプリ内からスキャンを開始する形式です。
実装: `modules/expo-felica-reader/`
## <20>📱 ビルド
```bash
# APKビルドAndroid

View File

@@ -1,12 +1,11 @@
import React, { FC } from "react";
import { Platform, StatusBar, View } from "react-native";
import { Platform, StatusBar } from "react-native";
import { useThemeColors } from "@/lib/theme";
const StatusbarDetect: FC = () => {
if (Platform.OS == "ios") {
return <StatusBar barStyle="dark-content" />;
} else if (Platform.OS == "android") {
return <View />;
}
const { isDark } = useThemeColors();
const barStyle = isDark ? "light-content" : "dark-content";
return <StatusBar barStyle={barStyle} translucent backgroundColor="transparent" />;
};
export default StatusbarDetect;

24
Top.tsx
View File

@@ -1,6 +1,7 @@
import React, { useEffect } from "react";
import React, { useCallback, useEffect, useRef } from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { useNavigation } from "@react-navigation/native";
import { useColorScheme } from "react-native";
import Apps from "./components/Apps";
import TrainBase from "./components/trainbaseview";
import HowTo from "./howto";
@@ -18,10 +19,16 @@ import { StationDiagramView } from "@/components/StationDiagram/StationDiagramVi
const Stack = createStackNavigator();
export const Top = () => {
const { webview } = useCurrentTrain();
const { navigate, addListener, isFocused } = useNavigation();
const { navigate, addListener, isFocused } = useNavigation<any>();
const isDark = useColorScheme() === "dark";
const bgColor = isDark ? "#1c1c1e" : "#ffffff";
//地図用
const { mapSwitch } = useTrainMenu();
const mapSwitchRef = useRef(mapSwitch);
useEffect(() => {
mapSwitchRef.current = mapSwitch;
}, [mapSwitch]);
const goToFavoriteList = () =>
navigate("positions", { screen: "favoriteList" });
@@ -31,26 +38,29 @@ export const Top = () => {
return unsubscribe;
}, []);
const goToTrainMenu = () => {
const goToTrainMenu = useCallback(() => {
if (Platform.OS === "web") {
Linking.openURL("https://train.jr-shikoku.co.jp/");
setTimeout(() => navigate("topMenu", { screen: "menu" }), 100);
return;
}
if (!isFocused()) navigate("positions", { screen: "Apps" });
else if (mapSwitch == "true")
else if (mapSwitchRef.current == "true")
navigate("positions", { screen: "trainMenu" });
else webview.current?.injectJavaScript(`AccordionClassEvent()`);
return;
};
}, [isFocused, navigate, webview]);
useEffect(() => {
const unsubscribe = addListener("tabPress", goToTrainMenu);
return unsubscribe;
}, [addListener, mapSwitch]);
}, [addListener, goToTrainMenu]);
return (
<Stack.Navigator id={null} detachInactiveScreens={false}>
<Stack.Navigator
id={null}
screenOptions={{ cardStyle: { backgroundColor: bgColor } }}
>
<Stack.Screen
name="Apps"
options={{

View File

@@ -2,6 +2,7 @@ import { Platform, ToastAndroid } from "react-native";
import * as Updates from "expo-updates";
export const UpdateAsync = () => {
if (__DEV__) return; // dev client では expo-updates は無効
Updates.checkForUpdateAsync()
.then((update) => {
if (!update.isAvailable) return;
@@ -16,7 +17,7 @@ export const UpdateAsync = () => {
50
);
}
Updates.fetchUpdateAsync().then(Updates.reloadAsync);
Updates.fetchUpdateAsync().then(() => Updates.reloadAsync());
return;
})
.catch((e) => {

564
app.json
View File

@@ -2,12 +2,14 @@
"expo": {
"name": "JR四国非公式",
"slug": "jrshikoku",
"scheme": "jrshikoku",
"platforms": [
"ios",
"android",
"web"
],
"version": "6.0.4",
"version": "7.0.0",
"userInterfaceStyle": "automatic",
"orientation": "default",
"icon": "./assets/icons/s8600.png",
"splash": {
@@ -22,9 +24,10 @@
"**/*"
],
"ios": {
"buildNumber": "50",
"supportsTablet": false,
"buildNumber": "61",
"supportsTablet": true,
"bundleIdentifier": "jrshikokuinfo.xprocess.hrkn",
"appleTeamId": "54CRDT797G",
"config": {
"googleMapsApiKey": "AIzaSyAVGDTjBkR_0wkQiNkoo5WDLhqXCjrjk8Y"
},
@@ -34,20 +37,57 @@
"0003",
"FE00"
],
"ITSAppUsesNonExemptEncryption": false
"ITSAppUsesNonExemptEncryption": false,
"NSSupportsLiveActivities": true,
"NSSupportsLiveActivitiesFrequentUpdates": true,
"UIBackgroundModes": [
"audio"
]
},
"entitlements": {
"com.apple.developer.nfc.readersession.formats": [
"TAG"
],
"com.apple.security.application-groups": [
"group.jrshikokuinfo.xprocess.hrkn"
]
}
},
"android": {
"package": "jrshikokuinfo.xprocess.hrkn",
"versionCode": 28,
"versionCode": 30,
"intentFilters": [
{
"action": "VIEW",
"data": [
{
"scheme": "jrshikoku"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
},
{
"action": "VIEW",
"data": [
{
"scheme": "jrshikoku",
"host": "open",
"pathPrefix": "/felica"
}
],
"category": [
"BROWSABLE",
"DEFAULT"
]
}
],
"permissions": [
"ACCESS_FINE_LOCATION",
"NFC",
"POST_NOTIFICATIONS",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION"
],
@@ -67,7 +107,20 @@
"policy": "sdkVersion"
},
"plugins": [
"expo-font",
"./plugins/with-android-local-properties",
"./plugins/with-nfc-widget-guard",
"@bacons/apple-targets",
[
"expo-font",
{
"fonts": [
"./assets/fonts/jr-nishi.otf",
"./assets/fonts/DelaGothicOne-Regular.ttf",
"./assets/fonts/JNRfont_pict.ttf",
"./assets/fonts/DiaPro-Regular.otf"
]
}
],
"expo-localization",
[
"expo-screen-orientation",
@@ -91,17 +144,7 @@
"minWidth": "70dp",
"minHeight": "50dp",
"description": "JR四国列車遅延速報EXのウィジェットです。30分ごとに自動更新します。タッチすると強制更新します。",
"previewImage": "./assets/icon.png",
"updatePeriodMillis": 1800000,
"resizeMode": "horizontal|vertical"
},
{
"name": "JR_shikoku_train_strange",
"label": "怪レい列車",
"minWidth": "70dp",
"minHeight": "50dp",
"description": "JR四国怪レい列車BOTのウィジェットです。30分ごとに自動更新します。タッチすると強制更新します。",
"previewImage": "./assets/icon.png",
"previewImage": "./assets/widgetResource/JR_shikoku_train_info.png",
"updatePeriodMillis": 1800000,
"resizeMode": "horizontal|vertical"
},
@@ -111,7 +154,7 @@
"minWidth": "70dp",
"minHeight": "50dp",
"description": "JR四国運行情報のウィジェットです。30分ごとに自動更新します。タッチすると強制更新します。",
"previewImage": "./assets/icon.png",
"previewImage": "./assets/widgetResource/JR_shikoku_info.png",
"updatePeriodMillis": 1800000,
"resizeMode": "horizontal|vertical"
},
@@ -121,7 +164,17 @@
"minWidth": "70dp",
"minHeight": "50dp",
"description": "JR四国非公式アプリの各種リンクを表示するウィジェットです。",
"previewImage": "./assets/icon.png",
"previewImage": "./assets/widgetResource/JR_shikoku_apps_shortcut.png",
"updatePeriodMillis": 1800000,
"resizeMode": "horizontal|vertical"
},
{
"name": "JR_shikoku_felica_balance",
"label": "ICカード残高",
"minWidth": "70dp",
"minHeight": "50dp",
"description": "Felica対応ICカードの残高をホーム画面に表示するウィジェットです。タップでスキャン画面を開きます。",
"previewImage": "./assets/widgetResource/JR_shikoku_felica_balance.png",
"updatePeriodMillis": 1800000,
"resizeMode": "horizontal|vertical"
}
@@ -450,9 +503,480 @@
"foregroundImage": "./assets/icons/ef210.png",
"backgroundColor": "#001413"
}
},
{
"name": "32ns",
"ios": "./assets/icons/s32ns.png",
"android": {
"foregroundImage": "./assets/icons/s32ns.png",
"backgroundColor": "#001413"
}
},
{
"name": "32s",
"ios": "./assets/icons/s32s.png",
"android": {
"foregroundImage": "./assets/icons/s32s.png",
"backgroundColor": "#001413"
}
},
{
"name": "32kpuy1",
"ios": "./assets/icons/s32kpuy1.png",
"android": {
"foregroundImage": "./assets/icons/s32kpuy1.png",
"backgroundColor": "#001413"
}
},
{
"name": "32kpuy2",
"ios": "./assets/icons/s32kpuy2.png",
"android": {
"foregroundImage": "./assets/icons/s32kpuy2.png",
"backgroundColor": "#001413"
}
},
{
"name": "32thtk",
"ios": "./assets/icons/s32thtk.png",
"android": {
"foregroundImage": "./assets/icons/s32thtk.png",
"backgroundColor": "#001413"
}
},
{
"name": "32oni1",
"ios": "./assets/icons/s32oni1.png",
"android": {
"foregroundImage": "./assets/icons/s32oni1.png",
"backgroundColor": "#001413"
}
},
{
"name": "32oni1k",
"ios": "./assets/icons/s32oni1k.png",
"android": {
"foregroundImage": "./assets/icons/s32oni1k.png",
"backgroundColor": "#001413"
}
},
{
"name": "32to4",
"ios": "./assets/icons/s32to4.png",
"android": {
"foregroundImage": "./assets/icons/s32to4.png",
"backgroundColor": "#001413"
}
},
{
"name": "32toai",
"ios": "./assets/icons/s32to_ai.png",
"android": {
"foregroundImage": "./assets/icons/s32to_ai.png",
"backgroundColor": "#001413"
}
},
{
"name": "54s",
"ios": "./assets/icons/s54s.png",
"android": {
"foregroundImage": "./assets/icons/s54s.png",
"backgroundColor": "#001413"
}
},
{
"name": "54to0ys",
"ios": "./assets/icons/s54to0ys.png",
"android": {
"foregroundImage": "./assets/icons/s54to0ys.png",
"backgroundColor": "#001413"
}
},
{
"name": "54nany1",
"ios": "./assets/icons/s54nany1.png",
"android": {
"foregroundImage": "./assets/icons/s54nany1.png",
"backgroundColor": "#001413"
}
},
{
"name": "54nany2",
"ios": "./assets/icons/s54nany2.png",
"android": {
"foregroundImage": "./assets/icons/s54nany2.png",
"backgroundColor": "#001413"
}
},
{
"name": "54smek1",
"ios": "./assets/icons/s32smek1.png",
"android": {
"foregroundImage": "./assets/icons/s32smek1.png",
"backgroundColor": "#001413"
}
},
{
"name": "40s",
"ios": "./assets/icons/s40.png",
"android": {
"foregroundImage": "./assets/icons/s40.png",
"backgroundColor": "#001413"
}
},
{
"name": "40w",
"ios": "./assets/icons/w40.png",
"android": {
"foregroundImage": "./assets/icons/w40.png",
"backgroundColor": "#001413"
}
},
{
"name": "185cm",
"ios": "./assets/icons/s185cm.png",
"android": {
"foregroundImage": "./assets/icons/s185cm.png",
"backgroundColor": "#001413"
}
},
{
"name": "185g",
"ios": "./assets/icons/s185g.png",
"android": {
"foregroundImage": "./assets/icons/s185g.png",
"backgroundColor": "#001413"
}
},
{
"name": "185tu_uzu",
"ios": "./assets/icons/s185tu_uzu.png",
"android": {
"foregroundImage": "./assets/icons/s185tu_uzu.png",
"backgroundColor": "#001413"
}
},
{
"name": "185ap1",
"ios": "./assets/icons/s185ap1.png",
"android": {
"foregroundImage": "./assets/icons/s185ap1.png",
"backgroundColor": "#001413"
}
},
{
"name": "185mm2",
"ios": "./assets/icons/s185mm2.png",
"android": {
"foregroundImage": "./assets/icons/s185mm2.png",
"backgroundColor": "#001413"
}
},
{
"name": "185ym2",
"ios": "./assets/icons/s185ym2.png",
"android": {
"foregroundImage": "./assets/icons/s185ym2.png",
"backgroundColor": "#001413"
}
},
{
"name": "1200",
"ios": "./assets/icons/s1200.png",
"android": {
"foregroundImage": "./assets/icons/s1200.png",
"backgroundColor": "#001413"
}
},
{
"name": "1201",
"ios": "./assets/icons/s1201.png",
"android": {
"foregroundImage": "./assets/icons/s1201.png",
"backgroundColor": "#001413"
}
},
{
"name": "1501",
"ios": "./assets/icons/s1501.png",
"android": {
"foregroundImage": "./assets/icons/s1501.png",
"backgroundColor": "#001413"
}
},
{
"name": "1550",
"ios": "./assets/icons/s1550.png",
"android": {
"foregroundImage": "./assets/icons/s1550.png",
"backgroundColor": "#001413"
}
},
{
"name": "1551",
"ios": "./assets/icons/s1551.png",
"android": {
"foregroundImage": "./assets/icons/s1551.png",
"backgroundColor": "#001413"
}
},
{
"name": "2000uwa",
"ios": "./assets/icons/s2000_uwa.png",
"android": {
"foregroundImage": "./assets/icons/s2000_uwa.png",
"backgroundColor": "#001413"
}
},
{
"name": "2000nl",
"ios": "./assets/icons/s2000nl.png",
"android": {
"foregroundImage": "./assets/icons/s2000nl.png",
"backgroundColor": "#001413"
}
},
{
"name": "2000-3",
"ios": "./assets/icons/s2000-3.png",
"android": {
"foregroundImage": "./assets/icons/s2000-3.png",
"backgroundColor": "#001413"
}
},
{
"name": "2000ganp1",
"ios": "./assets/icons/s2000ganp1.png",
"android": {
"foregroundImage": "./assets/icons/s2000ganp1.png",
"backgroundColor": "#001413"
}
},
{
"name": "2600apr",
"ios": "./assets/icons/s2600apr.png",
"android": {
"foregroundImage": "./assets/icons/s2600apr.png",
"backgroundColor": "#001413"
}
},
{
"name": "2600apb",
"ios": "./assets/icons/s2600apb.png",
"android": {
"foregroundImage": "./assets/icons/s2600apb.png",
"backgroundColor": "#001413"
}
},
{
"name": "2700asi",
"ios": "./assets/icons/s2700_asi.png",
"android": {
"foregroundImage": "./assets/icons/s2700_asi.png",
"backgroundColor": "#001413"
}
},
{
"name": "2700smn",
"ios": "./assets/icons/s2700_smn.png",
"android": {
"foregroundImage": "./assets/icons/s2700_smn.png",
"backgroundColor": "#001413"
}
},
{
"name": "2700uzu",
"ios": "./assets/icons/s2700_uzu.png",
"android": {
"foregroundImage": "./assets/icons/s2700_uzu.png",
"backgroundColor": "#001413"
}
},
{
"name": "2700apy1",
"ios": "./assets/icons/s2700apy1.png",
"android": {
"foregroundImage": "./assets/icons/s2700apy1.png",
"backgroundColor": "#001413"
}
},
{
"name": "2700apr1",
"ios": "./assets/icons/s2700apr1.png",
"android": {
"foregroundImage": "./assets/icons/s2700apr1.png",
"backgroundColor": "#001413"
}
},
{
"name": "3600",
"ios": "./assets/icons/s3600.png",
"android": {
"foregroundImage": "./assets/icons/s3600.png",
"backgroundColor": "#001413"
}
},
{
"name": "6000f",
"ios": "./assets/icons/s6000f.png",
"android": {
"foregroundImage": "./assets/icons/s6000f.png",
"backgroundColor": "#001413"
}
},
{
"name": "7001",
"ios": "./assets/icons/s7001.png",
"android": {
"foregroundImage": "./assets/icons/s7001.png",
"backgroundColor": "#001413"
}
},
{
"name": "8001nr",
"ios": "./assets/icons/s8001nr.png",
"android": {
"foregroundImage": "./assets/icons/s8001nr.png",
"backgroundColor": "#001413"
}
},
{
"name": "9000",
"ios": "./assets/icons/s9000.png",
"android": {
"foregroundImage": "./assets/icons/s9000.png",
"backgroundColor": "#001413"
}
},
{
"name": "9640ht",
"ios": "./assets/icons/tosa9640ht.png",
"android": {
"foregroundImage": "./assets/icons/tosa9640ht.png",
"backgroundColor": "#001413"
}
},
{
"name": "9640mo1",
"ios": "./assets/icons/tosa9640mo1.png",
"android": {
"foregroundImage": "./assets/icons/tosa9640mo1.png",
"backgroundColor": "#001413"
}
},
{
"name": "9640mo2",
"ios": "./assets/icons/tosa9640mo2.png",
"android": {
"foregroundImage": "./assets/icons/tosa9640mo2.png",
"backgroundColor": "#001413"
}
},
{
"name": "9640tyg",
"ios": "./assets/icons/tosa9640tyg.png",
"android": {
"foregroundImage": "./assets/icons/tosa9640tyg.png",
"backgroundColor": "#001413"
}
},
{
"name": "9640tyb",
"ios": "./assets/icons/tosa9640tyb.png",
"android": {
"foregroundImage": "./assets/icons/tosa9640tyb.png",
"backgroundColor": "#001413"
}
},
{
"name": "9640jbl",
"ios": "./assets/icons/tosa9640jbl.png",
"android": {
"foregroundImage": "./assets/icons/tosa9640jbl.png",
"backgroundColor": "#001413"
}
},
{
"name": "W741",
"ios": "./assets/icons/w741.png",
"android": {
"foregroundImage": "./assets/icons/w741.png",
"backgroundColor": "#001413"
}
},
{
"name": "EF210a",
"ios": "./assets/icons/ef210a.png",
"android": {
"foregroundImage": "./assets/icons/ef210a.png",
"backgroundColor": "#001413"
}
},
{
"name": "EF210n",
"ios": "./assets/icons/ef210n.png",
"android": {
"foregroundImage": "./assets/icons/ef210n.png",
"backgroundColor": "#001413"
}
},
{
"name": "EF210n1",
"ios": "./assets/icons/ef210n1.png",
"android": {
"foregroundImage": "./assets/icons/ef210n1.png",
"backgroundColor": "#001413"
}
},
{
"name": "EF210l3",
"ios": "./assets/icons/ef210l3.png",
"android": {
"foregroundImage": "./assets/icons/ef210l3.png",
"backgroundColor": "#001413"
}
},
{
"name": "T45000",
"ios": "./assets/icons/s150001to.png",
"android": {
"foregroundImage": "./assets/icons/s150001to.png",
"backgroundColor": "#001413"
}
},
{
"name": "Wk141",
"ios": "./assets/icons/w141jg.png",
"android": {
"foregroundImage": "./assets/icons/w141jg.png",
"backgroundColor": "#001413"
}
}
]
],
[
"expo-build-properties",
{
"android": {
"kotlinVersion": "2.1.20"
},
"ios": {
"deploymentTarget": "16.2"
}
}
],
"expo-audio",
"expo-video",
"expo-web-browser",
"expo-asset",
"expo-sharing",
[
"react-native-maps",
{
"iosGoogleMapsApiKey": "AIzaSyAVGDTjBkR_0wkQiNkoo5WDLhqXCjrjk8Y",
"androidGoogleMapsApiKey": "AIzaSyAmFb-Yj033bXZWlSzNrfq_0jc1PgRrWcE"
}
]
]
}
}
}

BIN
assets/icons/ef210a.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
assets/icons/ef210l3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
assets/icons/ef210n.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
assets/icons/ef210n1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -1,45 +1,101 @@
export default () =>{
return [
{ "id": "32", "name": "キハ32形", "icon": require("./32.png") },
{ "id": "32kpuuy", "name": "キハ32形かっぱうようよ号", "icon": require("./32kpuuy.png") },
//{ "id": "32", "name": "キハ32形", "icon": require("./32.png") },
{ "id": "32ns", "name": "キハ32形(標準色)", "icon": require("./s32ns.png") },
{ "id": "32s", "name": "キハ32形(新塗装)", "icon": require("./s32s.png") },
//{ "id": "32kpuuy", "name": "キハ32形かっぱうようよ号", "icon": require("./32kpuuy.png") },
{ "id": "32kpuy1", "name": "キハ32形かっぱうようよ号①", "icon": require("./s32kpuy1.png") },
{ "id": "32kpuy2", "name": "キハ32形かっぱうようよ号②", "icon": require("./s32kpuy2.png") },
{ "id": "32tht", "name": "キハ32形新幹線ホビートレイン", "icon": require("./32tht.png") },
{ "id": "32thtk", "name": "キクハ32形新幹線ホビートレイン", "icon": require("./s32thtk.png") },
{ "id": "32oni1", "name": "キハ32形特別塗装①", "icon": require("./s32oni1.png") },
{ "id": "32oni1k", "name": "キハ32形特別塗装①(連結)", "icon": require("./s32oni1k.png") },
//{ "id": "32to4", "name": "キハ32形(土讃色)", "icon": require("./s32to4.png") },
{ "id": "32toai", "name": "キクハ32形藍よしのがわトロッコ", "icon": require("./s32to_ai.png") },
{ "id": "32at", "name": "キクハ32形アンパンマントロッコ", "icon": require("./32at.png") },
{ "id": "54", "name": "キハ54形", "icon": require("./54.png") },
//{ "id": "54", "name": "キハ54形", "icon": require("./54.png") },
{ "id": "54s", "name": "キハ54形(四国色)", "icon": require("./s54s.png") },
{ "id": "54st", "name": "キハ54形しまんトロッコ", "icon": require("./54st.png") },
{ "id": "40", "name": "キハ40", "icon": require("./40.png") },
//{ "id": "54to0ys", "name": "キハ54形(ゆとりすと)", "icon": require("./s54to0ys.png") },
{ "id": "54nany1", "name": "キハ54形なんよ号①", "icon": require("./s54nany1.png") },
{ "id": "54nany2", "name": "キハ54形なんよ号②", "icon": require("./s54nany2.png") },
{ "id": "54smek1", "name": "キハ54形しまんトロッコ(連結)", "icon": require("./s32smek1.png") },
//{ "id": "40", "name": "キハ40", "icon": require("./40.png") },
{ "id": "40s", "name": "キハ40(四国色)", "icon": require("./s40.png") },
{ "id": "40w", "name": "キハ40(JR西日本色)", "icon": require("./w40.png") },
{ "id": "185mrt", "name": "キハ185系四国色", "icon": require("./s185_mrt.png") },
{ "id": "185cm", "name": "キハ185系普通列車色", "icon": require("./s185cm.png") },
{ "id": "185g", "name": "キハ185系(緑帯)", "icon": require("./s185g.png") },
{ "id": "185tu", "name": "キハ185系剣山色", "icon": require("./s185tu.png") },
{ "id": "185tu_uzu", "name": "キハ185系剣山色(うずしお)", "icon": require("./s185tu_uzu.png") },
{ "id": "185iyor", "name": "キハ185系伊予灘ものがたり(赤)", "icon": require("./s185iyor.png") },
{ "id": "185iyoy", "name": "キハ185系伊予灘ものがたり(黄)", "icon": require("./s185iyoy.png") },
{ "id": "185toai", "name": "キハ185系藍よしのがわトロッコ", "icon": require("./s185to_ai.png") },
{ "id": "185mm1", "name": "キハ185系四国まんなか千年ものがたり(緑)", "icon": require("./s185mm1.png") },
{ "id": "185mm2", "name": "キハ185系四国まんなか千年ものがたり(赤)", "icon": require("./s185mm2.png") },
{ "id": "185ym1", "name": "キハ185系時代の夜明けのものがたり(茶)", "icon": require("./s185ym1.png") },
{ "id": "185ym2", "name": "キハ185系時代の夜明けのものがたり(緑)", "icon": require("./s185ym2.png") },
{ "id": "185ap1", "name": "キハ185系アンパンマン", "icon": require("./s185ap1.png") },
{ "id": "1000", "name": "1000形", "icon": require("./s1000.png") },
{ "id": "1200n", "name": "1200形", "icon": require("./s1200n.png") },
{ "id": "1500", "name": "1500形", "icon": require("./s1500.png") },
{ "id": "5001", "name": "5000系(二階建て)", "icon": require("./s5001.png") },
{ "id": "5001k", "name": "5000系(平屋側)", "icon": require("./s5001k.png") },
{ "id": "6000p", "name": "6000系", "icon": require("./s6000p.png") },
{ "id": "7000", "name": "7000系", "icon": require("./s7000.png") },
{ "id": "7200", "name": "7200系", "icon": require("./s7200.png") },
//{ "id": "1200n", "name": "1200形", "icon": require("./s1200n.png") },
{ "id": "1200", "name": "1200形(旧塗装)", "icon": require("./s1200.png") },
{ "id": "1201", "name": "1201形", "icon": require("./s1201.png") },
//{ "id": "1500", "name": "1500形", "icon": require("./s1500.png") },
{ "id": "1501", "name": "1500形 1501", "icon": require("./s1501.png") },
{ "id": "1550", "name": "1500形 1550", "icon": require("./s1550.png") },
{ "id": "1551", "name": "1500形 1551", "icon": require("./s1551.png") },
{ "id": "2000asi", "name": "2000系", "icon": require("./s2000_asi.png") },
{ "id": "2000uwa", "name": "2000系(宇和海色)", "icon": require("./s2000_uwa.png") },
{ "id": "N2000", "name": "N2000系", "icon": require("./s2000n.png") },
{ "id": "2000nl", "name": "N2000系(南風色)", "icon": require("./s2000nl.png") },
{ "id": "2000-3", "name": "N2000系(試作)", "icon": require("./s2000-3.png") },
{ "id": "2000ganp1", "name": "2000系(復活塗装)", "icon": require("./s2000ganp1.png") },
{ "id": "2002a", "name": "2000系アンパンマン", "icon": require("./s2002a.png") },
{ "id": "2600", "name": "2600系" , "icon": require("./s2600.png")},
{ "id": "2600apr", "name": "2600系アンパンマン(赤)", "icon": require("./s2600apr.png") },
{ "id": "2600apb", "name": "2600系アンパンマン(青)", "icon": require("./s2600apb.png") },
{ "id": "2700", "name": "2700系", "icon": require("./s2700.png") },
{ "id": "2700asi", "name": "2700系(あしずり色)", "icon": require("./s2700_asi.png") },
{ "id": "2700smn", "name": "2700系(しまんと色)", "icon": require("./s2700_smn.png") },
{ "id": "2700uzu", "name": "2700系(うずしお色)", "icon": require("./s2700_uzu.png") },
{ "id": "2700apy", "name": "2700系アンパンマン(黄)", "icon": require("./s2700apy.png") },
{ "id": "2700apr", "name": "2700系アンパンマン(赤)", "icon": require("./s2700apr.png") },
{ "id": "2700apy1", "name": "2700系アンパンマン(黄)①", "icon": require("./s2700apy1.png") },
{ "id": "2700apr1", "name": "2700系アンパンマン(赤)①", "icon": require("./s2700apr1.png") },
{ "id": "3600", "name": "3600系", "icon": require("./s3600.png") },
{ "id": "5001", "name": "5000系(二階建て)", "icon": require("./s5001.png") },
{ "id": "5001k", "name": "5000系(平屋側)", "icon": require("./s5001k.png") },
//{ "id": "6000p", "name": "6000系", "icon": require("./s6000p.png") },
{ "id": "6000f", "name": "6000系(Fライナー)", "icon": require("./s6000f.png") },
{ "id": "7000", "name": "7000系", "icon": require("./s7000.png") },
//{ "id": "7001", "name": "7000系 7001", "icon": require("./s7001.png") },
{ "id": "7200", "name": "7200系", "icon": require("./s7200.png") },
{ "id": "8000no", "name": "8000系(オレンジ)", "icon": require("./s8000no.png") },
{ "id": "8000nr", "name": "8000系(赤)", "icon": require("./s8000nr.png") },
{ "id": "8001nr", "name": "8000系(赤) 8001", "icon": require("./s8001nr.png") },
{ "id": "8000ap", "name": "8000系アンパンマン", "icon": require("./s8000ap.png") },
{ "id": "8000nn", "name": "8000系リニューアル改", "icon": require("./s8000nn.png") },
{ "id": "8600", "name": "8600系", "icon": require("./s8600.png") },
{ "id": "9000", "name": "9000系", "icon": require("./s9000.png") },
{ "id": "9640", "name": "9640形(白)", "icon": require("./tosa9640.png") },
{ "id": "9640ht", "name": "9640形(赤帯)", "icon": require("./tosa9640ht.png") },
{ "id": "9640mo1", "name": "9640形もみじ号①", "icon": require("./tosa9640mo1.png") },
{ "id": "9640mo2", "name": "9640形もみじ号②", "icon": require("./tosa9640mo2.png") },
{ "id": "9640tyg", "name": "9640形(緑)", "icon": require("./tosa9640tyg.png") },
{ "id": "9640tyb", "name": "9640形(青)", "icon": require("./tosa9640tyb.png") },
{ "id": "9640jgr", "name": "9640形オープンデッキ(緑)", "icon": require("./tosa9640jgr.png") },
{ "id": "9640jbl", "name": "9640形オープンデッキ(青)", "icon": require("./tosa9640jbl.png") },
{ "id": "285", "name": "285系サンライズ瀬戸", "icon": require("./w285.png") },
{ "id": "213w", "name": "ラ・マル・ド・ボァ", "icon": require("./w213w.png") },
{ "id": "W741", "name": "DEC741系", "icon": require("./w741.png") },
{ "id": "Wk141", "name": "キヤ141系", "icon": require("./w141jg.png") },
{ "id": "EF65", "name": "EF65", "icon": require("./ef65.png") },
{ "id": "EF210", "name": "EF210", "icon": require("./ef210.png") },
//{ "id": "EF210", "name": "EF210", "icon": require("./ef210.png") },
{ "id": "EF210a", "name": "EF210(300番代)", "icon": require("./ef210a.png") },
{ "id": "EF210n", "name": "EF210(新塗装)", "icon": require("./ef210n.png") },
{ "id": "EF210n1", "name": "EF210(新塗装①)", "icon": require("./ef210n1.png") },
{ "id": "EF210l3", "name": "EF210(L3編成)", "icon": require("./ef210l3.png") },
{ "id": "T45000", "name": "トラ45000", "icon": require("./s150001to.png") },
]
}
}

BIN
assets/icons/s1200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
assets/icons/s1201.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
assets/icons/s150001to.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
assets/icons/s1501.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
assets/icons/s1550.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
assets/icons/s1551.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

BIN
assets/icons/s185ap1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

BIN
assets/icons/s185cm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
assets/icons/s185g.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
assets/icons/s185mm2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
assets/icons/s185tu_uzu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
assets/icons/s185ym2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

BIN
assets/icons/s2000-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
assets/icons/s2000_uwa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
assets/icons/s2000ganp1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
assets/icons/s2000nl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
assets/icons/s2600apb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
assets/icons/s2600apr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
assets/icons/s2700_asi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
assets/icons/s2700_smn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

BIN
assets/icons/s2700_uzu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
assets/icons/s2700apr1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
assets/icons/s2700apy1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
assets/icons/s32kpuy1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
assets/icons/s32kpuy2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

BIN
assets/icons/s32ns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
assets/icons/s32oni1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
assets/icons/s32oni1k.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
assets/icons/s32s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
assets/icons/s32smek1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

BIN
assets/icons/s32thtk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

BIN
assets/icons/s32to4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

BIN
assets/icons/s32to_ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
assets/icons/s3600.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
assets/icons/s40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
assets/icons/s54nany1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

BIN
assets/icons/s54nany2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
assets/icons/s54s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
assets/icons/s54to0ys.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
assets/icons/s6000f.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
assets/icons/s7001.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
assets/icons/s8001nr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
assets/icons/s9000.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
assets/icons/tosa9640ht.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
assets/icons/w141jg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
assets/icons/w40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
assets/icons/w741.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -0,0 +1,21 @@
/**
* 観光スポットデータ
* StationNumber は "SP" プレフィックスで管理(路線とは別系統)
* Station_JP の先頭ドット(.)はダイヤデータのキーと一致させるための命名規則
*/
export default [
{
Station_JP: ".与島",
Station_EN: "Yoshima",
MyStation: "0",
StationNumber: null,
DispNum: "3",
StationTimeTable: "",
StationMap: "https://www.google.co.jp/maps/place/34.389472,133.816444",
JrHpUrl: "https://www.jb-honshi.co.jp/yoshimapa/",
jslodApi: "spot",
lat: 34.389472,
lng: 133.816444,
isSpot: true,
},
];

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

BIN
assets/sound/rikka-test.mp3 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -1,6 +1,15 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
presets: [
['babel-preset-expo', {
'react-compiler': { enabled: false },
lazyImports: true,
}],
],
plugins: [
'./plugins/babel-plugin-disable-font-scaling',
'react-native-reanimated/plugin',
],
};
};

View File

@@ -2,16 +2,19 @@ import React, { useRef } from "react";
import { Platform } from "react-native";
import ActionSheet from "react-native-actions-sheet";
import { EachTrainInfoCore } from "./EachTrainInfoCore";
import { useSheetMaxHeight } from "./useSheetMaxHeight";
export const EachTrainInfo = ({ payload }) => {
if (!payload) return <></>;
const actionSheetRef = useRef(null);
const maxHeight = useSheetMaxHeight();
if (!payload) return <></>;
return (
<ActionSheet
gestureEnabled={true}
CustomHeaderComponent={<></>}
ref={actionSheetRef}
drawUnderStatusBar={false}
isModal={Platform.OS == "ios"}
isModal={Platform.OS === "ios" && !Platform.isPad}
containerStyle={{ maxHeight }}
//useBottomSafeAreaPadding={Platform.OS == "android"}
>

View File

@@ -1,6 +1,8 @@
import React, { FC } from "react";
import { View, Text, TouchableWithoutFeedback, Alert } from "react-native";
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
export const DataConnectedButton: FC<{
i: string;
@@ -8,6 +10,8 @@ export const DataConnectedButton: FC<{
}> = ({ i, openTrainInfo }) => {
const [station, se, time] = i.split(",");
const { keyList } = useAllTrainDiagram();
const { colors } = useThemeColors();
const { fontScale, moderateScale } = useResponsive();
// 列番が有効かどうかをチェックする関数
const isValidTrainNumber = (trainNum: string): boolean => {
return keyList.includes(trainNum);
@@ -30,31 +34,31 @@ export const DataConnectedButton: FC<{
}}
key={station + time}
>
<View style={{ flexDirection: "row", backgroundColor: "#f5f5f5" }}>
<View style={{ flexDirection: "row", backgroundColor: colors.backgroundTertiary }}>
<View
style={{
padding: 8,
flexDirection: "row",
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
borderBottomColor: colors.borderLight,
flex: 1,
}}
>
<View
style={{
width: 35,
width: moderateScale(35),
position: "relative",
marginHorizontal: 15,
flexDirection: "row",
height: "10%",
}}
/>
<Text style={{ fontSize:16, fontFamily: "DiaPro" }}>
<Text style={{ fontSize: fontScale(16), fontFamily: "DiaPro", color: colors.text }}>
{se === "増" ? "⬐" : "↳"}
</Text>
<Text style={{ fontSize: 20, color: "#0000EE" }}>{time}</Text>
<Text style={{ fontSize: fontScale(20), color: colors.textLink }}>{time}</Text>
<View style={{ flex: 1 }} />
<Text style={{ fontSize: 18, width: 50 }}>
<Text style={{ fontSize: fontScale(18), width: moderateScale(50), color: colors.text }}>
{se === "増" ? "増結" : "解結"}
</Text>
</View>

View File

@@ -11,6 +11,8 @@ import {
parseSeString,
} from "@/utils/seUtils";
import type { SeTypes } from "@/types";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
type seTypes =
| "発編"
@@ -63,6 +65,8 @@ export const EachStopList: FC<props> = ({
array,
isNotService = false,
}) => {
const { colors: themeColors, isDark } = useThemeColors();
const { fontScale, moderateScale } = useResponsive();
const [station, se, time, platformNum] = i.split(",") as [
string,
seTypes,
@@ -70,22 +74,22 @@ export const EachStopList: FC<props> = ({
string
]; // 阿波池田,発,6:21,1
let beforeSameStationData = null;
// 運休系でない通常の発のみ、前の着を統合する
// 休編(非推奨)は発着が不明なため、次の発と統合する
if ((se.includes("発") && !se.includes("休")) || se === "休編") {
// 発(通常発・休発・休発編)の場合、前の着(通常着・休着・休着編)と統合する
if (se.includes("発")) {
if (index > 0) {
const beforeData = array[index - 1].split(",") as [string, seTypes, string];
if (beforeData[0] == station) {
// 前が着(通常着でも休着でも)の場合は統合
if (beforeData[0] == station && beforeData[1].includes("着")) {
beforeSameStationData = beforeData;
}
}
}
let afterSameStationData = null;
// 運休系でない通常の着のみ、次の発と統合する
// 運休着(休着、休着編)は独立して表示する必要がある
if (se.includes("着") && !se.includes("休")) {
// 着(通常着・休着・休着編)の場合、次の発(通常発・休発・休発編)と統合される(非表示)
if (se.includes("着")) {
const afterData = array[index + 1]?.split(",") as [string, seTypes, string];
if (afterData && afterData[0] == station) {
// 次が発(通常発でも休発でも)なら、この着を非表示にして次の発で両方表示
if (afterData && afterData[0] == station && afterData[1].includes("発")) {
afterSameStationData = afterData;
return <></>;
}
@@ -135,7 +139,8 @@ export const EachStopList: FC<props> = ({
isCommunity,
isCanceled,
isDelayed,
isNotService
isNotService,
isDark
);
// 打ち消し線用の通常色(遅延していない時の色)
const normalColors = getStopListColors(
@@ -143,7 +148,8 @@ export const EachStopList: FC<props> = ({
isCommunity,
isCanceled,
false,
isNotService
isNotService,
isDark
);
// beforeSameStationData用の色設定
@@ -161,14 +167,16 @@ export const EachStopList: FC<props> = ({
beforeIsCommunity,
isCanceled,
isDelayed,
isNotService
isNotService,
isDark
);
const beforeNormalColors = getStopListColors(
beforeIsThrough,
beforeIsCommunity,
isCanceled,
false,
isNotService
isNotService,
isDark
);
beforeTimeTextColor = beforeColors.timeText;
beforeNormalTimeTextColor = beforeNormalColors.timeText;
@@ -191,7 +199,7 @@ export const EachStopList: FC<props> = ({
>
<View
style={{
width: 35,
width: moderateScale(35),
position: "relative",
marginHorizontal: 15,
flexDirection: "row",
@@ -216,7 +224,7 @@ export const EachStopList: FC<props> = ({
<View style={{ position: "relative", height: 25 }}>
<Text
style={{
fontSize: 20,
fontSize: fontScale(20),
position: "absolute",
textAlignVertical: "center",
left: -15,
@@ -233,7 +241,7 @@ export const EachStopList: FC<props> = ({
padding: 8,
flexDirection: "row",
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
borderBottomColor: themeColors.borderLight,
flex: 1,
}}
>
@@ -241,7 +249,7 @@ export const EachStopList: FC<props> = ({
<View style={{ flex: 1 }} />
<Text
style={{
fontSize: 20,
fontSize: fontScale(20),
color: colors.text,
fontStyle: isCommunity ? "italic" : "normal",
textAlignVertical: "center",
@@ -302,20 +310,21 @@ const TimeText: FC<{
normalTextColor,
}) => {
const [seString, seType] = parseSeString(se);
const { fontScale, moderateScale } = useResponsive();
return (
<View style={{ flexDirection: "row", alignItems: "center" }}>
<View style={{ flexDirection: "row", alignItems: "center", opacity: isBefore ? 0.5 : 1 }}>
{!!currentTrainData?.delay &&
currentTrainData?.delay != "入線" &&
currentTrainData?.delay != 0 && (
<Text
style={{
fontSize: isBefore ? 14 : 15,
fontSize: fontScale(isBefore ? 14 : 15),
marginVertical: isDouble ? -2 : 0,
color: normalTextColor,
width: 60,
width: moderateScale(60),
position: "absolute",
right: isBefore ? 125 : 120,
right: isBefore ? moderateScale(125) : moderateScale(120),
textAlign: "right",
textDecorationLine: "line-through",
fontStyle: seType == "community" ? "italic" : "normal",
@@ -335,8 +344,8 @@ const TimeText: FC<{
/>
<Text
style={{
fontSize: isBefore ? 14 : 18,
width: 50,
fontSize: fontScale(isBefore ? 14 : 18),
width: moderateScale(50),
color: textColor,
marginVertical: isDouble ? -2 : 0,
fontStyle: seType == "community" ? "italic" : "normal",
@@ -349,6 +358,8 @@ const TimeText: FC<{
};
const StationNumbersBox: FC<{ stn: string; se: seTypes }> = (props) => {
const { stn, se } = props;
const { fixed } = useThemeColors();
const { fontScale } = useResponsive();
const lineColor = lineColorList[stn.charAt(0)];
const hasThrew =
se == "通過" ||
@@ -366,9 +377,9 @@ const StationNumbersBox: FC<{ stn: string; se: seTypes }> = (props) => {
<View style={{ flex: 1 }} />
<Text
style={{
color: "white",
color: fixed.textOnPrimary,
textAlign: "center",
fontSize: 10,
fontSize: fontScale(10),
fontWeight: "bold",
}}
>
@@ -393,6 +404,7 @@ type StationTimeBoxType = {
const StationTimeBox: FC<StationTimeBoxType> = (props) => {
const { delay, textColor, seType, se, time, isDouble, isBefore } = props;
const { fontScale, moderateScale } = useResponsive();
const dates = dayjs()
.set("hour", parseInt(time.split(":")[0]))
.set("minute", parseInt(time.split(":")[1]))
@@ -401,10 +413,10 @@ const StationTimeBox: FC<StationTimeBoxType> = (props) => {
return (
<Text
style={{
fontSize: isBefore ? 14 : 20,
fontSize: fontScale(isBefore ? 14 : 20),
marginVertical: isDouble ? -2 : 0,
color: textColor,
width: 60,
width: moderateScale(60),
fontStyle: seType == "community" ? "italic" : "normal",
}}
>

View File

@@ -0,0 +1,144 @@
import React, { FC, useCallback, useEffect } from "react";
import { Platform, TouchableOpacity, Text, StyleSheet } from "react-native";
import { useTrainFollowActivity } from "@/lib/useTrainFollowActivity";
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
import { trainDataType } from "@/lib/trainPositionTextArray";
type Props = {
currentTrainData: trainDataType;
currentPosition: string[] | undefined;
};
export const LiveActivityButton: FC<Props> = ({
currentTrainData,
currentPosition,
}) => {
const liveActivity = useTrainFollowActivity();
const { allTrainDiagram } = useAllTrainDiagram();
const onLine = !!currentPosition?.toString().length;
const handleLiveActivity = useCallback(async () => {
if (liveActivity.status === "active") {
await liveActivity.end();
return;
}
if (!onLine) return;
const diagramStr: string =
(allTrainDiagram as Record<string, string>)[currentTrainData?.num] ?? "";
const stations = diagramStr
.split("#")
.filter(Boolean)
.map((s) => s.split(",")[0]);
const destination =
stations.length > 0 ? stations[stations.length - 1] : "";
const curSt = currentPosition?.[0] ?? currentTrainData?.Pos ?? "";
const curIdx = stations.indexOf(curSt);
const nextSt =
curIdx >= 0 && curIdx + 1 < stations.length
? stations[curIdx + 1]
: currentPosition?.[1] ?? "";
await liveActivity.start({
trainNumber: currentTrainData?.num ?? "",
lineName: currentTrainData?.Line ?? "",
destination,
currentStation: curSt,
nextStation: nextSt,
delayMinutes:
typeof currentTrainData?.delay === "number"
? currentTrainData.delay
: 0,
scheduledArrival: "",
});
}, [
liveActivity,
onLine,
allTrainDiagram,
currentTrainData,
currentPosition,
]);
// 列車位置が変わったら Live Activity を自動更新
useEffect(() => {
if (liveActivity.status !== "active") return;
if (!currentTrainData?.Pos) return;
const diagramStr: string =
(allTrainDiagram as Record<string, string>)[currentTrainData?.num] ?? "";
const stations = diagramStr
.split("#")
.filter(Boolean)
.map((s) => s.split(",")[0]);
const curSt = currentPosition?.[0] ?? currentTrainData.Pos;
const curIdx = stations.indexOf(curSt);
const nextSt =
curIdx >= 0 && curIdx + 1 < stations.length
? stations[curIdx + 1]
: currentPosition?.[1] ?? "";
liveActivity.update({
currentStation: curSt,
nextStation: nextSt,
delayMinutes:
typeof currentTrainData.delay === "number"
? currentTrainData.delay
: 0,
scheduledArrival: "",
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
currentTrainData?.Pos,
currentTrainData?.delay,
currentPosition?.toString(),
]);
if (Platform.OS !== "ios" || !liveActivity.available || !onLine) {
return null;
}
return (
<TouchableOpacity
onPress={handleLiveActivity}
disabled={liveActivity.status === "loading"}
style={[
styles.liveActivityButton,
liveActivity.status === "active" && styles.liveActivityButtonActive,
]}
>
<Text style={styles.liveActivityButtonText}>
{liveActivity.status === "active"
? "🔴 列車追従 Live Activity 終了"
: liveActivity.status === "loading"
? "⏳ 開始中..."
: "🟢 列車追従 Live Activity 開始"}
</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
liveActivityButton: {
marginHorizontal: 10,
marginVertical: 6,
paddingVertical: 10,
paddingHorizontal: 16,
borderRadius: 10,
backgroundColor: "rgba(0, 153, 204, 0.12)",
borderWidth: 1,
borderColor: "rgba(0, 153, 204, 0.5)",
alignItems: "center",
},
liveActivityButtonActive: {
backgroundColor: "rgba(220, 50, 50, 0.1)",
borderColor: "rgba(220, 50, 50, 0.5)",
},
liveActivityButtonText: {
fontSize: 14,
fontWeight: "600",
color: "#0099cc",
},
});

View File

@@ -1,5 +1,5 @@
import React, { FC } from "react";
import { ScrollView } from "react-native";
import { View } from "react-native";
import { TrainDataView } from "./TrainDataView";
import { trainDataType } from "@/lib/trainPositionTextArray";
import type { NavigateFunction } from "@/types";
@@ -18,12 +18,13 @@ export const LongHeader:FC<props> = ({
navigate,
}) => {
return (
<ScrollView
<View
style={{
flexDirection: "row",
flex: 1,
width: "100%",
alignSelf: "stretch",
}}
horizontal
pagingEnabled
>
<TrainDataView
currentTrainData={currentTrainData}
@@ -33,6 +34,6 @@ export const LongHeader:FC<props> = ({
navigate={navigate}
key={"LongHeader"}
/>
</ScrollView>
</View>
);
};

View File

@@ -1,6 +1,8 @@
import { trainPosition, trainDataType } from "@/lib/trainPositionTextArray";
import React, { FC } from "react";
import { View, Text, TextStyle, ViewStyle } from "react-native";
import { View, Text, ViewStyle } from "react-native";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
type stateBox = {
currentTrainData: trainDataType | undefined;
@@ -21,6 +23,8 @@ export const PositionBox: FC<stateBox> = (props) => {
platformDescription,
lineNumber,
} = props;
const { colors } = useThemeColors();
const { fontScale } = useResponsive();
let firstText = "";
let secondText = "";
let marginText = "";
@@ -46,22 +50,22 @@ export const PositionBox: FC<stateBox> = (props) => {
}
}
return (
<View style={{ ...(mode == 2 ? boxStyle2 : boxStyle), ...style }}>
<Text style={{ fontSize: 12, color: "#0099CC" }}>{title}</Text>
<View style={{ ...(mode == 2 ? boxStyle2 : boxStyle), backgroundColor: colors.surface, ...style }}>
<Text style={{ fontSize: fontScale(12), color: colors.textAccent }}>{title}</Text>
<View style={{ flex: 1 }} />
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
{firstText && (
<Text style={mode == 2 ? boxTextStyle2 : (isBetween ? boxTextStyle : boxTextStyleBig)}>
<Text style={[mode == 2 ? { fontSize: fontScale(18), textAlign: "right" as const } : (isBetween ? { fontSize: fontScale(25), textAlign: "right" as const } : { fontSize: fontScale(28), textAlign: "right" as const }), { color: colors.textAccent }]}>
{firstText}
</Text>
)}
{marginText && (
<Text style={{ color: "#0099CC", textAlign: "right" }}>
<Text style={{ color: colors.textAccent, textAlign: "right" }}>
{marginText}
</Text>
)}
{secondText && (
<Text style={mode == 2 ? boxTextStyle2 :(isBetween ? boxTextStyle : boxTextStyleMini)}>
<Text style={[mode == 2 ? { fontSize: fontScale(18), textAlign: "right" as const } :(isBetween ? { fontSize: fontScale(25), textAlign: "right" as const } : { fontSize: fontScale(16), textAlign: "right" as const }), { color: colors.textAccent }]}>
{secondText}
</Text>
)}
@@ -70,8 +74,9 @@ export const PositionBox: FC<stateBox> = (props) => {
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
<Text
style={{
...{ ...(mode == 2 ? boxTextStyle2 : boxTextStyle) },
fontSize: 10,
...(mode == 2 ? { fontSize: fontScale(18), textAlign: "right" as const } : { fontSize: fontScale(25), textAlign: "right" as const }),
fontSize: fontScale(10),
color: colors.textAccent,
}}
>
{" " + externalText}
@@ -84,39 +89,13 @@ export const PositionBox: FC<stateBox> = (props) => {
};
const boxStyle: ViewStyle = {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
padding: 10,
margin: 10,
};
const boxStyle2: ViewStyle = {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
padding: 5,
margin: 5,
};
const boxTextStyle2: TextStyle = {
fontSize: 18,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyleBig: TextStyle = {
fontSize: 28,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyleMini: TextStyle = {
fontSize: 16,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyle: TextStyle = {
fontSize: 25,
color: "#0099CC",
textAlign: "right",
};

View File

@@ -1,13 +1,17 @@
import React from "react";
import { View, Text, LayoutAnimation, TouchableOpacity } from "react-native";
import { View, Text, TouchableOpacity } from "react-native";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
export const ScrollStickyContent = (props) => {
const { currentTrainData, showThrew, setShowThrew, haveThrough } = props;
const { colors } = useThemeColors();
const { fontScale } = useResponsive();
return (
<View
style={{
alignItems: "center",
backgroundColor: "#ffffffc2",
backgroundColor: colors.surface,
flexDirection: "row",
}}
>
@@ -16,18 +20,18 @@ export const ScrollStickyContent = (props) => {
padding: 8,
flexDirection: "row",
borderBottomWidth: 1,
borderBottomColor: "#ffffffc2",
borderBottomColor: colors.border,
flex: 1,
}}
>
<Text style={{ fontSize: 20 }}></Text>
<Text style={{ fontSize: fontScale(20), color: colors.text }}></Text>
<View style={{ flex: 1 }} />
<View style={{ flexDirection: "row" }}>
{!isNaN(currentTrainData?.delay) && currentTrainData?.delay != 0 && (
<Text
style={{
fontSize: 15,
color: "black",
fontSize: fontScale(15),
color: colors.text,
position: "absolute",
right: 110,
textAlign: "right",
@@ -39,12 +43,12 @@ export const ScrollStickyContent = (props) => {
)}
<Text
style={{
fontSize: 20,
fontSize: fontScale(20),
color: isNaN(currentTrainData?.delay)
? "black"
? colors.text
: currentTrainData?.delay == 0
? "black"
: "red",
? colors.text
: colors.textError,
width: 60,
}}
>
@@ -53,23 +57,20 @@ export const ScrollStickyContent = (props) => {
<TouchableOpacity
onPress={() => {
if (!haveThrough) return;
LayoutAnimation.configureNext({
duration: 200,
update: { type: "easeInEaseOut", springDamping: 0.6 },
});
setShowThrew(!showThrew);
}}
>
<View style={{ flex: 1 }} />
<Text
style={{
fontSize: 12,
fontSize: fontScale(12),
width: 50,
paddingBottom: 0,
margin: "auto",
textAlign: "center",
textAlignVertical: "center",
opacity: haveThrough ? 1 : 0,
color: colors.text,
}}
>
({showThrew ? "▼" : "▶"})

View File

@@ -1,5 +1,5 @@
import React, { FC } from "react";
import { ScrollView } from "react-native";
import { View } from "react-native";
import { TrainDataView } from "./TrainDataView";
import { trainDataType } from "@/lib/trainPositionTextArray";
import type { NavigateFunction } from "@/types";
@@ -18,13 +18,13 @@ export const ShortHeader:FC<props> = ({
navigate,
}) => {
return (
<ScrollView
<View
style={{
flexDirection: "row",
flex: 1,
width: "100%",
alignSelf: "stretch",
}}
horizontal
pagingEnabled
>
<TrainDataView
mode={2}
@@ -35,6 +35,6 @@ export const ShortHeader:FC<props> = ({
navigate={navigate}
key={"ShortHeader"}
/>
</ScrollView>
</View>
);
};

View File

@@ -1,12 +1,14 @@
import { Text, TouchableOpacity } from "react-native";
import React, { useState } from "react";
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
import { useThemeColors } from "@/lib/theme";
export const ShowSpecialTrain = ({
isTrainDataNothing,
setTrainData,
trueTrainID,
}) => {
const { allTrainDiagram } = useAllTrainDiagram();
const { colors } = useThemeColors();
const replaceSpecialTrainDetail = (trainNum) => {
let TD = allTrainDiagram[trainNum];
if (!TD) return;
@@ -22,7 +24,7 @@ export const ShowSpecialTrain = ({
style={{
padding: 10,
flexDirection: "row",
borderColor: "blue",
borderColor: colors.textAccent,
borderWidth: 1,
margin: 10,
borderRadius: 5,
@@ -30,7 +32,7 @@ export const ShowSpecialTrain = ({
}}
>
<Text
style={{ fontSize: 18, fontWeight: "bold", color: "black" }}
style={{ fontSize: 18, fontWeight: "bold", color: colors.text }}
>
:({ids})
</Text>

View File

@@ -1,5 +1,7 @@
import React, { CSSProperties, FC } from "react";
import { View, Text, StyleProp, TextStyle, ViewStyle } from "react-native";
import { View, Text, StyleProp, ViewStyle } from "react-native";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
type stateBox = {
text: string;
@@ -10,19 +12,22 @@ type stateBox = {
};
export const StateBox: FC<stateBox> = (props) => {
const { text, title, style, mode, endText } = props;
const { colors } = useThemeColors();
const { fontScale } = useResponsive();
return (
<View style={{ ...(mode == 2 ? boxStyle2 : boxStyle), ...style }}>
<Text style={{ fontSize: 12, color: "#0099CC" }}>{title}</Text>
<View style={{ ...(mode == 2 ? boxStyle2 : boxStyle), backgroundColor: colors.surface, ...style }}>
<Text style={{ fontSize: fontScale(12), color: colors.textAccent }}>{title}</Text>
<View style={{ flex: 1 }} />
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
<Text style={mode == 2 ? boxTextStyle2 : boxTextStyle}>{text}</Text>
<Text style={[mode == 2 ? { fontSize: fontScale(18), textAlign: "right" as const } : { fontSize: fontScale(25), textAlign: "right" as const }, { color: colors.textAccent }]}>{text}</Text>
</View>
{endText && (
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
<Text
style={{
...{ ...(mode == 2 ? boxTextStyle2 : boxTextStyle) },
fontSize: 10,
...(mode == 2 ? { fontSize: fontScale(18), textAlign: "right" as const } : { fontSize: fontScale(25), textAlign: "right" as const }),
fontSize: fontScale(10),
color: colors.textAccent,
}}
>
{endText}
@@ -34,26 +39,13 @@ export const StateBox: FC<stateBox> = (props) => {
};
const boxStyle: ViewStyle = {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
padding: 10,
margin: 10,
};
const boxStyle2: ViewStyle = {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
padding: 5,
margin: 5,
};
const boxTextStyle2: TextStyle = {
fontSize: 18,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyle: TextStyle = {
fontSize: 25,
color: "#0099CC",
textAlign: "right",
};

View File

@@ -1,5 +1,5 @@
import React, { useState, useEffect, FC } from "react";
import { View, TouchableOpacity, useWindowDimensions } from "react-native";
import { View, TouchableOpacity, Text } from "react-native";
import { StateBox } from "./StateBox";
import { PositionBox } from "./PositionBox";
import { useDeviceOrientationChange } from "../../../stateBox/useDeviceOrientationChange";
@@ -33,7 +33,6 @@ export const TrainDataView:FC<props> = ({
}) => {
const { stationList } = useStationList();
const { width, height } = useWindowDimensions();
const { isLandscape } = useDeviceOrientationChange();
const { setInjectData } = useCurrentTrain();
@@ -129,6 +128,8 @@ export const TrainDataView:FC<props> = ({
setDialog(true);
};
return (
<>
<StationPosPushDialog
@@ -153,7 +154,7 @@ export const TrainDataView:FC<props> = ({
flexDirection: "row",
//minHeight: 200,
//height: heightPercentageToDP("20%"),
width: isLandscape ? (width / 100) * 40 : width,
width: "100%",
flex: 1,
}}
>

View File

@@ -18,13 +18,15 @@ type ColorScheme = {
* @param isCanceled 運休かどうか
* @param isDelayed 遅延しているかどうか
* @param isNotService 回送列車かどうか
* @param isDark ダークモードかどうか
*/
export const getStopListColors = (
isThrough: boolean,
isCommunity: boolean,
isCanceled: boolean,
isDelayed: boolean,
isNotService: boolean = false
isNotService: boolean = false,
isDark: boolean = false
): ColorScheme => {
// 最優先: 回送列車の場合
if (isNotService) {
@@ -32,45 +34,47 @@ export const getStopListColors = (
// 回送 + 運休
if (isThrough) {
return {
background: '#1a1a1a', // 非常に濃いグレー
text: isCommunity ? '#8090c0' : '#909090', // 暗めの青灰色 or 暗めのグレー
timeText: isDelayed
? (isCommunity ? '#aa7799' : '#aa7777') // 遅延時: 暗めのピンク系の赤
: (isCommunity ? '#8090c0' : '#909090'),
seText: isCommunity ? '#8090c0' : '#909090',
background: isDark ? '#111111' : '#1a1a1a',
text: isCommunity ? '#8090c0' : (isDark ? '#707070' : '#909090'),
timeText: isDelayed
? (isCommunity ? '#aa7799' : '#aa7777')
: (isCommunity ? '#8090c0' : (isDark ? '#707070' : '#909090')),
seText: isCommunity ? '#8090c0' : (isDark ? '#707070' : '#909090'),
opacity: '0.5',
};
} else {
return {
background: '#3a3a3a', // 濃いグレー
text: isCommunity ? '#8090c0' : '#b0b0b0', // 暗めの青灰色 or 明るめのグレー
background: isDark ? '#2a2a2a' : '#3a3a3a',
text: isCommunity ? '#8090c0' : (isDark ? '#c0c0c0' : '#b0b0b0'),
timeText: isDelayed
? (isCommunity ? '#bb8899' : '#bb8888') // 遅延時: 暗めのピンク系の赤
: (isCommunity ? '#8090c0' : '#b0b0b0'),
seText: isCommunity ? '#8090c0' : '#b0b0b0',
? (isCommunity ? '#bb8899' : '#bb8888')
: (isCommunity ? '#8090c0' : (isDark ? '#c0c0c0' : '#b0b0b0')),
seText: isCommunity ? '#8090c0' : (isDark ? '#c0c0c0' : '#b0b0b0'),
opacity: '0.8',
};
}
} else if (isThrough) {
// 回送 + 通過
return {
background: '#e8e8e8', // 薄いグレー
text: isCommunity ? '#6677cc' : '#777777', // 暗めの青 or グレー
timeText: isDelayed
? (isCommunity ? '#cc5577' : '#dd5555') // 遅延時: 暗めの赤
: (isCommunity ? '#6677cc' : '#777777'),
seText: isCommunity ? '#6677cc' : '#777777',
background: isDark ? '#2a2a2a' : '#e8e8e8',
text: isCommunity ? (isDark ? '#8899ee' : '#6677cc') : (isDark ? '#aaaaaa' : '#777777'),
timeText: isDelayed
? (isCommunity ? '#cc5577' : '#dd5555')
: (isCommunity ? (isDark ? '#8899ee' : '#6677cc') : (isDark ? '#aaaaaa' : '#777777')),
seText: isCommunity ? (isDark ? '#8899ee' : '#6677cc') : (isDark ? '#aaaaaa' : '#777777'),
opacity: '0.6',
};
} else {
// 回送 + 通常停車
return {
background: isDelayed ? '#f5f0f0' : '#f5f5f5', // 遅延時は少し赤みのあるグレー
text: isCommunity ? '#4455aa' : '#555555', // 暗めの青 or ダークグレー
timeText: isDelayed
? (isCommunity ? '#bb3355' : '#cc0000') // 遅延時: 暗めの赤
: (isCommunity ? '#4455aa' : '#555555'),
seText: isCommunity ? '#4455aa' : '#555555',
background: isDark
? (isDelayed ? '#2a1a1a' : '#222222')
: (isDelayed ? '#f5f0f0' : '#f5f5f5'),
text: isCommunity ? (isDark ? '#7788cc' : '#4455aa') : (isDark ? '#aaaaaa' : '#555555'),
timeText: isDelayed
? (isCommunity ? '#bb3355' : '#cc0000')
: (isCommunity ? (isDark ? '#7788cc' : '#4455aa') : (isDark ? '#aaaaaa' : '#555555')),
seText: isCommunity ? (isDark ? '#7788cc' : '#4455aa') : (isDark ? '#aaaaaa' : '#555555'),
opacity: '0.85',
};
}
@@ -81,10 +85,10 @@ export const getStopListColors = (
if (isThrough) {
// 通過系 + 運休
return {
background: '#2a2a2a', // 濃いグレー
text: isCommunity ? '#a8b5ff' : '#c0c0c0', // 薄い青 or 薄いグレー
timeText: isDelayed
? (isCommunity ? '#dd99bb' : '#dd9999') // 遅延時: 薄いピンク系の赤
background: isDark ? '#1e1e2e' : '#2a2a2a',
text: isCommunity ? '#a8b5ff' : '#c0c0c0',
timeText: isDelayed
? (isCommunity ? '#dd99bb' : '#dd9999')
: (isCommunity ? '#a8b5ff' : '#c0c0c0'),
seText: isCommunity ? '#a8b5ff' : '#c0c0c0',
opacity: '0.6',
@@ -92,10 +96,10 @@ export const getStopListColors = (
} else {
// 通常停車 + 運休
return {
background: '#5a5a5a', // 中程度のグレー
text: isCommunity ? '#a8b5ff' : '#ffffff', // 薄い青 or 白
background: isDark ? '#3a3a4a' : '#5a5a5a',
text: isCommunity ? '#a8b5ff' : '#ffffff',
timeText: isDelayed
? (isCommunity ? '#ffaacc' : '#ffaaaa') // 遅延時: 明るいピンク系の赤
? (isCommunity ? '#ffaacc' : '#ffaaaa')
: (isCommunity ? '#a8b5ff' : '#ffffff'),
seText: isCommunity ? '#a8b5ff' : '#ffffff',
opacity: '0.9',
@@ -106,12 +110,14 @@ export const getStopListColors = (
// 次: 通過系の場合
if (isThrough) {
return {
background: isCommunity ? '#f0f4ff' : '#fafafa', // 薄い青背景 or 薄いグレー
text: isCommunity ? '#5577ff' : '#888888', // 中程度の青 or グレー
timeText: isDelayed
? (isCommunity ? '#dd5588' : '#ff6666') // 遅延時: コミュニティは紫がかった赤、通常は明るい赤
: (isCommunity ? '#5577ff' : '#888888'),
seText: isCommunity ? '#5577ff' : '#888888',
background: isDark
? (isCommunity ? '#1a1e2e' : '#1e1e1e')
: (isCommunity ? '#f0f4ff' : '#fafafa'),
text: isCommunity ? (isDark ? '#7799ff' : '#5577ff') : (isDark ? '#666666' : '#888888'),
timeText: isDelayed
? (isCommunity ? '#dd5588' : '#ff6666')
: (isCommunity ? (isDark ? '#7799ff' : '#5577ff') : (isDark ? '#666666' : '#888888')),
seText: isCommunity ? (isDark ? '#7799ff' : '#5577ff') : (isDark ? '#666666' : '#888888'),
opacity: '0.5',
};
}
@@ -120,19 +126,27 @@ export const getStopListColors = (
if (isCommunity) {
// コミュニティ投稿
return {
background: isDelayed ? '#fff5f5' : '#ffffff', // 遅延時は薄い赤背景
text: '#3355dd', // 明確な青
timeText: isDelayed ? '#cc2266' : '#3355dd', // 遅延時: 紫がかった赤
seText: '#3355dd',
background: isDark
? (isDelayed ? '#2a1a2a' : '#1e1e2e')
: (isDelayed ? '#fff5f5' : '#ffffff'),
text: isDark ? '#8899ff' : '#3355dd',
timeText: isDelayed
? (isDark ? '#ee4488' : '#cc2266')
: (isDark ? '#8899ff' : '#3355dd'),
seText: isDark ? '#8899ff' : '#3355dd',
opacity: '0.95',
};
} else {
// 公式データ
return {
background: isDelayed ? '#fff5f5' : '#ffffff', // 遅延時は薄い赤背景
text: '#000000', // 黒
timeText: isDelayed ? '#dd0000' : '#000000', // 遅延時: 標準的な赤
seText: '#000000',
background: isDark
? (isDelayed ? '#2a1a1a' : '#1e1e2e')
: (isDelayed ? '#fff5f5' : '#ffffff'),
text: isDark ? '#e0e0e0' : '#000000',
timeText: isDelayed
? (isDark ? '#ff5555' : '#dd0000')
: (isDark ? '#e0e0e0' : '#000000'),
seText: isDark ? '#e0e0e0' : '#000000',
opacity: '0.95',
};
}

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
View,
Text,
@@ -8,7 +8,7 @@ import {
BackHandler,
Linking,
} from "react-native";
import { SheetManager, useScrollHandlers } from "react-native-actions-sheet";
import { SheetManager } from "react-native-actions-sheet";
import { getTrainType } from "../../lib/getTrainType";
import { customTrainDataDetector } from "../custom-train-data";
import { useDeviceOrientationChange } from "../../stateBox/useDeviceOrientationChange";
@@ -22,7 +22,9 @@ import { ShowSpecialTrain } from "./EachTrainInfo/ShowSpecialTrain";
import { useTrainMenu } from "../../stateBox/useTrainMenu";
import { HeaderText } from "./EachTrainInfoCore/HeaderText";
import { useStationList } from "../../stateBox/useStationList";
import { useThemeColors } from "@/lib/theme";
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
import { useResponsive } from "@/lib/responsive";
// Custom hooks
import { useTrainDiagramData } from "./EachTrainInfoCore/hooks/useTrainDiagramData";
@@ -42,14 +44,13 @@ export const EachTrainInfoCore = ({
}) => {
const { stationList } = useStationList();
const { allCustomTrainData } = useAllTrainDiagram();
const { colors, fixed } = useThemeColors();
const { verticalScale, moderateScale } = useResponsive();
const { setTrainInfo } = useTrainMenu();
const { height } = useWindowDimensions();
const { isLandscape } = useDeviceOrientationChange();
const scrollHandlers = actionSheetRef
? //@ts-ignore
useScrollHandlers("scrollview-1", actionSheetRef)
: null;
const scrollRef = useRef<any>(null);
// Custom hooks for data management
const { trainData, setTrainData, trueTrainID } = useTrainDiagramData(
data.trainNum
@@ -78,7 +79,7 @@ export const EachTrainInfoCore = ({
useAutoScroll(
points,
trainDataWithThrough,
scrollHandlers,
scrollRef,
isJumped,
setIsJumped,
setShowThrew
@@ -125,7 +126,6 @@ export const EachTrainInfoCore = ({
} else {
SheetManager.hide("EachTrainInfo").then(() => {
setTimeout(() => {
// @ts-expect-error - SheetManager payload type is too restrictive
SheetManager.show("EachTrainInfo", { payload });
}, 200);
});
@@ -135,20 +135,20 @@ export const EachTrainInfoCore = ({
return (
<View
style={{
backgroundColor: "#0099CC",
backgroundColor: fixed.primary,
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
borderColor: "dark",
borderColor: colors.border,
borderWidth: 1,
}}
>
<View style={{ height: 26, width: "100%" }}>
<View style={{ height: verticalScale(26), width: "100%" }}>
<View
style={{
height: 6,
width: 45,
height: verticalScale(6),
width: moderateScale(45),
borderRadius: 100,
backgroundColor: "#f0f0f0",
backgroundColor: colors.borderLight,
marginVertical: 10,
alignSelf: "center",
}}
@@ -164,18 +164,18 @@ export const EachTrainInfoCore = ({
navigate={navigate}
from={from}
fontLoaded={true}
scrollHandlers={scrollHandlers}
scrollRef={scrollRef}
/>
<DynamicHeaderScrollView
from={from}
styles={styles as any}
scrollHandlers={scrollHandlers}
scrollRef={scrollRef}
containerProps={{
style: {
maxHeight: isLandscape ? height - 94 : (height / 100) * 70,
maxHeight: height * 0.7,
backgroundColor:
customTrainType.data === "notService" ? "#777777ff" : "white",
customTrainType.data === "notService" ? "#777777ff" : colors.surface,
},
}}
shortHeader={
@@ -207,7 +207,7 @@ export const EachTrainInfoCore = ({
}
>
{customTrainType.data === "notService" && (
<Text style={{ backgroundColor: "#ffffffc2", fontWeight: "bold" }}>
<Text style={{ backgroundColor: colors.surface, color: colors.text, fontWeight: "bold" }}>
</Text>
)}
@@ -219,10 +219,10 @@ export const EachTrainInfoCore = ({
onPress={() =>
extendToHeadStation(item.station, item.dia, index)
}
style={styles.extendStationButton}
style={[styles.extendStationButton, { borderColor: colors.textAccent }]}
key={`${item.station}-head${index}`}
>
<Text style={styles.extendStationText}>
<Text style={[styles.extendStationText, { color: colors.text }]}>
</Text>
</TouchableOpacity>
@@ -238,9 +238,9 @@ export const EachTrainInfoCore = ({
onPress={() =>
Linking.openURL(`https://twitter.com/search?q=${data.trainNum}`)
}
style={styles.twitterSearchButton}
style={[styles.twitterSearchButton, { borderColor: colors.textAccent, backgroundColor: colors.backgroundOverlay }]}
>
<Text style={styles.extendStationText}>Twitterで検索</Text>
<Text style={[styles.extendStationText, { color: colors.text }]}>Twitterで検索</Text>
</TouchableOpacity>
)}
{trainDataWithThrough.map((item, index, array) =>
@@ -251,7 +251,7 @@ export const EachTrainInfoCore = ({
openTrainInfo={openTrainInfo}
/>
) : item.split(",")[1].includes(".") ? (
<></>
<React.Fragment key={`${item}-skip`} />
) : (
<EachStopList
i={item}
@@ -267,7 +267,7 @@ export const EachTrainInfoCore = ({
/>
)
)}
<Text style={styles.customDataNote}>
<Text style={[styles.customDataNote, { color: colors.textSecondary }]}>
,
</Text>
{tailStation.length > 0 &&
@@ -276,17 +276,17 @@ export const EachTrainInfoCore = ({
!showTailStation.includes(index) && (
<TouchableOpacity
onPress={() => extendToTailStation(station, dia, index)}
style={styles.extendStationButton}
style={[styles.extendStationButton, { borderColor: colors.textAccent }]}
key={`${station}-tail${index}`}
>
<Text style={styles.extendStationText}>
<Text style={[styles.extendStationText, { color: colors.text }]}>
</Text>
</TouchableOpacity>
)
)}
<View style={styles.bottomSpacer} />
<View style={[styles.bottomSpacer, { borderBottomColor: colors.borderLight }]} />
</DynamicHeaderScrollView>
</View>
);
@@ -320,7 +320,6 @@ const styles = StyleSheet.create({
extendStationText: {
fontSize: 18,
fontWeight: "bold",
color: "black",
},
twitterSearchButton: {
padding: 10,
@@ -333,14 +332,12 @@ const styles = StyleSheet.create({
backgroundColor: "#ffffffc2",
},
customDataNote: {
backgroundColor: "#ffffffc2",
},
bottomSpacer: {
flexDirection: "row",
padding: 8,
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
backgroundColor: "#ffffffc2",
flex: 1,
},
});

View File

@@ -1,11 +1,5 @@
import React, { FC, useMemo } from "react";
import {
Text,
View,
TextStyle,
TouchableOpacity,
useWindowDimensions,
} from "react-native";
import React, { FC, useMemo, useState } from "react";
import { Text, View, TextStyle, TouchableOpacity } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { migrateTrainName } from "../../../lib/eachTrainInfoCoreLib/migrateTrainName";
import { TrainIconStatus } from "./trainIconStatus";
@@ -19,6 +13,10 @@ import { getStringConfig } from "@/lib/getStringConfig";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { getPDFViewURL } from "@/lib/getPdfViewURL";
import type { NavigateFunction } from "@/types";
import { useUnyohub } from "@/stateBox/useUnyohub";
import { useElesite } from "@/stateBox/useElesite";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
type Props = {
data: { trainNum: string; limited: string };
@@ -30,13 +28,12 @@ type Props = {
navigate: NavigateFunction;
from: string;
fontLoaded: boolean;
scrollHandlers: any;
scrollRef: any;
};
const textConfig: TextStyle = {
fontSize: 17,
fontWeight: "bold",
color: "white",
};
export const HeaderText: FC<Props> = ({
@@ -48,15 +45,30 @@ export const HeaderText: FC<Props> = ({
tailStation,
navigate,
from,
scrollHandlers,
scrollRef,
}) => {
const { limited, trainNum } = data;
const { height, width } = useWindowDimensions();
const { fixed } = useThemeColors();
const { fontScale } = useResponsive();
const { updatePermission } = useTrainMenu();
const { allCustomTrainData, getTodayOperationByTrainId } =
useAllTrainDiagram();
const { expoPushToken } = useNotification();
const {
getUnyohubByTrainNumber,
getUnyohubEntriesByTrainNumber,
useUnyohub: unyohubEnabled,
} = useUnyohub();
const { getElesiteEntriesByTrainNumber, useElesite: elesiteEnabled } =
useElesite();
// 追加ソースのON/OFFをここで管理将来ソースが増えたらここに足す
const additionalSources = {
unyohub: unyohubEnabled,
elesite: elesiteEnabled,
};
const hasAdditionalSources = Object.values(additionalSources).some(Boolean);
// 列車名、種別、フォントの取得
const [
@@ -68,7 +80,10 @@ export const HeaderText: FC<Props> = ({
priority,
uwasa,
trainInfoUrl,
directions,
customTrainData,
] = useMemo(() => {
const result = customTrainDataDetector(trainNum, allCustomTrainData);
const {
type,
train_name,
@@ -78,15 +93,14 @@ export const HeaderText: FC<Props> = ({
uwasa,
train_info_url,
to_data,
} = customTrainDataDetector(trainNum, allCustomTrainData);
directions,
} = result;
const [typeString, fontAvailable, isOneMan] = getStringConfig(
type,
trainNum,
);
switch (true) {
case train_name !== "":
// 特急の場合は、列車名を取得
// 列番対称データがある場合はそれから列車番号を取得
return [
typeString,
train_name +
@@ -99,6 +113,8 @@ export const HeaderText: FC<Props> = ({
priority,
uwasa,
train_info_url,
directions,
result,
];
case trainData[trainData.length - 1] === undefined:
return [
@@ -110,9 +126,10 @@ export const HeaderText: FC<Props> = ({
priority,
uwasa,
train_info_url,
directions,
result,
];
case to_data && to_data !== "":
// 行先がある場合は、行先を取得
return [
typeString,
to_data + "行き",
@@ -122,9 +139,10 @@ export const HeaderText: FC<Props> = ({
priority,
uwasa,
train_info_url,
directions,
result,
];
default:
// 行先がある場合は、行先を取得
return [
typeString,
migrateTrainName(
@@ -136,18 +154,53 @@ export const HeaderText: FC<Props> = ({
priority,
uwasa,
train_info_url,
directions,
result,
];
}
}, [trainData]);
}, [trainData, trainNum, allCustomTrainData]);
const todayOperation = getTodayOperationByTrainId(trainNum).filter(
(d) => d.state !== 100,
);
let iconTrainDirection =
parseInt(trainNum.replace(/[^\d]/g, "")) % 2 == 0 ? true : false;
if (directions != undefined) {
iconTrainDirection = directions ? true : false;
}
const unyohubLookupNum = customTrainData?.train_number_override || trainNum;
const unyohubFormation = getUnyohubByTrainNumber(unyohubLookupNum);
const unyohubEntries = getUnyohubEntriesByTrainNumber(unyohubLookupNum);
const elesiteEntries = getElesiteEntriesByTrainNumber(trainNum);
// 車番(formations)が空でないエントリが1件以上あれば「運用Hub情報あり」と判定
const hasUnyohubFormation = unyohubEntries.some(
(e) => !!e.formations && e.formations.trim() !== "",
);
const hasElesiteFormation = elesiteEntries.some(
(e) => (e.formation_config?.units?.length ?? 0) > 0,
);
const hasExtraInfo =
priority > 200 ||
todayOperation?.length > 0 ||
hasUnyohubFormation ||
hasElesiteFormation;
const [isWrapped, setIsWrapped] = useState(false);
return (
<View
style={{ padding: 10, flexDirection: "row", alignItems: "center" }}
style={{
padding: 10,
flexDirection: "row",
alignItems: "center",
width: "100%",
}}
onTouchStart={() =>
scrollHandlers.ref.current?.scrollTo({ y: 0, animated: true })
scrollRef.current?.scrollTo({ y: 0, animated: true })
}
>
<TrainIconStatus
@@ -155,18 +208,42 @@ export const HeaderText: FC<Props> = ({
navigate={navigate}
from={from}
todayOperation={todayOperation}
direction={iconTrainDirection}
/>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginBottom: 2,
}}
>
<Text
style={{
fontSize: fontScale(20),
color: fixed.textOnPrimary,
fontFamily: fontAvailable ? "JR-Nishi" : undefined,
fontWeight: !fontAvailable ? "bold" : undefined,
marginRight: 5,
}}
>
{isWrapped ? typeName.replace(/(.{2})/g, "$1\n").trim() : typeName}
</Text>
{isOneMan && <OneManText />}
</View>
<TouchableOpacity
style={{
borderRadius: 5,
flexDirection: "row",
alignItems: "center",
flexShrink: 1,
flexWrap: "wrap",
...(trainInfoUrl
? {
borderWidth: 0,
borderBottomWidth: 1,
borderStyle: "solid",
borderColor: "white",
borderColor: fixed.textOnPrimary,
}
: {}),
}}
@@ -180,64 +257,59 @@ export const HeaderText: FC<Props> = ({
}}
disabled={!trainInfoUrl}
>
<Text
style={{
fontSize: 20,
color: "white",
fontFamily: fontAvailable ? "JR-Nishi" : undefined,
fontWeight: !fontAvailable ? "bold" : undefined,
marginRight: 5,
}}
>
{typeName}
</Text>
{isOneMan && <OneManText />}
<Text
style={{
...textConfig,
...(trainName.length > 10 ? { fontSize: 14 } : {}),
maxWidth: width * 0.6,
color: fixed.textOnPrimary,
...(trainName.length > 10 ? { fontSize: fontScale(16) } : { fontSize: fontScale(17) }),
flexShrink: 1,
}}
onTextLayout={(e) => {
if (e.nativeEvent.lines.length > 1) setIsWrapped(true);
}}
>
{trainName}
</Text>
<InfogramText infogram={infogram} />
{/* {trainInfoUrl && (
<MaterialCommunityIcons
name={"open-in-new"}
color="white"
size={15}
/>
)} */}
</TouchableOpacity>
<View style={{ flex: 1 }} />
<TouchableOpacity
onLongPress={() => {
if (!updatePermission) return;
const uri = `https://jr-shikoku-data-system.pages.dev/trainData/${trainNum}?userID=${expoPushToken}&from=eachTrainInfo`;
navigate("generalWebView", { uri, useExitButton: false });
SheetManager.hide("EachTrainInfo");
}}
disabled={!updatePermission}
>
<Text style={textConfig}>
{showHeadStation.map((d) => `${headStation[d].id} + `)}
{trainNum}
{showTailStation.map((d) => ` + ${tailStation[d].id}`)}
</Text>
</TouchableOpacity>
<Text style={{ ...textConfig, fontSize: fontScale(17), color: fixed.textOnPrimary }}>
{showHeadStation.map((d) => `${headStation[d].id} + `)}
{trainNum}
{showTailStation.map((d) => ` + ${tailStation[d].id}`)}
</Text>
<MaterialCommunityIcons
name="database"
color={
priority > 200 || todayOperation?.length > 0 ? "yellow" : "white"
}
color={hasExtraInfo ? "yellow" : fixed.textOnPrimary}
size={30}
style={{ margin: 5 }}
onPress={() => {
const uri = `https://jr-shikoku-data-system.pages.dev/trainData/${trainNum}?userID=${expoPushToken}&from=eachTrainInfo`;
navigate("generalWebView", { uri, useExitButton: false });
SheetManager.hide("EachTrainInfo");
if (hasAdditionalSources) {
(SheetManager.show as any)("TrainDataSources", {
payload: {
trainNum,
unyohubTrainNum: unyohubLookupNum,
unyohubEntries,
elesiteEntries,
todayOperation,
navigate,
expoPushToken,
priority,
direction: iconTrainDirection,
customTrainData,
typeName,
trainName,
departureStation: trainData[0]?.split(",")[0] ?? "",
destinationStation:
trainData[trainData.length - 1]?.split(",")[0] ?? "",
},
});
} else {
// 追加ソースが全てオフ → 元の挙動(直接 DB ページを開く)
const uri = `https://jr-shikoku-data-system.pages.dev/trainData/${trainNum}?userID=${expoPushToken}&from=eachTrainInfo`;
navigate("generalWebView", { uri, useExitButton: false });
SheetManager.hide("EachTrainInfo");
}
}}
/>
</View>

View File

@@ -1,14 +1,16 @@
import React, { FC } from "react";
import { Text } from "react-native";
import { useThemeColors } from "@/lib/theme";
type props = {
infogram: string;
}
export const InfogramText: FC<props> = ({infogram}) => {
const { fixed } = useThemeColors();
return (
<Text
style={{
fontSize: 20,
color: "white",
color: fixed.textOnPrimary,
fontFamily: "JNR-font",
}}
>

View File

@@ -1,10 +1,12 @@
import React, { FC } from "react";
import { Text, View } from "react-native";
import { useThemeColors } from "@/lib/theme";
export const OneManText: FC = () => {
const { fixed } = useThemeColors();
const styles = {
fontSize: 12,
margin: -2,
color: "white",
color: fixed.textOnPrimary,
fontFamily: "Zou",
};
return (

View File

@@ -1,41 +1,38 @@
import React,{ useEffect, MutableRefObject } from 'react';
import { LayoutAnimation, ScrollView } from 'react-native';
import { useEffect, MutableRefObject } from 'react';
import { InteractionManager } from 'react-native';
export const useAutoScroll = (
points: boolean[] | undefined,
trainDataWithThrough: string[],
scrollHandlers: any,
scrollRef: MutableRefObject<any>,
isJumped: boolean,
setIsJumped: (value: boolean) => void,
setShowThrew: (value: boolean) => void
) => {
useEffect(() => {
if (isJumped || !points?.length || !scrollHandlers) return;
if (isJumped || !points?.length || !scrollRef) return;
const currentPositionIndex = points.findIndex((d) => d === true);
if (currentPositionIndex === -1) return;
setShowThrew(true);
// ActionSheetの開閉アニメーション完了後にレイアウト変更を行う
const handle = InteractionManager.runAfterInteractions(() => {
setShowThrew(true);
const isPassingThrough = trainDataWithThrough[currentPositionIndex]?.split(',')[1] === '通過';
if (isPassingThrough) {
LayoutAnimation.configureNext({
duration: 400,
update: { type: 'easeInEaseOut', springDamping: 0.6 },
});
}
// 5駅以内の場合はスクロールしない
if (currentPositionIndex < 5) {
setIsJumped(true);
return;
}
// 5駅以内の場合はスクロールしない
if (currentPositionIndex < 5) {
setIsJumped(true);
return;
}
const scrollPosition = currentPositionIndex * 44 - 50;
setTimeout(() => {
scrollRef.current?.scrollTo({ y: scrollPosition, animated: true });
setIsJumped(true);
}, 100);
});
const scrollPosition = currentPositionIndex * 44 - 50;
setTimeout(() => {
scrollHandlers.ref.current?.scrollTo({ y: scrollPosition, animated: true });
setIsJumped(true);
}, 400);
}, [points, trainDataWithThrough, scrollHandlers, isJumped, setIsJumped, setShowThrew]);
return () => handle.cancel();
}, [points, trainDataWithThrough, scrollRef, isJumped, setIsJumped, setShowThrew]);
};

View File

@@ -25,7 +25,7 @@ export const useTrainPosition = (
const trainPosData = getCurrentStationData(trainNum);
if (trainPosData) {
logger.debug('Train position data:', trainPosData);
setCurrentTrainData(trainPosData);
setCurrentTrainData(trainPosData as TrainData);
}
}, [currentTrain, trainNum, getCurrentStationData]);

View File

@@ -2,11 +2,10 @@ import React, { ComponentProps, FC, useEffect, useState } from "react";
import { View, Image, TouchableOpacity } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import dayjs from "dayjs";
import { useInterval } from "../../../lib/useInterval";
import { Icon } from "@expo/vector-icons/build/createIconSet";
import { SheetManager } from "react-native-actions-sheet";
import { customTrainDataDetector } from "../../custom-train-data";
import { useAllTrainDiagram } from "@/stateBox/useAllTrainDiagram";
import { useInterval } from "@/lib/useInterval";
import type { NavigateFunction } from "@/types";
import { OperationLogs } from "@/lib/CommonTypes";
@@ -17,17 +16,18 @@ type Props = {
navigate: NavigateFunction;
from: string;
todayOperation: OperationLogs[];
direction?: boolean;
};
type apt = {
name: GlyphNames;
color: string;
};
export const TrainIconStatus: FC<Props> = (props) => {
const { data, navigate, from, todayOperation } = props;
const { data, navigate, from, todayOperation, direction } = props;
const [anpanmanStatus, setAnpanmanStatus] = useState<apt>();
const { allCustomTrainData } = useAllTrainDiagram();
const [trainIconData, setTrainIcon] = useState<
{ vehicle_info_img: string; vehicle_info_url: string }[]
{ vehicle_info_img: string;vehicle_info_right_img: string; vehicle_info_url: string }[]
>([]);
useEffect(() => {
if (!data.trainNum) return;
@@ -53,7 +53,7 @@ export const TrainIconStatus: FC<Props> = (props) => {
...(operation.train_ids || []),
...(operation.related_train_ids || []),
];
// data.trainNumの接頭辞と一致するものを探す
for (const trainId of allTrainIds) {
const prefix = trainId.split(',')[0]; // カンマ前の部分
@@ -63,30 +63,58 @@ export const TrainIconStatus: FC<Props> = (props) => {
}
return null;
};
const aTrainId = findMatchingTrainId(a);
const bTrainId = findMatchingTrainId(b);
// マッチしたものがない場合は元の順序を保持
if (!aTrainId || !bTrainId) {
return aTrainId ? -1 : bTrainId ? 1 : 0;
}
const aOrder = extractOrderNumber(aTrainId);
const bOrder = extractOrderNumber(bTrainId);
return aOrder - bOrder;
})
.map((op) => ({
vehicle_info_img: op.vehicle_img || vehicle_info_img,
vehicle_info_right_img: op.vehicle_img_right || op.vehicle_img || vehicle_info_img,
vehicle_info_url: op.vehicle_info_url,
})) || [];
setTrainIcon(returnData);
} else if (vehicle_info_img) {
setTrainIcon([{ vehicle_info_img, vehicle_info_url }]);
setTrainIcon([{ vehicle_info_img, vehicle_info_right_img: vehicle_info_img, vehicle_info_url }]);
}
// アンパンマンステータスAPIのエンドポイント判定
let anpanmanApiPath: string | null = null;
switch (data.trainNum) {
// 予讃線 → yosan-anpanman
// しおかぜ 8000 アンパン
case "10M":
case "22M":
case "9M":
case "21M":
// いしづち 8000 アンパン
case "1010M":
case "1022M":
case "1009M":
case "1021M":
// いしづち 三桁 アンパン
case "1041M":
case "1044M":
// 宇和海 2000 アンパン
case "1058D":
case "1066D":
case "1074D":
case "1053D":
case "1059D":
case "1067D":
anpanmanApiPath = "yosan-anpanman";
break;
// 土讃線 → dosan-anpanman
// 南風 2700 アンパン
case "32D":
case "36D":
case "44D":
@@ -97,62 +125,45 @@ export const TrainIconStatus: FC<Props> = (props) => {
case "45D":
case "49D":
case "57D":
fetch(
`https://n8n.haruk.in/webhook/dosan-anpanman?trainNum=${
data.trainNum
}&month=${dayjs().format("M")}&day=${dayjs().format("D")}`
)
.then((d) => d.json())
.then((d) => {
if (d.trainStatus == "") {
//setAnpanmanStatus({name:"checkmark-circle-outline",color:"blue"});
} else if (d.trainStatus == "▲") {
setAnpanmanStatus({ name: "warning-outline", color: "yellow" });
} else if (d.trainStatus == "×") {
setAnpanmanStatus({ name: "close-circle-outline", color: "red" });
}
});
break;
case "2074D":
case "2076D":
case "2080D":
case "2082D":
case "2071D":
case "2073D":
case "2079D":
case "2081D":
fetch(
`https://n8n.haruk.in/webhook/dosan-anpanman-first?trainNum=${
data.trainNum
}&month=${dayjs().format("M")}&day=${dayjs().format("D")}`
)
.then((d) => d.json())
.then((d) => {
if (d.trainStatus == "") {
//setAnpanmanStatus({name:"checkmark-circle-outline",color:"blue"});
} else if (d.trainStatus == "▲") {
setAnpanmanStatus({ name: "warning-outline", color: "yellow" });
} else if (d.trainStatus == "×") {
//setAnpanmanStatus({ name: "close-circle-outline", color: "red" });
}
});
anpanmanApiPath = "dosan-anpanman";
break;
}
if (anpanmanApiPath) {
fetch(
`https://n8n.haruk.in/webhook/${anpanmanApiPath}?trainNum=${
data.trainNum
}&month=${dayjs().format("M")}&day=${dayjs().format("D")}`
)
.then((d) => d.json())
.then((d) => {
if (d.trainStatus == "" || d.trainStatus == "○") {
//setAnpanmanStatus({name:"checkmark-circle-outline",color:"blue"});
} else if (d.trainStatus == "▲") {
setAnpanmanStatus({ name: "warning-outline", color: "yellow" });
} else if (d.trainStatus == "×") {
setAnpanmanStatus({ name: "close-circle-outline", color: "red" });
}
})
.catch(() => {});
}
}, [data.trainNum, allCustomTrainData, todayOperation]);
const [move, setMove] = useState(true);
useInterval(
() => {
// anpanmanStatusがデータを持っているなら実行
if (anpanmanStatus) setMove(!move);
},
1000,
true
);
// JSスレッドでの点滅useInterval + useState
// reanimated の withRepeat はUIスレッドで毎フレーム更新し続けるため
// ActionSheetのスプリングアニメーションと競合する
const [showIcon, setShowIcon] = useState(false);
useInterval(() => {
if (anpanmanStatus) {
setShowIcon((prev) => !prev);
}
}, 1000, !!anpanmanStatus);
return (
<>
{trainIconData.map(
({ vehicle_info_img: trainIcon, vehicle_info_url: address }, index) => (
({ vehicle_info_img: trainIcon, vehicle_info_right_img: trainIconRight, vehicle_info_url: address }, index) => (
<TouchableOpacity
key={`${trainIcon}-${index}`}
onPress={() => {
navigate("howto", {
info: address,
@@ -162,26 +173,30 @@ export const TrainIconStatus: FC<Props> = (props) => {
}}
disabled={!address}
>
{move ? (
<Image
source={{ uri: trainIcon }}
style={{
height: index > 0 ? 15 : 30,
width: index > 0 ? 12 : 24,
marginRight: 5,
marginLeft: index > 0 ? -10 : 0,
marginTop: index > 0 ? 10 : 0,
//display: index == 0 ? "flex" : "none", //暫定対応:複数アイコンがある場合は最初のアイコンのみ表示
}}
resizeMethod="resize"
/>
) : (
<Ionicons
{...anpanmanStatus}
size={24}
style={{ marginRight: 5 }}
/>
)}
<View>
<View style={{ opacity: anpanmanStatus && showIcon ? 0 : 1 }}>
<Image
source={{ uri: direction ? trainIcon : trainIconRight || trainIcon }}
style={{
height: index > 0 ? 15 : 30,
width: index > 0 ? 12 : 24,
marginRight: 5,
marginLeft: index > 0 ? -10 : 0,
marginTop: index > 0 ? 10 : 0,
}}
resizeMethod="resize"
/>
</View>
{anpanmanStatus && showIcon && (
<View style={{ position: "absolute", top: 0, left: 0 }}>
<Ionicons
{...anpanmanStatus}
size={24}
style={{ marginRight: 5 }}
/>
</View>
)}
</View>
</TouchableOpacity>
)
)}

View File

@@ -1,8 +1,8 @@
import React, { FC, useEffect, useState } from "react";
import { Ionicons } from "@expo/vector-icons";
import { LayoutAnimation } from "react-native";
import { SheetManager } from "react-native-actions-sheet";
import { getType } from "../../../lib/eachTrainInfoCoreLib/getType";
import { useThemeColors } from "@/lib/theme";
import type { NavigateFunction } from "@/types";
type Props = {
@@ -11,6 +11,7 @@ type Props = {
from: string;
};
export const TrainViewIcon: FC<Props> = ({ data, navigate, from }) => {
const { fixed } = useThemeColors();
const [isTrainView, setIsTrainView] = useState(false);
//トレインビュー表示対象(特急、マリン)かを判定
useEffect(() => {
@@ -21,7 +22,6 @@ export const TrainViewIcon: FC<Props> = ({ data, navigate, from }) => {
);
}, [data.limited]);
const onPressTrainView = () => {
LayoutAnimation.easeInEaseOut(); //setLoadingDelayData(true);
navigate("trainbase", {
info: "train.html?tn=" + data.trainNum,
from,
@@ -31,7 +31,7 @@ export const TrainViewIcon: FC<Props> = ({ data, navigate, from }) => {
return isTrainView ? (
<Ionicons
name="subway"
color="white"
color={fixed.textOnPrimary}
size={30}
style={{ margin: 5 }}
onPress={onPressTrainView}

View File

@@ -1,13 +1,13 @@
import React, { useEffect, useRef } from "react";
import {
View,
LayoutAnimation,
ScrollView,
Linking,
Text,
TouchableOpacity,
Platform,
BackHandler,
useWindowDimensions,
} from "react-native";
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
import dayjs from "dayjs";
@@ -21,13 +21,19 @@ import ViewShot from "react-native-view-shot";
import * as Sharing from "expo-sharing";
import { useTrainDelayData } from "../../stateBox/useTrainDelayData";
import { BottomButtons } from "./JRSTraInfo/BottomButtons";
import { useThemeColors } from "@/lib/theme";
import { useResponsive } from "@/lib/responsive";
import { useSheetMaxHeight } from "./useSheetMaxHeight";
export const JRSTraInfo = () => {
const { getTime, delayData, loadingDelayData, setLoadingDelayData } =
useTrainDelayData();
const { colors, fixed } = useThemeColors();
const { fontScale, moderateScale, verticalScale } = useResponsive();
const timeData = dayjs(getTime).format("HH:mm");
const actionSheetRef = useRef(null);
const scrollHandlers = useScrollHandlers("scrollview-1", actionSheetRef);
const scrollHandlers = useScrollHandlers();
const insets = useSafeAreaInsets();
const maxHeight = useSheetMaxHeight();
const viewShot = useRef(null);
const onCapture = async () => {
@@ -37,7 +43,7 @@ export const JRSTraInfo = () => {
if (ok) {
await Sharing.shareAsync(
"file://" + url,
(options = { mimeType: "image/jpeg", dialogTitle: "Share this image" })
{ mimeType: "image/jpeg", dialogTitle: "Share this image" }
);
}
};
@@ -47,31 +53,33 @@ export const JRSTraInfo = () => {
gestureEnabled
CustomHeaderComponent={<></>}
ref={actionSheetRef}
isModal={Platform.OS == "ios"}
containerStyle={
Platform.OS == "android" ? { paddingBottom: insets.bottom } : {}
}
isModal={Platform.OS === "ios" && !Platform.isPad}
containerStyle={{
...(Platform.OS == "android" ? { paddingBottom: insets.bottom } : {}),
...(maxHeight != null ? { maxHeight } : {}),
}}
useBottomSafeAreaPadding={Platform.OS == "android"}
>
<Handler />
<View
style={{
backgroundColor: "#0099CC",
borderTopRadius: 5,
borderColor: "dark",
backgroundColor: fixed.primary,
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
borderColor: colors.border,
borderWidth: 1,
}}
>
<ViewShot ref={viewShot} options={{ format: "jpg" }}>
<View
style={{ height: 26, width: "100%", backgroundColor: "#0099CC" }}
style={{ height: verticalScale(26), width: "100%", backgroundColor: fixed.primary }}
>
<View
style={{
height: 6,
width: 45,
height: verticalScale(6),
width: moderateScale(45),
borderRadius: 100,
backgroundColor: "#f0f0f0",
backgroundColor: colors.borderLight,
marginVertical: 10,
alignSelf: "center",
}}
@@ -82,31 +90,31 @@ export const JRSTraInfo = () => {
padding: 10,
flexDirection: "row",
alignItems: "center",
backgroundColor: "#0099CC",
backgroundColor: fixed.primary,
}}
>
<Text style={{ fontSize: 30, fontWeight: "bold", color: "white" }}>
<Text style={{ fontSize: fontScale(30), fontWeight: "bold", color: fixed.textOnPrimary }}>
EX
</Text>
<View style={{ flex: 1 }} />
<Text style={{ fontSize: 30, fontWeight: "bold", color: "white" }}>
<Text style={{ fontSize: fontScale(30), fontWeight: "bold", color: fixed.textOnPrimary }}>
{timeData}
</Text>
<Ionicons
name="reload"
color="white"
size={30}
color={fixed.textOnPrimary}
size={moderateScale(30)}
style={{ margin: 5 }}
onPress={() => {
LayoutAnimation.easeInEaseOut(), setLoadingDelayData(true);
setLoadingDelayData(true);
}}
/>
</View>
<ScrollView {...scrollHandlers}>
<ScrollView {...scrollHandlers as any}>
<View
style={{
padding: 10,
backgroundColor: "white",
backgroundColor: colors.surface,
}}
>
{loadingDelayData ? (
@@ -114,7 +122,7 @@ export const JRSTraInfo = () => {
<LottieView
autoPlay
loop
style={{ width: 150, height: 150, backgroundColor: "#fff" }}
style={{ width: 150, height: 150, backgroundColor: colors.surface }}
source={require("../../assets/51690-loading-diamonds.json")}
/>
</View>
@@ -123,26 +131,26 @@ export const JRSTraInfo = () => {
let data = d.split(" ");
return (
<View style={{ flexDirection: "row" }} key={data[1]}>
<Text style={{ flex: 15, fontSize: 18 }}>
<Text style={{ flex: 15, fontSize: fontScale(18), color: colors.text }}>
{data[0].replace("\n", "")}
</Text>
<Text style={{ flex: 5, fontSize: 18 }}>{data[1]}</Text>
<Text style={{ flex: 6, fontSize: 18 }}>{data[3]}</Text>
<Text style={{ flex: 5, fontSize: fontScale(18), color: colors.text }}>{data[1]}</Text>
<Text style={{ flex: 6, fontSize: fontScale(18), color: colors.text }}>{data[3]}</Text>
</View>
);
})
) : (
<Text>5</Text>
<Text style={{ color: colors.text }}>5</Text>
)}
</View>
<View style={{ padding: 10, backgroundColor: "#0099CC" }}>
<View style={{ padding: 10, backgroundColor: fixed.primary }}>
<Text
style={{ fontSize: 20, fontWeight: "bold", color: "white" }}
style={{ fontSize: fontScale(20), fontWeight: "bold", color: fixed.textOnPrimary }}
>
EXについて
</Text>
<Text style={{ color: "white" }}>
<Text style={{ color: fixed.textOnPrimary }}>
JR四国公式列車運行情報より5分毎に取得しますTwitterにて投稿している内容と同一のものとなります
</Text>
</View>

View File

@@ -8,23 +8,27 @@ import {
ViewStyle,
} from "react-native";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { useThemeColors } from "@/lib/theme";
const styles: StyleProp<ViewStyle> = {
padding: 10,
flexDirection: "row",
borderColor: "white",
borderWidth: 1,
margin: 10,
borderRadius: 5,
alignItems: "center",
backgroundColor: "#0099CC",
};
export const BottomButtons: FC<{ onCapture: () => void }> = ({ onCapture }) => {
const { fixed } = useThemeColors();
const styles: StyleProp<ViewStyle> = {
padding: 10,
flexDirection: "row",
borderColor: fixed.textOnPrimary,
borderWidth: 1,
margin: 10,
borderRadius: 5,
alignItems: "center",
backgroundColor: fixed.primary,
};
return (
<View
style={{
padding: 10,
backgroundColor: "#0099CC",
backgroundColor: fixed.primary,
flexDirection: "row",
justifyContent: "space-between",
}}
@@ -33,16 +37,16 @@ export const BottomButtons: FC<{ onCapture: () => void }> = ({ onCapture }) => {
style={{ ...styles, flex: 1 }}
onPress={() => Linking.openURL("https://mstdn.y-zu.org/@JRSTraInfoEX")}
>
<MaterialCommunityIcons name="mastodon" color="white" size={30} />
<MaterialCommunityIcons name="mastodon" color={fixed.textOnPrimary} size={30} />
<View style={{ flex: 1 }} />
<Text style={{ fontSize: 25, fontWeight: "bold", color: "white" }}>
<Text style={{ fontSize: 25, fontWeight: "bold", color: fixed.textOnPrimary }}>
MastodonBOT
</Text>
<View style={{ flex: 1 }} />
</TouchableOpacity>
<TouchableOpacity style={styles} onPress={onCapture}>
<MaterialCommunityIcons name="share-variant" color="white" size={30} />
<MaterialCommunityIcons name="share-variant" color={fixed.textOnPrimary} size={30} />
</TouchableOpacity>
</View>
);

View File

@@ -3,44 +3,48 @@ import { View, Platform, Text } from "react-native";
import ActionSheet ,{ ScrollView } from "react-native-actions-sheet";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ListItem } from "@rneui/themed";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { MaterialCommunityIcons, Foundation } from "@expo/vector-icons";
import { Linking } from "react-native";
import TouchableScale from "react-native-touchable-scale";
import { useThemeColors } from "@/lib/theme";
import { useSheetMaxHeight } from "./useSheetMaxHeight";
export const Social = () => {
const actionSheetRef = useRef(null);
const insets = useSafeAreaInsets();
const { colors, fixed } = useThemeColors();
const maxHeight = useSheetMaxHeight();
return (
<ActionSheet
gestureEnabled
CustomHeaderComponent={<></>}
ref={actionSheetRef}
isModal={Platform.OS == "ios"}
containerStyle={
Platform.OS == "android"
? {
paddingBottom: insets.bottom,
}
: {}
}
isModal={Platform.OS === "ios" && !Platform.isPad}
containerStyle={{
...(Platform.OS == "android"
? { paddingBottom: insets.bottom }
: {}),
...(maxHeight != null ? { maxHeight } : {}),
}}
useBottomSafeAreaPadding={Platform.OS == "android"}
>
<View
style={{
backgroundColor: "#0099CC",
borderTopRadius: 5,
backgroundColor: fixed.primary,
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
borderColor: "dark",
borderWidth: 1,
height: "100%",
}}
>
<View style={{ height: 26, width: "100%", backgroundColor: "#0099CC" }}>
<View style={{ height: 26, width: "100%", backgroundColor: fixed.primary }}>
<View
style={{
height: 6,
width: 45,
borderRadius: 100,
backgroundColor: "#f0f0f0",
backgroundColor: colors.borderLight,
marginVertical: 10,
alignSelf: "center",
}}
@@ -53,17 +57,39 @@ export const Social = () => {
<MaterialCommunityIcons
name="web"
style={{ padding: 5 }}
color="white"
color={fixed.textOnPrimary}
size={30}
/>
<Text style={{ fontSize: 30, fontWeight: "bold", color: "white" }}>
<Text style={{ fontSize: 30, fontWeight: "bold", color: fixed.textOnPrimary }}>
JR四国公式SNS一族
</Text>
</View>
<ListItem
bottomDivider
onPress={() => Linking.openURL("tel:0570-00-4592")}
// @ts-ignore: TouchableScale props passed through Component
friction={90}
tension={100}
activeScale={0.95}
Component={TouchableScale}
containerStyle={{ backgroundColor: "#8AE234" }}
>
<Foundation name="telephone" color={fixed.textOnPrimary} size={30} />
<ListItem.Content>
<ListItem.Title style={{ color: fixed.textOnPrimary, fontWeight: "bold" }}>
JR四国案内センター
</ListItem.Title>
<ListItem.Subtitle style={{ color: fixed.textOnPrimary }}>
0570-00-45928:0020:00
</ListItem.Subtitle>
</ListItem.Content>
{/* @ts-ignore: ListItem.Chevron doesn't expose color prop */}
<ListItem.Chevron color={"white"} />
</ListItem>
<ScrollView
style={{
padding: 10,
backgroundColor: "white",
backgroundColor: colors.surface,
borderBottomLeftRadius: 10,
borderBottomRightRadius: 10,
flex:1
@@ -128,13 +154,15 @@ export const Social = () => {
bottomDivider
onPress={() => Linking.openURL(d.url)}
key={d.url}
// @ts-ignore: TouchableScale props passed through Component
friction={90} //
tension={100} // These props are passed to the parent component (here TouchableScale)
activeScale={0.95} //
Component={TouchableScale}
containerStyle={{ backgroundColor: colors.surface }}
>
<ListItem.Content>
<ListItem.Title>{d.name}</ListItem.Title>
<ListItem.Title style={{ color: colors.text }}>{d.name}</ListItem.Title>
</ListItem.Content>
<ListItem.Chevron />
</ListItem>

Some files were not shown because too many files have changed in this diff Show More