react-native-debug-toolkit
Version:
A simple yet powerful debugging toolkit for React Native with a convenient floating UI for development
315 lines (288 loc) • 7.72 kB
JavaScript
import React from 'react'
import { View, Text, StyleSheet, Clipboard } from 'react-native'
import { ScrollView, Pressable } from 'react-native'
import JSONTree from 'react-native-json-tree'
// Re-using the theme from HttpLogDetails for consistency
const theme = {
scheme: 'monokai',
author: 'wimer hazenberg (http://www.monokai.nl)',
base00: '#272822',
base01: '#383830',
base02: '#49483e',
base03: '#75715e',
base04: '#a59f85',
base05: '#f8f8f2',
base06: '#f5f4f1',
base07: '#f9f8f5',
base08: '#f92672',
base09: '#fd971f',
base0A: '#f4bf75',
base0B: '#a6e22e',
base0C: '#a1efe4',
base0D: '#66d9ef',
base0E: '#ae81ff',
base0F: '#cc6633'
};
const CopyButton = ({ text, style }) => {
const [copied, setCopied] = React.useState(false)
const handleCopy = async () => {
await Clipboard.setString(text)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
return (
<Pressable style={[styles.copyButton, style]} onPress={handleCopy}>
<Text style={styles.copyButtonText}>{copied ? 'Copied!' : 'Copy'}</Text>
</Pressable>
)
}
const CollapsibleSection = ({ title, children, initiallyExpanded = false }) => {
const [expanded, setExpanded] = React.useState(initiallyExpanded)
return (
<View style={styles.collapsibleSection}>
<Pressable
style={styles.sectionHeader}
onPress={() => setExpanded(!expanded)}>
<Text style={styles.sectionTitle}>{title}</Text>
<Text style={styles.expandIcon}>{expanded ? '▼' : '▶'}</Text>
</Pressable>
{expanded && children}
</View>
)
}
// Basic JSONValue renderer, might need adjustments for console log specifics
const JSONValue = ({ value }) => {
if (value === null) {
return <Text style={styles.jsonNull}>null</Text>
}
if (value === undefined) {
return <Text style={styles.jsonNull}>undefined</Text>
}
if (typeof value === 'boolean') {
return <Text style={styles.jsonBoolean}>{value.toString()}</Text>
}
if (typeof value === 'number') {
return <Text style={styles.jsonNumber}>{value}</Text>
}
if (typeof value === 'string') {
return (
<Text style={styles.jsonString} selectable={true}>
"{value}"
</Text>
)
}
if (typeof value === 'object') {
return (
<JSONTree
data={value}
theme={theme}
invertTheme={true}
hideRoot={true}
shouldExpandNode={(keyPath, nodeData, currentLevel) => currentLevel < 1}
/>
)
}
return <Text style={styles.jsonOther}>{String(value)}</Text>
}
const ConsoleLogDetails = ({ log }) => {
if (!log) {
return (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Log data is missing</Text>
</View>
)
}
const logData = log.data || [] // Console logs can have multiple arguments
const timestamp = log.timestamp
const level = log.level || 'log' // Default to 'log' if level is not provided
// Simple function to format log data for display and copying
const formatLogData = (dataArray) => {
return dataArray.map(item => {
if (typeof item === 'object') {
try {
return JSON.stringify(item, null, 2);
} catch (e) {
return '[unserializable object]';
}
}
return String(item);
}).join(' ');
}
const formattedLog = formatLogData(logData);
return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={true}
scrollEventThrottle={16}
keyboardShouldPersistTaps='handled'>
<View style={styles.header}>
<View style={styles.headerInfo}>
<Text style={[styles.levelIndicator, { color: getLevelColor(level) }]}>
{level.toUpperCase()}
</Text>
<Text style={styles.timestamp}>
{timestamp
? new Date(timestamp).toLocaleString()
: 'Unknown time'}
</Text>
</View>
<CopyButton text={formattedLog} />
</View>
<CollapsibleSection title='Log Data' initiallyExpanded={true}>
<View style={styles.dataContentWrapper}>
<View style={styles.dataContent}>
{logData.map((item, index) => (
<View key={index} style={[styles.logDataItem, index === logData.length - 1 && styles.logDataItemLast]}>
<JSONValue value={item} />
</View>
))}
</View>
</View>
</CollapsibleSection>
</ScrollView>
)
}
const getLevelColor = (level) => {
switch (level?.toLowerCase()) {
case 'log':
return '#333'; // Dark gray for standard log
case 'info':
return '#0D96F2'; // Blue for info
case 'warn':
return '#FCA130'; // Orange for warning
case 'error':
return '#F93E3E'; // Red for error
default:
return '#666666'; // Default gray
}
};
const getLevelTextStyle = (level) => {
const baseStyle = { fontSize: 14, fontFamily: 'monospace' }; // Use monospace font
return { ...baseStyle, color: getLevelColor(level) };
}
// Re-using and adapting styles from HttpLogDetails
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
contentContainer: {
paddingBottom: 20,
},
header: {
flexDirection: 'row',
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#eee',
alignItems: 'center', // Align items vertically
justifyContent: 'space-between', // Space out items
},
headerInfo: {
flexDirection: 'row', // Arrange level and timestamp horizontally
alignItems: 'center',
flexShrink: 1, // Allow info to shrink if needed
marginRight: 10,
},
levelIndicator: {
fontSize: 14,
fontWeight: 'bold',
marginRight: 10,
},
timestamp: {
fontSize: 13,
color: '#666',
},
collapsibleSection: {
marginBottom: 1,
backgroundColor: '#fff',
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 15,
backgroundColor: '#f5f5f5',
},
sectionTitle: {
fontSize: 15,
fontWeight: 'bold',
color: '#333',
},
expandIcon: {
fontSize: 14,
color: '#666',
},
content: {
padding: 15,
},
dataContentWrapper: {
flex: 1,
padding: 10, // Add padding around the scroll view
},
dataContent: {
backgroundColor: '#f8f9fa',
padding: 10,
borderRadius: 4,
borderWidth: 1,
borderColor: '#e9ecef',
},
logDataItem: {
borderBottomWidth: 1,
borderBottomColor: '#eee',
paddingVertical: 8,
},
logDataItemLast: {
borderBottomWidth: 0,
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
errorText: {
color: '#ff4444',
fontSize: 16,
},
copyButton: {
backgroundColor: '#e9ecef',
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: 4,
flexShrink: 0, // Prevent button from shrinking
},
copyButtonText: {
fontSize: 12,
color: '#666',
},
// JSON Value Styles (simplified)
jsonString: {
color: '#CB772F',
fontFamily: 'monospace',
fontSize: 13,
},
jsonNumber: {
color: '#AE81FF',
fontFamily: 'monospace',
fontSize: 13,
},
jsonBoolean: {
color: '#66D9EF',
fontWeight: 'bold',
fontFamily: 'monospace',
fontSize: 13,
},
jsonNull: {
color: '#F92672',
fontStyle: 'italic',
fontFamily: 'monospace',
fontSize: 13,
},
jsonOther: {
color: '#75715e',
fontFamily: 'monospace',
fontSize: 13,
},
});
export default ConsoleLogDetails