expo-duooomi-app/app/webview.tsx

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>
)
}