@@ -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