194 lines
6.6 KiB
TypeScript
194 lines
6.6 KiB
TypeScript
import React from "react";
|
|
import { NavigationContainer, DarkTheme, DefaultTheme } from "@react-navigation/native";
|
|
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
|
import { Animated, Platform, ActivityIndicator, View, StyleSheet, StatusBar } 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;
|
|
topMenu: undefined;
|
|
information: undefined;
|
|
};
|
|
|
|
type TabProps = {
|
|
name: string;
|
|
label: string;
|
|
icon: string;
|
|
iconFamily: string;
|
|
tabBarBadge?: string;
|
|
isInfo?: boolean;
|
|
};
|
|
|
|
const Tab = createBottomTabNavigator<RootTabParamList>();
|
|
|
|
export function AppContainer() {
|
|
const { areaInfo, areaIconBadgeText, isInfo } = useAreaInfo();
|
|
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: {},
|
|
},
|
|
topMenu: {
|
|
screens: {
|
|
menu: "topMenu/menu",
|
|
setting: {
|
|
screens: {
|
|
settingTopPage: "topMenu/setting",
|
|
FelicaHistoryPage: "topMenu/setting/FelicaHistoryPage",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
information: "information",
|
|
},
|
|
},
|
|
};
|
|
|
|
const getTabProps = (
|
|
name: keyof RootTabParamList,
|
|
label: string,
|
|
icon: string,
|
|
iconFamily: "Ionicons" | "AntDesign",
|
|
tabBarBadge?: string,
|
|
isInfo?: boolean
|
|
) => ({
|
|
name,
|
|
options: {
|
|
tabBarLabel: label,
|
|
headerShown: false,
|
|
gestureEnabled: true,
|
|
tabBarIcon: initIcon(icon as any, iconFamily, tabBarBadge, isInfo),
|
|
|
|
},
|
|
});
|
|
const [fontLoaded, error] = useFonts({
|
|
"JR-Nishi": require("./assets/fonts/jr-nishi.otf"),
|
|
Zou: require("./assets/fonts/DelaGothicOne-Regular.ttf"),
|
|
"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={rootNavigationRef}
|
|
linking={linking}
|
|
theme={isDark ? DarkTheme : DefaultTheme}
|
|
onStateChange={(state) => {
|
|
const activeRoute = state?.routes?.[state?.index ?? 0];
|
|
const hasExtra = (activeRoute?.state?.index ?? 0) > 0;
|
|
setIsExtraWindowOpen(hasExtra);
|
|
}}
|
|
>
|
|
<Tab.Navigator
|
|
initialRouteName="topMenu"
|
|
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,
|
|
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", "走行位置", "bar-chart", "AntDesign")}
|
|
component={Top}
|
|
/>
|
|
<Tab.Screen
|
|
{...getTabProps("topMenu", "トップメニュー", "radio", "Ionicons")}
|
|
component={MenuPage}
|
|
/>
|
|
|
|
<Tab.Screen
|
|
{...getTabProps(
|
|
"information",
|
|
"運行情報",
|
|
"train",
|
|
"Ionicons",
|
|
areaInfo ? areaIconBadgeText : undefined,
|
|
isInfo
|
|
)}
|
|
children={TNDView}
|
|
/>
|
|
</Tab.Navigator>
|
|
</NavigationContainer>
|
|
);
|
|
}
|