Files
jrshikoku/ndView.tsx

216 lines
8.5 KiB
TypeScript

import React, { Ref, useRef, useState, useEffect, useCallback } from "react";
import {
View,
Platform,
TouchableOpacity,
StyleProp,
ViewStyle,
Linking,
AppState,
} from "react-native";
import { WebView } from "react-native-webview";
import Constants from "expo-constants";
import { Ionicons } from "@expo/vector-icons";
import { useNavigation, useFocusEffect } from "@react-navigation/native";
import { useThemeColors } from "@/lib/theme";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useWebViewRemount } from "@/lib/useWebViewRemount";
export default function tndView() {
const { remountKey, remount, processHandlers, pingHandlers, webViewRef } = useWebViewRemount({ pingEnabled: true, backgroundThresholdMs: 60_000 }); // 1分超でAppState remount
const { navigate, addListener, isFocused } = useNavigation();
const { fixed } = useThemeColors();
const { top } = useSafeAreaInsets();
// タブにフォーカスが来たとき、バックグラウンドから戻ってきた場合 remount
const bgAtRef = useRef<number | null>(null);
useEffect(() => {
const sub = AppState.addEventListener("change", (state) => {
if (state.match(/inactive|background/)) {
bgAtRef.current = Date.now();
} else if (state === "active") {
// useWebViewRemount側(1分超)とは独立: ここではフォーカス時判定用に保持するだけ
}
});
return () => sub.remove();
}, []);
// タブにフォーカスが来たとき、3秒以上バックグラウンドだった場合 remount
useFocusEffect(
useCallback(() => {
if (bgAtRef.current !== null) {
const elapsed = Date.now() - bgAtRef.current;
bgAtRef.current = null;
if (elapsed > 3_000) remount();
}
}, [remount])
);
const jsa = `
document.querySelector('.sitettl').style.display = 'none';
document.querySelector('.attention').style.display = 'none';
document.querySelector('.mapheader').style.display = 'none';
document.querySelector('.map').style.width = '100vw';
document.querySelector('.map').style.display = 'block';
document.querySelector('.map').style.marginLeft = '-5px';
document.querySelector('.map').style.marginTop = '0px';
document.querySelector('.pageInformation h1.accent span').style.width = '100%';
document.querySelector('.delay_status').style.width = 'calc(100vw - 4px)';
document.querySelector('.reload').style.width = '100vw';
document.querySelector('.delay_info').style.width = '100vw';
document.querySelector('.related_lnk').style.width = '100%';
document.querySelector('.related_lnk').style.padding = '0px';
document.querySelector('.related_lnk .ttl').style.padding = '20px';
document.querySelector('.related_lnk dl.lnk_item').style.float = 'none';
document.querySelector('.related_lnk dl.lnk_item').style.overflow = 'visible';
document.querySelector('.related_lnk dl.lnk_item').style.width = '100%';
document.querySelectorAll('.attention').forEach((el) => {
el.style.width = '100vw';
});
document.querySelector('.mapbase > img').style.width = '100vw';
document.querySelector('.ml_station > img').style.width = '100vw';
document.querySelector('.ml_seto >img').removeAttribute('width');
document.querySelector('.ml_seto >img').removeAttribute('height');
document.querySelector('.ml_seto >img').style.width = '14.5vw';
document.querySelector('.ml_seto').style.top = '0.2vw';
document.querySelector('.ml_seto').style.right = '25.8vw';
document.querySelector('.ml_yosan1 >img').removeAttribute('width');
document.querySelector('.ml_yosan1 >img').removeAttribute('height');
document.querySelector('.ml_yosan1 >img').style.width = '42.6vw';
document.querySelector('.ml_yosan1').style.top = '5.1vw';
document.querySelector('.ml_yosan1').style.left = '18.7vw';
document.querySelector('.ml_yosan2 >img').removeAttribute('width');
document.querySelector('.ml_yosan2 >img').removeAttribute('height');
document.querySelector('.ml_yosan2 >img').style.width = '12.2vw';
document.querySelector('.ml_yosan2').style.top = '8.6vw';
document.querySelector('.ml_yosan2').style.left = '8.0vw';
document.querySelector('.ml_dosan1 >img').removeAttribute('width');
document.querySelector('.ml_dosan1 >img').removeAttribute('height');
document.querySelector('.ml_dosan1 >img').style.width = '13.5vw';
document.querySelector('.ml_dosan1').style.top = '6.6vw';
document.querySelector('.ml_dosan1').style.left = '47.8vw';
document.querySelector('.ml_dosan2 >img').removeAttribute('width');
document.querySelector('.ml_dosan2 >img').removeAttribute('height');
document.querySelector('.ml_dosan2 >img').style.width = '22.5vw';
document.querySelector('.ml_dosan2').style.top = '32vw';
document.querySelector('.ml_dosan2').style.left = '26.5vw';
document.querySelector('.ml_kotoku >img').removeAttribute('width');
document.querySelector('.ml_kotoku >img').removeAttribute('height');
document.querySelector('.ml_kotoku >img').style.width = '17vw';
document.querySelector('.ml_kotoku').style.top = '6.5vw';
document.querySelector('.ml_kotoku').style.left = '72.9vw';
document.querySelector('.ml_mugi >img').removeAttribute('width');
document.querySelector('.ml_mugi >img').removeAttribute('height');
document.querySelector('.ml_mugi >img').style.width = '11.6vw';
document.querySelector('.ml_mugi').style.top = '23.6vw';
document.querySelector('.ml_mugi').style.left = '78.3vw';
document.querySelector('.ml_tokushima >img').removeAttribute('width');
document.querySelector('.ml_tokushima >img').removeAttribute('height');
document.querySelector('.ml_tokushima >img').style.width = '27.8vw';
document.querySelector('.ml_tokushima').style.top = '17vw';
document.querySelector('.ml_tokushima').style.left = '61.3vw';
document.querySelector('.ml_naruto >img').removeAttribute('width');
document.querySelector('.ml_naruto >img').removeAttribute('height');
document.querySelector('.ml_naruto >img').style.width = '5.5vw';
document.querySelector('.ml_naruto').style.top = '17.1vw';
document.querySelector('.ml_naruto').style.left = '88.5vw';
document.querySelector('.ml_yodo >img').removeAttribute('width');
document.querySelector('.ml_yodo >img').removeAttribute('height');
document.querySelector('.ml_yodo >img').style.width = '18.3vw';
document.querySelector('.ml_yodo').style.top = '30.2vw';
document.querySelector('.ml_yodo').style.left = '9.6vw';
setInterval(() => {
document.querySelectorAll('.ml_seto, .ml_yosan1, .ml_yosan2, .ml_dosan1, .ml_dosan2, .ml_kotoku, .ml_mugi, .ml_tokushima, .ml_naruto, .ml_yodo').forEach(el => {
el.style.visibility = el.style.visibility === 'hidden' ? 'visible' : 'hidden';
});
}, 1000);
`;
const goToTrainMenu = () => {
if (Platform.OS === "web") {
Linking.openURL("https://www.jr-shikoku.co.jp/info/");
setTimeout(() => {
// @ts-ignore
navigate("topMenu", { screen: "menu" });
}, 100);
return;
}
};
useEffect(() => {
// @ts-ignore
const unsubscribe = addListener("tabPress", goToTrainMenu);
return unsubscribe;
}, [addListener]);
return (
<View
style={{
backgroundColor: fixed.primary,
height: "100%",
paddingTop: top,
}}
>
<WebView
key={remountKey}
ref={webViewRef}
source={{ uri: "https://www.jr-shikoku.co.jp/info/" }}
originWhitelist={["https://www.jr-shikoku.co.jp"]}
mixedContentMode={"compatibility"}
contentMode="mobile"
javaScriptEnabled={true}
injectedJavaScript={jsa}
pullToRefreshEnabled
onError={() => remount()}
{...processHandlers}
{...pingHandlers}
onMessage={(event) => {
// pingHandlers の __ping メッセージを処理
pingHandlers.onMessage?.(event);
}}
/>
<ReloadButton
onPress={() => webViewRef.current?.reload()}
top={top}
/>
</View>
);
}
const ReloadButton = ({ onPress, top, LoadError = false }) => {
const { fixed } = useThemeColors();
const styles: StyleProp<ViewStyle> = {
position: "absolute",
top,
right: 10,
width: 50,
height: 50,
backgroundColor: LoadError ? "red" : fixed.primary,
borderColor: fixed.textOnPrimary,
borderStyle: "solid",
borderWidth: 1,
borderRadius: 50,
alignContent: "center",
alignSelf: "center",
alignItems: "center",
};
return (
<TouchableOpacity onPress={onPress} style={styles}>
<View style={{ flex: 1 }} />
<Ionicons name="reload" color={fixed.textOnPrimary} size={30} />
<View style={{ flex: 1 }} />
</TouchableOpacity>
);
};