Spaces:
Configuration error
Configuration error
| 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', | |
| }, | |
| }); | |