JavaScript · 6023 bytes Raw Blame History
1 // src/App.js - Complete working app with fixes
2 import React, { useState } from 'react';
3 import './App.css';
4 import { getRoastsForLocation } from './services/roastService';
5
6 function App() {
7 const [location, setLocation] = useState(null);
8 const [locationObj, setLocationObj] = useState(null); // Store the structured object separately
9 const [currentRoast, setCurrentRoast] = useState(null);
10 const [loading, setLoading] = useState(false);
11 const [error, setError] = useState(null);
12 const [manualInput, setManualInput] = useState('');
13
14 const detectLocation = async () => {
15 setLoading(true);
16 setError(null);
17
18 try {
19 // Try IP geolocation first (more reliable for getting city/state names)
20 try {
21 const response = await fetch('https://ipapi.co/json/');
22 const data = await response.json();
23
24 // Build location object from API response
25 const locationObj = {
26 city: data.city || null,
27 state: data.region || null,
28 country: data.country_name || null
29 };
30
31 // Create display string
32 let locationStr = '';
33 if (data.city) {
34 locationStr = data.city;
35 if (data.region) {
36 locationStr += `, ${data.region}`;
37 }
38 if (data.country_name) {
39 locationStr += `, ${data.country_name}`;
40 }
41 } else if (data.region) {
42 locationStr = data.region;
43 if (data.country_name) {
44 locationStr += `, ${data.country_name}`;
45 }
46 } else if (data.country_name) {
47 locationStr = data.country_name;
48 }
49
50 setLocation(locationStr || 'Unknown');
51 setLocationObj(locationObj); // Store the structured object
52 handleLocationFound(locationObj);
53 } catch (err) {
54 setError("Couldn't detect location. Try entering manually!");
55 setLoading(false);
56 }
57 } catch (err) {
58 setError("Couldn't detect location. Try entering manually!");
59 setLoading(false);
60 }
61 };
62
63 const handleLocationFound = (loc) => {
64 // Parse the location string if needed
65 let parsedLocationObj = { city: null, state: null, country: null };
66
67 if (typeof loc === 'string') {
68 // If it's a string, try to parse it
69 const parts = loc.split(',').map(p => p.trim());
70
71 if (parts.length === 1) {
72 // Could be city, state, or country
73 parsedLocationObj.city = parts[0];
74 } else if (parts.length === 2) {
75 // Likely "City, State" or "City, Country"
76 parsedLocationObj.city = parts[0];
77 parsedLocationObj.state = parts[1];
78 } else if (parts.length >= 3) {
79 // "City, State, Country" format
80 parsedLocationObj.city = parts[0];
81 parsedLocationObj.state = parts[1];
82 parsedLocationObj.country = parts[2];
83 }
84 } else if (typeof loc === 'object') {
85 // If it's already an object, use it directly
86 parsedLocationObj = loc;
87 }
88
89 // Store the parsed object for reuse
90 setLocationObj(parsedLocationObj);
91
92 // Get roasts using the service
93 const roasts = getRoastsForLocation(parsedLocationObj);
94
95 if (roasts.length > 0) {
96 const randomRoast = roasts[Math.floor(Math.random() * roasts.length)];
97 setCurrentRoast(randomRoast);
98 } else {
99 setCurrentRoast("Your location is so irrelevant, even our roast database gave up.");
100 }
101
102 setLoading(false);
103 };
104
105 const handleManualSubmit = (e) => {
106 e.preventDefault();
107 if (manualInput.trim()) {
108 const inputLocation = manualInput.trim();
109 setLocation(inputLocation);
110 handleLocationFound(inputLocation);
111 setManualInput('');
112 }
113 };
114
115 const getAnotherRoast = () => {
116 if (locationObj) {
117 // Use the stored location object instead of parsing the display string
118 const roasts = getRoastsForLocation(locationObj);
119 if (roasts.length > 0) {
120 const randomRoast = roasts[Math.floor(Math.random() * roasts.length)];
121 setCurrentRoast(randomRoast);
122 }
123 }
124 };
125
126 return (
127 <div className="App">
128 <div className="container">
129 <div className="logo">🔥</div>
130 <h1>LocalRoast</h1>
131 <p className="subtitle">Get absolutely torched based on where you're from</p>
132
133 <div className="roast-box">
134 {loading ? (
135 <div className="loading"></div>
136 ) : currentRoast ? (
137 <p className="roast-text">{currentRoast}</p>
138 ) : (
139 <p className="roast-text">
140 Ready to get roasted? Hit the button if you can handle it...
141 </p>
142 )}
143 </div>
144
145 {location && (
146 <div className="location-display">📍 {location}</div>
147 )}
148
149 {!currentRoast && !loading && (
150 <button onClick={detectLocation} className="primary-button">
151 Roast Me! 🔥
152 </button>
153 )}
154
155 {currentRoast && (
156 <div className="button-group">
157 <button onClick={getAnotherRoast} className="primary-button">
158 Hit me again! 😤
159 </button>
160 </div>
161 )}
162
163 <div className="manual-input">
164 <p>Can't detect location? Enter it manually:</p>
165 <form onSubmit={handleManualSubmit}>
166 <input
167 type="text"
168 value={manualInput}
169 onChange={(e) => setManualInput(e.target.value)}
170 placeholder="Enter city or region"
171 disabled={loading}
172 />
173 <button type="submit" disabled={loading || !manualInput.trim()}>
174 Roast! 🎯
175 </button>
176 </form>
177 </div>
178
179 {error && <div className="error">{error}</div>}
180
181 <div className="footer">
182 <p>Made with 🔥 and tears | Not responsible for hurt feelings</p>
183 </div>
184 </div>
185 </div>
186 );
187 }
188
189 export default App;