JavaScript · 7090 bytes Raw Blame History
1 // src/App.js - Complete working app in one file for quick testing
2 import React, { useState } from 'react';
3 import './App.css';
4
5 // Mini roast database for testing
6 const roastDatabase = {
7 'New York': [
8 "NYC? Let me guess, you've mentioned you're from New York within 5 minutes of every conversation you've ever had.",
9 "From NYC? Cool, how's that superiority complex and vitamin D deficiency working out?",
10 "New York City: Where everyone walks fast to nowhere important and calls it ambition."
11 ],
12 'California': [
13 "California? How's that $8 gas and $15 avocado toast treating you?",
14 "Oh, you're from California? Which wellness trend are you pretending changed your life this week?",
15 "California: Come for the weather, stay because you can't afford to leave."
16 ],
17 'Texas': [
18 "Texas? Everything's bigger there, especially the egos and the power grid failures.",
19 "From Texas? Let me guess, you've already mentioned how big your state is three times today.",
20 "Texas: Where 105°F is 'nice weather' and a light dusting of snow shuts down civilization."
21 ],
22 'Virginia': [
23 "Virginia? The state that can't decide if it's the South or just South of Maryland.",
24 "From Virginia? Home of 'Virginia is for Lovers' - because you need a slogan when you have no personality.",
25 "Virginia: Where Northern Virginia pretends it's DC and the rest pretends it's still 1865.",
26 "Oh, Virginia? The state whose biggest achievement is being close to somewhere important.",
27 "Virginia: Where everyone works for the government but swears they're a 'small government conservative'."
28 ],
29 'default': [
30 "Your location is so irrelevant, even Google Maps just shrugs.",
31 "From there? I'd roast your hometown but it would require me to care about it first.",
32 "Your area is so forgettable, even this roast generator had nothing prepared."
33 ]
34 };
35
36 function App() {
37 const [location, setLocation] = useState(null);
38 const [currentRoast, setCurrentRoast] = useState(null);
39 const [loading, setLoading] = useState(false);
40 const [error, setError] = useState(null);
41 const [manualInput, setManualInput] = useState('');
42
43 const detectLocation = async () => {
44 setLoading(true);
45 setError(null);
46
47 try {
48 // Try IP geolocation first (more reliable for getting city/state names)
49 try {
50 const response = await fetch('https://ipapi.co/json/');
51 const data = await response.json();
52
53 // Build location string from most specific to least specific
54 let locationStr = '';
55 if (data.city) {
56 locationStr = data.city;
57 if (data.region) {
58 locationStr = data.region; // Use state/region as primary identifier
59 }
60 } else if (data.region) {
61 locationStr = data.region;
62 } else if (data.country_name) {
63 locationStr = data.country_name;
64 }
65
66 handleLocationFound(locationStr || 'Unknown');
67 } catch (err) {
68 setError("Couldn't detect location. Try entering manually!");
69 setLoading(false);
70 }
71 } catch (err) {
72 setError("Couldn't detect location. Try entering manually!");
73 setLoading(false);
74 }
75 };
76
77 const handleLocationFound = (loc) => {
78 setLocation(loc);
79
80 // Normalize the location for database lookup
81 const normalizedLoc = loc.trim()
82 .split(' ')
83 .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
84 .join(' ');
85
86 // Try exact match first, then try just the first word (for "Los Angeles, California" -> "California")
87 let roasts = roastDatabase[normalizedLoc];
88
89 if (!roasts && loc.includes(',')) {
90 // Try the state/country part after the comma
91 const parts = loc.split(',');
92 const statePart = parts[1].trim()
93 .split(' ')
94 .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
95 .join(' ');
96 roasts = roastDatabase[statePart];
97 }
98
99 if (!roasts) {
100 // Try some common variations
101 const variations = {
102 'Ny': 'New York',
103 'Nyc': 'New York',
104 'La': 'California',
105 'Los Angeles': 'California',
106 'San Francisco': 'California',
107 'Sf': 'California',
108 'Tx': 'Texas',
109 'Va': 'Virginia',
110 'Cali': 'California'
111 };
112
113 const variation = variations[normalizedLoc];
114 if (variation) {
115 roasts = roastDatabase[variation];
116 }
117 }
118
119 // Default to generic roasts if nothing found
120 roasts = roasts || roastDatabase.default;
121
122 const randomRoast = roasts[Math.floor(Math.random() * roasts.length)];
123 setCurrentRoast(randomRoast);
124 setLoading(false);
125 };
126
127 const handleManualSubmit = (e) => {
128 e.preventDefault();
129 if (manualInput.trim()) {
130 handleLocationFound(manualInput.trim());
131 setManualInput('');
132 }
133 };
134
135 const getAnotherRoast = () => {
136 if (location) {
137 const roasts = roastDatabase[location] || roastDatabase.default;
138 const randomRoast = roasts[Math.floor(Math.random() * roasts.length)];
139 setCurrentRoast(randomRoast);
140 }
141 };
142
143 return (
144 <div className="App">
145 <div className="container">
146 <div className="logo">🔥</div>
147 <h1>LocalRoast</h1>
148 <p className="subtitle">Get absolutely torched based on where you're from</p>
149
150 <div className="roast-box">
151 {loading ? (
152 <div className="loading"></div>
153 ) : currentRoast ? (
154 <p className="roast-text">{currentRoast}</p>
155 ) : (
156 <p className="roast-text">
157 Ready to get roasted? Hit the button if you can handle it...
158 </p>
159 )}
160 </div>
161
162 {location && (
163 <div className="location-display">📍 {location}</div>
164 )}
165
166 {!currentRoast && !loading && (
167 <button onClick={detectLocation} className="primary-button">
168 Roast Me! 🔥
169 </button>
170 )}
171
172 {currentRoast && (
173 <div className="button-group">
174 <button onClick={getAnotherRoast} className="primary-button">
175 Hit me again! 😤
176 </button>
177 </div>
178 )}
179
180 <div className="manual-input">
181 <p>Can't detect location? Enter it manually:</p>
182 <form onSubmit={handleManualSubmit}>
183 <input
184 type="text"
185 value={manualInput}
186 onChange={(e) => setManualInput(e.target.value)}
187 placeholder="Enter city or region"
188 disabled={loading}
189 />
190 <button type="submit" disabled={loading || !manualInput.trim()}>
191 Roast! 🎯
192 </button>
193 </form>
194 </div>
195
196 {error && <div className="error">{error}</div>}
197
198 <div className="footer">
199 <p>Made with 🔥 and tears | Not responsible for hurt feelings</p>
200 </div>
201 </div>
202 </div>
203 );
204 }
205
206 export default App;