From 7493cff0d57edd6603a645c54c0271075392059c Mon Sep 17 00:00:00 2001 From: Andrew Mendelsohn Date: Wed, 30 Aug 2023 17:53:11 -0500 Subject: [PATCH] Fix validation on access and sale modal (#3981) --- packages/common/src/models/Track.ts | 1 + packages/common/src/utils/dogEarUtils.ts | 8 ++- .../data-entry/AccessAndSaleModalLegacy.tsx | 70 ++++++++---------- .../upload-page/fields/AccessAndSaleField.tsx | 72 +++++++++++++------ 4 files changed, 88 insertions(+), 63 deletions(-) diff --git a/packages/common/src/models/Track.ts b/packages/common/src/models/Track.ts index 998695eb7e..19ddc7548e 100644 --- a/packages/common/src/models/Track.ts +++ b/packages/common/src/models/Track.ts @@ -97,6 +97,7 @@ export type PremiumConditionsUSDCPurchase = { } export type PremiumConditions = + | {} | PremiumConditionsCollectibleGated | PremiumConditionsFollowGated | PremiumConditionsTipGated diff --git a/packages/common/src/utils/dogEarUtils.ts b/packages/common/src/utils/dogEarUtils.ts index 27430c0dbb..aa433a310c 100644 --- a/packages/common/src/utils/dogEarUtils.ts +++ b/packages/common/src/utils/dogEarUtils.ts @@ -1,3 +1,5 @@ +import { isEmpty } from 'lodash' + import { DogEarType } from 'models/DogEar' import { PremiumConditions } from 'models/Track' @@ -33,7 +35,11 @@ export const getDogEarType = ({ } // Show premium variants for track owners or if user does not yet have access - if ((isOwner || !doesUserHaveAccess) && premiumConditions != null) { + if ( + (isOwner || !doesUserHaveAccess) && + premiumConditions != null && + !isEmpty(premiumConditions) + ) { if ('usdc_purchase' in premiumConditions) { return DogEarType.USDC_PURCHASE } else if ('nft_collection' in premiumConditions) { diff --git a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx index a2529a22c9..444adba074 100644 --- a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx +++ b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx @@ -18,14 +18,14 @@ import { IconSpecialAccess, IconVisibilityPublic } from '@audius/stems' -import { set, isEmpty, get } from 'lodash' -import { z } from 'zod' +import { set, get } from 'lodash' import { toFormikValidationSchema } from 'zod-formik-adapter' import { TrackMetadataState } from 'components/track-availability-modal/types' import { defaultFieldVisibility } from 'pages/track-page/utils' import { AVAILABILITY_TYPE, + AccessAndSaleFormSchema, AccessAndSaleFormValues, AccessAndSaleMenuFields, FIELD_VISIBILITY, @@ -54,37 +54,16 @@ const messages = { errors: { price: { tooLow: 'Price must be at least $0.99', - tooHigh: 'Price must be less than $9.99' + tooHigh: 'Price must be less than $9999.99' }, preview: { tooEarly: 'Preview must start during the track', tooLate: - 'Preview must start at lest 15 seconds before the end of the track' + 'Preview must start at least 30 seconds before the end of the track' } } } -const AccessAndSaleFormSchema = (trackLength: number) => - z.object({ - [PREMIUM_CONDITIONS]: z.nullable( - z.object({ - // TODO: there are other types - usdc_purchase: z.object({ - price: z - .number() - .lte(999, messages.errors.price.tooHigh) - .gte(99, messages.errors.price.tooLow) - }) - }) - ), - [PREVIEW]: z.optional( - z - .number() - .gte(0, messages.errors.preview.tooEarly) - .lte(trackLength - 15, messages.errors.preview.tooLate) - ) - }) - type AccessAndSaleModalLegacyProps = { isRemix: boolean isUpload: boolean @@ -155,23 +134,34 @@ export const AccessAndSaleModalLegacy = ( const onSubmit = (values: AccessAndSaleFormValues) => { let newState = { - ...metadataState, - is_premium: !isEmpty(values[PREMIUM_CONDITIONS]), - premium_conditions: values[PREMIUM_CONDITIONS], - unlisted: values.is_unlisted, - preview_start_seconds: values[PREVIEW] ?? 0 + ...metadataState } - if ( - get(values, AVAILABILITY_TYPE) === TrackAvailabilityType.USDC_PURCHASE - ) { - newState.is_premium = true - const price = Math.round(get(values, PRICE)) - newState.premium_conditions = { - // @ts-ignore splits get added in saga - usdc_purchase: { - price + const availabilityType = get(values, AVAILABILITY_TYPE) + switch (availabilityType) { + case TrackAvailabilityType.PUBLIC: { + newState.is_premium = false + newState.unlisted = false + newState.premium_conditions = {} + break + } + case TrackAvailabilityType.USDC_PURCHASE: { + newState.is_premium = true + const price = Math.round(get(values, PRICE)) + newState.premium_conditions = { + // @ts-ignore splits get added in saga + usdc_purchase: { + price + } } + newState.preview_start_seconds = get(values, PREVIEW) ?? 0 + break + } + case TrackAvailabilityType.COLLECTIBLE_GATED: + case TrackAvailabilityType.SPECIAL_ACCESS: { + newState.is_premium = true + newState.premium_conditions = get(values, PREMIUM_CONDITIONS) + break } } @@ -179,6 +169,7 @@ export const AccessAndSaleModalLegacy = ( newState = { ...newState, ...(get(values, FIELD_VISIBILITY) ?? undefined), + premium_conditions: {}, unlisted: true } } else { @@ -218,7 +209,6 @@ export const AccessAndSaleModalLegacy = ( initialValues={initialValues} onSubmit={onSubmit} validationSchema={toFormikValidationSchema( - // @ts-ignore AccessAndSaleFormSchema(trackLength) )} menuFields={ diff --git a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx index 2f4bfa3d07..a4dc3b2c7d 100644 --- a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx +++ b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx @@ -109,12 +109,12 @@ const messages = { errors: { price: { tooLow: 'Price must be at least $0.99.', - tooHigh: 'Price must be less than $9.99.' + tooHigh: 'Price must be less than $9999.99.' }, preview: { tooEarly: 'Preview must start during the track.', tooLate: - 'Preview must start at lest 15 seconds before the end of the track.' + 'Preview must start at least 30 seconds before the end of the track.' } } } @@ -140,26 +140,47 @@ export type AccessAndSaleFormValues = { [PREVIEW]?: number } -const AccessAndSaleFormSchema = (trackLength: number) => - z.object({ - [PREMIUM_CONDITIONS]: z.nullable( - z.object({ - // TODO: there are other types - usdc_purchase: z.object({ - price: z - .number() - .lte(999, messages.errors.price.tooHigh) - .gte(99, messages.errors.price.tooLow) +export const AccessAndSaleFormSchema = (trackLength: number) => + z + .object({ + [PREMIUM_CONDITIONS]: z.nullable( + z.object({ + // TODO: there are other types + usdc_purchase: z.optional( + z.object({ + price: z + .number() + .lte(999999, messages.errors.price.tooHigh) + .gte(99, messages.errors.price.tooLow) + }) + ) }) - }) - ), - [PREVIEW]: z.optional( - z - .number() - .gte(0, messages.errors.preview.tooEarly) - .lte(trackLength - 15, messages.errors.preview.tooLate) + ), + [PREVIEW]: z.optional(z.nullable(z.number())) + }) + .refine( + (values) => { + const formValues = values as AccessAndSaleFormValues + if (isPremiumContentUSDCPurchaseGated(formValues[PREMIUM_CONDITIONS])) { + return formValues[PREVIEW] !== undefined && formValues[PREVIEW] >= 0 + } + return true + }, + { message: messages.errors.preview.tooEarly, path: [PREVIEW] } + ) + .refine( + (values) => { + const formValues = values as AccessAndSaleFormValues + if (isPremiumContentUSDCPurchaseGated(formValues[PREMIUM_CONDITIONS])) { + return ( + formValues[PREVIEW] === undefined || + (formValues[PREVIEW] >= 0 && formValues[PREVIEW] < trackLength - 30) + ) + } + return true + }, + { message: messages.errors.preview.tooLate, path: [PREVIEW] } ) - }) type AccessAndSaleFieldProps = { isUpload?: boolean @@ -470,8 +491,14 @@ export const AccessAndSaleMenuFields = (props: AccesAndSaleMenuFieldsProps) => { } case TrackAvailabilityType.USDC_PURCHASE: { if (!isPremiumContentUSDCPurchaseGated(premiumConditionsValue)) { - setPremiumConditionsValue(null) + setPremiumConditionsValue({ + // @ts-ignore splits added in saga + usdc_purchase: { + price: 0 + } + }) } + if (!previewValue) { setPreviewValue(0) } @@ -492,9 +519,10 @@ export const AccessAndSaleMenuFields = (props: AccesAndSaleMenuFieldsProps) => { isPremiumContentCollectibleGated(premiumConditionsValue) ) break - setPremiumConditionsValue(null) + setPremiumConditionsValue({}) break case TrackAvailabilityType.HIDDEN: + setPremiumConditionsValue({}) if (!fieldVisibilityValue) break setfieldVisibilityValue({ ...fieldVisibilityValue,