updated UI for relative screensize. Handle long payload by truncating the displayes content

This commit is contained in:
2024-08-06 11:38:40 +08:00
parent 6032aebd6a
commit 7cb5cbbe34
2 changed files with 164 additions and 118 deletions

View File

@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, Image, TouchableOpacity, Modal, ActivityIndicator, Alert } from 'react-native'; import { View, Text, StyleSheet, Image, TouchableOpacity, Modal, ActivityIndicator, ScrollView, Dimensions } 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 icons import { Ionicons, MaterialCommunityIcons, SimpleLineIcons } from '@expo/vector-icons';
import { getQRCodeDetails } from '../api/qrCodeAPI'; import { getQRCodeDetails } from '../api/qrCodeAPI';
import SecureWebView from '../components/SecureWebView'; // Import the SecureWebView component import SecureWebView from '../components/SecureWebView';
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
// Define Props for ScannedDataBox component
interface ScannedDataBoxProps { interface ScannedDataBoxProps {
qrCodeId: string; qrCodeId: string;
clearScanData: () => void; clearScanData: () => void;
@@ -14,6 +15,7 @@ interface ScannedDataBoxProps {
const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData }) => { 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 [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('');
@@ -42,7 +44,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
); );
} }
// Handle cases where data might be undefined
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';
@@ -52,7 +53,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
const securityHeaders = details.hstsHeader || ['No Headers']; const securityHeaders = details.hstsHeader || ['No Headers'];
const redirectChain = details.redirectChain || ['No Redirects']; const redirectChain = details.redirectChain || ['No Redirects'];
// Determine the result text based on scan result
const getResultText = () => { const getResultText = () => {
if (!secureConnection || redirects > 0) { if (!secureConnection || redirects > 0) {
return 'DANGEROUS'; return 'DANGEROUS';
@@ -63,7 +63,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
} }
}; };
// Determine the result color based on result text
const getResultColor = () => { const getResultColor = () => {
const result = getResultText(); const result = getResultText();
if (result === 'DANGEROUS') { if (result === 'DANGEROUS') {
@@ -77,97 +76,102 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
} }
}; };
// Determine the appropriate icon for redirects
const getRedirectIcon = () => { const getRedirectIcon = () => {
if (redirects === 0) { if (redirects === 0) {
return <Ionicons name="shield-checkmark" size={18} color="#44c167" />; return <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color="#44c167" />;
} else if (redirects <= 2) { } else if (redirects <= 2) {
return <Ionicons name="shield" size={18} color="#ffa500" />; return <Ionicons name="shield" size={screenWidth * 0.045} color="#ffa500" />;
} else { } else {
return <MaterialCommunityIcons name="shield-alert" size={18} color="#ff0000" />; return <MaterialCommunityIcons name="shield-alert" size={screenWidth * 0.045} color="#ff0000" />;
} }
}; };
// Open the WebView for the URL
const openWebView = (url: string) => { const openWebView = (url: string) => {
setWebViewUrl(url); setWebViewUrl(url);
setIsWebViewVisible(true); setIsWebViewVisible(true);
}; };
const truncateContent = (content: string, length: number) => {
if (content.length > length) {
return `${content.substring(0, length)}...`;
}
return content;
};
return ( return (
<View style={styles.dataBox}> <View style={styles.dataBox}>
{/* Close button */}
<TouchableOpacity style={styles.closeButton} onPress={clearScanData}> <TouchableOpacity style={styles.closeButton} onPress={clearScanData}>
<Ionicons name="close-circle-outline" size={18} color="#ff69b4" /> <Ionicons name="close-circle-outline" size={screenWidth * 0.05} color="#ff69b4" />
</TouchableOpacity> </TouchableOpacity>
{/* Display scanned data */} <View style={[styles.row, styles.shadowBox]}>
<View style={styles.row}>
<Image source={require('../assets/ScanIcon3.png')} style={styles.scan_icon} /> <Image source={require('../assets/ScanIcon3.png')} style={styles.scan_icon} />
<Text style={styles.payload}>{contents}</Text> <Text style={styles.payload} onPress={() => setIsContentModalVisible(true)}>
</View> {truncateContent(contents, 30)} {/* Truncated content further */}
<View style={styles.divider} />
<Text style={styles.timestampText}>{data.createdAt ? new Date(data.createdAt).toLocaleString() : 'Invalid Date'}</Text>
<View style={styles.qrContainer}>
<QRCode value={contents || 'No Data'} size={75} backgroundColor="transparent" />
<Text style={[styles.resultText, { color: getResultColor() }]}>
Result: {getResultText()}
</Text> </Text>
</View> </View>
<View style={styles.mainContent}>
<View style={styles.qrSection}>
<QRCode value={contents || 'No Data'} size={screenWidth * 0.2} backgroundColor="transparent" />
</View>
<View style={styles.dividerVertical} />
<View style={styles.detailsSection}>
<Text style={styles.timestampText}>{data.createdAt ? new Date(data.createdAt).toLocaleString() : 'Invalid Date'}</Text>
<Text style={styles.typeText}>Description: {type}</Text>
<Text>{'\n'}</Text>
</View>
</View>
{/* Display data type */}
<View style={styles.divider} />
<Text style={styles.typeText}>Type: {type}</Text>
<View style={styles.divider} />
<Text style={styles.blankLine}>{'\n'}</Text>
{/* Display scan checks */} <Text style={[styles.resultText, { color: getResultColor() }]}>
Result: {getResultText()}
</Text>
{type === 'URL' && ( {type === 'URL' && (
<> <>
<View style={styles.displayCheck}> <View style={styles.displayCheck}>
{secureConnection ? ( {secureConnection ? (
<> <>
<Ionicons name="shield-checkmark" size={18} color="#44c167" /> <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color="#44c167" />
<Text style={styles.checksText}>Secure Connection</Text> <Text style={styles.moreInfoButtonText}>Secure Connection</Text>
</> </>
) : ( ) : (
<> <>
<SimpleLineIcons name="shield" size={18} color="#ff0000" /> <SimpleLineIcons name="shield" size={screenWidth * 0.045} color="#ff0000" />
<Text style={styles.checksText}>Not Secure</Text> <Text style={styles.moreInfoButtonText}>Not Secure</Text>
</> </>
)} )}
</View> </View>
<TouchableOpacity style={styles.moreInfoButton} onPress={() => setIsRedirectModalVisible(true)}> <TouchableOpacity style={styles.moreInfoButton} onPress={() => setIsRedirectModalVisible(true)}>
{getRedirectIcon()} {getRedirectIcon()}
<Text style={styles.moreInfoButtonText}>Redirects</Text> <Text style={styles.moreInfoButtonText}>Redirects</Text>
<Ionicons name="chevron-forward" size={18} color="#ff69b4" /> <Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#ff69b4" />
</TouchableOpacity> </TouchableOpacity>
</> </>
)} )}
{type === 'SMS' && ( {type === 'SMS' && (
<> <>
<Text style={styles.checksText}>Recipient Phone Number: {details.phone || 'Undefined'}</Text> <Text style={styles.moreInfoButtonText}>Recipient Phone Number: {details.phone || 'Undefined'}</Text>
<Text style={styles.checksText}>Message Content: {details.message || 'Undefined'}</Text> <Text style={styles.moreInfoButtonText}>Message Content: {details.message || 'Undefined'}</Text>
</> </>
)} )}
{type === 'TEXT' && ( {type === 'TEXT' && (
<> <TouchableOpacity style={[styles.contentBox, styles.shadowBox]} onPress={() => setIsContentModalVisible(true)}>
<Text style={styles.checksText}>Content: {contents}</Text> <Text style={styles.moreInfoButtonText}>
</> Content: {truncateContent(contents, 30)} {/* Truncated content further */}
</Text>
</TouchableOpacity>
)} )}
<TouchableOpacity style={styles.moreInfoButton} onPress={() => setIsModalVisible(true)}> <TouchableOpacity style={styles.moreInfoButton} onPress={() => setIsModalVisible(true)}>
<Ionicons name="shield-checkmark" size={18} color="#ff69b4" /> <Ionicons name="shield-checkmark" size={screenWidth * 0.045} color="#ff69b4" />
<Text style={styles.moreInfoButtonText}>Security Headers</Text> <Text style={styles.moreInfoButtonText}>Security Headers</Text>
<Ionicons name="chevron-forward" size={18} color="#ff69b4" /> <Ionicons name="chevron-forward" size={screenWidth * 0.045} color="#ff69b4" />
</TouchableOpacity> </TouchableOpacity>
{/* Modal for security headers */}
<Modal <Modal
visible={isModalVisible} visible={isModalVisible}
transparent={true} transparent={true}
@@ -187,7 +191,6 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</View> </View>
</Modal> </Modal>
{/* Modal for redirects */}
<Modal <Modal
visible={isRedirectModalVisible} visible={isRedirectModalVisible}
transparent={true} transparent={true}
@@ -207,18 +210,35 @@ const ScannedDataBox: React.FC<ScannedDataBoxProps> = ({ qrCodeId, clearScanData
</View> </View>
</Modal> </Modal>
{/* Action buttons */} <Modal
<View style={styles.divider} /> visible={isContentModalVisible}
transparent={true}
animationType="fade"
onRequestClose={() => setIsContentModalVisible(false)}
>
<View style={styles.modalContainer}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Full Content</Text>
<ScrollView style={styles.modalScrollContent}>
<Text style={styles.modalText}>{contents}</Text>
</ScrollView>
<TouchableOpacity style={styles.closeModalButton} onPress={() => setIsContentModalVisible(false)}>
<Text style={styles.closeModalButtonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
<View style={styles.iconContainer}> <View style={styles.iconContainer}>
{type === 'URL' && ( {type === 'URL' && (
<TouchableOpacity style={styles.iconButton} onPress={() => openWebView(contents)}> <TouchableOpacity style={styles.iconButton} onPress={() => openWebView(contents)}>
<Ionicons name="open" size={18} color="#2196F3" /> <Ionicons name="open" size={screenWidth * 0.045} color="#2196F3" />
<Text style={styles.iconText}>Open</Text> <Text style={styles.iconText}>Open</Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
</View> </View>
{/* SecureWebView Modal */}
<Modal <Modal
visible={isWebViewVisible} visible={isWebViewVisible}
transparent={true} transparent={true}
@@ -244,64 +264,77 @@ const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
}, },
scan_icon: { scan_icon: {
width: 37.5, width: screenWidth * 0.09,
height: 37.5, height: screenWidth * 0.09,
marginRight: 6, marginRight: screenWidth * 0.015,
}, },
payload: { payload: {
fontSize: 15, fontSize: screenWidth * 0.0375,
color: '#000', color: '#000',
flex: 1, // Allow text to use available space flex: 1,
}, },
dataBox: { dataBox: {
padding: 15, padding: screenWidth * 0.0375,
backgroundColor: '#ffe6f0', backgroundColor: '#ffe6f0',
borderRadius: 7.5, borderRadius: screenWidth * 0.01875,
shadowColor: '#000', shadowColor: '#000',
shadowOffset: { width: 0, height: 1.5 }, shadowOffset: { width: 0, height: screenHeight * 0.001875 },
shadowOpacity: 0.15, shadowOpacity: 0.15,
shadowRadius: 3.75, shadowRadius: screenWidth * 0.01875,
elevation: 2.25, elevation: screenWidth * 0.0135,
zIndex: 1, zIndex: 1,
}, },
qrContainer: { mainContent: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginVertical: 7.5, padding: screenWidth * 0.0125,
}, },
blankLine: { qrSection: {
height: 15, flex: 1,
alignItems: 'center',
}, },
divider: { dividerVertical: {
height: 0.75, width: screenWidth * 0.001875,
height: '100%',
backgroundColor: '#ddd', backgroundColor: '#ddd',
marginVertical: 7.5, marginHorizontal: screenWidth * 0.025,
alignSelf: 'stretch',
}, },
detailsSection: {
flex: 2,
},
timestampText: { timestampText: {
fontSize: 11, fontSize: screenWidth * 0.0275,
color: '#000', color: '#000',
marginBottom: 7.5, marginBottom: screenWidth * 0.01875,
}, },
resultText: { resultText: {
fontSize: 18, fontSize: screenWidth * 0.045,
marginBottom: 7.5, marginBottom: screenWidth * 0.01875,
textAlign: 'center', textAlign: 'center',
}, },
typeText: { typeText: {
fontSize: 12, fontSize: screenWidth * 0.03,
color: '#000', color: '#000',
marginBottom: 7.5, marginBottom: screenWidth * 0.01875,
},
moreInfoButtonText: {
fontSize: screenWidth * 0.03,
color: '#000',
marginLeft: screenWidth * 0.01875,
}, },
checksText: { checksText: {
fontSize: 12, fontSize: screenWidth * 0.03,
color: '#000', color: '#000',
marginBottom: 3.75, marginBottom: screenWidth * 0.009375,
marginLeft: 7.5, // Adjust margin for alignment marginLeft: screenWidth * 0.01875,
}, },
iconContainer: { iconContainer: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
marginTop: 7.5, marginTop: screenWidth * 0.01875,
}, },
iconButton: { iconButton: {
flexDirection: 'column', flexDirection: 'column',
@@ -310,46 +343,54 @@ const styles = StyleSheet.create({
}, },
iconText: { iconText: {
color: '#2196F3', color: '#2196F3',
marginTop: 3.75, marginTop: screenWidth * 0.009375,
textAlign: 'center', textAlign: 'center',
fontSize: 12, fontSize: screenWidth * 0.03,
}, },
moreInfoText: { moreInfoText: {
fontSize: 13.5, fontSize: screenWidth * 0.03375,
fontWeight: 'bold', fontWeight: 'bold',
color: '#000', color: '#000',
marginVertical: 7.5, marginVertical: screenWidth * 0.01875,
}, },
moreInfoButton: { moreInfoButton: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: 7.5, paddingVertical: screenWidth * 0.01875,
paddingHorizontal: 11.25, paddingHorizontal: screenWidth * 0.028125,
backgroundColor: '#ffe6f0', backgroundColor: '#ffe6f0',
borderRadius: 7.5, borderRadius: screenWidth * 0.01875,
marginTop: 7.5, marginTop: screenWidth * 0.01875,
borderWidth: 1, borderWidth: 1,
borderColor: '#ff69b4', borderColor: '#ff69b4',
}, },
moreInfoButtonText: {
flex: 1,
fontSize: 12,
color: '#000',
marginLeft: 7.5,
},
displayCheck: { displayCheck: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingVertical: 7.5, paddingVertical: screenWidth * 0.01875,
paddingHorizontal: 11.25, paddingHorizontal: screenWidth * 0.028125,
backgroundColor: '#ffe6f0', backgroundColor: '#ffe6f0',
borderRadius: 7.5, borderRadius: screenWidth * 0.01875,
marginTop: 7.5, marginTop: screenWidth * 0.01875,
},
contentBox: {
marginTop: screenWidth * 0.01875,
padding: screenWidth * 0.025,
backgroundColor: '#fff',
borderRadius: screenWidth * 0.01875,
borderWidth: 1,
borderColor: '#ff69b4',
shadowColor: '#000',
shadowOffset: { width: 0, height: screenHeight * 0.001875 },
shadowOpacity: 0.15,
shadowRadius: screenWidth * 0.01875,
elevation: screenWidth * 0.0135,
}, },
closeButton: { closeButton: {
position: 'absolute', position: 'absolute',
top: 7.5, top: screenWidth * 0.01875,
right: 7.5, right: screenWidth * 0.01875,
zIndex: 2,
}, },
modalContainer: { modalContainer: {
flex: 1, flex: 1,
@@ -360,30 +401,33 @@ const styles = StyleSheet.create({
modalContent: { modalContent: {
width: '80%', width: '80%',
backgroundColor: 'white', backgroundColor: 'white',
borderRadius: 7.5, borderRadius: screenWidth * 0.01875,
padding: 15, padding: screenWidth * 0.0375,
alignItems: 'center', alignItems: 'center',
}, },
modalTitle: { modalTitle: {
fontSize: 18, fontSize: screenWidth * 0.045,
fontWeight: 'bold', fontWeight: 'bold',
marginBottom: 7.5, marginBottom: screenWidth * 0.01875,
}, },
modalText: { modalText: {
fontSize: 12, fontSize: screenWidth * 0.03,
marginBottom: 3.75, marginBottom: screenWidth * 0.009375,
textAlign: 'left', textAlign: 'left',
width: '100%', width: '100%',
}, },
modalScrollContent: {
maxHeight: 200,
},
closeModalButton: { closeModalButton: {
marginTop: 15, marginTop: screenWidth * 0.0375,
paddingVertical: 7.5, paddingVertical: screenWidth * 0.01875,
paddingHorizontal: 15, paddingHorizontal: screenWidth * 0.0375,
backgroundColor: '#ff69b4', backgroundColor: '#ff69b4',
borderRadius: 3.75, borderRadius: screenWidth * 0.009375,
}, },
closeModalButtonText: { closeModalButtonText: {
fontSize: 12, fontSize: screenWidth * 0.03,
color: '#fff', color: '#fff',
}, },
loadingContainer: { loadingContainer: {
@@ -394,13 +438,15 @@ const styles = StyleSheet.create({
width: '100%', width: '100%',
height: '80%', height: '80%',
backgroundColor: 'white', backgroundColor: 'white',
borderRadius: 7.5, borderRadius: screenWidth * 0.01875,
overflow: 'hidden', overflow: 'hidden',
}, },
checksContainer: { shadowBox: {
flexDirection: 'row', shadowColor: '#000',
alignItems: 'center', shadowOffset: { width: 0, height: screenHeight * 0.001875 },
marginVertical: 3.75, shadowOpacity: 0.15,
shadowRadius: screenWidth * 0.01875,
elevation: screenWidth * 0.0135,
}, },
}); });

View File

@@ -19,8 +19,8 @@ const EmailScreen: React.FC = () => {
try { try {
// Call to start email fetching process // Call to start email fetching process
const response = await getEmails( const response = await getEmails(
'Access Token here', 'ya29.a0AcM612zTwLojArYvmKxAKiUKL1eBIs04ZBN2dp53BShPcPAhZigjmivq-mQmT6BgF5G1ernMKb2LCHmRgX3vlSaBj2hD8JDi7kvpexduM-_x8aG7QorKfyB2z6yJzFrwVes2Y9tHhb9vWUAqbPdiL4wqNqeE5HxZNhoaCgYKAS0SARISFQHGX2MikJkWByj0FaiKBj3jU7svGg0170',
'Refresh Token here' '1//0g-hOrh4_72p3CgYIARAAGBASNwF-L9IrYVyuPL7WPbsm_ePtzFugduBLmdSr3UpQx7GMSt17KcS2Y_Z3v4N5wZiWua88RFjJ3Zk'
); );
setRescanLoading(false); setRescanLoading(false);
} catch (error) { } catch (error) {
@@ -103,7 +103,7 @@ const EmailScreen: React.FC = () => {
)} )}
{item.qrCodeByURL && ( {item.qrCodeByURL && (
<View> <View>
<Text style={styles.qrCodeHeader}>QR Codes by URL:</Text> <Text style={styles.qrCodeHeader}>Decoded QR Codes:</Text>
{item.qrCodeByURL.map((qrCode, index) => ( {item.qrCodeByURL.map((qrCode, index) => (
<View key={index} style={styles.qrCodeContainer}> <View key={index} style={styles.qrCodeContainer}>
{qrCode.decodedContent.map((url, i) => ( {qrCode.decodedContent.map((url, i) => (