Updated TrackingDetection UI

This commit is contained in:
2024-08-15 22:16:27 +08:00
parent 8e37d9d2b6
commit 47898d1489

View File

@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, Image, TouchableOpacity, Modal, ActivityIndicator, ScrollView, Dimensions, Clipboard, Platform, Animated } from 'react-native'; import { View, Text, StyleSheet, Image, TouchableOpacity, Modal, ActivityIndicator, ScrollView, Dimensions, Clipboard, Platform, Animated } from 'react-native';
import QRCode from 'react-native-qrcode-svg'; import QRCode from 'react-native-qrcode-svg';
import { Ionicons, MaterialCommunityIcons, SimpleLineIcons } from '@expo/vector-icons'; import { Ionicons, MaterialCommunityIcons, SimpleLineIcons } from '@expo/vector-icons';
import { getQRCodeDetails } from '../api/qrCodeAPI'; import { getQRCodeDetails } from '../api/qrCodeAPI';
@@ -7,7 +7,6 @@ import SecureWebView from '../components/SecureWebView';
import { startActivityAsync, ActivityAction } from 'expo-intent-launcher'; import { startActivityAsync, ActivityAction } from 'expo-intent-launcher';
import * as Linking from 'expo-linking'; import * as Linking from 'expo-linking';
const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
interface ScannedDataBoxProps { interface ScannedDataBoxProps {
@@ -19,15 +18,13 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const [isRedirectModalVisible, setIsRedirectModalVisible] = useState(false); const [isRedirectModalVisible, setIsRedirectModalVisible] = useState(false);
const [isContentModalVisible, setIsContentModalVisible] = useState(false); const [isContentModalVisible, setIsContentModalVisible] = useState(false);
const [isTrackingModalVisible, setIsTrackingModalVisible] = useState(false);
const [qrDetails, setQrDetails] = useState<any>(null); const [qrDetails, setQrDetails] = useState<any>(null);
const [isWebViewVisible, setIsWebViewVisible] = useState(false); const [isWebViewVisible, setIsWebViewVisible] = useState(false);
const [webViewUrl, setWebViewUrl] = useState(''); const [webViewUrl, setWebViewUrl] = useState('');
const [error, setError] = useState<string | null>(null); // State to store error message const [error, setError] = useState<string | null>(null); // State to store error message
const [bannerOpacity] = useState(new Animated.Value(0)); // State for banner opacity const [bannerOpacity] = useState(new Animated.Value(0)); // State for banner opacity
useEffect(() => { useEffect(() => {
const fetchQRDetails = async () => { const fetchQRDetails = async () => {
try { try {
@@ -70,8 +67,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
}); });
}; };
const data = qrDetails.data || {}; const data = qrDetails.data || {};
const details = qrDetails.details || {}; const details = qrDetails.details || {};
const type = data.info?.type || 'Undefined'; const type = data.info?.type || 'Undefined';
@@ -86,7 +81,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
const isShorteningService = details.shorteningService === 'Yes'; const isShorteningService = details.shorteningService === 'Yes';
const classification = details.classifications || 'Unknown'; const classification = details.classifications || 'Unknown';
// Function to get security text and icon based on the URL description // Function to get security text and icon based on the URL description
const getSecurityStatus = () => { const getSecurityStatus = () => {
if (data.info?.description === "Secure Uniform Resource Locator") { if (data.info?.description === "Secure Uniform Resource Locator") {
@@ -103,9 +97,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
}; };
const { text: securityText, icon: securityIcon } = getSecurityStatus(); const { text: securityText, icon: securityIcon } = getSecurityStatus();
// Function to get result text and color based on the security status // Function to get result text and color based on the security status
const getResultStatus = () => { const getResultStatus = () => {
if (result === 'UNSAFE') { if (result === 'UNSAFE') {
@@ -122,9 +113,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
}; };
const { text: resultText, color: resultColor } = getResultStatus(); const { text: resultText, color: resultColor } = getResultStatus();
// Function to determine security header status // Function to determine security header status
const getSecurityHeaderStatus = (headers) => { const getSecurityHeaderStatus = (headers) => {
const filteredHeaders = headers.filter( const filteredHeaders = headers.filter(
@@ -148,8 +136,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
}; };
const redirectStatus = getRedirectStatus(details.redirect || 0); const redirectStatus = getRedirectStatus(details.redirect || 0);
// Truncate content string to specified length // Truncate content string to specified length
const truncateContent = (content: string, length: number) => { const truncateContent = (content: string, length: number) => {
if (content.length > length) { if (content.length > length) {
@@ -158,7 +144,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
return content; return content;
}; };
// Function to get encryption status and icon // Function to get encryption status and icon
const getEncryptionStatus = (encryption) => { const getEncryptionStatus = (encryption) => {
if (encryption === 'NO') { if (encryption === 'NO') {
@@ -186,7 +171,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
const { text: encryptionText, icon: encryptionIcon } = getEncryptionStatus(encryption); const { text: encryptionText, icon: encryptionIcon } = getEncryptionStatus(encryption);
// Function to open the Wi-Fi configuration in the OS // Function to open the Wi-Fi configuration in the OS
const handleOpenUrl = (url: string) => { const handleOpenUrl = (url: string) => {
Linking.openURL(url).catch(err => console.error('Error opening URL:', err)); Linking.openURL(url).catch(err => console.error('Error opening URL:', err));
@@ -216,9 +200,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
} }
}; };
const sslStrippingStatus = { const sslStrippingStatus = {
hasSSLStripping: details.sslStripping?.some(status => status === true) ?? false, hasSSLStripping: details.sslStripping?.some(status => status === true) ?? false,
text: details.sslStripping?.some(status => status === true) text: details.sslStripping?.some(status => status === true)
@@ -227,40 +208,23 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
color: details.sslStripping?.some(status => status === true) ? "#ff5942" : "#44c167", // Green for No SSL Stripping color: details.sslStripping?.some(status => status === true) ? "#ff5942" : "#44c167", // Green for No SSL Stripping
}; };
// Log to check what's happening
console.log('SSL Stripping Details:', details.sslStripping);
console.log('SSL Stripping Status:', sslStrippingStatus);
const hasExecutableStatus = { const hasExecutableStatus = {
hasExecutable: details.hasExecutable ?? false, hasExecutable: details.hasExecutable ?? false,
text: details.hasExecutable ? "Executable Detected" : "No Executable", text: details.hasExecutable ? "Executable Detected" : "No Executable",
color: details.hasExecutable ? "#FFA500" : "#44c167", color: details.hasExecutable ? "#ffa500" : "#44c167", // Orange for Executable Detected
iconName: details.hasExecutable ? "alert-circle" : "shield-checkmark", // Orange shield icon for Executable Detected
}; };
// Log to check what's happening
console.log('Executable Details:', details.hasExecutable);
console.log('Executable Status:', hasExecutableStatus);
const trackingStatus = { const trackingStatus = {
hasTracking: details.tracking ?? false, hasTracking: details.trackingDescriptions?.length > 0 ?? false,
text: details.tracking ? "Tracking Detected" : "No Tracking", text: details.trackingDescriptions?.length > 0 ? "Tracking Detected" : "No Tracking",
color: details.tracking ? "#ff5942" : "#44c167", // Green for No Tracking color: details.trackingDescriptions?.length > 0 ? "#ff5942" : "#44c167", // Red for Tracking Detected
}; };
// Log to check what's happening
console.log('Tracking Details:', details.tracking);
console.log('Tracking Status:', trackingStatus);
const redirectCount = details.redirect ?? 0; // Default to 0 if undefined const redirectCount = details.redirect ?? 0; // Default to 0 if undefined
return ( return (
<View style={styles.dataBox}> <View style={styles.dataBox}>
<TouchableOpacity style={styles.closeButton} onPress={clearScanData}> <TouchableOpacity style={styles.closeButton} onPress={clearScanData}>
<Ionicons name="close-circle-outline" size={screenWidth * 0.05} color="#ff69b4" /> <Ionicons name="close-circle-outline" size={screenWidth * 0.05} color="#ff69b4" />
</TouchableOpacity> </TouchableOpacity>
@@ -274,7 +238,7 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</View> </View>
<View style={styles.mainContent}> <View style={styles.mainContent}>
{/* Display QR Code , timestamp and Description */} {/* Display QR Code, timestamp and Description */}
<View style={styles.qrSection}> <View style={styles.qrSection}>
<QRCode value={contents || 'No Data'} size={screenWidth * 0.2} backgroundColor="transparent" /> <QRCode value={contents || 'No Data'} size={screenWidth * 0.2} backgroundColor="transparent" />
</View> </View>
@@ -282,11 +246,10 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
<View style={styles.detailsSection}> <View style={styles.detailsSection}>
<Text style={styles.timestampText}>{data.createdAt ? new Date(data.createdAt).toLocaleString() : 'Invalid Date'}</Text> <Text style={styles.timestampText}>{data.createdAt ? new Date(data.createdAt).toLocaleString() : 'Invalid Date'}</Text>
<Text style={styles.typeText}>Description: {description}</Text> <Text style={styles.typeText}>Description: {description}</Text>
{/* Conditionally display the shortening service message */} {/* Conditionally display the shortening service message */}
{isShorteningService && ( {isShorteningService && (
<Text style={styles.shorteningServiceText}>This is a shortening service</Text> <Text style={styles.shorteningServiceText}>This is a shortening service</Text>
)} )}
</View> </View>
</View> </View>
@@ -295,79 +258,83 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
Result: {resultText} Result: {resultText}
</Text> </Text>
{/* URL Type */} {/* URL Type */}
{type === 'URL' && ( {type === 'URL' && (
<> <>
<Text style={styles.classificationText}> <Text style={styles.classificationText}>
Classification: {classification} Classification: {classification}
</Text> </Text>
<View style={styles.mainContent}> <View style={styles.mainContent}>
{/* Left Container */} {/* Left Container */}
<View style={styles.leftContainer}> <View style={styles.leftContainer}>
<View style={styles.displayCheck}> <View style={styles.displayCheck}>
{securityIcon} {securityIcon}
<Text style={styles.DetailsInfo}>{securityText}</Text> <Text style={styles.DetailsInfo}>{securityText}</Text>
</View> </View>
{securityHeaderStatus.hasHeaders ? ( {securityHeaderStatus.hasHeaders ? (
<TouchableOpacity style={[styles.DetailsInfoButton, { borderColor: '#44c167' }]} onPress={() => setIsModalVisible(true)}> <TouchableOpacity style={[styles.DetailsInfoButton, { borderColor: '#44c167' }]} onPress={() => setIsModalVisible(true)}>
<Ionicons name="shield-checkmark" size={screenWidth * 0.045} color="#44c167" /> <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color="#44c167" />
<Text style={styles.DetailsInfo}>{securityHeaderStatus.text}</Text> <Text style={styles.DetailsInfo}>{securityHeaderStatus.text}</Text>
<Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#44c167" style={styles.chevronIcon} /> <Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#44c167" style={styles.chevronIcon} />
</TouchableOpacity> </TouchableOpacity>
) : ( ) : (
<View style={styles.displayCheck}> <View style={styles.displayCheck}>
<MaterialCommunityIcons name="shield-off" size={screenWidth * 0.045} color={securityHeaderStatus.color} /> <MaterialCommunityIcons name="shield-off" size={screenWidth * 0.045} color={securityHeaderStatus.color} />
<Text style={styles.DetailsInfo}>{securityHeaderStatus.text}</Text> <Text style={styles.DetailsInfo}>{securityHeaderStatus.text}</Text>
</View> </View>
)} )}
{/* Redirects Button */} {/* Redirects Button */}
{redirectCount > 0 ? ( {redirectCount > 0 ? (
<TouchableOpacity style={styles.DetailsInfoButton} onPress={() => setIsRedirectModalVisible(true)}> <TouchableOpacity style={styles.DetailsInfoButton} onPress={() => setIsRedirectModalVisible(true)}>
<Ionicons name="shield" size={screenWidth * 0.045} color={redirectStatus.color} /> <Ionicons name="shield" size={screenWidth * 0.045} color={redirectStatus.color} />
<Text style={styles.DetailsInfo}>{`Redirects: ${redirectCount}`}</Text> <Text style={styles.DetailsInfo}>{`Redirects: ${redirectCount}`}</Text>
<Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#ff69b4" style={styles.chevronIcon} /> <Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#ff69b4" style={styles.chevronIcon} />
</TouchableOpacity> </TouchableOpacity>
) : ( ) : (
<View style={styles.displayCheck}> <View style={styles.displayCheck}>
<Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={redirectStatus.color} /> <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={redirectStatus.color} />
<Text style={styles.DetailsInfo}>{`Redirects: ${redirectCount}`}</Text> <Text style={styles.DetailsInfo}>{`Redirects: ${redirectCount}`}</Text>
</View> </View>
)} )}
</View> </View>
{/* Vertical Divider */} {/* Vertical Divider */}
<View style={styles.verticalDivider} /> <View style={styles.verticalDivider} />
{/* Right Container */} {/* Right Container */}
<View style={styles.rightContainer}> <View style={styles.rightContainer}>
<View style={styles.displayCheck}> <View style={styles.displayCheck}>
<Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={sslStrippingStatus.color} /> <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={sslStrippingStatus.color} />
<Text style={styles.DetailsInfo}>{sslStrippingStatus.text}</Text> <Text style={styles.DetailsInfo}>{sslStrippingStatus.text}</Text>
</View> </View>
{/* Executtable checking */}
{/* Executtable checking */} {hasExecutableStatus.hasExecutable ? (
{hasExecutableStatus.hasExecutable ? ( <TouchableOpacity style={styles.displayCheck}>
<TouchableOpacity style={styles.displayCheck}> <Ionicons name="alert-circle" size={screenWidth * 0.045} color="#FFA500" />
<Ionicons name="alert-circle" size={screenWidth * 0.045} color="#FFA500" /> <Text style={styles.DetailsInfo}>{hasExecutableStatus.text}</Text>
<Text style={styles.DetailsInfo}>{hasExecutableStatus.text}</Text> </TouchableOpacity>
</TouchableOpacity> ) : (
) : ( <View style={styles.displayCheck}>
<View style={styles.displayCheck}> <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={hasExecutableStatus.color} />
<Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={hasExecutableStatus.color} /> <Text style={styles.DetailsInfo}>{hasExecutableStatus.text}</Text>
<Text style={styles.DetailsInfo}>{hasExecutableStatus.text}</Text> </View>
</View> )}
)} {/* Tracking checking */}
{trackingStatus.hasTracking ? (
<TouchableOpacity style={[styles.DetailsInfoButton, { borderColor: '#ff5942' }]} onPress={() => setIsTrackingModalVisible(true)}>
<Ionicons name="alert-circle" size={screenWidth * 0.045} color="#ff5942" />
<View style={styles.displayCheck}> <Text style={styles.DetailsInfo}>{trackingStatus.text}</Text>
<Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={trackingStatus.color} /> <Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#ff5942" style={styles.chevronIcon} />
<Text style={styles.DetailsInfo}>{trackingStatus.text}</Text> </TouchableOpacity>
</View> ) : (
</View> <View style={styles.displayCheck}>
<Ionicons name="shield-checkmark" size={screenWidth * 0.045} color={trackingStatus.color} />
<Text style={styles.DetailsInfo}>{trackingStatus.text}</Text>
</View>
)}
</View>
</View> </View>
{/* Divider */} {/* Divider */}
@@ -394,44 +361,37 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{/* Redirect Chain Pop UP */} {/* Redirect Chain Pop UP */}
<Modal <Modal
visible={isRedirectModalVisible} visible={isRedirectModalVisible}
transparent={true} transparent={true}
animationType="fade" animationType="fade"
onRequestClose={() => setIsRedirectModalVisible(false)} onRequestClose={() => setIsRedirectModalVisible(false)}
> >
<View style={styles.modalContainer}> <View style={styles.modalContainer}>
<View style={styles.modalContent}> <View style={styles.modalContent}>
<Text style={styles.modalTitle}>Redirect Chain</Text> <Text style={styles.modalTitle}>Redirect Chain</Text>
<ScrollView style={styles.modalScrollContent}> <ScrollView style={styles.modalScrollContent}>
{details.redirectChain?.map((redirect: string, index: number) => ( {details.redirectChain?.map((redirect: string, index: number) => (
<ScrollView <ScrollView
key={index} key={index}
horizontal={true} horizontal={true}
style={styles.horizontalScrollView} style={styles.horizontalScrollView}
contentContainerStyle={styles.horizontalContentContainer}> contentContainerStyle={styles.horizontalContentContainer}>
<Text style={styles.modalText}>{redirect}</Text> <Text style={styles.modalText}>{redirect}</Text>
</ScrollView> </ScrollView>
))} ))}
</ScrollView> </ScrollView>
<TouchableOpacity style={styles.closeModalButton} onPress={() => setIsRedirectModalVisible(false)}> <TouchableOpacity style={styles.closeModalButton} onPress={() => setIsRedirectModalVisible(false)}>
<Text style={styles.closeModalButtonText}>Close</Text> <Text style={styles.closeModalButtonText}>Close</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
</View> </View>
</Modal> </Modal>
</> </>
)} )}
{/* WIFI Type */} {/* WIFI Type */}
{type === 'WIFI' && ( {type === 'WIFI' && (
<> <>
{/* SSID */} {/* SSID */}
@@ -467,10 +427,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</> </>
)} )}
{type === 'PHONE Nume' && ( {type === 'PHONE Nume' && (
<TouchableOpacity style={styles.iconButton} onPress={() => Linking.openURL(contents)}> <TouchableOpacity style={styles.iconButton} onPress={() => Linking.openURL(contents)}>
<View style={styles.dividerHorizontal} /> <View style={styles.dividerHorizontal} />
@@ -507,8 +463,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</TouchableOpacity> </TouchableOpacity>
)} )}
{/* Full Content Modal */} {/* Full Content Modal */}
<Modal <Modal
visible={isContentModalVisible} visible={isContentModalVisible}
@@ -529,10 +483,7 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</View> </View>
</Modal> </Modal>
{/* POP UP Security Header and Redirect button */}
{/*POP UP Security Header and Redirect button*/}
{/* Security Headers Modal */} {/* Security Headers Modal */}
<Modal <Modal
visible={isModalVisible} visible={isModalVisible}
@@ -555,8 +506,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</View> </View>
</Modal> </Modal>
{/* WebView Modal */} {/* WebView Modal */}
<Modal <Modal
visible={isWebViewVisible} visible={isWebViewVisible}
@@ -573,6 +522,34 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</View> </View>
</View> </View>
</Modal> </Modal>
{/* Tracking Descriptions Modal */}
<Modal
visible={isTrackingModalVisible}
transparent={true}
animationType="fade"
onRequestClose={() => setIsTrackingModalVisible(false)}
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Tracking Descriptions</Text>
<ScrollView style={styles.modalScrollContent}>
{details.trackingDescriptions?.map((description: string, index: number) => (
<ScrollView
key={index}
horizontal={true} // Enable horizontal scrolling
style={styles.horizontalScrollView}
contentContainerStyle={styles.horizontalContentContainer}>
<Text style={styles.modalText}>{description}</Text>
</ScrollView>
))}
</ScrollView>
<TouchableOpacity style={styles.closeModalButton} onPress={() => setIsTrackingModalVisible(false)}>
<Text style={styles.closeModalButtonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</View> </View>
); );
}; };
@@ -665,6 +642,7 @@ const styles = StyleSheet.create({
borderColor: '#ff69b4', borderColor: '#ff69b4',
width: '100%', // Make it take full width width: '100%', // Make it take full width
}, },
// Display check styles // Display check styles
// Aligning the boxes // Aligning the boxes
displayCheck: { displayCheck: {
@@ -678,6 +656,7 @@ const styles = StyleSheet.create({
marginVertical: screenWidth * 0.01875, marginVertical: screenWidth * 0.01875,
width: '100%', // Make it take full width width: '100%', // Make it take full width
}, },
// Details info text styles // Details info text styles
DetailsInfo: { DetailsInfo: {
fontSize: screenWidth * 0.026, fontSize: screenWidth * 0.026,
@@ -740,7 +719,6 @@ const styles = StyleSheet.create({
flexGrow: 1, // Ensure the content container expands to fit its children flexGrow: 1, // Ensure the content container expands to fit its children
}, },
// Close modal button styles // Close modal button styles
closeModalButton: { closeModalButton: {
marginTop: screenWidth * 0.0375, marginTop: screenWidth * 0.0375,
@@ -874,6 +852,6 @@ const styles = StyleSheet.create({
textAlign: 'center', textAlign: 'center',
marginTop: screenWidth * 0.01875, // Add some spacing above marginTop: screenWidth * 0.01875, // Add some spacing above
}, },
}); });
export default ScannedDataBox; export default ScannedDataBox;