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
1 change: 1 addition & 0 deletions packages/common/src/store/ui/modals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export * from './upload-confirmation-modal'
export * from './edit-access-confirmation-modal'
export * from './early-release-confirmation-modal'
export * from './publish-confirmation-modal'
export * from './publish-hidden-tracks-confirmation-modal'
export * from './hide-confirmation-modal'
export * from './create-chat-blast-modal'
export * from './delete-track-confirmation-modal'
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/store/ui/modals/parentSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const initialState: BasicModalsState = {
EditAccessConfirmation: { isOpen: false },
EarlyReleaseConfirmation: { isOpen: false },
PublishConfirmation: { isOpen: false },
PublishHiddenTracksConfirmation: { isOpen: false },
HideContentConfirmation: { isOpen: false },
ReplaceTrackConfirmation: { isOpen: false },
ReplaceTrackProgress: { isOpen: false },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { createAction } from '@reduxjs/toolkit'

import { createModal } from '../createModal'

export type PublishHiddenTracksConfirmationModalState = {
contentType: 'album' | 'playlist'
hiddenTrackCount: number
}

const publishHiddenTracksConfirmationModal =
createModal<PublishHiddenTracksConfirmationModalState>({
reducerPath: 'PublishHiddenTracksConfirmation',
initialState: {
isOpen: false,
contentType: 'playlist',
hiddenTrackCount: 0
},
sliceSelector: (state) => state.ui.modals
})

export const {
hook: usePublishHiddenTracksConfirmationModal,
reducer: publishHiddenTracksConfirmationModalReducer,
actions: publishHiddenTracksConfirmationModalActions
} = publishHiddenTracksConfirmationModal

/**
* The publish sagas block on one of these after opening the modal. They carry no
* payload: the saga already knows which collection it is publishing.
*/
export const publishHiddenTracksConfirmed = createAction(
'modals/PublishHiddenTracksConfirmation/confirmed'
)
export const keepHiddenTracksPrivate = createAction(
'modals/PublishHiddenTracksConfirmation/keepPrivate'
)
2 changes: 2 additions & 0 deletions packages/common/src/store/ui/modals/reducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { notificationModalReducer } from './notification-modal'
import parentReducer, { initialState } from './parentSlice'
import { premiumContentPurchaseModalReducer } from './premium-content-purchase-modal'
import { publishConfirmationModalReducer } from './publish-confirmation-modal'
import { publishHiddenTracksConfirmationModalReducer } from './publish-hidden-tracks-confirmation-modal'
import { receiveTokensModalReducer } from './receive-tokens-modal'
import { replaceTrackConfirmationModalReducer } from './replace-track-confirmation-modal'
import { replaceTrackProgressModalReducer } from './replace-track-progress-modal'
Expand Down Expand Up @@ -83,6 +84,7 @@ const combinedReducers = combineReducers({
ReplaceTrackConfirmation: replaceTrackConfirmationModalReducer,
ReplaceTrackProgress: replaceTrackProgressModalReducer,
PublishConfirmation: publishConfirmationModalReducer,
PublishHiddenTracksConfirmation: publishHiddenTracksConfirmationModalReducer,
HideContentConfirmation: hideContentConfirmationModalReducer,
ExternalWalletSignUp: externalWalletSignUpModalReducer,
ConnectedWallets: connectedWalletsModalReducer,
Expand Down
3 changes: 3 additions & 0 deletions packages/common/src/store/ui/modals/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { InboxUnavailableModalState } from './inbox-unavailable-modal'
import { LeavingAudiusModalState } from './leaving-audius-modal'
import { PremiumContentPurchaseModalState } from './premium-content-purchase-modal'
import { PublishConfirmationModalState } from './publish-confirmation-modal'
import { PublishHiddenTracksConfirmationModalState } from './publish-hidden-tracks-confirmation-modal'
import { ReplaceTrackConfirmationModalState } from './replace-track-confirmation-modal'
import { ReplaceTrackProgressModalState } from './replace-track-progress-modal'
import { UploadConfirmationModalState } from './upload-confirmation-modal'
Expand Down Expand Up @@ -93,6 +94,7 @@ export type Modals =
| 'EditAccessConfirmation'
| 'EarlyReleaseConfirmation'
| 'PublishConfirmation'
| 'PublishHiddenTracksConfirmation'
| 'HideContentConfirmation'
| 'WithdrawUSDCModal'
| 'USDCPurchaseDetailsModal'
Expand Down Expand Up @@ -148,6 +150,7 @@ export type StatefulModalsState = {
EditAccessConfirmation: EditAccessConfirmationModalState
EarlyReleaseConfirmation: EarlyReleaseConfirmationModalState
PublishConfirmation: PublishConfirmationModalState
PublishHiddenTracksConfirmation: PublishHiddenTracksConfirmationModalState
HideContentConfirmation: HideContentConfirmationModalState
DeleteTrackConfirmation: DeleteTrackConfirmationModalState
ReplaceTrackConfirmation: ReplaceTrackConfirmationModalState
Expand Down
1 change: 1 addition & 0 deletions packages/web/src/app/registerNiceModals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import 'components/locked-content-modal/LockedContentModal'
import 'components/payout-wallet-modal/PayoutWalletModal'
import 'components/premium-content-purchase-modal/PremiumContentPurchaseModal'
import 'components/publish-confirmation-modal/PublishConfirmationModal'
import 'components/publish-hidden-tracks-confirmation-modal/PublishHiddenTracksConfirmationModal'
import 'components/receive-tokens-modal/ReceiveTokensModal'
import 'components/replace-track-confirmation-modal/ReplaceTrackConfirmationModal'
import 'components/replace-track-progress-modal/ReplaceTrackProgressModal'
Expand Down
147 changes: 113 additions & 34 deletions packages/web/src/common/store/cache/collections/commonSagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
PlaylistContents,
ID,
Collection,
Track,
isContentUSDCPurchaseGated
} from '@audius/common/models'
import {
Expand All @@ -27,7 +28,10 @@ import {
getContext,
confirmerActions,
trackPageActions,
getSDK
getSDK,
publishHiddenTracksConfirmationModalActions,
publishHiddenTracksConfirmed,
keepHiddenTracksPrivate
} from '@audius/common/store'
import {
squashNewLines,
Expand All @@ -36,7 +40,15 @@ import {
updatePlaylistArtwork
} from '@audius/common/utils'
import { Id, OptionalId } from '@audius/sdk'
import { all, call, put, takeEvery, takeLatest } from 'typed-redux-saga'
import {
all,
call,
put,
race,
take,
takeEvery,
takeLatest
} from 'typed-redux-saga'

import { make } from 'common/store/analytics/actions'
import watchTrackErrors from 'common/store/cache/collections/errorSagas'
Expand Down Expand Up @@ -64,6 +76,78 @@ const messages = {
reorderStale: 'This collection was updated elsewhere. Try again.'
}

/** Tracks the user deliberately hid, as opposed to pre-releases. */
const getHiddenTracks = (playlistTracks: Track[] | null | undefined) =>
(playlistTracks ?? []).filter(
(track) => track.is_unlisted && !track.is_scheduled_release
)

/**
* Asks whether deliberately-hidden tracks should be published along with their
* collection. Resolves to true when there is nothing to ask about.
*
* Ask before kicking off the write: the publish path applies the result inside
* a confirmer callback, which serializes writes for the collection, so blocking
* there would hold that queue for as long as the modal is up.
*/
export function* confirmPublishHiddenTracks(
hiddenTrackCount: number,
isAlbum: boolean
) {
if (hiddenTrackCount === 0) return true

// Mobile renders no drawer for this modal yet, so prompting there would block
// the publish forever. Until the drawer lands it keeps the previous
// publish-everything behavior.
const isNativeMobile = yield* getContext('isNativeMobile')
if (isNativeMobile) return true

yield* put(
publishHiddenTracksConfirmationModalActions.open({
contentType: isAlbum ? 'album' : 'playlist',
hiddenTrackCount
})
)

const { confirmed } = yield* race({
confirmed: take(publishHiddenTracksConfirmed.type),
keepPrivate: take(keepHiddenTracksPrivate.type)
})

return !!confirmed
}

/**
* Publishes the hidden tracks of a collection that is being made public.
*
* Deliberately-hidden tracks are published only when `publishHiddenTracks` is
* set, which the caller obtains from `confirmPublishHiddenTracks`. Scheduled
* releases are separate: they are published only as an early release, which
* requires the collection itself to be a scheduled release whose tracks are all
* scheduled.
*/
export function* publishHiddenChildTracks(
playlist: Collection,
playlistTracks: Track[] | null | undefined,
publishHiddenTracks: boolean
) {
const tracks = playlistTracks ?? []
const isEachTrackScheduled = tracks.every(
(track) => track.is_unlisted && track.is_scheduled_release
)
const isEarlyRelease = !!playlist.is_scheduled_release && isEachTrackScheduled

for (const track of tracks) {
if (!track.is_unlisted) continue
const shouldPublish = track.is_scheduled_release
? isEarlyRelease
: publishHiddenTracks
if (shouldPublish) {
yield* put(trackPageActions.makeTrackPublic(track.track_id))
}
}
}

/** Counts instances of trackId in a playlist. */
const countTrackIds = (
playlistContents: PlaylistContents | undefined,
Expand Down Expand Up @@ -157,22 +241,16 @@ function* editPlaylistAsync(
playlistId
)

// Publish all hidden tracks
// If the playlist is a scheduled release
// AND all tracks are scheduled releases, publish them all
const isEachTrackScheduled = playlistTracksForPublish?.every(
(track) => track.is_unlisted && track.is_scheduled_release
const publishHiddenTracks = yield* confirmPublishHiddenTracks(
getHiddenTracks(playlistTracksForPublish).length,
!!playlist.is_album
)

yield* publishHiddenChildTracks(
playlistBeforeEdit,
playlistTracksForPublish,
publishHiddenTracks
)
const isEarlyRelease =
playlistBeforeEdit.is_scheduled_release && isEachTrackScheduled
for (const track of playlistTracksForPublish ?? []) {
if (
track.is_unlisted &&
(!track.is_scheduled_release || isEarlyRelease)
) {
yield* put(trackPageActions.makeTrackPublic(track.track_id))
}
}
}
} catch (error) {
if (onComplete) yield* call(onComplete, false, error as Error)
Expand Down Expand Up @@ -482,15 +560,25 @@ function* publishPlaylistAsync(
if (!playlist) return
const playlistWithPublishing = { ...playlist, _is_publishing: true }

// Ask before the write starts, so the modal never holds the confirmer queue.
const tracksBeforePublish = yield* call(
queryCollectionTracks,
action.playlistId
)
const publishHiddenTracks = yield* confirmPublishHiddenTracks(
getHiddenTracks(tracksBeforePublish).length,
!!action.isAlbum
)

yield* call(updateCollectionData, [
{ playlist_id: playlist.playlist_id, _is_publishing: true }
])

yield* call(
confirmPublishPlaylist,
yield* confirmPublishPlaylist(
userId,
action.playlistId,
playlistWithPublishing,
publishHiddenTracks,
action.dismissToastKey,
action.isAlbum
)
Expand All @@ -500,6 +588,7 @@ function* confirmPublishPlaylist(
userId: ID,
playlistId: ID,
playlist: Collection,
publishHiddenTracks: boolean,
dismissToastKey?: string,
isAlbum?: boolean
) {
Expand Down Expand Up @@ -534,22 +623,12 @@ function* confirmPublishPlaylist(
yield* call(updateCollectionData, [confirmedPlaylist])

const playlistTracks = yield* call(queryCollectionTracks, playlistId)
// Publish all hidden tracks
// If the playlist is a scheduled release
// AND all tracks are scheduled releases, publish them all
const isEachTrackScheduled = playlistTracks?.every(
(track) => track.is_unlisted && track.is_scheduled_release

yield* publishHiddenChildTracks(
playlist,
playlistTracks,
publishHiddenTracks
)
const isEarlyRelease =
playlist.is_scheduled_release && isEachTrackScheduled
for (const track of playlistTracks ?? []) {
if (
track.is_unlisted &&
(!track.is_scheduled_release || isEarlyRelease)
) {
yield* put(trackPageActions.makeTrackPublic(track.track_id))
}
}

if (dismissToastKey) {
yield* put(manualClearToast({ key: dismissToastKey }))
Expand Down
Loading
Loading