diff --git a/packages/harmony/src/components/track-row/TrackRow.tsx b/packages/harmony/src/components/track-row/TrackRow.tsx index e7a7c449b0b..30d71033363 100644 --- a/packages/harmony/src/components/track-row/TrackRow.tsx +++ b/packages/harmony/src/components/track-row/TrackRow.tsx @@ -84,6 +84,7 @@ export const TrackRow = forwardRef( onTogglePlay, onToggleFavorite, menu, + context, playLabel = isPlaying ? 'Pause' : 'Play', favoriteLabel = isFavorited ? 'Unfavorite' : 'Favorite', className, @@ -123,6 +124,16 @@ export const TrackRow = forwardRef( [onToggleFavorite] ) + // Title column starts after the rank and artwork cells and two gaps. + const contextCss = css({ + display: 'flex', + alignItems: 'center', + gap: spacing.xs, + height: 16, + marginTop: spacing.xs, + paddingLeft: spacing.s + RANK_SIZE + ART_SIZE + spacing.m * 2 + }) + const rowCss = css({ display: 'grid', gridTemplateColumns: `${RANK_SIZE}px ${ART_SIZE}px minmax(0, 1fr) auto 32px 48px 32px`, @@ -189,14 +200,14 @@ export const TrackRow = forwardRef( const showEqualizer = isActive && isPlaying const PlayIcon = showEqualizer ? IconPause : IconPlay - return ( + const row = (
{rank != null && !showEqualizer ? ( @@ -297,5 +308,16 @@ export const TrackRow = forwardRef(
) + + if (!context) return row + + return ( +
+ + {context} + + {row} +
+ ) } ) diff --git a/packages/harmony/src/components/track-row/types.ts b/packages/harmony/src/components/track-row/types.ts index 9669b278ff3..722bc2aebac 100644 --- a/packages/harmony/src/components/track-row/types.ts +++ b/packages/harmony/src/components/track-row/types.ts @@ -32,6 +32,11 @@ export type TrackRowProps = { onToggleFavorite?: () => void /** Overflow menu trigger. Hidden until hover or focus. */ menu?: ReactNode + /** + * One short line above the row, aligned with the title column, for + * context such as "Artist Pick". Adds 16px to the row's height. + */ + context?: ReactNode /** Accessible name for the play button. */ playLabel?: string /** Accessible name for the favorite button. */ diff --git a/packages/web/src/components/lineup/Lineup.module.css b/packages/web/src/components/lineup/Lineup.module.css index cec044eaf31..edeef9a84f4 100644 --- a/packages/web/src/components/lineup/Lineup.module.css +++ b/packages/web/src/components/lineup/Lineup.module.css @@ -28,3 +28,8 @@ margin: 0px !important; margin-bottom: 24px !important; } + +/* Collection cards mixed into a row lineup (profile Reposts) */ +.collectionAmongRows { + margin-top: var(--harmony-spacing-m); +} diff --git a/packages/web/src/components/lineup/TrackLineup.tsx b/packages/web/src/components/lineup/TrackLineup.tsx index c743ad9fe3f..9f44b9b15ce 100644 --- a/packages/web/src/components/lineup/TrackLineup.tsx +++ b/packages/web/src/components/lineup/TrackLineup.tsx @@ -287,7 +287,10 @@ export const TrackLineup = ({ } else if (variant === LineupVariant.GRID) { tileSize = TrackTileSize.SMALL statSize = 'small' - containerClassName = styles.searchTrackTileContainer + // Rows are full width; the two-column grid is for small tiles only. + containerClassName = isRowLayout + ? undefined + : styles.searchTrackTileContainer } else if (variant === LineupVariant.CONDENSED) { tileSize = TrackTileSize.SMALL } @@ -436,7 +439,9 @@ export const TrackLineup = ({ playTrack: handlePlayCollectionTrack, pauseTrack, size: tileSize, - containerClassName, + containerClassName: cn(containerClassName, { + [styles.collectionAmongRows]: isRowLayout + }), id: item.id, isLoading: false, hasLoaded: () => {}, @@ -466,7 +471,8 @@ export const TrackLineup = ({ onClickTile, showArtistPick, TrackTile, - CollectionTile + CollectionTile, + isRowLayout ]) const isInitialLoad = isPending && tiles.length === 0 diff --git a/packages/web/src/components/track/desktop/TrackRowTile.tsx b/packages/web/src/components/track/desktop/TrackRowTile.tsx index 77a5f3e308d..247075bd252 100644 --- a/packages/web/src/components/track/desktop/TrackRowTile.tsx +++ b/packages/web/src/components/track/desktop/TrackRowTile.tsx @@ -21,6 +21,7 @@ import { Artwork, IconButton, IconKebabHorizontal, + IconPin, Text, TrackRow } from '@audius/harmony' @@ -62,6 +63,8 @@ export type TrackRowTileProps = { isFeed?: boolean onClick?: (trackId: ID) => void containerClassName?: string + /** Mark the owner's pinned track with an "Artist Pick" line. */ + showArtistPick?: boolean } /** @@ -79,7 +82,8 @@ export const TrackRowTile = ({ hasLoaded, isFeed = false, onClick, - containerClassName + containerClassName, + showArtistPick = false }: TrackRowTileProps) => { const dispatch = useDispatch() const { data: currentUserId } = useCurrentUserId() @@ -130,6 +134,8 @@ export const TrackRowTile = ({ const trackPosition = useSelector((state: CommonState) => getTrackPosition(state, { trackId, userId: currentUserId }) ) + const isArtistPick = + showArtistPick && partialUser?.artist_pick_track_id === trackId const { isFetchingNFTAccess, hasStreamAccess, isPreviewable } = useGatedContentAccess(trackWithFallback) @@ -319,6 +325,14 @@ export const TrackRowTile = ({ } menu={overflowMenu} playLabel={playLabel} + context={ + isArtistPick ? ( + <> + + {messages.artistPick} + + ) : undefined + } data-testid='track-row' /> ) diff --git a/packages/web/src/pages/profile-page/components/desktop/ProfilePage.tsx b/packages/web/src/pages/profile-page/components/desktop/ProfilePage.tsx index 7655641af96..ac25f22a969 100644 --- a/packages/web/src/pages/profile-page/components/desktop/ProfilePage.tsx +++ b/packages/web/src/pages/profile-page/components/desktop/ProfilePage.tsx @@ -17,8 +17,10 @@ import { getProfileRepostsQueryKey } from '@audius/common/api' import { useMuteUser } from '@audius/common/context' +import { useFeatureFlag } from '@audius/common/hooks' import { commentsMessages } from '@audius/common/messages' import { Status } from '@audius/common/models' +import { FeatureFlags } from '@audius/common/services' import { ProfilePageTabs } from '@audius/common/store' import { route } from '@audius/common/utils' import { @@ -89,6 +91,10 @@ const LeftColumnSpacer = () => ( ) const ProfilePage = ({ containerRef }: ProfilePageProps) => { + const { isEnabled: isTrackRowsEnabled } = useFeatureFlag( + FeatureFlags.TRACK_ROWS + ) + const tileVariant = isTrackRowsEnabled ? 'row' : 'tile' const profileFocusRootRef = useRef(null) usePreventOffscreenFocus(profileFocusRootRef) @@ -303,6 +309,7 @@ const ProfilePage = ({ containerRef }: ProfilePageProps) => { hasNextPage={userRepostsQuery.hasNextPage} loadNextPage={userRepostsQuery.loadNextPage} variant={LineupVariant.CONDENSED} + tileVariant={tileVariant} scrollParent={containerRef?.current ?? null} /> ) @@ -337,6 +344,7 @@ const ProfilePage = ({ containerRef }: ProfilePageProps) => { hasNextPage={artistTracksQuery.hasNextPage} loadNextPage={artistTracksQuery.loadNextPage} variant={LineupVariant.GRID} + tileVariant={tileVariant} leadingElementId={profile.artist_pick_track_id ?? undefined} showArtistPick scrollParent={containerRef?.current ?? null} @@ -382,6 +390,7 @@ const ProfilePage = ({ containerRef }: ProfilePageProps) => { hasNextPage={userRepostsQuery.hasNextPage} loadNextPage={userRepostsQuery.loadNextPage} variant={LineupVariant.CONDENSED} + tileVariant={tileVariant} maxEntries={profile.repost_count} scrollParent={containerRef?.current ?? null} />