TheOCEAN's picture
Add backend and frontend project files
aa57b75
import React, { useState } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
ActivityIndicator,
Keyboard,
ScrollView,
SafeAreaView,
StatusBar,
Platform,
} from 'react-native';
const API_URL = "http://192.168.1.8:5000/predict"; // update if needed
export default function App() {
const [text, setText] = useState('');
const [sentiment, setSentiment] = useState(null);
const [confidence, setConfidence] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const handlePredict = async () => {
if (text.trim() === '') {
setError('Please enter some text to analyze.');
return;
}
if (API_URL.includes("YOUR_COMPUTER_IP_ADDRESS")) {
setError("Please update the API_URL with your actual IP address.");
return;
}
Keyboard.dismiss();
setIsLoading(true);
setError(null);
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
let data;
try {
data = await response.json();
} catch {
throw new Error('Invalid response from server. Expected JSON.');
}
if (response.ok) {
setSentiment(data.sentiment);
setConfidence(data.confidence);
// NOTE: we intentionally do NOT clear the input so text is retained
} else {
throw new Error(data.error || 'API returned an error.');
}
} catch (err) {
console.error(err);
setError(
'Failed to connect to the API. Ensure your Flask server is running and your device is on the same Wi-Fi network.'
);
} finally {
setIsLoading(false);
}
};
const getSentimentStyle = (label) => {
switch (label?.toLowerCase()) {
case 'happy':
return { color: '#27ae60' };
case 'sad':
return { color: '#2980b9' };
case 'angry':
return { color: '#e74c3c' };
case 'surprise':
return { color: '#f1c40f' };
case 'disgust':
return { color: '#8e44ad' };
default:
return { color: '#7f8c8d' };
}
};
return (
<SafeAreaView style={styles.safeArea}>
<StatusBar
barStyle="dark-content"
backgroundColor={styles.safeArea.backgroundColor}
/>
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>TEXI'OR</Text>
<Text style={styles.subtitle}>Mood & Sentiment Analyzer</Text>
</View>
<View style={styles.card}>
<TextInput
style={styles.input}
placeholder="How are you feeling today?"
placeholderTextColor="#9aa0a6"
value={text}
onChangeText={setText}
multiline
textAlignVertical="top"
/>
<TouchableOpacity
style={[styles.button, isLoading && styles.buttonDisabled]}
onPress={handlePredict}
disabled={isLoading}
activeOpacity={0.85}
>
{isLoading ? (
<ActivityIndicator size="small" color="#fff" />
) : (
<Text style={styles.buttonText}>Analyze Mood</Text>
)}
</TouchableOpacity>
{error ? <Text style={styles.errorText}>{error}</Text> : null}
{sentiment && Number.isFinite(confidence) && (
<View style={styles.resultContainer}>
<Text style={styles.resultLabel}>PREDICTED MOOD</Text>
<Text style={[styles.resultSentiment, getSentimentStyle(sentiment)]}>
{String(sentiment).toUpperCase()}
</Text>
<View style={styles.confidenceBarBackground}>
<View
style={[
styles.confidenceBarFill,
{ width: `${Math.max(0, Math.min(100, (confidence * 100).toFixed(0)))}%` },
]}
/>
</View>
<Text style={styles.resultConfidence}>
{(Number.isFinite(confidence) ? (confidence * 100).toFixed(1) : '0.0')}% Confidence
</Text>
</View>
)}
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeArea: {
flex: 1,
backgroundColor: '#f4f6f8',
},
container: {
flexGrow: 1,
padding: 22,
alignItems: 'center',
justifyContent: 'center',
},
header: {
alignItems: 'center',
marginBottom: 18,
},
title: {
fontSize: 44,
fontWeight: '800',
color: '#243447',
letterSpacing: 1,
},
subtitle: {
fontSize: 16,
color: '#61707a',
marginTop: 6,
},
card: {
width: '100%',
backgroundColor: '#ffffff',
borderRadius: 16,
padding: 18,
// subtle shadow
...Platform.select({
ios: {
shadowColor: '#000',
shadowOpacity: 0.06,
shadowOffset: { width: 0, height: 6 },
shadowRadius: 12,
},
android: {
elevation: 4,
},
}),
},
input: {
width: '100%',
minHeight: 120,
maxHeight: 220,
borderRadius: 12,
borderWidth: 1,
borderColor: '#e6ecef',
padding: 14,
fontSize: 16,
backgroundColor: '#fbfdff',
color: '#243447',
},
button: {
marginTop: 14,
backgroundColor: '#3276d6',
paddingVertical: 13,
borderRadius: 12,
alignItems: 'center',
justifyContent: 'center',
},
buttonDisabled: {
opacity: 0.78,
},
buttonText: {
color: '#fff',
fontWeight: '700',
fontSize: 16,
letterSpacing: 0.3,
},
errorText: {
marginTop: 12,
color: '#e04444',
fontSize: 14,
textAlign: 'center',
},
resultContainer: {
marginTop: 18,
alignItems: 'center',
width: '100%',
paddingVertical: 14,
paddingHorizontal: 10,
borderRadius: 12,
backgroundColor: '#fcfeff',
borderWidth: 1,
borderColor: '#e9f0f6',
},
resultLabel: {
fontSize: 12,
color: '#6b7780',
fontWeight: '600',
letterSpacing: 1,
},
resultSentiment: {
fontSize: 30,
marginTop: 8,
fontWeight: '800',
},
confidenceBarBackground: {
width: '92%',
height: 10,
borderRadius: 6,
backgroundColor: '#eef3f7',
marginTop: 12,
overflow: 'hidden',
},
confidenceBarFill: {
height: '100%',
backgroundColor: '#46a0ff',
},
resultConfidence: {
marginTop: 10,
fontSize: 15,
color: '#34495e',
fontWeight: '600',
},
footer: {
marginTop: 18,
alignItems: 'center',
},
footerText: {
color: '#8b99a2',
fontSize: 13,
},
code: {
fontFamily: Platform.select({ ios: 'Menlo', android: 'monospace' }),
backgroundColor: '#eef6ff',
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 4,
color: '#2a6fdb',
},
});