Skip to content

Commit 6353b77

Browse files
committed
feat: Implement deep linking for selected species by synchronizing its state with the URL, handling browser history, and preventing URL updates during initial data loading.
1 parent 0a9d851 commit 6353b77

1 file changed

Lines changed: 58 additions & 6 deletions

File tree

src/App.tsx

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const App = () => {
4343

4444
// Sync state to URL
4545
useEffect(() => {
46+
if (loading) return; // DON'T sync URL while loading, or it might wipe out deep links
47+
4648
const params = new URLSearchParams();
4749
if (selectedMonth) params.set('month', selectedMonth);
4850
if (sortBy !== 'taxonomy') params.set('sort', sortBy);
@@ -54,10 +56,32 @@ const App = () => {
5456
if (onlyEndangered) params.set('endangered', 'true');
5557
if (onlyNewSpecies && selectedMonth) params.set('new', 'true');
5658
if (selectedRarities.length > 0) params.set('rarity', selectedRarities.join(','));
59+
if (selectedSpecies) params.set('species', selectedSpecies.latinName);
5760

5861
const newRelativePathQuery = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
5962
window.history.replaceState(null, '', newRelativePathQuery);
60-
}, [selectedMonth, sortBy, sortOrder, selectedSize, selectedColors, selectedHabitats, selectedHostFamilies, onlyEndangered, onlyNewSpecies, selectedRarities]);
63+
}, [selectedMonth, sortBy, sortOrder, selectedSize, selectedColors, selectedHabitats, selectedHostFamilies, onlyEndangered, onlyNewSpecies, selectedRarities, selectedSpecies, loading]);
64+
65+
// Handle Initial Species Popstate
66+
useEffect(() => {
67+
const handlePopState = () => {
68+
const params = new URLSearchParams(window.location.search);
69+
const speciesParam = params.get('species');
70+
if (speciesParam) {
71+
const decodedParam = decodeURIComponent(speciesParam.replace(/\+/g, ' '));
72+
const found = speciesList.find(s =>
73+
s.latinName.toLowerCase() === speciesParam.toLowerCase() ||
74+
s.latinName.toLowerCase() === decodedParam.toLowerCase()
75+
);
76+
if (found) setSelectedSpecies(found);
77+
} else {
78+
setSelectedSpecies(null);
79+
}
80+
};
81+
82+
window.addEventListener('popstate', handlePopState);
83+
return () => window.removeEventListener('popstate', handlePopState);
84+
}, [speciesList]);
6185

6286
useEffect(() => {
6387
const fetchData = async () => {
@@ -91,6 +115,18 @@ const App = () => {
91115
});
92116

93117
setSpeciesList(combined);
118+
119+
// Check for deep link on initial load
120+
const initialSpeciesParam = new URLSearchParams(window.location.search).get('species');
121+
if (initialSpeciesParam) {
122+
const decodedParam = decodeURIComponent(initialSpeciesParam.replace(/\+/g, ' '));
123+
const found = combined.find(s =>
124+
s.latinName.toLowerCase() === initialSpeciesParam.toLowerCase() ||
125+
s.latinName.toLowerCase() === decodedParam.toLowerCase()
126+
);
127+
if (found) setSelectedSpecies(found);
128+
}
129+
94130
} catch (error) {
95131
console.error('Erro ao carregar dados:', error);
96132
} finally {
@@ -181,19 +217,35 @@ const App = () => {
181217
return list;
182218
}, [speciesList, selectedMonth, sortBy, sortOrder, selectedSize, selectedColors, selectedHabitats, selectedHostFamilies, onlyEndangered, selectedRarities, onlyNewSpecies]);
183219

220+
// Use this function to change species so we push to history
221+
const handleSetSelectedSpecies = (species: Species | null) => {
222+
setSelectedSpecies(species);
223+
224+
// We construct the next URL manually here to push to history.
225+
// The useEffect above will also run, but 'replaceState' on the same URL is harmless.
226+
const params = new URLSearchParams(window.location.search);
227+
if (species) {
228+
params.set('species', species.latinName);
229+
} else {
230+
params.delete('species');
231+
}
232+
const newRelativePathQuery = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
233+
window.history.pushState(null, '', newRelativePathQuery);
234+
};
235+
184236
const handleNextSpecies = () => {
185237
if (!selectedSpecies) return;
186238
const currentIndex = filteredSpecies.findIndex(s => s.latinName === selectedSpecies.latinName);
187239
if (currentIndex !== -1 && currentIndex < filteredSpecies.length - 1) {
188-
setSelectedSpecies(filteredSpecies[currentIndex + 1]);
240+
handleSetSelectedSpecies(filteredSpecies[currentIndex + 1]);
189241
}
190242
};
191243

192244
const handlePrevSpecies = () => {
193245
if (!selectedSpecies) return;
194246
const currentIndex = filteredSpecies.findIndex(s => s.latinName === selectedSpecies.latinName);
195247
if (currentIndex > 0) {
196-
setSelectedSpecies(filteredSpecies[currentIndex - 1]);
248+
handleSetSelectedSpecies(filteredSpecies[currentIndex - 1]);
197249
}
198250
};
199251

@@ -368,7 +420,7 @@ const App = () => {
368420
<SpeciesCard
369421
key={species.latinName}
370422
species={species}
371-
onExpand={() => setSelectedSpecies(species)}
423+
onExpand={() => handleSetSelectedSpecies(species)}
372424
/>
373425
))
374426
)}
@@ -394,13 +446,13 @@ const App = () => {
394446
{selectedSpecies && (
395447
<SpeciesModal
396448
species={selectedSpecies}
397-
onClose={() => setSelectedSpecies(null)}
449+
onClose={() => handleSetSelectedSpecies(null)}
398450
hasNext={selectedSpeciesIndex !== -1 && selectedSpeciesIndex < filteredSpecies.length - 1}
399451
hasPrev={selectedSpeciesIndex > 0}
400452
onNext={handleNextSpecies}
401453
onPrev={handlePrevSpecies}
402454
allSpecies={speciesList}
403-
onSelectSpecies={setSelectedSpecies}
455+
onSelectSpecies={handleSetSelectedSpecies}
404456
/>
405457
)}
406458
</div>

0 commit comments

Comments
 (0)