- Add `currentTrainMetadata.ts` to manage carrying forward official position metadata between train snapshots.
- Introduce `lineDisplayName.ts` for mapping line IDs to display names.
- Create `officialPositionCache.ts` for parsing and serializing official position data with cache validation.
- Enhance `officialPositionService.ts` to support persistent caching of official positions and improve resolver functionality.
- Update `requestCoordinator.ts` to allow cancellation of polling requests without disposing of the coordinator.
- Modify `trainTimeFiltering.ts` to incorporate line ID context when checking for duplicate train data.
- Refactor `useCurrentTrain.tsx` to support polling for current train data with official position enrichment.
- Add tests for new features, including persistent cache behavior and handling of ambiguous train data.
- Split the existing useCurrentTrain hook into two separate hooks: useCurrentTrainData for accessing train data and useCurrentTrainView for accessing view-related properties.
- Updated all components and hooks to use the new hooks accordingly, ensuring that the functionality remains intact.
- This change improves the separation of concerns and enhances the maintainability of the codebase.
- Split the useAllTrainDiagram hook into three separate hooks: useTrainDiagram, useCustomTrainData, and useOperationLogs for better modularity and performance.
- Update components to utilize the new hooks, ensuring that all references to the old hook are replaced.
- Refactor the TrainMenu context to include a new TrainRecording context, encapsulating recording-related state and functions.
- Adjust components to use the new useTrainRecording hook for playback and recording functionalities.
- Clean up imports and state management across various components to align with the new structure.
- Implemented StationListLoadingState component for loading messages.
- Created MenuLED component to display LED information with loading animations.
- Developed MenuMap component for interactive map features, including user location markers and station markers.
- Introduced LEDFrame component for consistent styling of LED displays.
- Enhanced LED_vision component to support embedded rendering and content readiness.
- Added menuStationSelectors for managing nearby and favorite station groups.
- Updated Menu component to integrate new loading states and map functionalities.
- Added tests for menuStationSelectors to ensure correct grouping and selection logic.
- Replaced Sentry breadcrumbs and context setting with a custom telemetry implementation across various components including Apps, WebView, CarouselBox, and ndView.
- Introduced a new telemetry module to encapsulate observability logic and provide a unified API for logging events, messages, and exceptions.
- Updated documentation to reflect changes in observability architecture, consolidating Sentry initialization and telemetry usage.
- Enhanced error handling and logging in data fetching and application lifecycle management.
- Removed usage of `any` in navigation hooks and replaced with specific types.
- Added type definitions for navigation parameters in `navigation.ts`.
- Updated components to utilize the new navigation types for better type safety.
- Enhanced `SwitchArea` component with generic type support for boolean and string values.
- Introduced type checks and parsing functions for API responses in `mockApi` to ensure data integrity.
- Created a refactoring backlog document to track future improvements and refactoring tasks.
- Removed unused functions and cleaned up code in various components for better maintainability.
- Updated FelicaHistoryPage to use dayjs for formatting scannedAt timestamps.
- Refactored ResearchToolsSettings to utilize dayjs for date formatting.
- Modified VoicepeakDebugLogSection to replace Date with dayjs for createdAt timestamps.
- Changed settings component to use dayjs for scannedAt timestamps.
- Updated ExGridSimpleView and ExGridView to use dayjs for time parsing and formatting.
- Refactored ExGridSimpleViewItem and ExGridViewItem to utilize dayjs for time formatting.
- Adjusted ListView and ListViewItem to use dayjs for time handling.
- Updated EachData to use dayjs for train time calculations.
- Refactored LED_vidion to remove redundant getServiceMinute function.
- Replaced Date with dayjs in trainRecorder for exportedAt timestamps.
- Updated appLifecycleCrashSentinel to use dayjs for timestamp handling.
- Refactored operationInfoSchedule to use dayjs for fetchedAt timestamp.
- Updated trainTimeFiltering to utilize dayjs for time comparisons.
- Refactored useWebViewRemount to handle timeouts with dayjs.
- Updated voicepeak and voicepeakDebugLog to use dayjs for timestamp handling.
- Introduced timeUtils module for consistent time parsing and formatting using dayjs.
- Removed direct usage of favorite station state in multiple components (MenuPage, StationDetailView, WebView, FavoriteList, FavoriteSettings).
- Introduced utility functions for handling favorite station keys and normalization.
- Updated favorite station context to include methods for adding, removing, and toggling favorite stations.
- Implemented normalization logic to ensure consistency with the current station master.
- Added documentation for the favorite station normalization plan.
- Improved error handling and data validation for favorite stations.
- Moved the Elesite sorting logic to a new module `elesiteTrainOrder.ts`.
- Created utility functions to infer heading direction and sort Elesite entries by train number.
- Updated `injectJavascriptData` to utilize the new sorting logic and handle station sequences.
- Refactored `useElesite` to use the new sorting utility for improved readability and maintainability.
- positionMasters: add fetchMockTrainPositions() targeting
/train-positions/latest on the mock API server
- useTrainMenu: when mockApiFeatureEnabled and not recording/playing,
poll /train-positions/latest every 15s and update mockTrainPositions
(same interval as live mode); on mode switch updates immediately
- useCurrentTrain:
- playing: use local recording data (no network call)
- mock ON idle: fetch from mock API server (15s poll driven by useTrainMenu)
- mock OFF: fetch from real backend as before
- Recording still forces mock OFF so real API data is captured
Co-authored-by: Copilot <[email protected]>
- Add lib/mockApi/positionMasters.ts
- fetchPositionMasters() from jr-shikoku-backend-mock-api-v1.haruk.in
- buildPosLookup() / lookupPos() helpers
- serializePosLookupForJs() for baking into injected scripts
- webviewXhrInterceptor: accept positionMasters in MockApiConfig,
bake _POS_LOOKUP into injected JS, enrich Pos from PosNum on serve
- useTrainMenu: fetch position masters on mock-enable (and at startup
if already enabled); expose positionMasters + lookupPosText()
- useCurrentTrain: fill Pos via lookupPosText when mapping mock data
- mockApi/index.ts: re-export positionMasters types and helpers
Co-authored-by: Copilot <[email protected]>
Crash fix (React Hooks violation):
- PlaybackTimeline.tsx: move ALL hooks (useRef for PanResponder, useCallback)
to before the early return; use seekRef/totalRef to share values into
PanResponder handlers without stale closure issues
Multiple recordings support:
- trainRecorder.ts: redesign to id-based multi-recording system
- RecordingMeta type for lightweight list (no snapshots)
- TrainRecording now includes id field
- saveRecording/loadRecordingById/deleteRecordingById/loadRecordingList
- migrateOldRecording() migrates old single MOCK_RECORDING key on first launch
- constants/storage.ts: add MOCK_RECORDINGS_INDEX + MOCK_RECORDING_DATA_PREFIX keys
- useTrainMenu: savedRecording → recordingList (RecordingMeta[]) + activeRecording (TrainRecording|null)
- startPlayback(id) loads full recording on demand
- deleteRecording(id) deletes by id and refreshes list
- stopPlayback clears activeRecording
- DataSourceSettings: recording list UI
- shows all recordings with date/time, snapshot count, duration
- ▶ play and 削除 buttons per row
- recording/playing status indicator
Co-authored-by: Copilot <[email protected]>
- useTrainMenu: add playbackPaused state, pausePlayback/resumePlayback/seekToSnapshot
- playback loop now respects playbackPaused (no timer when paused)
- seekToSnapshot pauses playback and immediately applies snapshot data
- expose playbackIndex, playbackPaused + new functions in context value
- PlaybackTimeline.tsx: new component shown when recorderState === 'playing'
- absolute positioned bar at top of map screen (zIndex 2000)
- prev/play-pause/next frame buttons + skip-to-start/end
- time display (HH:mm:ss) + snapshot counter (n/total) + total duration
- PanResponder-based scrubber track with filled progress bar and draggable thumb
- Apps.tsx: render <PlaybackTimeline /> alongside FixedPositionBox
- Remove MockApiToggle.tsx (no longer used since settings-only toggle)
Co-authored-by: Copilot <[email protected]>
Implements snapshot-based recording and playback of live train
position data for mock API debugging (admin-only).
Recording:
- startRecording() disables mock mode and begins capturing live
train snapshots every ~15s (via getCurrentTrain polling)
- Each snapshot stores { t: elapsed_ms, trains: TrainEntry[] }
- stopRecording() saves completed recording to AsyncStorage
Playback:
- startPlayback() enables mock mode and loops through snapshots
at their original recorded timing using useEffect+setTimeout
- stopPlayback() returns to idle
Storage:
- Single slot in AsyncStorage (MOCK_RECORDING key)
- Loaded on app start in useTrainMenu
- deleteRecording() removes it
New files:
- lib/mockApi/trainRecorder.ts — TrainRecording/TrainSnapshot types
+ save/load/delete AsyncStorage helpers
Modified files:
- constants/storage.ts — add MOCK_RECORDING key
- stateBox/useTrainMenu.tsx — recorder state + all controls
- stateBox/useCurrentTrain.tsx — call addTrainSnapshot after live fetch
- components/Settings/DataSourceSettings.tsx — record/play UI with
status dot, snapshot count, and action buttons
Co-authored-by: Copilot <[email protected]>
When MOCK_API_FEATURE_ENABLED is on, getCurrentTrain() now returns
mock data instead of fetching from the n8n webhook or fallback API.
- App.tsx: move TrainMenuProvider before CurrentTrainProvider so
useTrainMenu() is available inside CurrentTrainProvider
- useCurrentTrain: import useTrainMenu + MOCK_TRAIN_POSITIONS;
getCurrentTrain() short-circuits to mock data when mockApiFeatureEnabled;
added useEffect on mockApiFeatureEnabled to reload immediately on toggle
Co-authored-by: Copilot <[email protected]>
The separate runtime MOCK switch on the map screen is removed.
Now the admin-only settings toggle (MOCK_API_FEATURE_ENABLED) is the
single control point — turning it on activates mock mode immediately,
turning it off deactivates it.
- useTrainMenu: remove mockApiEnabled/setMockApiEnabled state;
mockApiConfig now derives from mockApiFeatureEnabled alone
- WebView: use mockApiFeatureEnabled for key prop (triggers reload)
- Apps.tsx: remove MockApiToggle import and JSX usage
Co-authored-by: Copilot <[email protected]>
Two bugs caused the Tokyo UX to be stripped/broken when mock mode was ON:
1. **Double callback firing**: the interceptor's `setTimeout` called both
`onreadystatechange.call(self)` AND `dispatchEvent(new Event('readystatechange'))`.
Since `dispatchEvent` already fires `onXxx` property handlers via the DOM event
model, the page's callback fired twice, causing a second DOM re-render that could
overwrite Tokyo UX modifications. Fixed by relying on `dispatchEvent` only (with
`ProgressEvent` for load events), with a direct-call fallback only for environments
that lack `dispatchEvent`.
2. **No double-injection guard**: if `injectedJavaScriptBeforeContentLoaded` caused
the interceptor to run more than once (e.g., `onPageStarted` fires multiple times
on Android), `_origOpen` would capture the already-patched version, leading to
unexpected behaviour. Fixed by adding `if (window.__jrsMockActive) return;` at the
top of the interceptor IIFE.
3. **Belt-and-suspenders injection**: the interceptor is now also prepended to
`injectedJavaScript` (Tokyo UX script) via the restored `mockApiConfig` parameter
on `injectJavascriptData`. The `__jrsMockActive` guard ensures it's a no-op when
`injectedJavaScriptBeforeContentLoaded` already ran it, but guarantees the
interceptor is active on platforms where IJBCL is unreliable — before
`MoveDisplayStation` triggers the first train-data poll via `onLoadEnd`.
Co-authored-by: Copilot <[email protected]>