From c4ac350b77ce7ed499f560104083de7ab7f91bfc Mon Sep 17 00:00:00 2001 From: amendelsohn Date: Wed, 30 Aug 2023 15:53:27 -0500 Subject: [PATCH 1/6] fix validaiton on legacy access and sale --- .../data-entry/AccessAndSaleModalLegacy.tsx | 64 +++++++++++++------ .../upload-page/fields/AccessAndSaleField.tsx | 6 +- 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx index a2529a22c9..6f454f8ef3 100644 --- a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx +++ b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx @@ -41,6 +41,11 @@ import { SpecialAccessType } from 'pages/upload-page/fields/availability/Special import styles from './AccessAndSaleModalLegacy.module.css' import { ContextualMenu } from './ContextualMenu' +import { + PremiumConditionsFollowUserId, + PremiumConditionsNFTCollection, + PremiumConditionsTipUserId +} from '@audius/sdk' const messages = { title: 'Access & Sale', @@ -65,25 +70,46 @@ const messages = { } 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) + z + .object({ + [PREMIUM_CONDITIONS]: z.nullable( + z.object({ + // TODO: there are other types + usdc_purchase: z.optional( + 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) + ), + [PREVIEW]: z.optional(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 - 15) + ) + } + return true + }, + { message: messages.errors.preview.tooLate, path: [PREVIEW] } ) - }) type AccessAndSaleModalLegacyProps = { isRemix: boolean @@ -158,8 +184,7 @@ export const AccessAndSaleModalLegacy = ( ...metadataState, is_premium: !isEmpty(values[PREMIUM_CONDITIONS]), premium_conditions: values[PREMIUM_CONDITIONS], - unlisted: values.is_unlisted, - preview_start_seconds: values[PREVIEW] ?? 0 + unlisted: values.is_unlisted } if ( @@ -173,6 +198,7 @@ export const AccessAndSaleModalLegacy = ( price } } + newState.preview_start_seconds = values[PREVIEW] ?? 0 } if (get(values, AVAILABILITY_TYPE) === TrackAvailabilityType.HIDDEN) { diff --git a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx index 2f4bfa3d07..22a2939b84 100644 --- a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx +++ b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx @@ -23,7 +23,7 @@ import { RadioButtonGroup } from '@audius/stems' import cn from 'classnames' -import { useField } from 'formik' +import { useField, useFormikContext } from 'formik' import { get, isEmpty, set } from 'lodash' import { useSelector } from 'react-redux' import { z } from 'zod' @@ -516,6 +516,10 @@ export const AccessAndSaleMenuFields = (props: AccesAndSaleMenuFieldsProps) => { ] ) + const { values, errors } = useFormikContext() + + console.log({ values, errors }) + return (
{isRemix ? ( From d5f81fb23edc184f59602fcaaf73b6cf2ff8094d Mon Sep 17 00:00:00 2001 From: amendelsohn Date: Wed, 30 Aug 2023 16:18:34 -0500 Subject: [PATCH 2/6] fix availability type switching --- .../data-entry/AccessAndSaleModalLegacy.tsx | 91 ++++++------------- .../upload-page/fields/AccessAndSaleField.tsx | 66 ++++++++++---- 2 files changed, 73 insertions(+), 84 deletions(-) diff --git a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx index 6f454f8ef3..2ede316631 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, @@ -38,14 +38,8 @@ import { SPECIAL_ACCESS_TYPE } from 'pages/upload-page/fields/AccessAndSaleField' import { SpecialAccessType } from 'pages/upload-page/fields/availability/SpecialAccessFields' - import styles from './AccessAndSaleModalLegacy.module.css' import { ContextualMenu } from './ContextualMenu' -import { - PremiumConditionsFollowUserId, - PremiumConditionsNFTCollection, - PremiumConditionsTipUserId -} from '@audius/sdk' const messages = { title: 'Access & Sale', @@ -69,48 +63,6 @@ const messages = { } } -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(999, messages.errors.price.tooHigh) - .gte(99, messages.errors.price.tooLow) - }) - ) - }) - ), - [PREVIEW]: z.optional(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 - 15) - ) - } - return true - }, - { message: messages.errors.preview.tooLate, path: [PREVIEW] } - ) - type AccessAndSaleModalLegacyProps = { isRemix: boolean isUpload: boolean @@ -181,24 +133,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 + ...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 + 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 } - newState.preview_start_seconds = values[PREVIEW] ?? 0 } if (get(values, AVAILABILITY_TYPE) === TrackAvailabilityType.HIDDEN) { @@ -244,7 +206,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 22a2939b84..7f25d444e5 100644 --- a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx +++ b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx @@ -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(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) + ), + [PREVIEW]: z.optional(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 - 15) + ) + } + 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) } @@ -495,6 +522,7 @@ export const AccessAndSaleMenuFields = (props: AccesAndSaleMenuFieldsProps) => { setPremiumConditionsValue(null) break case TrackAvailabilityType.HIDDEN: + setPremiumConditionsValue(null) if (!fieldVisibilityValue) break setfieldVisibilityValue({ ...fieldVisibilityValue, From 8821d450bd842fdc85f57dcfbe54c2e6787a393d Mon Sep 17 00:00:00 2001 From: amendelsohn Date: Wed, 30 Aug 2023 16:21:13 -0500 Subject: [PATCH 3/6] remove debug logs; lint fixes --- .../src/components/data-entry/AccessAndSaleModalLegacy.tsx | 1 + .../web/src/pages/upload-page/fields/AccessAndSaleField.tsx | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx index 2ede316631..d60ca78380 100644 --- a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx +++ b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx @@ -38,6 +38,7 @@ import { SPECIAL_ACCESS_TYPE } from 'pages/upload-page/fields/AccessAndSaleField' import { SpecialAccessType } from 'pages/upload-page/fields/availability/SpecialAccessFields' + import styles from './AccessAndSaleModalLegacy.module.css' import { ContextualMenu } from './ContextualMenu' diff --git a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx index 7f25d444e5..ee9ef7688d 100644 --- a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx +++ b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx @@ -23,7 +23,7 @@ import { RadioButtonGroup } from '@audius/stems' import cn from 'classnames' -import { useField, useFormikContext } from 'formik' +import { useField } from 'formik' import { get, isEmpty, set } from 'lodash' import { useSelector } from 'react-redux' import { z } from 'zod' @@ -544,10 +544,6 @@ export const AccessAndSaleMenuFields = (props: AccesAndSaleMenuFieldsProps) => { ] ) - const { values, errors } = useFormikContext() - - console.log({ values, errors }) - return (
{isRemix ? ( From 02bde6ad03ed4f027e8109ea1d23ae072458a372 Mon Sep 17 00:00:00 2001 From: amendelsohn Date: Wed, 30 Aug 2023 16:39:45 -0500 Subject: [PATCH 4/6] update thresholds --- .../src/pages/upload-page/fields/AccessAndSaleField.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx index ee9ef7688d..6be9e8bb20 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 lest 30 seconds before the end of the track.' } } } @@ -150,7 +150,7 @@ export const AccessAndSaleFormSchema = (trackLength: number) => z.object({ price: z .number() - .lte(999, messages.errors.price.tooHigh) + .lte(999999, messages.errors.price.tooHigh) .gte(99, messages.errors.price.tooLow) }) ) @@ -174,7 +174,7 @@ export const AccessAndSaleFormSchema = (trackLength: number) => if (isPremiumContentUSDCPurchaseGated(formValues[PREMIUM_CONDITIONS])) { return ( formValues[PREVIEW] === undefined || - (formValues[PREVIEW] >= 0 && formValues[PREVIEW] < trackLength - 15) + (formValues[PREVIEW] >= 0 && formValues[PREVIEW] < trackLength - 30) ) } return true From 00362e0bf88805e395ee0796b91a1bac5927bc63 Mon Sep 17 00:00:00 2001 From: amendelsohn Date: Wed, 30 Aug 2023 16:58:20 -0500 Subject: [PATCH 5/6] null out premium_conditions --- .../web/src/components/data-entry/AccessAndSaleModalLegacy.tsx | 2 ++ .../web/src/pages/upload-page/fields/AccessAndSaleField.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx index d60ca78380..040226cb74 100644 --- a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx +++ b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx @@ -142,6 +142,7 @@ export const AccessAndSaleModalLegacy = ( case TrackAvailabilityType.PUBLIC: { newState.is_premium = false newState.unlisted = false + newState.premium_conditions = null break } case TrackAvailabilityType.USDC_PURCHASE: { @@ -168,6 +169,7 @@ export const AccessAndSaleModalLegacy = ( newState = { ...newState, ...(get(values, FIELD_VISIBILITY) ?? undefined), + premium_conditions: null, unlisted: true } } else { diff --git a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx index 6be9e8bb20..c25461bcbf 100644 --- a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx +++ b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx @@ -156,7 +156,7 @@ export const AccessAndSaleFormSchema = (trackLength: number) => ) }) ), - [PREVIEW]: z.optional(z.number()) + [PREVIEW]: z.optional(z.nullable(z.number())) }) .refine( (values) => { From 8968328c5fd6bc4970958d248778b9b90e6302a5 Mon Sep 17 00:00:00 2001 From: amendelsohn Date: Wed, 30 Aug 2023 17:41:47 -0500 Subject: [PATCH 6/6] use {} for empty premium_conditions; fix limits --- packages/common/src/models/Track.ts | 1 + packages/common/src/utils/dogEarUtils.ts | 8 +++++++- .../components/data-entry/AccessAndSaleModalLegacy.tsx | 8 ++++---- .../src/pages/upload-page/fields/AccessAndSaleField.tsx | 6 +++--- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/common/src/models/Track.ts b/packages/common/src/models/Track.ts index 7adb405908..0cf821833f 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 040226cb74..444adba074 100644 --- a/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx +++ b/packages/web/src/components/data-entry/AccessAndSaleModalLegacy.tsx @@ -54,12 +54,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' } } } @@ -142,7 +142,7 @@ export const AccessAndSaleModalLegacy = ( case TrackAvailabilityType.PUBLIC: { newState.is_premium = false newState.unlisted = false - newState.premium_conditions = null + newState.premium_conditions = {} break } case TrackAvailabilityType.USDC_PURCHASE: { @@ -169,7 +169,7 @@ export const AccessAndSaleModalLegacy = ( newState = { ...newState, ...(get(values, FIELD_VISIBILITY) ?? undefined), - premium_conditions: null, + premium_conditions: {}, unlisted: true } } else { diff --git a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx index c25461bcbf..a4dc3b2c7d 100644 --- a/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx +++ b/packages/web/src/pages/upload-page/fields/AccessAndSaleField.tsx @@ -114,7 +114,7 @@ const messages = { preview: { tooEarly: 'Preview must start during the track.', tooLate: - 'Preview must start at lest 30 seconds before the end of the track.' + 'Preview must start at least 30 seconds before the end of the track.' } } } @@ -519,10 +519,10 @@ export const AccessAndSaleMenuFields = (props: AccesAndSaleMenuFieldsProps) => { isPremiumContentCollectibleGated(premiumConditionsValue) ) break - setPremiumConditionsValue(null) + setPremiumConditionsValue({}) break case TrackAvailabilityType.HIDDEN: - setPremiumConditionsValue(null) + setPremiumConditionsValue({}) if (!fieldVisibilityValue) break setfieldVisibilityValue({ ...fieldVisibilityValue,