diff --git a/__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx b/__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx index f057d50628..4a01c7895e 100644 --- a/__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx +++ b/__tests__/components/waves/memes/submission/MemesArtSubmissionContainer.test.tsx @@ -1,29 +1,48 @@ -import { render, act } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import React from 'react'; -import MemesArtSubmissionContainer from '@/components/waves/memes/submission/MemesArtSubmissionContainer'; -import { SubmissionStep } from '@/components/waves/memes/submission/types/Steps'; -import { useArtworkSubmissionForm } from '@/components/waves/memes/submission/hooks/useArtworkSubmissionForm'; -import { useArtworkSubmissionMutation } from '@/components/waves/memes/submission/hooks/useArtworkSubmissionMutation'; -import { useSeizeConnectContext } from '@/components/auth/SeizeConnectContext'; -import type { InteractiveMediaMimeType } from '@/components/waves/memes/submission/constants/media'; +import { render, act } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import React from "react"; +import MemesArtSubmissionContainer from "@/components/waves/memes/submission/MemesArtSubmissionContainer"; +import { SubmissionStep } from "@/components/waves/memes/submission/types/Steps"; +import { useArtworkSubmissionForm } from "@/components/waves/memes/submission/hooks/useArtworkSubmissionForm"; +import { useArtworkSubmissionMutation } from "@/components/waves/memes/submission/hooks/useArtworkSubmissionMutation"; +import { useSeizeConnectContext } from "@/components/auth/SeizeConnectContext"; +import type { InteractiveMediaMimeType } from "@/components/waves/memes/submission/constants/media"; -jest.mock('@/components/waves/memes/submission/hooks/useArtworkSubmissionForm'); -jest.mock('@/components/waves/memes/submission/hooks/useArtworkSubmissionMutation'); -jest.mock('@/components/auth/SeizeConnectContext'); -jest.mock('@/components/waves/memes/submission/layout/ModalLayout', () => ({ children }: any) =>
{description}
++ {description} +
)} {upload.items.length > 0 && ( @@ -291,42 +303,49 @@ const AdditionalMediaUpload: FCAddress to receive split of minting proceeds.
++ {status.length}/{status.maxLength} characters.{" "} + {Math.abs(status.remaining)} over limit. +
+ ); + } + + return ( ++ {status.length}/{status.maxLength} characters. +
+ ); +} diff --git a/components/waves/memes/submission/utils/buildPreviewDrop.ts b/components/waves/memes/submission/utils/buildPreviewDrop.ts index 8cfa8fccd7..06e8320f69 100644 --- a/components/waves/memes/submission/utils/buildPreviewDrop.ts +++ b/components/waves/memes/submission/utils/buildPreviewDrop.ts @@ -1,7 +1,6 @@ "use client"; import type { ApiIdentity } from "@/generated/models/ApiIdentity"; -import type { ApiDropMetadata } from "@/generated/models/ApiDropMetadata"; import { ApiDropType } from "@/generated/models/ApiDropType"; import type { ApiWave } from "@/generated/models/ApiWave"; import { getBannerColorValue } from "@/helpers/profile-banner.helpers"; @@ -9,8 +8,7 @@ import { DropSize, getOptimisticDropId } from "@/helpers/waves/drop.helpers"; import type { ExtendedDrop } from "@/helpers/waves/drop.helpers"; import type { OperationalData } from "../types/OperationalData"; import type { TraitsData } from "../types/TraitsData"; -import { validateStrictAddress } from "./addressValidation"; -import { objectEntries } from "./objectEntries"; +import { buildSubmissionMetadata } from "./submissionMetadata"; interface PreviewMediaSelection { readonly mediaSource: "upload" | "url"; @@ -32,70 +30,6 @@ interface BuildPreviewDropInput { const FALLBACK_MEDIA_URL = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="; -const buildMetadata = ( - traits: TraitsData, - operationalData?: OperationalData -): ApiDropMetadata[] => { - const metadata: ApiDropMetadata[] = objectEntries(traits) - .map(([key, value]) => ({ - data_key: String(key), - data_value: value.toString(), - })) - .filter((item) => item.data_value.length > 0); - - if (!operationalData) { - return metadata; - } - - metadata.push( - { - data_key: "payment_info", - data_value: JSON.stringify(operationalData.payment_info), - }, - { - data_key: "commentary", - data_value: operationalData.commentary, - }, - { - data_key: "about_artist", - data_value: operationalData.about_artist, - } - ); - - if (operationalData.airdrop_config.length > 0) { - const validEntries = operationalData.airdrop_config.filter((entry) => { - const trimmedAddress = entry.address.trim(); - return validateStrictAddress(trimmedAddress) && entry.count > 0; - }); - - if (validEntries.length > 0) { - metadata.push({ - data_key: "airdrop_config", - data_value: JSON.stringify(validEntries), - }); - } - } - - if (operationalData.allowlist_batches.length > 0) { - metadata.push({ - data_key: "allowlist_batches", - data_value: JSON.stringify( - operationalData.allowlist_batches.map((batch) => ({ - contract: batch.contract, - token_ids: batch.token_ids_raw || "", - })) - ), - }); - } - - metadata.push({ - data_key: "additional_media", - data_value: JSON.stringify(operationalData.additional_media), - }); - - return metadata; -}; - const buildPreviewMedia = ({ mediaSelection, uploadArtworkUrl, @@ -142,7 +76,10 @@ export const buildPreviewDrop = ({ const id = getOptimisticDropId(); const now = Date.now(); const media = buildPreviewMedia({ mediaSelection, uploadArtworkUrl }); - const metadata = buildMetadata(traits, operationalData); + const metadata = buildSubmissionMetadata({ + traits, + operationalData, + }); const primaryAddress = connectedProfile?.primary_wallet ?? "0x0000000000000000000000000000000000000000"; diff --git a/components/waves/memes/submission/utils/submissionMetadata.ts b/components/waves/memes/submission/utils/submissionMetadata.ts new file mode 100644 index 0000000000..e7a3c092b0 --- /dev/null +++ b/components/waves/memes/submission/utils/submissionMetadata.ts @@ -0,0 +1,160 @@ +import type { ApiDropMetadata } from "@/generated/models/ApiDropMetadata"; +import type { OperationalData } from "../types/OperationalData"; +import type { TraitsData } from "../types/TraitsData"; +import { validateStrictAddress } from "./addressValidation"; +import { objectEntries } from "./objectEntries"; + +export const METADATA_VALUE_MAX_LENGTH = 5000; +const METADATA_VALUE_WARNING_THRESHOLD = 4500; + +export interface MetadataValueLengthStatus { + readonly dataKey: string; + readonly length: number; + readonly maxLength: number; + readonly warningThreshold: number; + readonly remaining: number; + readonly isWarning: boolean; + readonly isError: boolean; +} + +interface MetadataLengthValidationResult { + readonly statusesByKey: Partial