From c1a54886ebd56ab7e015f6bc6a32d3c93cec35e0 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 15 Oct 2024 15:47:34 +0200 Subject: [PATCH] [C-5161 C-5151] Improve desktop track page responsiveness (#10040) --- .../FeatureFlagOverrideScreen.tsx | 1 + .../FeatureFlagOverrideModal.tsx | 1 + packages/web/src/components/lineup/Lineup.tsx | 12 ++-- .../src/components/lineup/LineupProvider.tsx | 2 +- packages/web/src/components/lineup/types.ts | 5 +- .../components/track/mobile/BottomButtons.tsx | 24 +++++--- .../track/mobile/ConnectedTrackTile.tsx | 56 ++++++++++++++++++- .../track/mobile/TrackTile.module.css | 1 - .../src/components/track/mobile/TrackTile.tsx | 7 ++- .../components/desktop/AiPage.tsx | 4 +- .../components/mobile/AiPage.tsx | 4 +- .../components/desktop/DeletedPage.tsx | 4 +- .../components/mobile/DeletedPage.tsx | 4 +- .../components/desktop/RemixesPage.tsx | 4 +- .../components/mobile/RemixesPage.tsx | 4 +- .../search-results/TrackResults.tsx | 4 +- .../track-page/components/TrackRemixes.tsx | 19 ++++--- .../components/desktop/TrackPage.tsx | 21 ++++--- .../track-page/components/useTrackPageSize.ts | 7 +++ 19 files changed, 132 insertions(+), 52 deletions(-) create mode 100644 packages/web/src/pages/track-page/components/useTrackPageSize.ts diff --git a/packages/mobile/src/screens/feature-flag-override-screen/FeatureFlagOverrideScreen.tsx b/packages/mobile/src/screens/feature-flag-override-screen/FeatureFlagOverrideScreen.tsx index ef7ac4407c1..da54269540d 100644 --- a/packages/mobile/src/screens/feature-flag-override-screen/FeatureFlagOverrideScreen.tsx +++ b/packages/mobile/src/screens/feature-flag-override-screen/FeatureFlagOverrideScreen.tsx @@ -127,6 +127,7 @@ const FeatureFlagScreen = () => { diff --git a/packages/web/src/components/feature-flag-override-modal/FeatureFlagOverrideModal.tsx b/packages/web/src/components/feature-flag-override-modal/FeatureFlagOverrideModal.tsx index 7b3cd2f9c8e..39000f9d1a3 100644 --- a/packages/web/src/components/feature-flag-override-modal/FeatureFlagOverrideModal.tsx +++ b/packages/web/src/components/feature-flag-override-modal/FeatureFlagOverrideModal.tsx @@ -127,6 +127,7 @@ export const FeatureFlagOverrideModal = () => { ) : null} setFilter(e.target.value)} value={filter} diff --git a/packages/web/src/components/lineup/Lineup.tsx b/packages/web/src/components/lineup/Lineup.tsx index d15261e6b57..e330070a6f0 100644 --- a/packages/web/src/components/lineup/Lineup.tsx +++ b/packages/web/src/components/lineup/Lineup.tsx @@ -9,22 +9,22 @@ import { useIsMobile } from 'hooks/useIsMobile' import LineupProvider, { LineupProviderProps } from './LineupProvider' import { LineupVariant } from './types' -export type LineupWithoutTile = Omit< +export type LineupProps = Omit< LineupProviderProps, 'trackTile' | 'skeletonTile' | 'playlistTile' > -type LineupProps = LineupWithoutTile & { useSmallTiles?: boolean } /** A lineup renders a LineupProvider, injecting different tiles * depending on the client state. */ const Lineup = (props: LineupProps) => { - const { useSmallTiles } = props + const { variant } = props const isMobile = useIsMobile() const trackTile = - isMobile || useSmallTiles ? MobileTrackTile : DesktopTrackTile - const playlistTile = - isMobile || useSmallTiles ? MobilePlaylistTile : DesktopPlaylistTile + isMobile || variant === LineupVariant.SECTION + ? MobileTrackTile + : DesktopTrackTile + const playlistTile = isMobile ? MobilePlaylistTile : DesktopPlaylistTile return ( { if (variant === LineupVariant.MAIN || variant === LineupVariant.PLAYLIST) { tileSize = TrackTileSize.LARGE lineupStyle = styles.main - } else if (variant === LineupVariant.SECTION) { + } else if (variant === LineupVariant.GRID) { tileSize = TrackTileSize.SMALL lineupStyle = styles.section statSize = 'small' diff --git a/packages/web/src/components/lineup/types.ts b/packages/web/src/components/lineup/types.ts index 59462b0228c..2b8760586ac 100644 --- a/packages/web/src/components/lineup/types.ts +++ b/packages/web/src/components/lineup/types.ts @@ -1,6 +1,7 @@ export enum LineupVariant { MAIN = 'main', - SECTION = 'section', + GRID = 'grid', CONDENSED = 'condensed', - PLAYLIST = 'playlist' + PLAYLIST = 'playlist', + SECTION = 'section' } diff --git a/packages/web/src/components/track/mobile/BottomButtons.tsx b/packages/web/src/components/track/mobile/BottomButtons.tsx index de7e04032fa..ade8e6a56bc 100644 --- a/packages/web/src/components/track/mobile/BottomButtons.tsx +++ b/packages/web/src/components/track/mobile/BottomButtons.tsx @@ -12,6 +12,7 @@ import FavoriteButton from 'components/alt-button/FavoriteButton' import MoreButton from 'components/alt-button/MoreButton' import RepostButton from 'components/alt-button/RepostButton' import ShareButton from 'components/alt-button/ShareButton' +import { useIsMobile } from 'hooks/useIsMobile' import { useIsUSDCEnabled } from 'hooks/useIsUSDCEnabled' import { GatedConditionsPill } from '../GatedConditionsPill' @@ -39,9 +40,11 @@ type BottomButtonsProps = { isMatrixMode: boolean contentId: number contentType: string + renderOverflow?: () => React.ReactNode } const BottomButtons = (props: BottomButtonsProps) => { + const isMobile = useIsMobile() const isUSDCEnabled = useIsUSDCEnabled() const isUSDCPurchase = isUSDCEnabled && isContentUSDCPurchaseGated(props.streamConditions) @@ -51,15 +54,18 @@ const BottomButtons = (props: BottomButtonsProps) => { return null } - const moreButton = ( - - ) + const moreButton = + !isMobile && props.renderOverflow ? ( + props.renderOverflow() + ) : ( + + ) // Stream conditions without access if (!props.isLoading && props.streamConditions && !props.hasStreamAccess) { diff --git a/packages/web/src/components/track/mobile/ConnectedTrackTile.tsx b/packages/web/src/components/track/mobile/ConnectedTrackTile.tsx index 6c60a91b5b8..edf252d1b9f 100644 --- a/packages/web/src/components/track/mobile/ConnectedTrackTile.tsx +++ b/packages/web/src/components/track/mobile/ConnectedTrackTile.tsx @@ -25,10 +25,13 @@ import { playerSelectors } from '@audius/common/store' import { Genre, route } from '@audius/common/utils' +import { Box, IconButton, IconKebabHorizontal } from '@audius/harmony' import { push as pushRoute } from 'connected-react-router' import { connect } from 'react-redux' import { Dispatch } from 'redux' +import Menu from 'components/menu/Menu' +import { OwnProps as TrackMenuProps } from 'components/menu/TrackMenu' import { TrackTileProps } from 'components/track/types' import { useFlag } from 'hooks/useRemoteConfig' import { AppState } from 'store/types' @@ -144,6 +147,7 @@ const ConnectedTrackTile = ({ getUserWithFallback(user) const isOwner = user_id === currentUserId + const isArtistPick = showArtistPick && artist_pick_track_id === track_id const { isEnabled: isNewPodcastControlsEnabled } = useFlag( FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED, @@ -195,6 +199,55 @@ const ConnectedTrackTile = ({ goToRoute(track?.permalink + '?showComments=true') } + // We wanted to use mobile track tile on desktop, which means shimming in the desktop overflow + // menu whenever isMobile is false. + const renderOverflowMenu = () => { + const menu: Omit = { + extraMenuItems: [], + handle, + includeAddToPlaylist: !is_unlisted || isOwner, + includeAddToAlbum: isOwner && !ddexApp, + includeArtistPick: isOwner, + includeEdit: isOwner, + ddexApp: track?.ddex_app, + includeEmbed: !(is_unlisted || isStreamGated), + includeFavorite: hasStreamAccess, + includeRepost: hasStreamAccess, + includeShare: true, + includeTrackPage: true, + isArtistPick, + isDeleted: is_delete || user?.is_deactivated, + isFavorited: has_current_user_saved, + isOwner, + isReposted: has_current_user_reposted, + isUnlisted: is_unlisted, + trackId: track_id, + trackTitle: title, + genre: genre as Genre, + trackPermalink: permalink, + type: 'track' + } + + return ( + + {(ref, triggerPopup) => ( + + { + e.stopPropagation() + triggerPopup() + }} + aria-label='More' + color='subdued' + /> + + )} + + ) + } + const onClickOverflow = (trackId: ID) => { const isLongFormContent = genre === Genre.PODCASTS || genre === Genre.AUDIOBOOKS @@ -260,7 +313,7 @@ const ConnectedTrackTile = ({ fieldVisibility={field_visibility} coSign={_co_sign} // Artist Pick - isArtistPick={showArtistPick && artist_pick_track_id === track_id} + isArtistPick={isArtistPick} // Artist artistHandle={handle} artistName={name} @@ -276,6 +329,7 @@ const ConnectedTrackTile = ({ toggleSave={toggleSave} onShare={onShare} onClickOverflow={onClickOverflow} + renderOverflow={renderOverflowMenu} toggleRepost={toggleRepost} makeGoToRepostsPage={makeGoToRepostsPage} makeGoToFavoritesPage={makeGoToFavoritesPage} diff --git a/packages/web/src/components/track/mobile/TrackTile.module.css b/packages/web/src/components/track/mobile/TrackTile.module.css index bbaac1881de..c95066c01fb 100644 --- a/packages/web/src/components/track/mobile/TrackTile.module.css +++ b/packages/web/src/components/track/mobile/TrackTile.module.css @@ -10,7 +10,6 @@ display: flex; padding: var(--harmony-unit-2); border-radius: var(--harmony-unit-2); - max-width: 400px; cursor: pointer; transition: all 0.2 ease-in-out; user-select: none; diff --git a/packages/web/src/components/track/mobile/TrackTile.tsx b/packages/web/src/components/track/mobile/TrackTile.tsx index 6fc1e8506ef..006c7eddd27 100644 --- a/packages/web/src/components/track/mobile/TrackTile.tsx +++ b/packages/web/src/components/track/mobile/TrackTile.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, MouseEvent } from 'react' +import { useCallback, useEffect, MouseEvent, ReactNode } from 'react' import { useFeatureFlag } from '@audius/common/hooks' import { @@ -74,6 +74,7 @@ type ExtraProps = { hasPreview?: boolean hasStreamAccess: boolean trackId?: number + renderOverflow?: () => ReactNode } type CombinedProps = TrackTileProps & ExtraProps @@ -209,7 +210,8 @@ const TrackTile = (props: CombinedProps) => { containerClassName, hasPreview = false, title, - source + source, + renderOverflow } = props const hideShare: boolean = props.fieldVisibility @@ -515,6 +517,7 @@ const TrackTile = (props: CombinedProps) => { toggleSave={onToggleSave} onShare={onClickShare} onClickOverflow={onClickOverflowMenu} + renderOverflow={renderOverflow} onClickGatedUnlockPill={onClickPill} isOwner={isOwner} readonly={isReadonly} diff --git a/packages/web/src/pages/ai-attributed-tracks-page/components/desktop/AiPage.tsx b/packages/web/src/pages/ai-attributed-tracks-page/components/desktop/AiPage.tsx index 0f948562b51..405b93abbcc 100644 --- a/packages/web/src/pages/ai-attributed-tracks-page/components/desktop/AiPage.tsx +++ b/packages/web/src/pages/ai-attributed-tracks-page/components/desktop/AiPage.tsx @@ -3,7 +3,7 @@ import { IconRobot } from '@audius/harmony' import cn from 'classnames' import Header from 'components/header/desktop/Header' -import Lineup, { LineupWithoutTile } from 'components/lineup/Lineup' +import Lineup, { LineupProps } from 'components/lineup/Lineup' import Page from 'components/page/Page' import UserBadges from 'components/user-badges/UserBadges' import { fullAiPage } from 'utils/route' @@ -24,7 +24,7 @@ const messages = { export type AiPageProps = { title: string user: User | null - getLineupProps: () => LineupWithoutTile + getLineupProps: () => LineupProps goToArtistPage: () => void } diff --git a/packages/web/src/pages/ai-attributed-tracks-page/components/mobile/AiPage.tsx b/packages/web/src/pages/ai-attributed-tracks-page/components/mobile/AiPage.tsx index 938fdff2afe..d4ef82a2bff 100644 --- a/packages/web/src/pages/ai-attributed-tracks-page/components/mobile/AiPage.tsx +++ b/packages/web/src/pages/ai-attributed-tracks-page/components/mobile/AiPage.tsx @@ -6,7 +6,7 @@ import cn from 'classnames' import Header from 'components/header/mobile/Header' import { HeaderContext } from 'components/header/mobile/HeaderContextProvider' -import Lineup, { LineupWithoutTile } from 'components/lineup/Lineup' +import Lineup, { LineupProps } from 'components/lineup/Lineup' import MobilePageContainer from 'components/mobile-page-container/MobilePageContainer' import { useSubPageHeader } from 'components/nav/mobile/NavContext' import UserBadges from 'components/user-badges/UserBadges' @@ -27,7 +27,7 @@ const messages = { export type AiPageProps = { title: string user: User | null - getLineupProps: () => LineupWithoutTile + getLineupProps: () => LineupProps goToArtistPage: () => void } diff --git a/packages/web/src/pages/deleted-page/components/desktop/DeletedPage.tsx b/packages/web/src/pages/deleted-page/components/desktop/DeletedPage.tsx index e15686ddf81..74e1b2d3892 100644 --- a/packages/web/src/pages/deleted-page/components/desktop/DeletedPage.tsx +++ b/packages/web/src/pages/deleted-page/components/desktop/DeletedPage.tsx @@ -12,7 +12,7 @@ import { Button, IconUser } from '@audius/harmony' import { ArtistPopover } from 'components/artist/ArtistPopover' import CoverPhoto from 'components/cover-photo/CoverPhoto' import DynamicImage from 'components/dynamic-image/DynamicImage' -import Lineup, { LineupWithoutTile } from 'components/lineup/Lineup' +import Lineup, { LineupProps } from 'components/lineup/Lineup' import NavBanner from 'components/nav-banner/NavBanner' import Page from 'components/page/Page' import { StatBanner } from 'components/stat-banner/StatBanner' @@ -71,7 +71,7 @@ export type DeletedPageProps = { playable: Playable user: User | null - getLineupProps: () => LineupWithoutTile + getLineupProps: () => LineupProps goToArtistPage: () => void } diff --git a/packages/web/src/pages/deleted-page/components/mobile/DeletedPage.tsx b/packages/web/src/pages/deleted-page/components/mobile/DeletedPage.tsx index 8ca7aae2cde..f73d13951bf 100644 --- a/packages/web/src/pages/deleted-page/components/mobile/DeletedPage.tsx +++ b/packages/web/src/pages/deleted-page/components/mobile/DeletedPage.tsx @@ -11,7 +11,7 @@ import { Button, IconUser } from '@audius/harmony' import { ArtistPopover } from 'components/artist/ArtistPopover' import DynamicImage from 'components/dynamic-image/DynamicImage' -import Lineup, { LineupWithoutTile } from 'components/lineup/Lineup' +import Lineup, { LineupProps } from 'components/lineup/Lineup' import MobilePageContainer from 'components/mobile-page-container/MobilePageContainer' import UserBadges from 'components/user-badges/UserBadges' import { useCollectionCoverArt } from 'hooks/useCollectionCoverArt' @@ -68,7 +68,7 @@ export type DeletedPageProps = { playable: Playable user: User | null - getLineupProps: () => LineupWithoutTile + getLineupProps: () => LineupProps goToArtistPage: () => void } diff --git a/packages/web/src/pages/remixes-page/components/desktop/RemixesPage.tsx b/packages/web/src/pages/remixes-page/components/desktop/RemixesPage.tsx index 60194a372c3..0ee15562369 100644 --- a/packages/web/src/pages/remixes-page/components/desktop/RemixesPage.tsx +++ b/packages/web/src/pages/remixes-page/components/desktop/RemixesPage.tsx @@ -4,7 +4,7 @@ import { IconRemix as IconRemixes } from '@audius/harmony' import cn from 'classnames' import Header from 'components/header/desktop/Header' -import Lineup, { LineupWithoutTile } from 'components/lineup/Lineup' +import Lineup, { LineupProps } from 'components/lineup/Lineup' import Page from 'components/page/Page' import UserBadges from 'components/user-badges/UserBadges' import { fullTrackRemixesPage } from 'utils/route' @@ -26,7 +26,7 @@ export type RemixesPageProps = { count: number | null originalTrack: Track | null user: User | null - getLineupProps: () => LineupWithoutTile + getLineupProps: () => LineupProps goToTrackPage: () => void goToArtistPage: () => void } diff --git a/packages/web/src/pages/remixes-page/components/mobile/RemixesPage.tsx b/packages/web/src/pages/remixes-page/components/mobile/RemixesPage.tsx index 2ddb3566430..ce733b8eb66 100644 --- a/packages/web/src/pages/remixes-page/components/mobile/RemixesPage.tsx +++ b/packages/web/src/pages/remixes-page/components/mobile/RemixesPage.tsx @@ -7,7 +7,7 @@ import cn from 'classnames' import Header from 'components/header/mobile/Header' import { HeaderContext } from 'components/header/mobile/HeaderContextProvider' -import Lineup, { LineupWithoutTile } from 'components/lineup/Lineup' +import Lineup, { LineupProps } from 'components/lineup/Lineup' import MobilePageContainer from 'components/mobile-page-container/MobilePageContainer' import { useSubPageHeader } from 'components/nav/mobile/NavContext' import UserBadges from 'components/user-badges/UserBadges' @@ -30,7 +30,7 @@ export type RemixesPageProps = { count: number | null originalTrack: Track | null user: User | null - getLineupProps: () => LineupWithoutTile + getLineupProps: () => LineupProps goToTrackPage: () => void goToArtistPage: () => void } diff --git a/packages/web/src/pages/search-page-v2/search-results/TrackResults.tsx b/packages/web/src/pages/search-page-v2/search-results/TrackResults.tsx index 985e97008f2..90e32670d04 100644 --- a/packages/web/src/pages/search-page-v2/search-results/TrackResults.tsx +++ b/packages/web/src/pages/search-page-v2/search-results/TrackResults.tsx @@ -137,9 +137,7 @@ export const TrackResults = (props: TrackResultsProps) => { return ( { const { trackId } = props - const isMobile = useIsMobile() + const { isDesktop, isMobile } = useTrackPageSize() const dispatch = useDispatch() const remixesLineup = useSelector(getRemixesTracksLineup) const currentQueueItem = useSelector(getCurrentQueueItem) @@ -91,6 +92,11 @@ export const TrackRemixes = (props: TrackRemixesProrps) => { const isCommentingEnabled = commentsFlagEnabled && !comments_disabled const remixTrackIds = _remixes?.map(({ track_id }) => track_id) ?? null + const lineupVariant = + (isCommentingEnabled && isDesktop) || isMobile + ? LineupVariant.SECTION + : LineupVariant.CONDENSED + if (!remixTrackIds || !remixTrackIds.length) { return null } @@ -99,16 +105,16 @@ export const TrackRemixes = (props: TrackRemixesProrps) => { - + {messages.remixes} @@ -116,7 +122,7 @@ export const TrackRemixes = (props: TrackRemixesProrps) => { lineup={remixesLineup} actions={remixesPageLineupActions} count={Math.min(MAX_REMIXES_TO_DISPLAY, remixTrackIds.length)} - variant={LineupVariant.CONDENSED} + variant={lineupVariant} selfLoad playingUid={currentQueueItem.uid} playingSource={currentQueueItem.source} @@ -127,7 +133,6 @@ export const TrackRemixes = (props: TrackRemixesProrps) => { buffering={isBuffering} playTrack={handlePlay} pauseTrack={handlePause} - useSmallTiles={isCommentingEnabled} /> {remixTrackIds.length > MAX_REMIXES_TO_DISPLAY ? ( diff --git a/packages/web/src/pages/track-page/components/desktop/TrackPage.tsx b/packages/web/src/pages/track-page/components/desktop/TrackPage.tsx index 4f635e2dfb0..2ac1dc8ebf4 100644 --- a/packages/web/src/pages/track-page/components/desktop/TrackPage.tsx +++ b/packages/web/src/pages/track-page/components/desktop/TrackPage.tsx @@ -20,6 +20,7 @@ import { getTrackDefaults, emptyStringGuard } from 'pages/track-page/utils' import { trackRemixesPage } from 'utils/route' import { TrackRemixes } from '../TrackRemixes' +import { useTrackPageSize } from '../useTrackPageSize' import Remixes from './Remixes' import styles from './TrackPage.module.css' @@ -53,7 +54,6 @@ export type OwnProps = { isPreview?: boolean }) => void goToAllRemixesPage: () => void - goToParentRemixesPage: () => void onHeroShare: (trackId: ID) => void onHeroRepost: (isReposted: boolean, trackId: ID) => void onFollow: () => void @@ -87,7 +87,6 @@ const TrackPage = ({ trendingBadgeLabel, onHeroPlay, goToAllRemixesPage, - goToParentRemixesPage, onHeroShare, onHeroRepost, onSaveTrack, @@ -105,6 +104,7 @@ const TrackPage = ({ play, pause }: OwnProps) => { + const { isDesktop, isMobile } = useTrackPageSize() const { entries } = tracks const isOwner = heroTrack?.owner_id === userId const following = user?.does_current_user_follow ?? false @@ -204,7 +204,7 @@ const TrackPage = ({ ) const renderOriginalTrackTitle = () => ( - + {messages.originalTrack} ) @@ -216,7 +216,6 @@ const TrackPage = ({ color='default' variant='title' size='l' - textAlign='left' >{`${messages.moreBy} ${user?.name}`} ) : null @@ -225,6 +224,11 @@ const TrackPage = ({ const hasRemixes = fieldVisibility.remixes && remixTrackIds && remixTrackIds.length > 0 + const lineupVariant = + (isCommentingEnabled && isDesktop) || isMobile + ? LineupVariant.SECTION + : LineupVariant.CONDENSED + return ( ) : null} diff --git a/packages/web/src/pages/track-page/components/useTrackPageSize.ts b/packages/web/src/pages/track-page/components/useTrackPageSize.ts new file mode 100644 index 00000000000..cec5426dba2 --- /dev/null +++ b/packages/web/src/pages/track-page/components/useTrackPageSize.ts @@ -0,0 +1,7 @@ +import { useMedia } from 'react-use' + +export const useTrackPageSize = () => { + const isDesktop = useMedia('(min-width: 1054px)') + const isMobile = useMedia('(max-width: 768px)') + return { isDesktop, isMobile } +}