Skip to content

Commit cb31580

Browse files
Fix mobile track artist centering
1 parent 84dddd5 commit cb31580

9 files changed

Lines changed: 193 additions & 29 deletions

File tree

packages/mobile/src/components/lineup-tile/LineupTileMetadata.tsx

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ import { useSelector } from 'react-redux'
55

66
import { IconVolumeLevel2 } from '@audius/harmony-native'
77
import { Text, FadeInView } from 'app/components/core'
8-
import { UserLink } from 'app/components/user-link'
8+
import { UserBadges } from 'app/components/user-badges'
99
import { useNavigation } from 'app/hooks/useNavigation'
1010
import { makeStyles } from 'app/styles'
1111
import type { GestureResponderHandler } from 'app/types/gesture'
1212
import { useThemeColors } from 'app/utils/theme'
1313

1414
import { LineupTileArt } from './LineupTileArt'
1515
import { LineupTileTopRight } from './LineupTileTopRight'
16+
import type { ArtistNameCollaborator } from './artistNames'
17+
import { getTrackArtistNames } from './artistNames'
1618
import { useStyles as useTileStyles } from './styles'
1719
import type { RenderImage } from './types'
1820

@@ -27,6 +29,16 @@ const useStyles = makeStyles(({ palette }) => ({
2729
playingIndicator: {
2830
marginLeft: 8
2931
},
32+
artistText: {
33+
flexShrink: 1,
34+
minWidth: 0
35+
},
36+
artistBadges: {
37+
flexDirection: 'row',
38+
alignItems: 'center',
39+
flexShrink: 0,
40+
gap: 4
41+
},
3042
coSignLabel: {
3143
position: 'absolute',
3244
bottom: -3,
@@ -45,6 +57,8 @@ type Props = {
4557
renderImage: RenderImage
4658
title: string
4759
userId: ID
60+
userName: string
61+
collaborators?: ArtistNameCollaborator[] | null
4862
isPlayingUid: boolean
4963
type: 'track' | 'playlist' | 'album'
5064
trackId: ID
@@ -59,6 +73,8 @@ export const LineupTileMetadata = ({
5973
renderImage,
6074
title,
6175
userId,
76+
userName,
77+
collaborators,
6278
isPlayingUid,
6379
type,
6480
trackId,
@@ -72,6 +88,7 @@ export const LineupTileMetadata = ({
7288
const navigation = useNavigation()
7389

7490
const isActive = isPlayingUid
91+
const artistNames = getTrackArtistNames(userName, collaborators)
7592

7693
const isPlaying = useSelector((state) => {
7794
return getPlaying(state) && isActive
@@ -139,14 +156,24 @@ export const LineupTileMetadata = ({
139156
onPressIn={onPressWithPropagationBlock}
140157
onPress={handlePressArtist}
141158
activeOpacity={0.7}
142-
style={{ alignSelf: 'flex-start' }}
159+
style={tileStyles.artist}
143160
>
144-
<View pointerEvents='none'>
145-
<UserLink
146-
variant={isActive ? 'active' : 'default'}
147-
textVariant='body'
148-
userId={userId}
149-
/>
161+
<Text
162+
color={isActive ? 'primary' : 'neutral'}
163+
numberOfLines={1}
164+
ellipsizeMode='tail'
165+
style={styles.artistText}
166+
>
167+
{artistNames}
168+
</Text>
169+
<View style={styles.artistBadges}>
170+
<UserBadges userId={userId} />
171+
{collaborators?.map((collaborator) => (
172+
<UserBadges
173+
key={collaborator.user_id}
174+
userId={collaborator.user_id}
175+
/>
176+
))}
150177
</View>
151178
</TouchableOpacity>
152179
</FadeInView>

packages/mobile/src/components/lineup-tile/TrackTile.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const TrackTileComponent = (props: TrackTileProps) => {
8585
title: track.title,
8686
track_id: track.track_id,
8787
genre: track.genre,
88+
collaborators: track.collaborators,
8889
stream_conditions: track.stream_conditions,
8990

9091
ddex_app: track.ddex_app,
@@ -100,6 +101,7 @@ const TrackTileComponent = (props: TrackTileProps) => {
100101
const { data: user } = useUser(track?.owner_id, {
101102
select: (user) => ({
102103
artist_pick_track_id: user.artist_pick_track_id,
104+
name: user.name,
103105
user_id: user.user_id,
104106
is_deactivated: user.is_deactivated
105107
})
@@ -309,6 +311,8 @@ const TrackTileComponent = (props: TrackTileProps) => {
309311
onPressWithPropagationBlock={handlePressWithPropagationBlock}
310312
title={track.title}
311313
userId={user.user_id}
314+
userName={user.name}
315+
collaborators={track.collaborators}
312316
isPlayingUid={isPlayingUid}
313317
type='track'
314318
trackId={track.track_id}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { getTrackArtistNames } from './artistNames'
2+
3+
describe('getTrackArtistNames', () => {
4+
it('combines owner and collaborator names', () => {
5+
expect(
6+
getTrackArtistNames('ray61626b', [
7+
{ user_id: 2, name: 'dj g8r' },
8+
{ user_id: 3, name: 'tim' }
9+
])
10+
).toBe('ray61626b, dj g8r, tim')
11+
})
12+
13+
it('omits missing collaborator names', () => {
14+
expect(
15+
getTrackArtistNames('ray61626b', [
16+
{ user_id: 2, name: null },
17+
{ user_id: 3, name: undefined },
18+
{ user_id: 4, name: 'dj g8r' }
19+
])
20+
).toBe('ray61626b, dj g8r')
21+
})
22+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { ID } from '@audius/common/models'
2+
3+
export type ArtistNameCollaborator = {
4+
user_id: ID
5+
name?: string | null
6+
}
7+
8+
export const getTrackArtistNames = (
9+
userName: string,
10+
collaborators?: ArtistNameCollaborator[] | null
11+
) => {
12+
const collaboratorNames =
13+
collaborators?.map((collaborator) => collaborator.name).filter(Boolean) ??
14+
[]
15+
16+
return [userName, ...collaboratorNames].join(', ')
17+
}

packages/mobile/src/components/lineup-tile/styles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export const useStyles = makeStyles(({ palette }) => ({
5656
},
5757
artist: {
5858
...flexRowCentered(),
59+
justifyContent: 'flex-start',
60+
gap: spacing(1),
61+
width: '100%',
5962
marginBottom: 'auto',
6063
paddingRight: spacing(10),
6164
minHeight: 20
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { render, screen } from '@testing-library/react-native'
2+
3+
import { TrackArtists } from './TrackArtists'
4+
5+
jest.mock(
6+
'@audius/harmony-native',
7+
() => {
8+
const React = require('react')
9+
const { Text, View } = require('react-native')
10+
11+
return {
12+
Flex: ({ children, style }: any) =>
13+
React.createElement(View, { style }, children),
14+
Text: ({ children }: any) => React.createElement(Text, null, children)
15+
}
16+
},
17+
{ virtual: true }
18+
)
19+
20+
jest.mock('./UserLink', () => {
21+
const React = require('react')
22+
const { Text } = require('react-native')
23+
24+
return {
25+
UserLink: ({
26+
hideBadges,
27+
userId
28+
}: {
29+
hideBadges?: boolean
30+
userId: number
31+
}) =>
32+
React.createElement(
33+
Text,
34+
null,
35+
`${userId}:${hideBadges ? 'badges-hidden' : 'badges-visible'}`
36+
)
37+
}
38+
})
39+
40+
jest.mock('../user-badges', () => {
41+
const React = require('react')
42+
const { Text } = require('react-native')
43+
44+
return {
45+
UserBadges: ({ userId }: { userId: number }) =>
46+
React.createElement(Text, null, `badges:${userId}`)
47+
}
48+
})
49+
50+
describe('TrackArtists', () => {
51+
it('centers artist names and renders badges for each artist', () => {
52+
render(<TrackArtists userId={1} collaborators={[{ user_id: 2 }]} />)
53+
54+
expect(screen.getByText('1:badges-hidden')).toBeOnTheScreen()
55+
expect(screen.getByText('2:badges-hidden')).toBeOnTheScreen()
56+
expect(screen.queryByText(/badges-visible/)).toBeNull()
57+
expect(screen.getByText('badges:1')).toBeOnTheScreen()
58+
expect(screen.getByText('badges:2')).toBeOnTheScreen()
59+
})
60+
})

packages/mobile/src/components/user-link/TrackArtists.tsx

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import type { StyleProp, ViewStyle } from 'react-native'
77

88
import { Flex, Text } from '@audius/harmony-native'
99

10+
import { UserBadges } from '../user-badges'
11+
1012
import { UserLink } from './UserLink'
1113

1214
type Collaborator = { user_id: ID }
@@ -45,41 +47,67 @@ export const CollaboratorLinks = ({
4547
)
4648
}
4749

48-
type TrackArtistsProps = Omit<ComponentProps<typeof UserLink>, 'style'> & {
50+
type TrackArtistsProps = Omit<
51+
ComponentProps<typeof UserLink>,
52+
'style' | 'hideBadges'
53+
> & {
4954
/** Accepted collaborator artists embedded on the track. */
5055
collaborators?: Collaborator[] | null
5156
style?: StyleProp<ViewStyle>
5257
}
5358

5459
/**
55-
* A track's artist line for mobile: the owner `<UserLink>` plus accepted
56-
* collaborators. With the flag off (or no collaborators) it renders just the
57-
* owner — a safe drop-in for an existing owner `<UserLink>`.
60+
* A track's centered artist line for mobile: the owner `<UserLink>` plus
61+
* accepted collaborators. User badges render on their own centered row so the
62+
* names stay visually centered under the title.
5863
*/
5964
export const TrackArtists = ({
6065
collaborators,
6166
style,
6267
...userLinkProps
6368
}: TrackArtistsProps) => {
6469
return (
65-
<Flex
66-
row
67-
alignItems='center'
68-
justifyContent='center'
69-
w='100%'
70-
style={[styles.artistRow, style]}
71-
>
72-
<UserLink {...userLinkProps} style={styles.artistLink} />
73-
<CollaboratorLinks collaborators={collaborators} {...userLinkProps} />
70+
<Flex alignItems='center' w='100%' style={[styles.artistColumn, style]}>
71+
<Flex row alignItems='center' justifyContent='center' w='100%'>
72+
<UserLink {...userLinkProps} hideBadges style={styles.artistLink} />
73+
<CollaboratorLinks
74+
collaborators={collaborators}
75+
{...userLinkProps}
76+
hideBadges
77+
/>
78+
</Flex>
79+
<Flex
80+
row
81+
alignItems='center'
82+
justifyContent='center'
83+
style={styles.badges}
84+
>
85+
<UserBadges
86+
userId={userLinkProps.userId}
87+
badgeSize={userLinkProps.badgeSize}
88+
hideFanClubBadge={userLinkProps.hideFanClubBadge}
89+
/>
90+
{collaborators?.map((collaborator) => (
91+
<UserBadges
92+
key={collaborator.user_id}
93+
userId={collaborator.user_id}
94+
badgeSize={userLinkProps.badgeSize}
95+
hideFanClubBadge={userLinkProps.hideFanClubBadge}
96+
/>
97+
))}
98+
</Flex>
7499
</Flex>
75100
)
76101
}
77102

78103
const styles = StyleSheet.create({
79-
artistRow: {
104+
artistColumn: {
80105
flexShrink: 1,
81106
overflow: 'hidden'
82107
},
108+
badges: {
109+
gap: 4
110+
},
83111
artistLink: {
84112
flexShrink: 1,
85113
minWidth: 0

packages/mobile/src/components/user-link/UserLink.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type UserLinkProps = Omit<TextLinkProps<ParamList>, 'to' | 'children'> & {
2424
badgeSize?: IconSize
2525
textLinkStyle?: StyleProp<TextStyle>
2626
disabled?: boolean
27+
hideBadges?: boolean
2728
hideFanClubBadge?: boolean
2829
mint?: string
2930
}
@@ -35,6 +36,7 @@ export const UserLink = (props: UserLinkProps) => {
3536
style,
3637
textLinkStyle,
3738
disabled,
39+
hideBadges,
3840
hideFanClubBadge,
3941
mint,
4042
...other
@@ -89,12 +91,14 @@ export const UserLink = (props: UserLinkProps) => {
8991
>
9092
{userName}
9193
</TextLink>
92-
<UserBadges
93-
userId={userId}
94-
badgeSize={badgeSize}
95-
mint={mint}
96-
hideFanClubBadge={hideFanClubBadge}
97-
/>
94+
{hideBadges ? null : (
95+
<UserBadges
96+
userId={userId}
97+
badgeSize={badgeSize}
98+
mint={mint}
99+
hideFanClubBadge={hideFanClubBadge}
100+
/>
101+
)}
98102
</AnimatedFlex>
99103
</Pressable>
100104
)

packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,6 @@ export const TrackScreenDetailsTile = ({
630630
userId={user.user_id}
631631
collaborators={collaborators}
632632
size='l'
633-
badgeSize='s'
634633
style={{ justifyContent: 'center' }}
635634
/>
636635
) : null}

0 commit comments

Comments
 (0)