Skip to content

Commit

Permalink
[C-2478 C-2480] Add suggestedTracks (#3689)
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanjeffers authored Jul 3, 2023
1 parent 5bb451a commit ea7e3b1
Show file tree
Hide file tree
Showing 17 changed files with 486 additions and 38 deletions.
25 changes: 25 additions & 0 deletions packages/common/src/api/favorites.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createApi } from 'audius-query'
import { ID } from 'models/Identifiers'
import { Nullable } from 'utils/typeUtils'

type GetFavoritedTrackListArgs = {
currentUserId: Nullable<ID>
limit?: number
}

const favoritesApi = createApi({
reducerPath: 'favoritesApi',
endpoints: {
getFavoritedTrackList: {
fetch: async (args: GetFavoritedTrackListArgs, { apiClient }) => {
const { currentUserId, limit = 10000 } = args
if (!currentUserId) return null
return await apiClient.getFavorites({ currentUserId, limit })
},
options: {}
}
}
})

export const { useGetFavoritedTrackList } = favoritesApi.hooks
export const favoritesApiReducer = favoritesApi.reducer
1 change: 1 addition & 0 deletions packages/common/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './track'
export * from './collection'
export * from './user'
export * from './developerApps'
export * from './suggestedTracks'
4 changes: 3 additions & 1 deletion packages/common/src/api/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { combineReducers } from 'redux'

import { collectionApiReducer } from './collection'
import { developerAppsApiReducer } from './developerApps'
import { favoritesApiReducer } from './favorites'
import { relatedArtistsApiReducer } from './relatedArtists'
import { trackApiReducer } from './track'
import { userApiReducer } from './user'
Expand All @@ -11,5 +12,6 @@ export default combineReducers({
relatedArtistsApi: relatedArtistsApiReducer,
trackApi: trackApiReducer,
userApi: userApiReducer,
developerAppsApi: developerAppsApiReducer
developerAppsApi: developerAppsApiReducer,
favoritesApi: favoritesApiReducer
})
31 changes: 31 additions & 0 deletions packages/common/src/api/suggestedTracks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useMemo } from 'react'

import { sampleSize } from 'lodash'
import { useSelector } from 'react-redux'

import { getUserId } from 'store/account/selectors'

import { useGetFavoritedTrackList } from './favorites'
import { useGetTracksByIds } from './track'

export const useGetSuggestedTracks = () => {
const currentUserId = useSelector(getUserId)
const { data: favoritedTracks } = useGetFavoritedTrackList(
{ currentUserId },
{ disabled: !currentUserId }
)

const favoritedTracksSample = useMemo(() => {
return sampleSize(favoritedTracks, 5)
}, [favoritedTracks])

return useGetTracksByIds(
{
currentUserId,
ids: favoritedTracksSample?.map((favorite) => favorite.save_item_id)
},
{
disabled: !currentUserId || favoritedTracksSample.length === 0
}
)
}
2 changes: 1 addition & 1 deletion packages/mobile/src/assets/images/iconRefresh.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 136 additions & 0 deletions packages/mobile/src/components/suggested-tracks/SuggestedTracks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import type { UserTrackMetadata } from '@audius/common'
import { SquareSizes, useGetSuggestedTracks } from '@audius/common'
import { View } from 'react-native'

import IconRefresh from 'app/assets/images/iconRefresh.svg'
import { Button, Divider, Text, TextButton, Tile } from 'app/components/core'
import { makeStyles } from 'app/styles'

import { TrackImage } from '../image/TrackImage'
import { UserBadges } from '../user-badges'

const messages = {
title: 'Add some tracks',
description:
'Placeholder copy: dependent on backend logic and what we decide to do with this new feature.',
addTrack: 'Add',
refresh: 'Refresh'
}

const useStyles = makeStyles(({ spacing, typography, palette }) => ({
root: { marginBottom: spacing(12) },
heading: {
gap: spacing(2),
padding: spacing(4)
},
suggestedTrack: {
flexDirection: 'row',
alignItems: 'center',
padding: spacing(4),
gap: spacing(3)
},
trackDetails: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing(3),
flex: 1
},
trackInfo: {
gap: 2,
flex: 1
},
trackImage: {
height: spacing(10),
width: spacing(10),
borderRadius: 2
},
artistName: {
fontSize: typography.fontSize.small,
fontFamily: typography.fontByWeight.medium,
color: palette.neutralLight4
},
refreshButton: {
marginVertical: spacing(4),
alignSelf: 'center'
},
buttonText: { textTransform: 'none', marginHorizontal: spacing(1) }
}))

type SuggestedTrackProps = {
track: UserTrackMetadata
}

const SuggestedTrack = (props: SuggestedTrackProps) => {
const { track } = props
const { title, user } = track
const styles = useStyles()

return (
<View style={styles.suggestedTrack}>
<View style={styles.trackDetails}>
<TrackImage
track={track}
size={SquareSizes.SIZE_150_BY_150}
style={styles.trackImage}
/>
<View style={styles.trackInfo}>
<Text
numberOfLines={1}
ellipsizeMode='tail'
fontSize='small'
weight='demiBold'
>
{title}
</Text>
<UserBadges user={user} nameStyle={styles.artistName} />
</View>
</View>
<View>
<Button
variant='common'
title={messages.addTrack}
size='small'
styles={{ text: styles.buttonText }}
/>
</View>
</View>
)
}

export const SuggestedTracks = () => {
const styles = useStyles()
const { data: suggestedTracks } = useGetSuggestedTracks()

return (
<Tile style={styles.root}>
<View style={styles.heading}>
<Text fontSize='large' weight='heavy' textTransform='uppercase'>
{messages.title}
</Text>
<Text fontSize='medium' weight='medium'>
{messages.description}
</Text>
</View>
<View>
<Divider />
{suggestedTracks?.map((suggestedTrack) => (
<>
<SuggestedTrack
key={suggestedTrack.track_id}
track={suggestedTrack}
/>
<Divider />
</>
))}
</View>
<TextButton
variant='neutralLight4'
icon={IconRefresh}
iconPosition='left'
title={messages.refresh}
TextProps={{ weight: 'bold' }}
style={styles.refreshButton}
/>
</Tile>
)
}
1 change: 1 addition & 0 deletions packages/mobile/src/components/suggested-tracks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SuggestedTracks'
74 changes: 48 additions & 26 deletions packages/mobile/src/screens/collection-screen/CollectionScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
RepostType,
repostsUserListActions,
favoritesUserListActions,
createPlaylistModalUIActions
createPlaylistModalUIActions,
FeatureFlags
} from '@audius/common'
import type {
Collection,
Expand All @@ -36,16 +37,20 @@ import { useDispatch, useSelector } from 'react-redux'
import {
ScreenContent,
Screen,
VirtualizedScrollView
VirtualizedScrollView,
Divider
} from 'app/components/core'
import { CollectionImage } from 'app/components/image/CollectionImage'
import type { ImageProps } from 'app/components/image/FastImage'
import { SuggestedTracks } from 'app/components/suggested-tracks'
import { useIsOfflineModeEnabled } from 'app/hooks/useIsOfflineModeEnabled'
import { useNavigation } from 'app/hooks/useNavigation'
import { useFeatureFlag } from 'app/hooks/useRemoteConfig'
import { useRoute } from 'app/hooks/useRoute'
import { setVisibility } from 'app/store/drawers/slice'
import { getIsCollectionMarkedForDownload } from 'app/store/offline-downloads/selectors'
import { makeStyles } from 'app/styles'
import { useThemePalette } from 'app/utils/theme'

import { CollectionScreenDetailsTile } from './CollectionScreenDetailsTile'
import { CollectionScreenSkeleton } from './CollectionScreenSkeleton'
Expand All @@ -70,6 +75,10 @@ const { requestOpen: openPublishConfirmation } =
const useStyles = makeStyles(({ spacing }) => ({
root: {
padding: spacing(3)
},
divider: {
marginTop: spacing(2),
marginBottom: spacing(8)
}
}))

Expand Down Expand Up @@ -150,6 +159,11 @@ const CollectionScreenComponent = (props: CollectionScreenComponentProps) => {
updated_at
} = collection
const isOfflineModeEnabled = useIsOfflineModeEnabled()
const { isEnabled: arePlaylistUpdatesEnabled } = useFeatureFlag(
FeatureFlags.PLAYLIST_UPDATES_PRE_QA
)

const { neutralLight5 } = useThemePalette()

const url = useMemo(() => {
return `/${encodeUrlName(user.handle)}/${
Expand Down Expand Up @@ -273,30 +287,38 @@ const CollectionScreenComponent = (props: CollectionScreenComponentProps) => {
<Screen url={url}>
<ScreenContent isOfflineCapable={isOfflineModeEnabled}>
<VirtualizedScrollView style={styles.root}>
<CollectionScreenDetailsTile
description={description ?? ''}
extraDetails={extraDetails}
hasReposted={has_current_user_reposted}
hasSaved={has_current_user_saved}
isAlbum={is_album}
collectionId={playlist_id}
isPrivate={is_private}
isPublishing={_is_publishing ?? false}
onPressEdit={handlePressEdit}
onPressFavorites={handlePressFavorites}
onPressOverflow={handlePressOverflow}
onPressPublish={handlePressPublish}
onPressRepost={handlePressRepost}
onPressReposts={handlePressReposts}
onPressSave={handlePressSave}
onPressShare={handlePressShare}
renderImage={renderImage}
repostCount={repost_count}
saveCount={save_count}
trackCount={track_ids.length}
title={playlist_name}
user={user}
/>
<>
<CollectionScreenDetailsTile
description={description ?? ''}
extraDetails={extraDetails}
hasReposted={has_current_user_reposted}
hasSaved={has_current_user_saved}
isAlbum={is_album}
collectionId={playlist_id}
isPrivate={is_private}
isPublishing={_is_publishing ?? false}
onPressEdit={handlePressEdit}
onPressFavorites={handlePressFavorites}
onPressOverflow={handlePressOverflow}
onPressPublish={handlePressPublish}
onPressRepost={handlePressRepost}
onPressReposts={handlePressReposts}
onPressSave={handlePressSave}
onPressShare={handlePressShare}
renderImage={renderImage}
repostCount={repost_count}
saveCount={save_count}
trackCount={track_ids.length}
title={playlist_name}
user={user}
/>
{isOwner && !is_album && arePlaylistUpdatesEnabled ? (
<>
<Divider style={styles.divider} color={neutralLight5} />
<SuggestedTracks />
</>
) : null}
</>
</VirtualizedScrollView>
</ScreenContent>
</Screen>
Expand Down
1 change: 1 addition & 0 deletions packages/stems/src/assets/styles/transforms.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
:root,
root {
--grow: perspective(1px) scale3d(1.04, 1.04, 1.04);
--grow-large: perspective(1px) scale3d(1.1, 1.1, 1.1);
--shrink: scale(0.95);
}
6 changes: 5 additions & 1 deletion packages/web/src/components/divider/Divider.module.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
.root {
.default {
border-color: var(--neutral-light-5);
}

.subdued {
border-color: var(--neutral-light-8);
}

Expand Down
11 changes: 7 additions & 4 deletions packages/web/src/components/divider/Divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,25 @@ import cn from 'classnames'

import styles from './Divider.module.css'

type DividerProps = AntDividerProps
type DividerProps = AntDividerProps & {
variant?: 'default' | 'subdued'
}

export const Divider = (props: DividerProps) => {
const { className, ...other } = props
const { className, variant = 'subdued', ...other } = props

const { type = 'horizontal' } = other

return (
<AntDivider
className={cn(
styles.root,
styles[variant],
{
[styles.horizontal]: type === 'horizontal'
},
className
)}
{...props}
{...other}
/>
)
}
Loading

0 comments on commit ea7e3b1

Please sign in to comment.