/ app / cloudflare.tsx
cloudflare.tsx
  1  import React, { useState } from 'react';
  2  import {
  3    View,
  4    Text,
  5    TouchableOpacity,
  6    StyleSheet,
  7    ActivityIndicator,
  8    Platform,
  9    useColorScheme,
 10  } from 'react-native';
 11  import { useTheme } from '@/constants/ThemeContext';
 12  import { Colors, ColorScheme } from '@/constants/Colors';
 13  import { useRouter } from 'expo-router';
 14  import { MANGA_API_URL } from '@/constants/Config';
 15  import CustomWebView from '@/components/CustomWebView';
 16  import { Ionicons } from '@expo/vector-icons';
 17  import { WebViewNavigation } from 'react-native-webview';
 18  
 19  import { useCloudflareDetection } from '@/hooks/useCloudflareDetection';
 20  
 21  export default function CloudflarePage() {
 22    const router = useRouter();
 23    const { theme } = useTheme();
 24    const systemColorScheme = useColorScheme() as ColorScheme;
 25    const colorScheme =
 26      theme === 'system' ? systemColorScheme : (theme as ColorScheme);
 27    const colors = Colors[colorScheme];
 28    const styles = getStyles(colors);
 29  
 30    const [isLoading, setIsLoading] = useState(true);
 31    const [error, setError] = useState<string | null>(null);
 32    const [verificationComplete, setVerificationComplete] = useState(false);
 33  
 34    const handleLoadEnd = () => {
 35      setIsLoading(false);
 36    };
 37  
 38    const handleError = () => {
 39      setError('Failed to load verification page. Please try again.');
 40      setIsLoading(false);
 41    };
 42  
 43    const { handleVerificationComplete } = useCloudflareDetection();
 44  
 45    const handleNavigationStateChange = (navState: WebViewNavigation) => {
 46      // Check if we're no longer on a Cloudflare page
 47      if (
 48        !navState.url.includes('cf-browser-verification') &&
 49        !navState.url.includes('cf_captcha_kind')
 50      ) {
 51        setVerificationComplete(true);
 52        handleVerificationComplete(); // This will route back to the previous page
 53      }
 54    };
 55  
 56    const handleBackPress = () => {
 57      router.back();
 58    };
 59  
 60    const handleRetry = () => {
 61      setError(null);
 62      setIsLoading(true);
 63      setVerificationComplete(false);
 64    };
 65  
 66    // Inject JavaScript to modify the page appearance and remove unnecessary elements
 67    const injectedJavaScript = `
 68      document.body.style.backgroundColor = '${colors.background}';
 69      document.body.style.color = '${colors.text}';
 70      const elements = document.querySelectorAll('header, footer, nav, aside');
 71      elements.forEach(element => element.style.display = 'none');
 72      true;
 73    `;
 74  
 75    return (
 76      <View style={styles.container}>
 77        {isLoading && (
 78          <View style={styles.loadingContainer}>
 79            <ActivityIndicator size="large" color={colors.primary} />
 80          </View>
 81        )}
 82  
 83        {error ? (
 84          <View style={styles.errorContainer}>
 85            <Text style={styles.errorText}>{error}</Text>
 86            <TouchableOpacity style={styles.retryButton} onPress={handleRetry}>
 87              <Text style={styles.retryButtonText}>Retry</Text>
 88            </TouchableOpacity>
 89          </View>
 90        ) : (
 91          <>
 92            <CustomWebView
 93              source={{ uri: MANGA_API_URL }}
 94              style={styles.webView}
 95              onLoadEnd={handleLoadEnd}
 96              onError={handleError}
 97              injectedJavaScript={injectedJavaScript}
 98              allowedHosts={['mangafire.to']}
 99              javaScriptEnabled={true}
100              domStorageEnabled={true}
101              onNavigationStateChange={handleNavigationStateChange}
102              decelerationRate={Platform.OS === 'ios' ? 'normal' : 0.98}
103            />
104  
105            <TouchableOpacity style={styles.backButton} onPress={handleBackPress}>
106              <Ionicons name="chevron-back" size={24} color={colors.text} />
107            </TouchableOpacity>
108  
109            {verificationComplete && (
110              <View style={styles.completeBanner}>
111                <Text style={styles.completeText}>Verification Complete!</Text>
112                <TouchableOpacity
113                  style={styles.continueButton}
114                  onPress={handleBackPress}
115                >
116                  <Text style={styles.continueButtonText}>Continue</Text>
117                </TouchableOpacity>
118              </View>
119            )}
120          </>
121        )}
122      </View>
123    );
124  }
125  
126  const getStyles = (colors: typeof Colors.light) =>
127    StyleSheet.create({
128      container: {
129        flex: 1,
130        backgroundColor: colors.background,
131      },
132      webView: {
133        flex: 1,
134      },
135      loadingContainer: {
136        position: 'absolute',
137        left: 0,
138        right: 0,
139        top: 0,
140        bottom: 0,
141        alignItems: 'center',
142        justifyContent: 'center',
143        backgroundColor: colors.background,
144      },
145      errorContainer: {
146        flex: 1,
147        justifyContent: 'center',
148        alignItems: 'center',
149        padding: 20,
150      },
151      errorText: {
152        fontSize: 18,
153        textAlign: 'center',
154        color: colors.error,
155        marginBottom: 20,
156      },
157      retryButton: {
158        backgroundColor: colors.primary,
159        paddingHorizontal: 32,
160        paddingVertical: 12,
161        borderRadius: 8,
162      },
163      retryButtonText: {
164        color: colors.card,
165        fontSize: 16,
166        fontWeight: 'bold',
167      },
168      backButton: {
169        position: 'absolute',
170        top: 50,
171        left: 10,
172        zIndex: 1000,
173        backgroundColor: 'rgba(0, 0, 0, 0.3)',
174        borderRadius: 20,
175        padding: 8,
176      },
177      completeBanner: {
178        position: 'absolute',
179        bottom: 0,
180        left: 0,
181        right: 0,
182        backgroundColor: colors.card,
183        padding: 20,
184        alignItems: 'center',
185        borderTopLeftRadius: 15,
186        borderTopRightRadius: 15,
187        shadowColor: '#000',
188        shadowOffset: {
189          width: 0,
190          height: -2,
191        },
192        shadowOpacity: 0.25,
193        shadowRadius: 3.84,
194        elevation: 5,
195      },
196      completeText: {
197        color: colors.primary,
198        fontSize: 18,
199        fontWeight: 'bold',
200        marginBottom: 10,
201      },
202      continueButton: {
203        backgroundColor: colors.primary,
204        paddingHorizontal: 32,
205        paddingVertical: 12,
206        borderRadius: 8,
207        minWidth: 200,
208      },
209      continueButtonText: {
210        color: colors.card,
211        fontSize: 16,
212        fontWeight: 'bold',
213        textAlign: 'center',
214      },
215    });