Skip to content

Commit

Permalink
[C-2543, C-2544] Initial updates to mobile playlist management (#3287)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Shanks committed May 1, 2023
1 parent 350f465 commit 1ce85f6
Show file tree
Hide file tree
Showing 30 changed files with 410 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export enum FeatureFlags {
RELATED_ARTISTS_ON_PROFILE_ENABLED = 'related_artists_on_profile_enabled',
PROXY_WORMHOLE = 'proxy_wormhole',
STORAGE_V2_TRACK_UPLOAD = 'storage_v2_track_upload',
STORAGE_V2_SIGNUP = 'storage_v2_signup'
STORAGE_V2_SIGNUP = 'storage_v2_signup',
PLAYLIST_UPDATES_PRE_QA = 'playlist_updates_pre_qa',
PLAYLIST_UPDATES_POST_QA = 'playlist_updates_post_qa'
}

type FlagDefaults = Record<FeatureFlags, boolean>
Expand Down Expand Up @@ -108,5 +110,7 @@ export const flagDefaults: FlagDefaults = {
[FeatureFlags.RELATED_ARTISTS_ON_PROFILE_ENABLED]: false,
[FeatureFlags.PROXY_WORMHOLE]: false,
[FeatureFlags.STORAGE_V2_TRACK_UPLOAD]: false,
[FeatureFlags.STORAGE_V2_SIGNUP]: false
[FeatureFlags.STORAGE_V2_SIGNUP]: false,
[FeatureFlags.PLAYLIST_UPDATES_PRE_QA]: false,
[FeatureFlags.PLAYLIST_UPDATES_POST_QA]: false
}
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ export function orderPlaylistFailed(
return { type: ORDER_PLAYLIST_FAILED, error, params, metadata }
}

export function publishPlaylist(playlistId: ID) {
return { type: PUBLISH_PLAYLIST, playlistId }
export function publishPlaylist(playlistId: ID, dismissToastKey?: string) {
return { type: PUBLISH_PLAYLIST, playlistId, dismissToastKey }
}

export function publishPlaylistFailed(
Expand Down
4 changes: 4 additions & 0 deletions apps/audius-client/packages/common/src/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ import { MobileOverflowModalState } from './ui/mobile-overflow-menu/types'
import modalsReducer from './ui/modals/slice'
import { ModalsState } from './ui/modals/types'
import nowPlayingReducer, { NowPlayingState } from './ui/now-playing/slice'
import publishPlaylistConfirmationReducer from './ui/publish-playlist-confirmation-modal/slice'
import { PublishPlaylistConfirmationModalState } from './ui/publish-playlist-confirmation-modal/types'
import reactionsReducer, { ReactionsState } from './ui/reactions/slice'
import relatedArtistsReducer from './ui/related-artists/slice'
import { RelatedArtistsState } from './ui/related-artists/types'
Expand Down Expand Up @@ -171,6 +173,7 @@ export const reducers = () => ({
createPlaylistModal: createPlaylistModalReducer,
collectibleDetails: collectibleDetailsReducer,
deletePlaylistConfirmationModal: deletePlaylistConfirmationReducer,
publishPlaylistConfirmationModal: publishPlaylistConfirmationReducer,
mobileOverflowModal: mobileOverflowModalReducer,
modals: modalsReducer,
musicConfetti: musicConfettiReducer,
Expand Down Expand Up @@ -283,6 +286,7 @@ export type CommonState = {
createPlaylistModal: CreatePlaylistModalState
collectibleDetails: CollectibleDetailsState
deletePlaylistConfirmationModal: DeletePlaylistConfirmationModalState
publishPlaylistConfirmationModal: PublishPlaylistConfirmationModalState
mobileOverflowModal: MobileOverflowModalState
modals: ModalsState
musicConfetti: MusicConfettiState
Expand Down
2 changes: 2 additions & 0 deletions apps/audius-client/packages/common/src/store/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
searchUsersModalSagas,
toastSagas,
deletePlaylistConfirmationModalUISagas,
publishPlaylistConfirmationModalUISagas,
mobileOverflowMenuUISagas,
shareModalUISagas
} from 'store/ui'
Expand Down Expand Up @@ -45,6 +46,7 @@ export const sagas = (_ctx: CommonStoreContext) => ({
shareModalUI: shareModalUISagas,
mobileOverflowMenuUI: mobileOverflowMenuUISagas,
deletePlaylistConfirmationModalUI: deletePlaylistConfirmationModalUISagas,
publishPlaylistConfirmationModalUI: publishPlaylistConfirmationModalUISagas,
player: playerSagas,
playbackPosition: playbackPositionSagas,
playlistUpdates: playlistUpdatesSagas
Expand Down
8 changes: 8 additions & 0 deletions apps/audius-client/packages/common/src/store/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ export {
actions as nowPlayingUIActions
} from './now-playing/slice'

export * as publishPlaylistConfirmationModalUISelectors from './publish-playlist-confirmation-modal/selectors'
export {
default as publishPlaylistConfirmationModalUIReducer,
actions as publishPlaylistConfirmationModalUIActions
} from './publish-playlist-confirmation-modal/slice'
export { default as publishPlaylistConfirmationModalUISagas } from './publish-playlist-confirmation-modal/sagas'
export * from './publish-playlist-confirmation-modal/types'

export {
default as reactionsUIReducer,
actions as reactionsUIActions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ const initialState: ModalsState = {
InboxSettings: false,
LockedContent: false,
PlaybackRate: false,
ProfileActions: false
ProfileActions: false,
PublishPlaylistConfirmation: false
}

const slice = createSlice({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ export type Modals =
| 'LockedContent'
| 'PlaybackRate'
| 'ProfileActions'
| 'PublishPlaylistConfirmation'

export type ModalsState = { [modal in Modals]: boolean | 'closing' }
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { takeEvery } from 'redux-saga/effects'
import { put } from 'typed-redux-saga'

import { setVisibility } from '../modals/slice'

import { open, OpenPayload, requestOpen } from './slice'

function* handleRequestOpen(action: OpenPayload) {
const { payload } = action
yield* put(open(payload))
yield* put(
setVisibility({ modal: 'PublishPlaylistConfirmation', visible: true })
)
}

function* watchHandleRequestOpen() {
yield takeEvery(requestOpen, handleRequestOpen)
}

export default function sagas() {
return [watchHandleRequestOpen]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { CommonState } from 'store/commonStore'

export const getPlaylistId = (state: CommonState) =>
state.ui.publishPlaylistConfirmationModal.playlistId
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'

import { Nullable } from 'utils/typeUtils'

import { ID } from '../../../models/Identifiers'

type PublishPlaylistConfirmationState = {
playlistId: Nullable<ID>
}

export type OpenPayload = PayloadAction<{ playlistId: ID }>

const initialState: PublishPlaylistConfirmationState = {
playlistId: null
}

const slice = createSlice({
name: 'applications/ui/publishPlaylistConfirmation',
initialState,
reducers: {
requestOpen: (_state, _action: OpenPayload) => {},
open: (state, action: OpenPayload) => {
state.playlistId = action.payload.playlistId
}
}
})

export const { open, requestOpen } = slice.actions
export const actions = slice.actions
export default slice.reducer
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ID } from '../../../models/Identifiers'

export type PublishPlaylistConfirmationModalState = {
isOpen: boolean
playlistId: ID | null
}
12 changes: 11 additions & 1 deletion apps/audius-client/packages/common/src/store/ui/toast/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createSlice } from '@reduxjs/toolkit'
import {
AddToastAction,
DissmissToastAction,
ManualClearToastAction,
ToastAction,
ToastState
} from './types'
Expand All @@ -20,6 +21,14 @@ const slice = createSlice({
const toast = action.payload
state.toasts.push(toast)
},
manualClearToast: (state, action: ManualClearToastAction) => {
const toastIdx = state.toasts.findIndex(
(t) => t.key === action.payload.key
)
// NOTE: Set the toast timeout to 0 so that the Toast component animates out and dismissed the toast
// Used for mobile toasts
state.toasts[toastIdx].timeout = 0
},
dismissToast: (state, action: DissmissToastAction) => {
const { key } = action.payload
const toasts = state.toasts.filter((toast) => toast.key !== key)
Expand All @@ -31,7 +40,8 @@ const slice = createSlice({
}
})

export const { toast, dismissToast, addToast, clearToasts } = slice.actions
export const { toast, dismissToast, addToast, clearToasts, manualClearToast } =
slice.actions

export const actions = slice.actions
export default slice.reducer
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type Toast = {
content: ToastContent
type?: ToastType
key: string
timeout?: number
timeout?: number | 'MANUAL'
}

export type ToastState = {
Expand All @@ -22,3 +22,4 @@ export type ToastAction = PayloadAction<{
}>
export type AddToastAction = PayloadAction<Toast>
export type DissmissToastAction = PayloadAction<{ key: string }>
export type ManualClearToastAction = PayloadAction<{ key: string }>
4 changes: 3 additions & 1 deletion apps/audius-client/packages/mobile/src/app/Drawers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { LockedContentDrawer } from 'app/components/locked-content-drawer'
import { OverflowMenuDrawer } from 'app/components/overflow-menu-drawer'
import { PlaybackRateDrawer } from 'app/components/playback-rate-drawer'
import { ProfileActionsDrawer } from 'app/components/profile-actions-drawer'
import { PublishPlaylistDrawer } from 'app/components/publish-playlist-drawer'
import { RateCtaDrawer } from 'app/components/rate-cta-drawer'
import { ShareDrawer } from 'app/components/share-drawer'
import { ShareToTikTokDrawer } from 'app/components/share-to-tiktok-drawer'
Expand Down Expand Up @@ -98,7 +99,8 @@ const commonDrawersMap: { [Modal in Modals]?: ComponentType } = {
DeletePlaylistConfirmation: DeletePlaylistConfirmationDrawer,
VipDiscord: VipDiscordDrawer,
ProfileActions: ProfileActionsDrawer,
PlaybackRate: PlaybackRateDrawer
PlaybackRate: PlaybackRateDrawer,
PublishPlaylistConfirmation: PublishPlaylistDrawer
}

const nativeDrawersMap: { [DrawerName in Drawer]?: ComponentType } = {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,13 @@ export const DetailsTile = ({
hideShare,
isPlaying,
isPlayable = true,
isPlaylist = false,
isPublished = true,
onPressEdit,
onPressFavorites,
onPressOverflow,
onPressPlay,
onPressPublish,
onPressRepost,
onPressReposts,
onPressSave,
Expand Down Expand Up @@ -383,10 +387,14 @@ export const DetailsTile = ({
hideRepost={hideRepost}
hideShare={hideShare}
isOwner={isOwner}
isPlaylist={isPlaylist}
isPublished={isPublished}
onPressEdit={onPressEdit}
onPressOverflow={onPressOverflow}
onPressRepost={onPressRepost}
onPressSave={onPressSave}
onPressShare={onPressShare}
onPressPublish={onPressPublish}
/>
</View>
{isGatedContentEnabled && doesUserHaveAccess && premiumConditions && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { FeatureFlags } from '@audius/common'
import { View } from 'react-native'

import IconKebabHorizontal from 'app/assets/images/iconKebabHorizontal.svg'
import IconPencil from 'app/assets/images/iconPencil.svg'
import IconRocket from 'app/assets/images/iconRocket.svg'
import IconShare from 'app/assets/images/iconShare.svg'
import { IconButton } from 'app/components/core'
import { FavoriteButton } from 'app/components/favorite-button'
import { RepostButton } from 'app/components/repost-button'
import { useFeatureFlag } from 'app/hooks/useRemoteConfig'
import { flexRowCentered, makeStyles } from 'app/styles'
import type { GestureResponderHandler } from 'app/types/gesture'
import { useThemeColors } from 'app/utils/theme'
Expand All @@ -13,11 +17,14 @@ type DetailsTileActionButtonsProps = {
hasReposted: boolean
hasSaved: boolean
isOwner: boolean
isPlaylist?: boolean
isPublished?: boolean
hideFavorite?: boolean
hideOverflow?: boolean
hideRepost?: boolean
hideShare?: boolean
onPressEdit?: GestureResponderHandler
onPressPublish?: GestureResponderHandler
onPressRepost?: GestureResponderHandler
onPressSave?: GestureResponderHandler
onPressShare?: GestureResponderHandler
Expand Down Expand Up @@ -54,19 +61,25 @@ const useStyles = makeStyles(({ palette }) => ({
export const DetailsTileActionButtons = ({
hasReposted,
hasSaved,
isPlaylist,
isOwner,
isPublished = true,
isPublished,
hideFavorite,
hideOverflow,
hideRepost,
hideShare,
onPressEdit,
onPressPublish,
onPressOverflow,
onPressRepost,
onPressSave,
onPressShare
}: DetailsTileActionButtonsProps) => {
const styles = useStyles()
const { neutralLight4 } = useThemeColors()
const { isEnabled: isPlaylistUpdatesEnabled } = useFeatureFlag(
FeatureFlags.PLAYLIST_UPDATES_PRE_QA
)

const repostButton = (
<RepostButton
Expand Down Expand Up @@ -105,11 +118,35 @@ export const DetailsTileActionButtons = ({
/>
)

const editButton = (
<IconButton
fill={neutralLight4}
icon={IconPencil}
onPress={onPressEdit}
styles={{ icon: styles.actionButton }}
/>
)

const publishButton = (
<IconButton
fill={neutralLight4}
icon={IconRocket}
// TODO: Add isDisabled based on if playlist is publishable logic
// Needs to check for hidden tracks and things like that
// isDisabled
onPress={onPressPublish}
styles={{ icon: styles.actionButton }}
/>
)

const isPlaylistOwner = isPlaylistUpdatesEnabled && isPlaylist && isOwner

return (
<View style={styles.root}>
{hideRepost ? null : repostButton}
{hideFavorite ? null : favoriteButton}
{isPlaylistOwner ? editButton : hideRepost ? null : repostButton}
{isPlaylistOwner || hideFavorite ? null : favoriteButton}
{hideShare ? null : shareButton}
{isPlaylistOwner && !isPublished ? publishButton : null}
{hideOverflow ? null : overflowMenu}
</View>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ export type DetailsTileProps = {
/** Is the item loaded and in a playable state */
isPlayable?: boolean

/** Is the tile being loaded for a playlist */
isPlaylist?: boolean

/** Is the item loaded published */
isPublished?: boolean

/** Function to call when the edit button is pressed */
onPressEdit?: GestureResponderHandler

/** Function to call when the favorites count is pressed */
onPressFavorites?: GestureResponderHandler

Expand All @@ -73,6 +82,9 @@ export type DetailsTileProps = {
/** Function to call when play button is pressed */
onPressPlay: GestureResponderHandler

/** Function to call when publish button is pressed */
onPressPublish?: GestureResponderHandler

/** Function to call when repost is pressed */
onPressRepost?: GestureResponderHandler

Expand Down
Loading

0 comments on commit 1ce85f6

Please sign in to comment.