@@ -13,9 +13,9 @@ import { getFeedCategories } from "../../../db/feedCategories.js"
1313import SectionWrapper from "~/components/SectionWrapper/SectionWrapper.tsx"
1414import button from "@chainlink/design-system/button.module.css"
1515import { updateTableOfContents } from "~/components/TableOfContents/tocStore.ts"
16- import alertIcon from "../../../components/Alert/Assets/alert-icon.svg"
1716import { ChainSelector } from "~/components/ChainSelector/ChainSelector.tsx"
1817import { isFeedVisible } from "../utils/feedVisibility.ts"
18+ import { updateUrlClean , clearFilters } from "./urlStateHelpers.ts"
1919
2020export type DataFeedType =
2121 | "default"
@@ -260,10 +260,16 @@ export const FeedList = ({
260260 } , [ ] )
261261
262262 // Regular query string states
263- const [ searchValue , setSearchValue ] = useQueryString ( "search" , "" )
264- const [ testnetSearchValue , setTestnetSearchValue ] = useQueryString ( "testnetSearch" , "" )
265- const [ selectedFeedCategories , setSelectedFeedCategories ] = useQueryString ( "categories" , [ ] )
266- const [ currentPage , setCurrentPage ] = useQueryString ( "page" , "1" )
263+ const [ searchValue , setSearchValue ] = useQueryString ( "search" )
264+ const [ testnetSearchValue , setTestnetSearchValue ] = useQueryString ( "testnetSearch" )
265+ const [ selectedFeedCategoriesRaw , setSelectedFeedCategories ] = useQueryString ( "categories" )
266+ // Ensure categories is always an array
267+ const selectedFeedCategories = Array . isArray ( selectedFeedCategoriesRaw )
268+ ? selectedFeedCategoriesRaw
269+ : selectedFeedCategoriesRaw
270+ ? [ selectedFeedCategoriesRaw ]
271+ : [ ]
272+ const [ currentPage , setCurrentPage ] = useQueryString ( "page" )
267273
268274 // Initialize all other states
269275 const [ showCategoriesDropdown , setShowCategoriesDropdown ] = useState < boolean > ( false )
@@ -283,10 +289,26 @@ export const FeedList = ({
283289 const setTestnetStreamCategoryFilter = ( next : StreamsRwaFeedTypeValue ) => {
284290 setTestnetStreamCategoryFilterParam ( next === "all" ? [ ] : next )
285291 }
286- const [ showExtraDetails , setShowExtraDetails ] = useState ( false )
292+
293+ // Checkbox states backed by URL params
294+ const [ showDetailsParam , setShowDetailsParam ] = useQueryString ( "showDetails" )
295+ const showExtraDetails = showDetailsParam === "true"
296+ const setShowExtraDetails = ( value : boolean ) => {
297+ setShowDetailsParam ( value ? "true" : "" )
298+ updateUrlClean ( { showDetails : value || undefined } )
299+ }
300+
301+ const [ showSvrParam , setShowSvrParam ] = useQueryString ( "showSvr" )
302+ const showOnlySVR = showSvrParam === "true"
303+ const setShowOnlySVR = ( value : boolean ) => {
304+ setShowSvrParam ( value ? "true" : "" )
305+ updateUrlClean ( { showSvr : value || undefined } )
306+ if ( value ) paginate ( 1 )
307+ }
308+
309+ // MVR and DEX filters are not in URL (too specialized)
287310 const [ showOnlyMVRFeeds , setShowOnlyMVRFeeds ] = useState ( false )
288311 const [ showOnlyMVRFeedsTestnet , setShowOnlyMVRFeedsTestnet ] = useState ( false )
289- const [ showOnlySVR , setShowOnlySVR ] = useState ( false )
290312 const [ showOnlyDEXFeeds , setShowOnlyDEXFeeds ] = useState ( false )
291313 const [ showOnlyDEXFeedsTestnet , setShowOnlyDEXFeedsTestnet ] = useState ( false )
292314 const [ rwaSchemaFilterParam , setRwaSchemaFilterParam ] = useQueryString ( "schema" )
@@ -313,16 +335,26 @@ export const FeedList = ({
313335 } )
314336 }
315337 const closeAllDropdowns = ( ) => setOpenDropdownId ( null )
316- const paginate = ( pageNumber ) => setCurrentPage ( String ( pageNumber ) )
338+ const paginate = ( pageNumber ) => {
339+ const pageStr = String ( pageNumber )
340+ setCurrentPage ( pageStr )
341+ updateUrlClean ( { page : pageNumber === 1 ? undefined : pageNumber } )
342+ }
317343 const addrPerPage = ecosystem === "deprecating" && isStreams ? 10 : ecosystem === "deprecating" ? 10000 : 8
318- const lastAddr = Number ( currentPage ) * addrPerPage
344+ const currentPageNum = Number ( currentPage ) || 1
345+ const lastAddr = currentPageNum * addrPerPage
319346 const firstAddr = lastAddr - addrPerPage
320347
321348 // Pagination for testnet table
322- const [ testnetCurrentPage , setTestnetCurrentPage ] = useQueryString ( "testnetPage" , "1" )
323- const testnetPaginate = ( pageNumber ) => setTestnetCurrentPage ( String ( pageNumber ) )
349+ const [ testnetCurrentPage , setTestnetCurrentPage ] = useQueryString ( "testnetPage" )
350+ const testnetPaginate = ( pageNumber ) => {
351+ const pageStr = String ( pageNumber )
352+ setTestnetCurrentPage ( pageStr )
353+ updateUrlClean ( { testnetPage : pageNumber === 1 ? undefined : pageNumber } )
354+ }
324355 const testnetAddrPerPage = ecosystem === "deprecating" && isStreams ? 10 : ecosystem === "deprecating" ? 10000 : 8
325- const testnetLastAddr = Number ( testnetCurrentPage ) * testnetAddrPerPage
356+ const testnetPageNum = Number ( testnetCurrentPage ) || 1
357+ const testnetLastAddr = testnetPageNum * testnetAddrPerPage
326358 const testnetFirstAddr = testnetLastAddr - testnetAddrPerPage
327359
328360 // Dynamic feed categories loaded from Supabase
@@ -500,56 +532,54 @@ export const FeedList = ({
500532 function handleNetworkSelect ( chain : Chain ) {
501533 closeAllDropdowns ( )
502534 if ( ! isStreams ) {
503- const params = new URLSearchParams ( window . location . search )
504- params . set ( "network" , chain . page )
505- // Clear hash when changing networks
506- const newUrl = window . location . pathname + "?" + params . toString ( )
507- window . history . replaceState ( { path : newUrl } , "" , newUrl )
508535 setCurrentNetwork ( chain . page )
536+ // Clear all filters and pagination when switching networks
537+ setSearchValue ( "" )
538+ setTestnetSearchValue ( "" )
539+ setSelectedFeedCategories ( [ ] )
540+ setCurrentPage ( "" )
541+ setTestnetCurrentPage ( "" )
542+ setShowOnlyMVRFeeds ( false )
543+ setShowOnlyMVRFeedsTestnet ( false )
544+ // Update URL with just the network (and networkType if not mainnet)
545+ const params = new URLSearchParams ( window . location . search )
546+ const networkType = params . get ( "networkType" )
547+ updateUrlClean ( {
548+ network : chain . page ,
549+ networkType : networkType === "testnet" ? "testnet" : undefined ,
550+ search : undefined ,
551+ testnetSearch : undefined ,
552+ page : undefined ,
553+ testnetPage : undefined ,
554+ } )
509555 }
510- setSearchValue ( "" )
511- setSelectedFeedCategories ( [ ] )
512- setCurrentPage ( "1" )
513- setShowOnlyMVRFeeds ( false )
514- setShowOnlyMVRFeedsTestnet ( false )
515556 }
516557
517558 // Network type change handler for testnet/mainnet switching
518559 function handleNetworkTypeChange ( networkType : "mainnet" | "testnet" ) {
519560 closeAllDropdowns ( )
520- // Update the selected network type
521561 setSelectedNetworkType ( networkType )
522562
523- // Update URL parameters to reflect network type state
524- if ( typeof window !== "undefined" ) {
525- const params = new URLSearchParams ( window . location . search )
526-
527- if ( networkType === "testnet" ) {
528- // Set networkType parameter to testnet
529- params . set ( "networkType" , "testnet" )
530- // Ensure testnetPage is set (default to 1 if not present)
531- if ( ! params . get ( "testnetPage" ) ) {
532- params . set ( "testnetPage" , "1" )
533- }
534- } else {
535- // Remove testnet-specific parameters when switching to mainnet
536- params . delete ( "networkType" )
537- params . delete ( "testnetSearch" )
538- // Keep testnetPage for potential future navigation
539- }
540-
541- const newUrl = window . location . pathname + "?" + params . toString ( )
542- window . history . replaceState ( { path : newUrl } , "" , newUrl )
543- }
544-
545563 // Reset filters and pagination when switching network types
546564 setSearchValue ( "" )
547565 setTestnetSearchValue ( "" )
548566 setSelectedFeedCategories ( [ ] )
549- setCurrentPage ( "1 " )
550- setTestnetCurrentPage ( "1 " )
567+ setCurrentPage ( "" )
568+ setTestnetCurrentPage ( "" )
551569 setShowOnlyMVRFeeds ( false )
552570 setShowOnlyMVRFeedsTestnet ( false )
571+
572+ // Update URL with clean params
573+ const params = new URLSearchParams ( window . location . search )
574+ const network = params . get ( "network" )
575+ updateUrlClean ( {
576+ network : network || undefined ,
577+ networkType : networkType === "testnet" ? "testnet" : undefined ,
578+ search : undefined ,
579+ testnetSearch : undefined ,
580+ page : undefined ,
581+ testnetPage : undefined ,
582+ } )
553583 }
554584
555585 const handleCategorySelection = ( category ) => {
@@ -567,18 +597,14 @@ export const FeedList = ({
567597 }
568598
569599 useEffect ( ( ) => {
600+ // Clean up empty search params
570601 if ( searchValue === "" ) {
571- const searchParams = new URLSearchParams ( window . location . search )
572- searchParams . delete ( "search" )
573- const hashFragment = window . location . hash
574- const newUrl = window . location . pathname + "?" + searchParams . toString ( ) + hashFragment
575- window . history . replaceState ( { path : newUrl } , "" , newUrl )
576- const inputElement = document . getElementById ( "search" ) as HTMLInputElement
577- if ( inputElement ) {
578- inputElement . placeholder = "Search"
579- }
602+ updateUrlClean ( { search : undefined } )
580603 }
581- } , [ chainMetadata . processedData , searchValue ] )
604+ if ( testnetSearchValue === "" ) {
605+ updateUrlClean ( { testnetSearch : undefined } )
606+ }
607+ } , [ searchValue , testnetSearchValue ] )
582608
583609 const useOutsideAlerter = ( ref : RefObject < HTMLDivElement > ) => {
584610 useEffect ( ( ) => {
@@ -1082,7 +1108,7 @@ export const FeedList = ({
10821108 lastAddr = { lastAddr }
10831109 firstAddr = { firstAddr }
10841110 addrPerPage = { addrPerPage }
1085- currentPage = { Number ( currentPage ) }
1111+ currentPage = { currentPageNum }
10861112 paginate = { paginate }
10871113 searchValue = { typeof searchValue === "string" ? searchValue : "" }
10881114 />
@@ -1201,7 +1227,7 @@ export const FeedList = ({
12011227 firstAddr = { testnetFirstAddr }
12021228 lastAddr = { testnetLastAddr }
12031229 addrPerPage = { testnetAddrPerPage }
1204- currentPage = { Number ( testnetCurrentPage ) }
1230+ currentPage = { testnetPageNum }
12051231 paginate = { testnetPaginate }
12061232 searchValue = { typeof testnetSearchValue === "string" ? testnetSearchValue : "" }
12071233 />
@@ -1294,7 +1320,7 @@ export const FeedList = ({
12941320 lastAddr = { lastAddr }
12951321 firstAddr = { firstAddr }
12961322 addrPerPage = { addrPerPage }
1297- currentPage = { Number ( currentPage ) }
1323+ currentPage = { currentPageNum }
12981324 paginate = { paginate }
12991325 searchValue = { typeof searchValue === "string" ? searchValue : "" }
13001326 />
@@ -1485,7 +1511,7 @@ export const FeedList = ({
14851511 type = "checkbox"
14861512 style = "width:15px;height:15px;display:inline;margin-right:8px;"
14871513 checked = { showExtraDetails }
1488- onChange = { ( ) => setShowExtraDetails ( ( old ) => ! old ) }
1514+ onChange = { ( ) => setShowExtraDetails ( ! showExtraDetails ) }
14891515 />
14901516 Show more details
14911517 </ label >
@@ -1512,10 +1538,7 @@ export const FeedList = ({
15121538 type = "checkbox"
15131539 style = "width:15px;height:15px;display:inline;margin-right:8px;"
15141540 checked = { showOnlySVR }
1515- onChange = { ( ) => {
1516- setShowOnlySVR ( ( old ) => ! old )
1517- setCurrentPage ( "1" )
1518- } }
1541+ onChange = { ( ) => setShowOnlySVR ( ! showOnlySVR ) }
15191542 />
15201543 Show Smart Value Recapture (SVR) feeds
15211544 </ label >
@@ -1540,7 +1563,7 @@ export const FeedList = ({
15401563 lastAddr = { lastAddr }
15411564 firstAddr = { firstAddr }
15421565 addrPerPage = { addrPerPage }
1543- currentPage = { Number ( currentPage ) }
1566+ currentPage = { currentPageNum }
15441567 paginate = { paginate }
15451568 searchValue = { typeof searchValue === "string" ? searchValue : "" }
15461569 />
@@ -1647,7 +1670,7 @@ export const FeedList = ({
16471670 type = "checkbox"
16481671 style = "width:15px;height:15px;display:inline;margin-right:8px;"
16491672 checked = { showExtraDetails }
1650- onChange = { ( ) => setShowExtraDetails ( ( old ) => ! old ) }
1673+ onChange = { ( ) => setShowExtraDetails ( ! showExtraDetails ) }
16511674 />
16521675 Show more details
16531676 </ label >
@@ -1717,7 +1740,7 @@ export const FeedList = ({
17171740 firstAddr = { testnetFirstAddr }
17181741 lastAddr = { testnetLastAddr }
17191742 addrPerPage = { testnetAddrPerPage }
1720- currentPage = { Number ( testnetCurrentPage ) }
1743+ currentPage = { testnetPageNum }
17211744 paginate = { testnetPaginate }
17221745 searchValue = { typeof testnetSearchValue === "string" ? testnetSearchValue : "" }
17231746 />
0 commit comments