Files
jrshikoku/App.tsx
harukin-expo-dev-env 92f4b37861 feat: apply mock data to currentTrain (app-side train positions)
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 <223556219+Copilot@users.noreply.github.com>
2026-05-01 12:40:30 +00:00

188 lines
5.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect } from "react";
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";
import { UpdateAsync } from "./UpdateAsync";
import { LogBox } from "react-native";
import { FavoriteStationProvider } from "./stateBox/useFavoriteStation";
import { CurrentTrainProvider } from "./stateBox/useCurrentTrain";
import { AreaInfoProvider } from "./stateBox/useAreaInfo";
import { BusAndTrainDataProvider } from "./stateBox/useBusAndTrainData";
import { AllTrainDiagramProvider } from "./stateBox/useAllTrainDiagram";
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";
import { DeviceOrientationChangeProvider } from "./stateBox/useDeviceOrientationChange";
import { TrainMenuProvider } from "./stateBox/useTrainMenu";
import { buildProvidersTree } from "./lib/providerTreeProvider";
import { StationListProvider } from "./stateBox/useStationList";
import { NotificationProvider } from "./stateBox/useNotifications";
import { UserPositionProvider } from "./stateBox/useUserPosition";
import { rootNavigationRef, stackAwareNavigate } from "./lib/rootNavigation";
import { AppThemeProvider } from "./lib/theme";
import StatusbarDetect from "./StatusbarDetect";
LogBox.ignoreLogs([
"ViewPropTypes will be removed",
"ColorPropType will be removed",
]);
if (Platform.OS === "android") {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
export default function App() {
useEffect(() => {
UpdateAsync();
}, []);
useEffect(() => {
const openFelicaPage = (retryCount = 0) => {
if (!rootNavigationRef.isReady()) {
if (retryCount < 8) {
setTimeout(() => openFelicaPage(retryCount + 1), 250);
}
return;
}
stackAwareNavigate("topMenu", {
screen: "setting",
params: {
screen: "FelicaHistoryPage",
},
});
};
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(() => {
stackAwareNavigate("topMenu", { screen: "menu" });
setTimeout(() => {
SheetManager.show("JRSTraInfo");
}, 450);
}, url, retryCount);
} else if (normalized.includes("open/operation")) {
navigateWhenReady(() => {
stackAwareNavigate("information");
}, url, retryCount);
} else if (normalized.includes("open/settings")) {
navigateWhenReady(() => {
stackAwareNavigate("topMenu", {
screen: "setting",
});
}, url, retryCount);
} else if (normalized.includes("open/topmenu")) {
navigateWhenReady(() => {
stackAwareNavigate("topMenu", {
screen: "menu",
});
}, url, retryCount);
} else if (normalized.includes("positions/apps")) {
navigateWhenReady(() => {
stackAwareNavigate("positions");
}, 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,
UserPositionProvider,
StationListProvider,
FavoriteStationProvider,
TrainDelayDataProvider,
TrainMenuProvider, // CurrentTrainProvider より先に置くことで useTrainMenu が使える
CurrentTrainProvider,
AreaInfoProvider,
BusAndTrainDataProvider,
SheetProvider,
]);
return (
<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>
);
}