diff --git a/packages/web/src/pages/upload-page/forms/UploadTrackForm.test.ts b/packages/web/src/pages/upload-page/forms/UploadTrackForm.test.ts new file mode 100644 index 00000000000..e5763dfb480 --- /dev/null +++ b/packages/web/src/pages/upload-page/forms/UploadTrackForm.test.ts @@ -0,0 +1,92 @@ +import { TrackMetadataForUpload } from '@audius/common/store' +import { describe, expect, it } from 'vitest' + +import { getTrackEditInitialMetadata } from './UploadTrackForm' + +const makeMetadata = ( + overrides: Partial = {} +): TrackMetadataForUpload => + ({ + title: 'leftover szn - Run It Back', + genre: 'Electronic', + ...overrides + }) as TrackMetadataForUpload + +describe('getTrackEditInitialMetadata', () => { + it('seeds blank values for a fresh upload', () => { + const result = getTrackEditInitialMetadata(makeMetadata()) + + expect(result.description).toBe('') + expect(result.tags).toBe('') + expect(result.stems).toEqual([]) + expect(result.isrc).toBe('') + expect(result.iswc).toBe('') + expect(result.field_visibility).toEqual({ + genre: true, + mood: true, + tags: true, + share: false, + play_count: false, + remixes: true + }) + }) + + it('seeds from initialMetadata when the track has nothing yet', () => { + const result = getTrackEditInitialMetadata(makeMetadata(), { + description: 'seeded description', + tags: 'seeded,tags' + }) + + expect(result.description).toBe('seeded description') + expect(result.tags).toBe('seeded,tags') + }) + + // Regression: EditTrackForm runs with `enableReinitialize`, and + // `formState.tracks` is rewritten with the user's edits on every submit. When + // this function blanked description/tags unconditionally, that reset wiped + // them and the track published with no description and no tags. + it('preserves user-entered values when the form reinitializes', () => { + const edited = makeMetadata({ + description: 'Stream elsewhere & follow on IG', + tags: 'dubstep,bassmusic,bass', + isrc: 'USX9P1234567', + iswc: 'T-123.456.789-0', + stems: [{ metadata: {} } as any], + collaborators: [{ user_id: 12345 } as any] + }) + + const result = getTrackEditInitialMetadata(edited) + + expect(result.description).toBe('Stream elsewhere & follow on IG') + expect(result.tags).toBe('dubstep,bassmusic,bass') + expect(result.isrc).toBe('USX9P1234567') + expect(result.iswc).toBe('T-123.456.789-0') + expect(result.stems).toHaveLength(1) + expect(result.collaborators).toEqual([{ user_id: 12345 }]) + }) + + it('preserves a user-disabled remixes visibility across reinitialization', () => { + const result = getTrackEditInitialMetadata( + makeMetadata({ + field_visibility: { + genre: true, + mood: true, + tags: true, + share: false, + play_count: false, + remixes: false + } + }) + ) + + expect(result.field_visibility?.remixes).toBe(false) + }) + + it('is stable across repeated calls so the form does not reset spuriously', () => { + const metadata = makeMetadata({ description: 'kept', tags: 'a,b' }) + + expect(getTrackEditInitialMetadata(metadata)).toEqual( + getTrackEditInitialMetadata(metadata) + ) + }) +}) diff --git a/packages/web/src/pages/upload-page/forms/UploadTrackForm.tsx b/packages/web/src/pages/upload-page/forms/UploadTrackForm.tsx index 280422c2a74..5757bfdec5b 100644 --- a/packages/web/src/pages/upload-page/forms/UploadTrackForm.tsx +++ b/packages/web/src/pages/upload-page/forms/UploadTrackForm.tsx @@ -5,10 +5,12 @@ import { TrackForUpload, TrackMetadataForUpload } from '@audius/common/store' -import dayjs from 'dayjs' import { EditTrackForm } from 'components/edit-track/EditTrackForm' -import { TrackEditFormValues } from 'components/edit-track/types' +import { + SingleTrackEditValues, + TrackEditFormValues +} from 'components/edit-track/types' type UploadTrackFormProps = { formState: TrackFormState @@ -25,6 +27,41 @@ const defaultHiddenFields = { // REMIXES handled by a separate field } +/** + * Seeds the edit form for one track. + * + * `EditTrackForm` runs with `enableReinitialize`, so this value is not just the + * first render's defaults — Formik resets the whole form to it whenever it + * deep-changes. `formState.tracks` is rewritten with the user's edits every + * time the form is submitted (see `onSubmit` below, and `EditPage.onContinue`, + * which calls `setFormState` while still on the edit phase), so a recompute is + * routine rather than exceptional. + * + * That makes it critical to prefer what's already on `track.metadata` over the + * `initialMetadata` seed. Blanking `description`/`tags`/`stems` unconditionally + * discarded whatever the user had typed on every reset, and publishing after + * that point uploaded a track with an empty description and no tags. + */ +export const getTrackEditInitialMetadata = ( + metadata: TrackMetadataForUpload, + initialMetadata?: Partial +): SingleTrackEditValues => + ({ + ...metadata, + ...initialMetadata, + description: metadata.description ?? initialMetadata?.description ?? '', + tags: metadata.tags ?? initialMetadata?.tags ?? '', + field_visibility: { + ...defaultHiddenFields, + ...initialMetadata?.field_visibility, + ...metadata.field_visibility, + remixes: metadata.field_visibility?.remixes ?? true + }, + stems: metadata.stems ?? initialMetadata?.stems ?? [], + isrc: metadata.isrc ?? initialMetadata?.isrc ?? '', + iswc: metadata.iswc ?? initialMetadata?.iswc ?? '' + }) as SingleTrackEditValues + export const UploadTrackForm = (props: UploadTrackFormProps) => { const { formState, onContinue, initialMetadata } = props const { tracks } = formState @@ -33,23 +70,9 @@ export const UploadTrackForm = (props: UploadTrackFormProps) => { () => ({ trackMetadatasIndex: 0, tracks: tracks as TrackForUpload[], - trackMetadatas: tracks.map((track) => ({ - ...track.metadata, - ...initialMetadata, - description: initialMetadata?.description ?? '', - releaseDate: initialMetadata?.release_date - ? new Date(initialMetadata.release_date) - : new Date(dayjs().toString()), - tags: initialMetadata?.tags ?? '', - field_visibility: { - ...defaultHiddenFields, - ...initialMetadata?.field_visibility, - remixes: true - }, - stems: initialMetadata?.stems ?? [], - isrc: initialMetadata?.isrc ?? '', - iswc: initialMetadata?.iswc ?? '' - })) + trackMetadatas: tracks.map((track) => + getTrackEditInitialMetadata(track.metadata, initialMetadata) + ) }), [tracks, initialMetadata] )