Skip to content

Commit 1b1c38a

Browse files
dylanjeffersclaude
andauthored
claude/fix-mobile-profile-tracks-h214W (#14125)
The profile TracksTab called `<Lineup selfLoad />` unconditionally, so the initial fetch could fire before `useProfileUser` had populated `handle`. The profile tracks saga (`getTracks`) short-circuits with `[]` when `handle` is undefined, which flips `hasMore` to false and leaves the tab permanently stuck showing the empty tile. Gate `selfLoad` on `handleLower` so the fetch is deferred until the handle is available, and match the RepostsTab pattern of only showing the empty tile once the lineup has moved past IDLE (or `track_count` is known to be 0). Also: AlbumsTab / PlaylistsTab forwarded `isLoading={isPending}` to `CollectionList` regardless of the known collection count, which rendered skeleton tiles for users with zero albums/playlists before settling on the empty tile. When the count is known to be 0 we can skip the loading state and render the empty tile immediately. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 30cafec commit 1b1c38a

8 files changed

Lines changed: 65 additions & 26 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@audius/mobile": patch
3+
---
4+
5+
Fix mobile profile tracks tab showing "no tracks" regression when visiting an artist's profile, and stop rendering skeleton tiles for the albums/playlists tabs when the user has zero of them.

packages/common/src/api/tan-query/users/useUserAlbums.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,22 @@ export const useUserAlbums = (
8282
enabled: options?.enabled !== false && !!userId
8383
})
8484

85-
const { data: collections } = useCollections(queryRes.data)
85+
const {
86+
data: collections,
87+
isPending: isCollectionsPending,
88+
isLoading: isCollectionsLoading
89+
} = useCollections(queryRes.data)
90+
91+
// The ID query can resolve before the per-collection entity queries do; if
92+
// we only reported `queryRes.isPending` the consumer would see an empty
93+
// `data` array in that gap and flash a "no albums" state.
94+
const hasPendingCollections =
95+
(queryRes.data?.length ?? 0) > 0 && isCollectionsPending
8696

8797
return {
8898
data: collections,
89-
isPending: queryRes.isPending,
90-
isLoading: queryRes.isLoading,
99+
isPending: queryRes.isPending || hasPendingCollections,
100+
isLoading: queryRes.isLoading || (hasPendingCollections && isCollectionsLoading),
91101
hasNextPage: queryRes.hasNextPage,
92102
isFetchingNextPage: queryRes.isFetchingNextPage,
93103
fetchNextPage: queryRes.fetchNextPage

packages/common/src/api/tan-query/users/useUserPlaylists.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,22 @@ export const useUserPlaylists = (
8282
enabled: options?.enabled !== false && !!userId
8383
})
8484

85-
const { data: collections } = useCollections(queryRes.data)
85+
const {
86+
data: collections,
87+
isPending: isCollectionsPending,
88+
isLoading: isCollectionsLoading
89+
} = useCollections(queryRes.data)
90+
91+
// The ID query can resolve before the per-collection entity queries do; if
92+
// we only reported `queryRes.isPending` the consumer would see an empty
93+
// `data` array in that gap and flash a "no playlists" state.
94+
const hasPendingCollections =
95+
(queryRes.data?.length ?? 0) > 0 && isCollectionsPending
8696

8797
return {
8898
data: collections,
89-
isPending: queryRes.isPending,
90-
isLoading: queryRes.isLoading,
99+
isPending: queryRes.isPending || hasPendingCollections,
100+
isLoading: queryRes.isLoading || (hasPendingCollections && isCollectionsLoading),
91101
hasNextPage: queryRes.hasNextPage,
92102
isFetchingNextPage: queryRes.isFetchingNextPage,
93103
fetchNextPage: queryRes.fetchNextPage

packages/mobile/src/screens/profile-screen/ProfileCoverPhoto.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const AnimatedBlurView = Animated.createAnimatedComponent(BlurView)
1717

1818
// Height below the status bar. The avatar is 80px tall and is positioned so
1919
// it extends ~32px below the cover photo, matching the Figma spec.
20-
const COVER_PHOTO_CONTENT_HEIGHT = 96
20+
export const COVER_PHOTO_CONTENT_HEIGHT = 96
2121

2222
const useStyles = makeStyles(({ spacing }) => ({
2323
darkOverlay: {

packages/mobile/src/screens/profile-screen/ProfileScreenSkeleton.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,21 @@ import { useMemo } from 'react'
22

33
import { times, random } from 'lodash'
44
import { View } from 'react-native'
5+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
56

67
import { Flex } from '@audius/harmony-native'
78
import Skeleton, { StaticSkeleton } from 'app/components/skeleton'
89
import { makeStyles } from 'app/styles'
910

11+
import { COVER_PHOTO_CONTENT_HEIGHT } from './ProfileCoverPhoto'
12+
1013
const useStyles = makeStyles(({ palette, spacing }) => ({
1114
root: {
1215
marginBottom: 40
1316
},
14-
coverPhoto: {
15-
height: 96
16-
},
1717
profilePicture: {
1818
position: 'absolute',
19-
top: 52,
20-
left: 12,
19+
left: spacing(4),
2120
zIndex: 101,
2221

2322
height: 82,
@@ -147,12 +146,14 @@ export const ExpandableSectionSkeleton = () => {
147146

148147
export const ProfileHeaderSkeleton = () => {
149148
const styles = useStyles()
149+
const insets = useSafeAreaInsets()
150150
const statSkeleton = <StaticSkeleton style={styles.stat} />
151+
const coverPhotoHeight = insets.top + COVER_PHOTO_CONTENT_HEIGHT
151152

152153
return (
153154
<Flex backgroundColor='white'>
154-
<StaticSkeleton height={96} />
155-
<Skeleton style={styles.profilePicture} />
155+
<StaticSkeleton height={coverPhotoHeight} />
156+
<Skeleton style={[styles.profilePicture, { top: insets.top + 48 }]} />
156157
<Flex p='l' gap='s' backgroundColor='white'>
157158
<Flex row justifyContent='space-between' backgroundColor='white'>
158159
<Flex mt='3xl'>

packages/mobile/src/screens/profile-screen/ProfileTabs/AlbumsTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export const AlbumsTab = () => {
5050
disableTopTabScroll
5151
showsVerticalScrollIndicator={false}
5252
totalCount={album_count}
53-
isLoading={isPending}
53+
isLoading={album_count > 0 && isPending}
5454
onEndReached={handleEndReached}
5555
isLoadingMore={isFetchingNextPage}
5656
/>

packages/mobile/src/screens/profile-screen/ProfileTabs/PlaylistsTab.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const PlaylistsTab = () => {
5353
totalCount={playlist_count}
5454
showCreateCollectionTile={isOwner}
5555
createPlaylistSource={CreatePlaylistSource.PROFILE_PAGE}
56-
isLoading={isPending}
56+
isLoading={playlist_count > 0 && isPending}
5757
onEndReached={handleEndReached}
5858
isLoadingMore={isFetchingNextPage}
5959
/>

packages/mobile/src/screens/profile-screen/ProfileTabs/TracksTab.tsx

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useMemo } from 'react'
22

33
import { useProfileUser } from '@audius/common/api'
44
import { useProxySelector } from '@audius/common/hooks'
5+
import { Status } from '@audius/common/models'
56
import {
67
profilePageTracksLineupActions as tracksActions,
78
profilePageSelectors
@@ -14,14 +15,19 @@ import { EmptyProfileTile } from '../EmptyProfileTile'
1415
const { getProfileTracksLineup } = profilePageSelectors
1516

1617
export const TracksTab = () => {
17-
const { handle, user_id, artist_pick_track_id } =
18-
useProfileUser({
19-
select: (user) => ({
20-
handle: user.handle,
21-
user_id: user.user_id,
22-
artist_pick_track_id: user.artist_pick_track_id
23-
})
24-
}).user ?? {}
18+
const {
19+
handle,
20+
user_id,
21+
track_count = 0,
22+
artist_pick_track_id
23+
} = useProfileUser({
24+
select: (user) => ({
25+
handle: user.handle,
26+
user_id: user.user_id,
27+
track_count: user.track_count,
28+
artist_pick_track_id: user.artist_pick_track_id
29+
})
30+
}).user ?? {}
2531

2632
const handleLower = handle?.toLowerCase()
2733

@@ -33,9 +39,14 @@ export const TracksTab = () => {
3339
const fetchPayload = useMemo(() => ({ userId: user_id }), [user_id])
3440
const extraFetchOptions = useMemo(() => ({ handle }), [handle])
3541

42+
// `selfLoad` fired with an undefined handle makes the saga return [],
43+
// which poisons `hasMore` and leaves the tab stuck on the empty state.
44+
const canSelfLoad = !!handleLower
45+
const canShowEmptyTile = track_count === 0 || lineup.status !== Status.IDLE
46+
3647
return (
3748
<Lineup
38-
selfLoad
49+
selfLoad={canSelfLoad}
3950
pullToRefresh
4051
leadingElementId={artist_pick_track_id}
4152
showArtistPick={true}
@@ -44,7 +55,9 @@ export const TracksTab = () => {
4455
fetchPayload={fetchPayload}
4556
extraFetchOptions={extraFetchOptions}
4657
disableTopTabScroll
47-
LineupEmptyComponent={<EmptyProfileTile tab='tracks' />}
58+
LineupEmptyComponent={
59+
canShowEmptyTile ? <EmptyProfileTile tab='tracks' /> : undefined
60+
}
4861
showsVerticalScrollIndicator={false}
4962
/>
5063
)

0 commit comments

Comments
 (0)