Skip to content

Commit

Permalink
Add mobile hidden track playlist logic (#3505)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Shanks authored Jun 5, 2023
1 parent cf9c5d5 commit 7d26233
Show file tree
Hide file tree
Showing 17 changed files with 155 additions and 61 deletions.
26 changes: 26 additions & 0 deletions packages/common/src/store/cache/collections/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ export const getCollectionsByUid = (state: CommonState) => {
}, {} as { [uid: string]: Collection | null })
}

const getCollectionTracks = (state: CommonState, { id }: { id?: ID }) => {
const collection = getCollection(state, { id })
const collectionTrackIds = collection?.playlist_contents.track_ids.map(
(track_id) => track_id.track // track === actual track id, oof
)
return getTracks(state, { ids: collectionTrackIds })
}

export const getIsCollectionEmpty = (
state: CommonState,
{ id }: { id?: ID }
) => {
const collectionTracks = getCollectionTracks(state, { id })

return Object.values(collectionTracks).length === 0
}

export const getCollecitonHasHiddenTracks = (
state: CommonState,
{ id }: { id?: ID }
) => {
const collectionTracks = getCollectionTracks(state, { id })

return Object.values(collectionTracks)?.some((track) => track.is_unlisted)
}

export const getStatuses = (state: CommonState, props: { ids: ID[] }) => {
const statuses: { [id: number]: Status } = {}
props.ids.forEach((id) => {
Expand Down
12 changes: 10 additions & 2 deletions packages/common/src/store/ui/add-to-playlist/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ export const CLOSE = 'ADD_TO_PLAYLIST/CLOSE'

export const requestOpen = createCustomAction(
REQUEST_OPEN,
(trackId: ID, trackTitle: string) => ({ trackId, trackTitle })
(trackId: ID, trackTitle: string, isUnlisted?: boolean) => ({
trackId,
trackTitle,
isUnlisted
})
)
export const open = createCustomAction(
OPEN,
(trackId: ID, trackTitle: string) => ({ trackId, trackTitle })
(trackId: ID, trackTitle: string, isUnlisted?: boolean) => ({
trackId,
trackTitle,
isUnlisted
})
)
export const close = createCustomAction(CLOSE, () => {})
10 changes: 7 additions & 3 deletions packages/common/src/store/ui/add-to-playlist/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ type AddToPlaylistActions = ActionType<typeof actions>
export type AddToPlaylistState = {
trackId: ID | null
trackTitle: string | null
isUnlisted: boolean
}

const initialState = {
isOpen: false,
trackId: null,
trackTitle: null
trackTitle: null,
isUnlisted: false
}

const reducer = createReducer<AddToPlaylistState, AddToPlaylistActions>(
Expand All @@ -24,14 +26,16 @@ const reducer = createReducer<AddToPlaylistState, AddToPlaylistActions>(
return {
...state,
trackId: action.trackId,
trackTitle: action.trackTitle
trackTitle: action.trackTitle,
isUnlisted: action.isUnlisted ?? false
}
},
[actions.CLOSE](state, _action) {
return {
...state,
trackId: null,
trackTitle: null
trackTitle: null,
isUnlisted: false
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/store/ui/add-to-playlist/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ const getBaseState = (state: CommonState) => state.ui.addToPlaylist
export const getTrackId = (state: CommonState) => getBaseState(state).trackId
export const getTrackTitle = (state: CommonState) =>
getBaseState(state).trackTitle
export const getTrackIsUnlisted = (state: CommonState) =>
getBaseState(state).isUnlisted
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import { AddCollectionCard } from '../collection-list/AddCollectionCard'
import type { ImageProps } from '../image/FastImage'

const { addTrackToPlaylist, createPlaylist } = cacheCollectionsActions
const { getTrackId, getTrackTitle } = addToPlaylistUISelectors
const { getTrackId, getTrackTitle, getTrackIsUnlisted } =
addToPlaylistUISelectors
const { getAccountWithOwnPlaylists } = accountSelectors

const messages = {
title: 'Add To Playlist',
addedToast: 'Added To Playlist!'
addedToast: 'Added To Playlist!',
hiddenAdd: 'You cannot add hidden tracks to a public playlist.'
}

const useStyles = makeStyles(() => ({
Expand All @@ -55,6 +57,7 @@ export const AddToPlaylistDrawer = () => {
const { onClose } = useDrawerState('AddToPlaylist')
const trackId = useSelector(getTrackId)
const trackTitle = useSelector(getTrackTitle)
const isTrackUnlisted = useSelector(getTrackIsUnlisted)
const user = useSelector(getAccountWithOwnPlaylists)
const { isEnabled: isPlaylistUpdatesEnabled } = useFeatureFlag(
FeatureFlags.PLAYLIST_UPDATES_PRE_QA
Expand Down Expand Up @@ -97,12 +100,18 @@ export const AddToPlaylistDrawer = () => {
/>
) : (
<Card
style={{ opacity: isTrackUnlisted && !item.is_private ? 0.5 : 1 }}
key={item.playlist_id}
type='collection'
id={item.playlist_id}
primaryText={item.playlist_name}
secondaryText={user?.name}
onPress={() => {
// Don't add if the track is hidden, but playlist is public
if (isTrackUnlisted && !item.is_private) {
toast({ content: messages.hiddenAdd })
return
}
toast({ content: messages.addedToast })
dispatch(addTrackToPlaylist(trackId!, item.playlist_id))
onClose()
Expand All @@ -113,6 +122,7 @@ export const AddToPlaylistDrawer = () => {
[
addToNewPlaylist,
dispatch,
isTrackUnlisted,
onClose,
renderImage,
toast,
Expand Down
2 changes: 2 additions & 0 deletions packages/mobile/src/components/details-tile/DetailsTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ const useStyles = makeStyles(({ palette, spacing, typography }) => ({
* The details shown at the top of the Track Screen and Collection Screen
*/
export const DetailsTile = ({
collectionId,
coSign,
description,
descriptionLinkPressSource,
Expand Down Expand Up @@ -393,6 +394,7 @@ export const DetailsTile = ({
hideShare={hideShare}
isOwner={isOwner}
isPlaylist={isPlaylist}
collectionId={collectionId}
isPublished={isPublished}
onPressEdit={onPressEdit}
onPressOverflow={onPressOverflow}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { FeatureFlags } from '@audius/common'
import type { CommonState, ID } from '@audius/common'
import { FeatureFlags, cacheCollectionsSelectors } from '@audius/common'
import { View } from 'react-native'
import { useSelector } from 'react-redux'

import IconKebabHorizontal from 'app/assets/images/iconKebabHorizontal.svg'
import IconPencil from 'app/assets/images/iconPencil.svg'
Expand All @@ -13,12 +15,18 @@ import { flexRowCentered, makeStyles } from 'app/styles'
import type { GestureResponderHandler } from 'app/types/gesture'
import { useThemeColors } from 'app/utils/theme'

// const messages = {
// publishButtonDisabledContent: 'You must add at least 1 track.',
// shareButtonDisabledContent: 'You can’t share an empty playlist.'
// }
const { getCollecitonHasHiddenTracks, getIsCollectionEmpty } =
cacheCollectionsSelectors

const messages = {
publishButtonEmptyDisabledContent: 'You must add at least 1 track.',
publishButtonHiddenDisabledContent:
'You cannot make a playlist with hidden tracks public.',
shareButtonDisabledContent: 'You can’t share an empty playlist.'
}

type DetailsTileActionButtonsProps = {
collectionId?: ID
hasReposted: boolean
hasSaved: boolean
isOwner: boolean
Expand Down Expand Up @@ -59,7 +67,7 @@ const useStyles = makeStyles(({ palette }) => ({
marginHorizontal: 16
},

editButton: {
editButtonIcon: {
width: 27
}
}))
Expand All @@ -68,6 +76,7 @@ const useStyles = makeStyles(({ palette }) => ({
* The action buttons on track and playlist screens
*/
export const DetailsTileActionButtons = ({
collectionId,
hasReposted,
hasSaved,
isPlaylist,
Expand All @@ -89,6 +98,12 @@ export const DetailsTileActionButtons = ({
const { isEnabled: isPlaylistUpdatesEnabled } = useFeatureFlag(
FeatureFlags.PLAYLIST_UPDATES_PRE_QA
)
const isCollectionEmpty = useSelector((state: CommonState) =>
getIsCollectionEmpty(state, { id: collectionId })
)
const collectionHasHiddenTracks = useSelector((state: CommonState) =>
getCollecitonHasHiddenTracks(state, { id: collectionId })
)

const repostButton = (
<RepostButton
Expand All @@ -112,10 +127,8 @@ export const DetailsTileActionButtons = ({
<IconButton
fill={neutralLight2}
icon={IconShare}
isDisabled={!isPublished}
// TODO: Add isDisabled based on if playlist is publishable logic
// Needs to check for hidden tracks and things like that
// disabledPressToastContent={messages.shareButtonDisabledContent}
isDisabled={isCollectionEmpty}
disabledPressToastContent={messages.shareButtonDisabledContent}
onPress={onPressShare}
styles={{ icon: [styles.actionButton, { height: 24, width: 24 }] }}
/>
Expand All @@ -135,18 +148,20 @@ export const DetailsTileActionButtons = ({
fill={neutralLight2}
icon={IconPencil}
onPress={onPressEdit}
styles={{ icon: [styles.actionButton, styles.editButton] }}
styles={{ icon: [styles.actionButton, styles.editButtonIcon] }}
/>
)

const publishButton = (
<IconButton
fill={neutralLight2}
icon={IconRocket}
// TODO: Add isDisabled based on if playlist is publishable logic
// Needs to check for hidden tracks and things like that
// isDisabled
// disabledPressToastContent={messages.publishButtonDisabledContent}
isDisabled={isCollectionEmpty || collectionHasHiddenTracks}
disabledPressToastContent={
collectionHasHiddenTracks
? messages.publishButtonHiddenDisabledContent
: messages.publishButtonEmptyDisabledContent
}
onPress={onPressPublish}
styles={{ icon: styles.actionButton }}
/>
Expand Down
5 changes: 4 additions & 1 deletion packages/mobile/src/components/details-tile/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react'

import type { Track, User, SearchTrack, SearchUser } from '@audius/common'
import type { Track, User, SearchTrack, SearchUser, ID } from '@audius/common'
import type { TextStyle } from 'react-native'

import type { GestureResponderHandler } from 'app/types/gesture'
Expand All @@ -16,6 +16,9 @@ export type DetailsTileDetail = {
}

export type DetailsTileProps = {
/** Id of the collection */
collectionId?: ID

/** Cosign information */
coSign?: Track['_co_sign']

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const TrackOverflowMenuDrawer = ({ render }: Props) => {
if (!track || !user) {
return null
}
const { owner_id, title } = track
const { owner_id, title, is_unlisted } = track
const { handle } = user

if (!id || !owner_id || !handle || !title) {
Expand All @@ -81,7 +81,7 @@ const TrackOverflowMenuDrawer = ({ render }: Props) => {
[OverflowAction.SHARE]: () =>
dispatch(shareTrack(id, ShareSource.OVERFLOW)),
[OverflowAction.ADD_TO_PLAYLIST]: () =>
dispatch(openAddToPlaylistModal(id, title)),
dispatch(openAddToPlaylistModal(id, title, is_unlisted)),
[OverflowAction.VIEW_TRACK_PAGE]: () => {
closeNowPlayingDrawer()
navigation?.push('Track', { id })
Expand Down
55 changes: 34 additions & 21 deletions packages/mobile/src/components/share-drawer/ShareDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import IconTwitterBird from 'app/assets/images/iconTwitterBird.svg'
import { useFeatureFlag } from 'app/hooks/useRemoteConfig'
import { useToast } from 'app/hooks/useToast'
import { makeStyles } from 'app/styles'
import { spacing } from 'app/styles/spacing'
import { useThemeColors } from 'app/utils/theme'

import ActionDrawer from '../action-drawer'
Expand All @@ -43,27 +42,34 @@ const { getAccountUser } = accountSelectors

export const shareToastTimeout = 1500

const useStyles = makeStyles(() => ({
const useStyles = makeStyles(({ spacing }) => ({
titleContainer: {
paddingBottom: spacing(4)
},
title: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
paddingTop: 8,
paddingBottom: 16
paddingTop: spacing(2),
paddingBottom: spacing(4)
},
titleText: {
textTransform: 'uppercase'
},
titleIcon: {
marginRight: spacing(3)
},
titleHelperText: {
paddingHorizontal: spacing(4),
textAlign: 'center'
},
row: {
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 24
paddingVertical: spacing(3),
paddingHorizontal: spacing(6)
},
viewShot: {
position: 'absolute',
Expand Down Expand Up @@ -257,21 +263,28 @@ export const ShareDrawer = () => {
modalName='Share'
rows={getRows()}
renderTitle={() => (
<View style={styles.title}>
<IconShare
style={styles.titleIcon}
fill={neutralLight2}
height={18}
width={20}
/>
<Text
weight='heavy'
color='neutralLight2'
fontSize='xl'
style={styles.titleText}
>
{messages.modalTitle(shareType)}
</Text>
<View style={styles.titleContainer}>
<View style={styles.title}>
<IconShare
style={styles.titleIcon}
fill={neutralLight2}
height={18}
width={20}
/>
<Text
weight='heavy'
color='neutralLight2'
fontSize='xl'
style={styles.titleText}
>
{messages.modalTitle(shareType)}
</Text>
</View>
{shareType === 'playlist' ? (
<Text style={styles.titleHelperText} fontSize={'large'}>
{messages.playlistShareHelperText}
</Text>
) : null}
</View>
)}
styles={{ row: styles.row }}
Expand Down
Loading

0 comments on commit 7d26233

Please sign in to comment.