diff --git a/packages/mobile/src/screens/explore-screen/components/TrendingGenres.tsx b/packages/mobile/src/screens/explore-screen/components/TrendingGenres.tsx index e047cba5f1e..ddcb4ebc08c 100644 --- a/packages/mobile/src/screens/explore-screen/components/TrendingGenres.tsx +++ b/packages/mobile/src/screens/explore-screen/components/TrendingGenres.tsx @@ -4,7 +4,6 @@ import { usePopularGenres } from '@audius/common/api' import { exploreMessages as messages } from '@audius/common/messages' import { trendingPageActions } from '@audius/common/store' import { toTrendingGenreValue } from '@audius/common/utils' -import { ScrollView } from 'react-native' import { useDispatch } from 'react-redux' import { Flex, SelectablePill, Skeleton } from '@audius/harmony-native' @@ -53,33 +52,21 @@ export const TrendingGenres = () => { return ( - - - {showSkeleton - ? Array.from({ length: SKELETON_COUNT }).map((_, i) => ( - - )) - : topGenres.map((genre) => ( - handleGenrePress(genre.value)} - /> - ))} - - + + {showSkeleton + ? Array.from({ length: SKELETON_COUNT }).map((_, i) => ( + + )) + : topGenres.map((genre) => ( + handleGenrePress(genre.value)} + /> + ))} + ) diff --git a/packages/web/src/pages/trending-page/components/desktop/TrendingPageContent.tsx b/packages/web/src/pages/trending-page/components/desktop/TrendingPageContent.tsx index b36945235fe..dd93e2df529 100644 --- a/packages/web/src/pages/trending-page/components/desktop/TrendingPageContent.tsx +++ b/packages/web/src/pages/trending-page/components/desktop/TrendingPageContent.tsx @@ -159,12 +159,20 @@ const TrendingPageContent = ({ containerRef }: TrendingPageContentProps) => { [dispatch] ) + // A `?genre=` in the URL is the user's intent for this visit and must win + // over whatever genre an earlier trending visit left behind in redux — + // otherwise arriving from Explore with `?genre=Jazz` silently keeps showing + // the previous genre. With no genre in the URL, redux still wins so the last + // filter is remembered (and gets written back to the URL by the sync effect + // below). + const genreFromUrlOnMount = useRef(parseUrlParams().genre) + useEffect(() => { const { genre, timeRange } = parseUrlParams() - if (trendingGenre) { - updateGenreUrlParam(trendingGenre, replaceRouteCallback) - } else if (isValidGenre(genre)) { - dispatch(trendingPageActions.setTrendingGenre(genre as any)) + if (isValidGenre(genre)) { + dispatch( + trendingPageActions.setTrendingGenre(toTrendingGenreValue(genre)) + ) } if (isValidTimeRange(timeRange)) { dispatch(trendingPageActions.setTrendingTimeRange(timeRange as TimeRange)) @@ -177,6 +185,13 @@ const TrendingPageContent = ({ containerRef }: TrendingPageContentProps) => { }, [trendingTimeRange, replaceRouteCallback]) useEffect(() => { + // Skip the first run when the URL already carries the genre: redux has not + // caught up with the effect above yet, and writing the stale value back + // would clobber the incoming param. + if (isValidGenre(genreFromUrlOnMount.current)) { + genreFromUrlOnMount.current = null + return + } updateGenreUrlParam(trendingGenre, replaceRouteCallback) }, [trendingGenre, replaceRouteCallback]) diff --git a/packages/web/src/pages/trending-page/components/mobile/TrendingPageContent.tsx b/packages/web/src/pages/trending-page/components/mobile/TrendingPageContent.tsx index 50c14d39ad3..39f7e2197f3 100644 --- a/packages/web/src/pages/trending-page/components/mobile/TrendingPageContent.tsx +++ b/packages/web/src/pages/trending-page/components/mobile/TrendingPageContent.tsx @@ -1,4 +1,11 @@ -import { useCallback, useContext, useEffect, useMemo, useState } from 'react' +import { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState +} from 'react' import { getTrendingQueryKey, @@ -13,7 +20,11 @@ import { trendingPageActions, trendingPageSelectors } from '@audius/common/store' -import { route, toTrendingGenre } from '@audius/common/utils' +import { + route, + toTrendingGenre, + toTrendingGenreValue +} from '@audius/common/utils' import { FilterButton, Flex, @@ -136,12 +147,20 @@ const TrendingPageMobileContent = ({ dispatch(pushRoute(TRENDING_GENRES_ROUTE)) }, [dispatch]) + // A `?genre=` in the URL is the user's intent for this visit and must win + // over whatever genre an earlier trending visit left behind in redux — + // otherwise arriving from Explore with `?genre=Jazz` silently keeps showing + // the previous genre. With no genre in the URL, redux still wins so the last + // filter is remembered (and gets written back to the URL by the sync effect + // below). + const genreFromUrlOnMount = useRef(parseUrlParams().genre) + useEffect(() => { const { genre, timeRange } = parseUrlParams() - if (trendingGenre) { - updateGenreUrlParam(trendingGenre, replaceRouteCallback) - } else if (isValidGenre(genre)) { - dispatch(trendingPageActions.setTrendingGenre(genre as any)) + if (isValidGenre(genre)) { + dispatch( + trendingPageActions.setTrendingGenre(toTrendingGenreValue(genre)) + ) } if (isValidTimeRange(timeRange)) { dispatch(trendingPageActions.setTrendingTimeRange(timeRange as TimeRange)) @@ -152,6 +171,13 @@ const TrendingPageMobileContent = ({ updateTimeRangeUrlParam(trendingTimeRange, replaceRouteCallback) }, [trendingTimeRange, replaceRouteCallback]) useEffect(() => { + // Skip the first run when the URL already carries the genre: redux has not + // caught up with the effect above yet, and writing the stale value back + // would clobber the incoming param. + if (isValidGenre(genreFromUrlOnMount.current)) { + genreFromUrlOnMount.current = null + return + } updateGenreUrlParam(trendingGenre, replaceRouteCallback) }, [trendingGenre, replaceRouteCallback])