190 lines
5.7 KiB
TypeScript
190 lines
5.7 KiB
TypeScript
import { Ionicons } from '@expo/vector-icons'
|
|
import { Block, Text } from '@share/components'
|
|
import { router, Stack, useLocalSearchParams } from 'expo-router'
|
|
import { useRef, useState } from 'react'
|
|
import { ActivityIndicator, Alert, Share } from 'react-native'
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
import { WebView, type WebViewNavigation } from 'react-native-webview'
|
|
|
|
export default function WebViewPage() {
|
|
const { url, title } = useLocalSearchParams<{
|
|
url: string
|
|
title?: string
|
|
}>()
|
|
const insets = useSafeAreaInsets()
|
|
const webViewRef = useRef<WebView>(null)
|
|
|
|
const [loading, setLoading] = useState(true)
|
|
const [canGoBack, setCanGoBack] = useState(false)
|
|
const [canGoForward, setCanGoForward] = useState(false)
|
|
const [currentUrl, setCurrentUrl] = useState(url || '')
|
|
const [progress, setProgress] = useState(0)
|
|
|
|
if (!url) {
|
|
return (
|
|
<Block className="flex-1 items-center justify-center bg-white">
|
|
<Ionicons name="alert-circle-outline" size={64} color="#666" />
|
|
<Text className="mt-4 text-lg text-black">URL参数缺失</Text>
|
|
<Block className="mt-4 rounded-lg bg-accent px-6 py-3" onClick={() => router.back()}>
|
|
<Text className="font-bold text-black">返回</Text>
|
|
</Block>
|
|
</Block>
|
|
)
|
|
}
|
|
|
|
const handleNavigationStateChange = (navState: WebViewNavigation) => {
|
|
setCanGoBack(navState.canGoBack)
|
|
setCanGoForward(navState.canGoForward)
|
|
setCurrentUrl(navState.url)
|
|
}
|
|
|
|
const handleError = () => {
|
|
Alert.alert('加载错误', '页面加载失败,请检查网络连接', [
|
|
{ text: '重试', onPress: () => reload() },
|
|
{ text: '返回', onPress: () => router.back() },
|
|
])
|
|
}
|
|
|
|
const handleLoadProgress = ({ nativeEvent }: any) => {
|
|
setProgress(nativeEvent.progress)
|
|
}
|
|
|
|
const goBack = () => {
|
|
if (canGoBack) {
|
|
webViewRef.current?.goBack()
|
|
} else {
|
|
router.back()
|
|
}
|
|
}
|
|
|
|
const goForward = () => {
|
|
webViewRef.current?.goForward()
|
|
}
|
|
|
|
const reload = () => {
|
|
webViewRef.current?.reload()
|
|
}
|
|
|
|
const shareUrl = async () => {
|
|
try {
|
|
await Share.share({
|
|
message: currentUrl,
|
|
title: title || '分享网页',
|
|
})
|
|
} catch (error) {
|
|
console.error('分享失败:', error)
|
|
}
|
|
}
|
|
|
|
// 注入的JavaScript代码
|
|
const injectedJavaScript = `
|
|
// 禁用长按选择
|
|
document.addEventListener('contextmenu', function(e) {
|
|
e.preventDefault();
|
|
});
|
|
|
|
// 添加样式优化
|
|
const style = document.createElement('style');
|
|
style.innerHTML = \`
|
|
body {
|
|
-webkit-user-select: none;
|
|
-webkit-touch-callout: none;
|
|
}
|
|
* {
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
\`;
|
|
document.head.appendChild(style);
|
|
|
|
// 向RN发送页面信息
|
|
window.ReactNativeWebView.postMessage(JSON.stringify({
|
|
type: 'pageInfo',
|
|
title: document.title,
|
|
url: window.location.href
|
|
}));
|
|
|
|
true; // 必须返回true
|
|
`
|
|
|
|
const handleMessage = (event: any) => {
|
|
try {
|
|
const data = JSON.parse(event.nativeEvent.data)
|
|
if (data.type === 'pageInfo') {
|
|
console.log('页面信息:', data)
|
|
// 可以根据页面信息执行相应操作
|
|
}
|
|
} catch (error) {
|
|
console.log('收到消息:', event.nativeEvent.data)
|
|
}
|
|
}
|
|
|
|
const renderHeader = () => (
|
|
<Block className="flex-row items-center justify-between px-[16px]" style={{ paddingTop: 12, paddingBottom: 12 }}>
|
|
<Block className="ml-[-8px] size-[40px] items-center justify-center" opacity={0.7} onClick={() => router.back()}>
|
|
<Ionicons color="black" name="chevron-back" size={24} />
|
|
</Block>
|
|
<Text className="text-[16px] font-[700] text-black">{title}</Text>
|
|
<Block className="w-[32px]" />
|
|
</Block>
|
|
)
|
|
return (
|
|
<Block className="flex-1" style={{ paddingTop: 0 }}>
|
|
<Stack.Screen options={{ headerShown: false }} />
|
|
|
|
{/* Header */}
|
|
{renderHeader()}
|
|
|
|
{/* Progress Bar */}
|
|
{loading && progress < 1 && (
|
|
<Block className="h-1 bg-white/10">
|
|
<Block className="h-full bg-accent" style={{ width: `${progress * 100}%` }} />
|
|
</Block>
|
|
)}
|
|
|
|
{/* Loading Indicator */}
|
|
{loading && (
|
|
<Block className="absolute inset-x-0 top-24 z-10 items-center">
|
|
<Block className="rounded-full bg-black/80 p-3">
|
|
<ActivityIndicator color="#FFE500" size="small" />
|
|
</Block>
|
|
</Block>
|
|
)}
|
|
|
|
{/* WebView */}
|
|
<WebView
|
|
ref={webViewRef}
|
|
source={{ uri: url }}
|
|
style={{ flex: 1 }}
|
|
onLoad={() => setLoading(false)}
|
|
onLoadStart={() => setLoading(true)}
|
|
onLoadEnd={() => setLoading(false)}
|
|
onLoadProgress={handleLoadProgress}
|
|
onError={handleError}
|
|
onNavigationStateChange={handleNavigationStateChange}
|
|
onMessage={handleMessage}
|
|
// 功能配置
|
|
allowsBackForwardNavigationGestures={true}
|
|
startInLoadingState={true}
|
|
mixedContentMode="compatibility"
|
|
javaScriptEnabled={true}
|
|
domStorageEnabled={true}
|
|
allowsInlineMediaPlayback={true}
|
|
mediaPlaybackRequiresUserAction={false}
|
|
// 注入JavaScript
|
|
injectedJavaScript={injectedJavaScript}
|
|
// 样式配置
|
|
bounces={false}
|
|
scrollEnabled={true}
|
|
showsHorizontalScrollIndicator={false}
|
|
showsVerticalScrollIndicator={true}
|
|
// 用户代理
|
|
userAgent="Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1 DuoomApp/1.0"
|
|
// 其他配置
|
|
cacheEnabled={true}
|
|
thirdPartyCookiesEnabled={true}
|
|
sharedCookiesEnabled={true}
|
|
/>
|
|
</Block>
|
|
)
|
|
}
|