Skip to content

Commit

Permalink
[C-2689] Add upload confirmation modal (#3934)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Shanks committed Aug 23, 2023
1 parent eb41fd3 commit b1fbef8
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 4 deletions.
4 changes: 4 additions & 0 deletions packages/common/src/store/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ import { StripeModalState } from './ui/stripe-modal/types'
import theme, { ThemeState } from './ui/theme/slice'
import toastReducer from './ui/toast/slice'
import transactionDetailsReducer from './ui/transaction-details/slice'
import uploadConfirmationReducer from './ui/upload-confirmation-modal/slice'
import { UploadConfirmationModalState } from './ui/upload-confirmation-modal/types'
import vipDiscordModalReducer from './ui/vip-discord-modal/slice'
import { VipDiscordModalState } from './ui/vip-discord-modal/types'
import upload from './upload/reducer'
Expand Down Expand Up @@ -202,6 +204,7 @@ export const reducers = () => ({
searchUsersModal: searchUsersModalReducer,
toast: toastReducer,
transactionDetails: transactionDetailsReducer,
uploadConfirmationModal: uploadConfirmationReducer,
userList: combineReducers({
followers: followersUserListReducer,
following: followingUserListReducer,
Expand Down Expand Up @@ -327,6 +330,7 @@ export type CommonState = {
stripeModal: StripeModalState
toast: ToastState
transactionDetails: TransactionDetailsState
uploadConfirmationModal: UploadConfirmationModalState
userList: {
mutuals: ReturnType<typeof mutualsUserListReducer>
notifications: ReturnType<typeof notificationsUserListReducer>
Expand Down
4 changes: 3 additions & 1 deletion packages/common/src/store/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
publishPlaylistConfirmationModalUISagas,
mobileOverflowMenuUISagas,
shareModalUISagas,
stripeModalUISagas
stripeModalUISagas,
uploadConfirmationModalUISagas
} from 'store/ui'

import { playlistUpdatesSagas } from './playlist-updates'
Expand Down Expand Up @@ -56,6 +57,7 @@ export const sagas = (_ctx: CommonStoreContext) => ({
mobileOverflowMenuUI: mobileOverflowMenuUISagas,
deletePlaylistConfirmationModalUI: deletePlaylistConfirmationModalUISagas,
duplidateAddConfirmationModalUI: duplicateAddConfirmationModalUISagas,
uploadConfirmationModalUI: uploadConfirmationModalUISagas,
publishPlaylistConfirmationModalUI: publishPlaylistConfirmationModalUISagas,
player: playerSagas,
playbackPosition: playbackPositionSagas,
Expand Down
8 changes: 8 additions & 0 deletions packages/common/src/store/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ export * from './buy-audio/types'
export * from './buy-audio/constants'
export * as buyAudioSelectors from './buy-audio/selectors'

export * as uploadConfirmationModalUISelectors from './upload-confirmation-modal/selectors'
export {
default as uploadConfirmationModalUIReducer,
actions as uploadConfirmationModalUIActions
} from './upload-confirmation-modal/slice'
export { default as uploadConfirmationModalUISagas } from './upload-confirmation-modal/sagas'
export * from './upload-confirmation-modal/types'

export {
default as withdrawUSDCReducer,
actions as withdrawUSDCActions
Expand Down
3 changes: 2 additions & 1 deletion packages/common/src/store/ui/modals/parentSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ export const initialState: BasicModalsState = {
PremiumContentPurchase: { isOpen: false },
CreateChatModal: { isOpen: false },
LeavingAudiusModal: { isOpen: false },
InboxUnavailableModal: { isOpen: false }
InboxUnavailableModal: { isOpen: false },
UploadConfirmation: { isOpen: false }
}

const slice = createSlice({
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/store/ui/modals/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type Modals =
| 'CreateChatModal'
| 'InboxUnavailableModal'
| 'LeavingAudiusModal'
| 'UploadConfirmation'

export type BasicModalsState = {
[modal in Modals]: BaseModalState
Expand Down
20 changes: 20 additions & 0 deletions packages/common/src/store/ui/upload-confirmation-modal/sagas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { takeEvery } from 'redux-saga/effects'
import { put } from 'typed-redux-saga'

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

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

function* handleRequestOpen(action: OpenPayload) {
const { payload } = action
yield* put(open(payload))
yield* put(setVisibility({ modal: 'UploadConfirmation', 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,7 @@
import { CommonState } from 'store/commonStore'

export const getHasPublicTracks = (state: CommonState) =>
state.ui.uploadConfirmationModal.hasPublicTracks

export const getConfirmCallback = (state: CommonState) =>
state.ui.uploadConfirmationModal.confirmCallback
30 changes: 30 additions & 0 deletions packages/common/src/store/ui/upload-confirmation-modal/slice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'

import { UploadConfirmationState } from './types'

export type OpenPayload = PayloadAction<{
hasPublicTracks: boolean
confirmCallback: () => void
}>

const initialState: UploadConfirmationState = {
hasPublicTracks: true,
confirmCallback: () => {}
}

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

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,8 @@
export type UploadConfirmationState = {
hasPublicTracks: boolean
confirmCallback: () => void
}

export type UploadConfirmationModalState = {
isOpen: boolean
} & UploadConfirmationState
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.titleIcon path {
fill: var(--neutral-light-2);
}

.modalText {
text-align: center;
}

.modalButton {
font-size: var(--font-l) !important;
font-weight: var(--font-bold) !important;
}

.modalFooter {
column-gap: var(--unit-2);
padding: 0 var(--unit-6) var(--unit-6);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useCallback } from 'react'

import { uploadConfirmationModalUISelectors } from '@audius/common'
import {
Button,
ButtonType,
IconUpload,
Modal,
ModalContent,
ModalContentText,
ModalFooter,
ModalHeader,
ModalTitle
} from '@audius/stems'

import { useModalState } from 'common/hooks/useModalState'
import { useSelector } from 'common/hooks/useSelector'
import { Text } from 'components/typography'

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

const { getConfirmCallback, getHasPublicTracks } =
uploadConfirmationModalUISelectors

const messages = {
title: 'Confirm Upload',
publicDescription:
'Ready to begin uploading? Your followers will be notified once your upload is complete.',
hiddenDescription: 'Ready to begin uploading?',
cancel: 'Go Back',
upload: 'Upload'
}

export const UploadConfirmationModal = () => {
const confirmCallback = useSelector(getConfirmCallback)
const hasPublicTracks = useSelector(getHasPublicTracks)
const [isOpen, setIsOpen] = useModalState('UploadConfirmation')

const onClose = useCallback(() => {
setIsOpen(false)
}, [setIsOpen])

const handleConfirm = useCallback(() => {
confirmCallback()
onClose()
}, [confirmCallback, onClose])

return (
<Modal isOpen={isOpen} onClose={onClose} size='small'>
<ModalHeader>
<ModalTitle
icon={<IconUpload className={styles.titleIcon} />}
title={
<Text
variant='label'
size='xLarge'
strength='strong'
color='neutralLight2'
>
{messages.title}
</Text>
}
/>
</ModalHeader>
<ModalContent>
<ModalContentText className={styles.modalText}>
{hasPublicTracks
? messages.publicDescription
: messages.hiddenDescription}
</ModalContentText>
</ModalContent>
<ModalFooter className={styles.modalFooter}>
<Button
textClassName={styles.modalButton}
fullWidth
text={messages.cancel}
type={ButtonType.COMMON}
onClick={onClose}
/>
<Button
textClassName={styles.modalButton}
fullWidth
text={messages.upload}
type={ButtonType.PRIMARY}
onClick={handleConfirm}
/>
</ModalFooter>
</Modal>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UploadConfirmationModal } from './UploadConfirmationModal'
2 changes: 2 additions & 0 deletions packages/web/src/pages/modals/Modals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import ConnectedMobileOverflowModal from 'components/track-overflow-modal/Connec
import { TransactionDetailsModal } from 'components/transaction-details-modal'
import UnfollowConfirmationModal from 'components/unfollow-confirmation-modal/UnfollowConfirmationModal'
import UnloadDialog from 'components/unload-dialog/UnloadDialog'
import { UploadConfirmationModal } from 'components/upload-confirmation-modal'
import TierExplainerModal from 'components/user-badges/TierExplainerModal'
import ConnectedUserListModal from 'components/user-list-modal/ConnectedUserListModal'
import AudioBreakdownModal from 'pages/audio-rewards-page/components/modals/AudioBreakdownModal'
Expand All @@ -60,6 +61,7 @@ const commonModalsMap: { [Modal in ModalTypes]?: ComponentType } = {
TiersExplainer: TierExplainerModal,
DeletePlaylistConfirmation: DeletePlaylistConfirmationModal,
DuplicateAddConfirmation: DuplicateAddConfirmationModal,
UploadConfirmation: UploadConfirmationModal,
BuyAudio: BuyAudioModal,
BuyAudioRecovery: BuyAudioRecoveryModal,
TransactionDetails: TransactionDetailsModal,
Expand Down
28 changes: 26 additions & 2 deletions packages/web/src/pages/upload-page/UploadPageNew.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useCallback, useEffect, useMemo, useState } from 'react'

import { UploadType, uploadActions } from '@audius/common'
import {
UploadType,
uploadActions,
uploadConfirmationModalUIActions
} from '@audius/common'
import { useDispatch } from 'react-redux'

import Header from 'components/header/desktop/Header'
Expand All @@ -13,6 +17,8 @@ import { EditPage } from './pages/EditPage'
import { UploadFormState } from './types'

const { uploadTracks } = uploadActions
const { requestOpen: openUploadConfirmationModal } =
uploadConfirmationModalUIActions

const messages = {
selectPageTitle: 'Upload Your Music',
Expand Down Expand Up @@ -99,7 +105,11 @@ export const UploadPageNew = () => {
formState={formState}
onContinue={(formState: UploadFormState) => {
setFormState(formState)
setPhase(Phase.FINISH)
const hasPublicTracks =
formState.tracks?.some(
(track) => !track.metadata.is_unlisted
) ?? true
openUploadConfirmation(hasPublicTracks)
}}
/>
)
Expand All @@ -123,6 +133,20 @@ export const UploadPageNew = () => {
}
}

const openUploadConfirmation = useCallback(
(hasPublicTracks: boolean) => {
dispatch(
openUploadConfirmationModal({
hasPublicTracks,
confirmCallback: () => {
setPhase(Phase.FINISH)
}
})
)
},
[dispatch]
)

const handleUpload = useCallback(() => {
if (!formState.tracks) return
const { tracks } = formState
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/store/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
remoteConfigSagas,
deletePlaylistConfirmationModalUISagas as deletePlaylistConfirmationModalSagas,
duplicateAddConfirmationModalUISagas as duplicateAddConfirmationModalSagas,
uploadConfirmationModalUISagas as uploadConfirmationModalSagas,
publishPlaylistConfirmationModalUISagas as publishPlaylistConfirmationModalSagas,
mobileOverflowMenuUISagas as overflowMenuSagas,
shareModalUISagas as shareModalSagas,
Expand Down Expand Up @@ -198,6 +199,7 @@ export default function* rootSaga() {
stemUploadSagas(),
themeSagas(),
tokenDashboardSagas(),
uploadConfirmationModalSagas(),
userListModalSagas(),
vipDiscordModalSagas(),
commonReachabilitySagas(),
Expand Down

0 comments on commit b1fbef8

Please sign in to comment.