@@ -1,40 +1,11 @@ |
| 1 | -// src/App.js - Complete working app in one file for quick testing | 1 | +// src/App.js - Complete working app with fixes |
| 2 | import React, { useState } from 'react'; | 2 | import React, { useState } from 'react'; |
| 3 | import './App.css'; | 3 | import './App.css'; |
| 4 | - | 4 | +import { getRoastsForLocation } from './services/roastService'; |
| 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 | | 5 | |
| 36 | function App() { | 6 | function App() { |
| 37 | const [location, setLocation] = useState(null); | 7 | const [location, setLocation] = useState(null); |
| | 8 | + const [locationObj, setLocationObj] = useState(null); // Store the structured object separately |
| 38 | const [currentRoast, setCurrentRoast] = useState(null); | 9 | const [currentRoast, setCurrentRoast] = useState(null); |
| 39 | const [loading, setLoading] = useState(false); | 10 | const [loading, setLoading] = useState(false); |
| 40 | const [error, setError] = useState(null); | 11 | const [error, setError] = useState(null); |
@@ -50,20 +21,35 @@ function App() { |
| 50 | const response = await fetch('https://ipapi.co/json/'); | 21 | const response = await fetch('https://ipapi.co/json/'); |
| 51 | const data = await response.json(); | 22 | const data = await response.json(); |
| 52 | | 23 | |
| 53 | - // Build location string from most specific to least specific | 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 |
| 54 | let locationStr = ''; | 32 | let locationStr = ''; |
| 55 | if (data.city) { | 33 | if (data.city) { |
| 56 | locationStr = data.city; | 34 | locationStr = data.city; |
| 57 | if (data.region) { | 35 | if (data.region) { |
| 58 | - locationStr = data.region; // Use state/region as primary identifier | 36 | + locationStr += `, ${data.region}`; |
| | 37 | + } |
| | 38 | + if (data.country_name) { |
| | 39 | + locationStr += `, ${data.country_name}`; |
| 59 | } | 40 | } |
| 60 | } else if (data.region) { | 41 | } else if (data.region) { |
| 61 | locationStr = data.region; | 42 | locationStr = data.region; |
| | 43 | + if (data.country_name) { |
| | 44 | + locationStr += `, ${data.country_name}`; |
| | 45 | + } |
| 62 | } else if (data.country_name) { | 46 | } else if (data.country_name) { |
| 63 | locationStr = data.country_name; | 47 | locationStr = data.country_name; |
| 64 | } | 48 | } |
| 65 | | 49 | |
| 66 | - handleLocationFound(locationStr || 'Unknown'); | 50 | + setLocation(locationStr || 'Unknown'); |
| | 51 | + setLocationObj(locationObj); // Store the structured object |
| | 52 | + handleLocationFound(locationObj); |
| 67 | } catch (err) { | 53 | } catch (err) { |
| 68 | setError("Couldn't detect location. Try entering manually!"); | 54 | setError("Couldn't detect location. Try entering manually!"); |
| 69 | setLoading(false); | 55 | setLoading(false); |
@@ -75,68 +61,65 @@ function App() { |
| 75 | }; | 61 | }; |
| 76 | | 62 | |
| 77 | const handleLocationFound = (loc) => { | 63 | const handleLocationFound = (loc) => { |
| 78 | - setLocation(loc); | 64 | + // Parse the location string if needed |
| 79 | - | 65 | + let parsedLocationObj = { city: null, state: null, country: null }; |
| 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 | | 66 | |
| 99 | - if (!roasts) { | 67 | + if (typeof loc === 'string') { |
| 100 | - // Try some common variations | 68 | + // If it's a string, try to parse it |
| 101 | - const variations = { | 69 | + const parts = loc.split(',').map(p => p.trim()); |
| 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 | | 70 | |
| 113 | - const variation = variations[normalizedLoc]; | 71 | + if (parts.length === 1) { |
| 114 | - if (variation) { | 72 | + // Could be city, state, or country |
| 115 | - roasts = roastDatabase[variation]; | 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]; |
| 116 | } | 83 | } |
| | 84 | + } else if (typeof loc === 'object') { |
| | 85 | + // If it's already an object, use it directly |
| | 86 | + parsedLocationObj = loc; |
| 117 | } | 87 | } |
| 118 | | 88 | |
| 119 | - // Default to generic roasts if nothing found | 89 | + // Store the parsed object for reuse |
| 120 | - roasts = roasts || roastDatabase.default; | 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 | + } |
| 121 | | 101 | |
| 122 | - const randomRoast = roasts[Math.floor(Math.random() * roasts.length)]; | | |
| 123 | - setCurrentRoast(randomRoast); | | |
| 124 | setLoading(false); | 102 | setLoading(false); |
| 125 | }; | 103 | }; |
| 126 | | 104 | |
| 127 | const handleManualSubmit = (e) => { | 105 | const handleManualSubmit = (e) => { |
| 128 | e.preventDefault(); | 106 | e.preventDefault(); |
| 129 | if (manualInput.trim()) { | 107 | if (manualInput.trim()) { |
| 130 | - handleLocationFound(manualInput.trim()); | 108 | + const inputLocation = manualInput.trim(); |
| | 109 | + setLocation(inputLocation); |
| | 110 | + handleLocationFound(inputLocation); |
| 131 | setManualInput(''); | 111 | setManualInput(''); |
| 132 | } | 112 | } |
| 133 | }; | 113 | }; |
| 134 | | 114 | |
| 135 | const getAnotherRoast = () => { | 115 | const getAnotherRoast = () => { |
| 136 | - if (location) { | 116 | + if (locationObj) { |
| 137 | - const roasts = roastDatabase[location] || roastDatabase.default; | 117 | + // Use the stored location object instead of parsing the display string |
| 138 | - const randomRoast = roasts[Math.floor(Math.random() * roasts.length)]; | 118 | + const roasts = getRoastsForLocation(locationObj); |
| 139 | - setCurrentRoast(randomRoast); | 119 | + if (roasts.length > 0) { |
| | 120 | + const randomRoast = roasts[Math.floor(Math.random() * roasts.length)]; |
| | 121 | + setCurrentRoast(randomRoast); |
| | 122 | + } |
| 140 | } | 123 | } |
| 141 | }; | 124 | }; |
| 142 | | 125 | |