Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/common/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export * from './tan-query/collection/useFeaturedPlaylists'
// Events
export * from './tan-query/events'

// Explore
export * from './tan-query/collection/useExploreContent'

// Lineups
export * from './tan-query/lineups/useFeed'
export * from './tan-query/lineups/useLibraryTracks'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ const STATIC_EXPLORE_CONTENT_URL =
type ExploreContentResponse = {
featuredPlaylists: string[]
featuredProfiles: string[]
featuredRemixContests: string[]
}

export type ExploreContent = {
featuredPlaylists: ID[]
featuredProfiles: ID[]
featuredRemixContests: ID[]
}

export const getExploreContentQueryKey = () => {
Expand All @@ -41,6 +43,9 @@ export const useExploreContent = <TResult = ExploreContent>(
),
featuredProfiles: json.featuredProfiles.map(
(id: string) => parseInt(id) as ID
),
featuredRemixContests: json.featuredRemixContests.map(
(id: string) => parseInt(id) as ID
)
}
},
Expand Down
6 changes: 4 additions & 2 deletions packages/common/src/services/remote-config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export enum FeatureFlags {
DOWNLOAD_ALL_TRACK_FILES = 'download_all_track_files',
REMIX_CONTEST = 'remix_contest',
WALLET_UI_UPDATE = 'wallet_ui_update',
SEARCH_EXPLORE = 'search_explore'
SEARCH_EXPLORE = 'search_explore',
EXPLORE_REMIX_SECTION = 'explore_remix_section'
}

type FlagDefaults = Record<FeatureFlags, boolean>
Expand Down Expand Up @@ -76,5 +77,6 @@ export const flagDefaults: FlagDefaults = {
[FeatureFlags.DOWNLOAD_ALL_TRACK_FILES]: false,
[FeatureFlags.REMIX_CONTEST]: false,
[FeatureFlags.WALLET_UI_UPDATE]: false,
[FeatureFlags.SEARCH_EXPLORE]: false
[FeatureFlags.SEARCH_EXPLORE]: false,
[FeatureFlags.EXPLORE_REMIX_SECTION]: false
}
3 changes: 2 additions & 1 deletion packages/harmony/src/components/text-link/TextLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ export const TextLink = forwardRef((props: TextLinkProps, ref: Ref<'a'>) => {
asChild
onClick={onClick}
css={{
display: 'inline-flex',
// MaxLines requires a special webkit display to work, dont override here
display: other.maxLines ? undefined : 'inline-flex',
gap: spacing.s,
color: variantColors[variant],
textDecoration: 'none',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type CollectionArtCardProps = {
id: ID
}

const ARTWORK_SIZE = 240

export const CollectionArtCard = ({ id }: CollectionArtCardProps) => {
const [isPerspectiveDisabled, setIsPerspectiveDisabled] = useState(false)

Expand All @@ -36,11 +38,17 @@ export const CollectionArtCard = ({ id }: CollectionArtCardProps) => {
<CollectionImage
collectionId={id}
size={SquareSizes.SIZE_480_BY_480}
h={240}
w={240}
h={ARTWORK_SIZE}
w={ARTWORK_SIZE}
/>
</PerspectiveCard>
<Flex column gap='xs' alignItems='center'>
<Flex
column
gap='xs'
alignItems='center'
ph='m'
css={{ maxWidth: ARTWORK_SIZE }}
>
<CollectionLink collectionId={id} textVariant='title' size='l'>
{playlist_name}
</CollectionLink>
Expand All @@ -52,7 +60,7 @@ export const CollectionArtCard = ({ id }: CollectionArtCardProps) => {
center
/>
</Flex>
<Flex gap='m'>
<Flex gap='m' ph='m' css={{ maxWidth: ARTWORK_SIZE }}>
<RepostStats
id={playlist_id}
entityType={UserListEntityType.COLLECTION}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useCallback } from 'react'

import { useFeatureFlag } from '@audius/common/hooks'
import { Variant as CollectionVariant, Variant } from '@audius/common/models'
import { FeatureFlags } from '@audius/common/services'
import { ExploreCollectionsVariant } from '@audius/common/store'
import { route } from '@audius/common/utils'
import { IconExplore } from '@audius/harmony'
Expand Down Expand Up @@ -39,6 +41,7 @@ import { BASE_URL, stripBaseUrl } from 'utils/route'
import styles from './ExplorePage.module.css'
import { FeaturedPlaylists } from './FeaturedPlaylists'
import { FeaturedProfiles } from './FeaturedProfiles'
import { FeaturedRemixContests } from './FeaturedRemixContests'
import Section, { Layout } from './Section'

const { EXPLORE_PAGE } = route
Expand All @@ -48,7 +51,8 @@ const messages = {
justForYouSubtitle: `Content curated for you based on your likes,
reposts, and follows. Refreshes often so if you like a track, favorite it.`,
lifestyle: 'Playlists to Fit Your Mood',
lifestyleSubtitle: 'Playlists made by Audius users, sorted by mood and feel'
lifestyleSubtitle: 'Playlists made by Audius users, sorted by mood and feel',
remixContests: 'Remix Contests'
}

export const justForYou = [
Expand Down Expand Up @@ -81,6 +85,9 @@ export type ExplorePageProps = {

const ExplorePage = ({ title, pageTitle, description }: ExplorePageProps) => {
const isUSDCPurchasesEnabled = useIsUSDCEnabled()
const { isEnabled: isRemixSectionEnabled } = useFeatureFlag(
FeatureFlags.EXPLORE_REMIX_SECTION
)
const justForYouTiles = justForYou.filter((tile) => {
const isPremiumTracksTile =
tile.variant === ExploreCollectionsVariant.DIRECT_LINK &&
Expand Down Expand Up @@ -119,6 +126,30 @@ const ExplorePage = ({ title, pageTitle, description }: ExplorePageProps) => {
contentClassName={styles.page}
header={header}
>
{isRemixSectionEnabled ? (
<>
<Section
title={messages.lifestyle}
subtitle={messages.lifestyleSubtitle}
>
{lifestyle.map((i) => (
<PerspectiveCard
key={i.title}
backgroundGradient={i.gradient}
shadowColor={i.shadow}
onClick={() => navigate(i.link)}
>
<EmojiInterior title={i.title} emoji={i.emoji} />
</PerspectiveCard>
))}
</Section>

<FeaturedPlaylists />
<FeaturedRemixContests />
<FeaturedProfiles />
</>
) : null}

<Section
title={messages.justForYou}
subtitle={messages.justForYouSubtitle}
Expand Down Expand Up @@ -162,22 +193,28 @@ const ExplorePage = ({ title, pageTitle, description }: ExplorePageProps) => {
)
})}
</Section>

<Section title={messages.lifestyle} subtitle={messages.lifestyleSubtitle}>
{lifestyle.map((i) => (
<PerspectiveCard
key={i.title}
backgroundGradient={i.gradient}
shadowColor={i.shadow}
onClick={() => navigate(i.link)}
{!isRemixSectionEnabled ? (
<>
<Section
title={messages.lifestyle}
subtitle={messages.lifestyleSubtitle}
>
<EmojiInterior title={i.title} emoji={i.emoji} />
</PerspectiveCard>
))}
</Section>
{lifestyle.map((i) => (
<PerspectiveCard
key={i.title}
backgroundGradient={i.gradient}
shadowColor={i.shadow}
onClick={() => navigate(i.link)}
>
<EmojiInterior title={i.title} emoji={i.emoji} />
</PerspectiveCard>
))}
</Section>

<FeaturedPlaylists />
<FeaturedProfiles />
<FeaturedPlaylists />
<FeaturedProfiles />
</>
) : null}
</Page>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useExploreContent } from '@audius/common/api'

import LoadingSpinner from 'components/loading-spinner/LoadingSpinner'

import Section from './Section'
import { TrackArtCard } from './TrackArtCard'

const messages = {
remixContests: 'Remix Contests'
}

export const FeaturedRemixContests = () => {
const { data: exploreContent, isLoading } = useExploreContent()
const contestIds = exploreContent?.featuredRemixContests ?? []

return (
<Section title={messages.remixContests}>
{isLoading ? (
<LoadingSpinner />
) : (
<>
{contestIds.slice(0, 4).map((id) => (
<TrackArtCard key={id} id={id} />
))}
</>
)}
</Section>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useCallback } from 'react'

import { useRemixContest, useTrack } from '@audius/common/api'
import { ID, SquareSizes } from '@audius/common/models'
import { dayjs } from '@audius/common/utils'
import { Flex, Skeleton, Text } from '@audius/harmony'
import { useNavigate } from 'react-router-dom-v5-compat'

import { TrackLink } from 'components/link/TrackLink'
import { UserLink } from 'components/link/UserLink'
import PerspectiveCard from 'components/perspective-card/PerspectiveCard'
import { TrackArtwork } from 'components/track/TrackArtwork'

type TrackArtCardProps = {
id: ID
}

const messages = {
remixContest: 'Remix Contest',
deadline: 'Deadline',
ended: 'Ended',
contestDeadline: (endDate: string | undefined) => {
if (!endDate) return null

const isContestEnded = dayjs(endDate).isBefore(dayjs())
return `${isContestEnded ? messages.ended : messages.deadline}: ${dayjs(endDate).format('MM/DD/YY')}`
}
}

const ARTWORK_SIZE = 240

export const TrackArtCard = ({ id }: TrackArtCardProps) => {
const navigate = useNavigate()

const { data: track, isLoading: isTrackLoading } = useTrack(id)
const { data: contest, isLoading: isContestLoading } = useRemixContest(id)
const isLoading = isTrackLoading || isContestLoading
const isRemixContest = !!contest

const goToTrack = useCallback(() => {
if (!track?.permalink) return
navigate(track.permalink)
}, [navigate, track?.permalink])

if (!track) return null

return (
<Flex column alignItems='center' gap='s'>
<PerspectiveCard onClick={goToTrack}>
<TrackArtwork
trackId={id}
size={SquareSizes.SIZE_480_BY_480}
h={ARTWORK_SIZE}
w={ARTWORK_SIZE}
/>
</PerspectiveCard>
<Flex
column
gap='xs'
alignItems='center'
ph='m'
css={{ maxWidth: ARTWORK_SIZE }}
>
{isLoading ? (
<>
<Skeleton h={24} w={160} />
<Skeleton h={24} w={100} />
</>
) : (
<>
<TrackLink maxLines={2} trackId={id} textVariant='title' size='l' />
<UserLink
userId={track.owner_id}
popover
textVariant='title'
strength='weak'
center
/>
{isRemixContest && (
<Text variant='body' size='s' strength='strong' color='subdued'>
{messages.contestDeadline(contest.endDate)}
</Text>
)}
</>
)}
</Flex>
</Flex>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,9 @@ export const RemixContestDetailsTab = ({
<Flex column gap='l' p='xl'>
<Flex row gap='s'>
<Text variant='title' size='m' color='accent'>
{messages.due}
</Text>
<Text variant='body'>
{isContestEnded
? messages.ended
: messages.deadline(remixContest?.endDate)}
{isContestEnded ? messages.ended : messages.due}
</Text>
<Text variant='body'>{messages.deadline(remixContest?.endDate)}</Text>
</Flex>
<CollapsibleContent
id='remix-contest-details-tab'
Expand Down