From dd6d511beeb2c768e56ac4f9f7e4239dbb3ca1b1 Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 12:04:11 +0200 Subject: [PATCH 1/8] Clarify launch onchain loading state and cap research airdrops by edition size. Signed-off-by: prxt6529 --- .../drop-forge-status.helpers.test.ts | 59 ++++++++++++++++++ ...e-launch-claim-page-client.helpers.test.ts | 27 +++++++- .../DropForgeClaimsListPageClient.tsx | 9 ++- .../drop-forge/drop-forge-status.helpers.ts | 23 ++++++- .../launch/DropForgeLaunchClaimPageClient.tsx | 62 +++++++++++++++++-- .../DropForgeLaunchClaimPageClient.view.tsx | 17 ++++- ...-forge-launch-claim-page-client.helpers.ts | 34 ++++++++++ 7 files changed, 218 insertions(+), 13 deletions(-) create mode 100644 __tests__/components/drop-forge/drop-forge-status.helpers.test.ts diff --git a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts new file mode 100644 index 0000000000..a0977589f8 --- /dev/null +++ b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts @@ -0,0 +1,59 @@ +jest.mock("@/components/drop-forge/launch/launchClaimHelpers", () => ({ + isMissingRequiredLaunchInfo: jest.fn(), +})); + +import { getClaimPrimaryStatus } from "@/components/drop-forge/drop-forge-status.helpers"; +import { isMissingRequiredLaunchInfo } from "@/components/drop-forge/launch/launchClaimHelpers"; +import type { MintingClaim } from "@/generated/models/MintingClaim"; + +const mockedIsMissingRequiredLaunchInfo = jest.mocked( + isMissingRequiredLaunchInfo +); + +describe("getClaimPrimaryStatus", () => { + const baseClaim = { + claim_id: 123, + metadata_location: "ar://example", + media_uploading: false, + } as MintingClaim; + + beforeEach(() => { + mockedIsMissingRequiredLaunchInfo.mockReturnValue(false); + }); + + it("returns a loading status while launch onchain state is still fetching", () => { + expect( + getClaimPrimaryStatus({ + claim: baseClaim, + manifoldClaim: null, + isManifoldClaimFetching: true, + }) + ).toMatchObject({ + key: "checking_onchain", + label: "Checking Onchain", + tone: "pending", + }); + }); + + it("returns pending initialization after launch onchain state has finished loading", () => { + expect( + getClaimPrimaryStatus({ + claim: baseClaim, + manifoldClaim: null, + isManifoldClaimFetching: false, + }) + ).toMatchObject({ + key: "pending_initialization", + label: "Pending Initialization", + tone: "pending", + }); + }); + + it("keeps craft claims in the published state", () => { + expect(getClaimPrimaryStatus({ claim: baseClaim })).toMatchObject({ + key: "published", + label: "Published", + tone: "success", + }); + }); +}); diff --git a/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts b/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts index 24c82c0bfc..d33909b8df 100644 --- a/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts +++ b/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts @@ -1,4 +1,8 @@ -import { getAutoSelectedLaunchPhase } from "@/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers"; +import { + clampResearchTargetEditionSize, + getAutoSelectedLaunchPhase, + getDefaultResearchTargetEditionSize, +} from "@/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers"; describe("getAutoSelectedLaunchPhase", () => { const phases = [ @@ -140,3 +144,24 @@ describe("getAutoSelectedLaunchPhase", () => { ).toBe("phase1"); }); }); + +describe("research target edition size helpers", () => { + it("defaults to the lower of the claim edition size and 310", () => { + expect(getDefaultResearchTargetEditionSize(305)).toBe(305); + expect(getDefaultResearchTargetEditionSize(500)).toBe(310); + }); + + it("falls back to 310 when the claim edition size is missing", () => { + expect(getDefaultResearchTargetEditionSize(null)).toBe(310); + expect(getDefaultResearchTargetEditionSize(0)).toBe(310); + }); + + it("caps the research target at the claim edition size", () => { + expect(clampResearchTargetEditionSize(310, 305)).toBe(305); + expect(clampResearchTargetEditionSize(304.8, 305)).toBe(304); + }); + + it("keeps the research target uncapped when there is no edition size limit", () => { + expect(clampResearchTargetEditionSize(310, null)).toBe(310); + }); +}); diff --git a/components/drop-forge/DropForgeClaimsListPageClient.tsx b/components/drop-forge/DropForgeClaimsListPageClient.tsx index daa318701d..da9b396082 100644 --- a/components/drop-forge/DropForgeClaimsListPageClient.tsx +++ b/components/drop-forge/DropForgeClaimsListPageClient.tsx @@ -366,10 +366,12 @@ function CraftClaimCard({ claim }: Readonly<{ claim: MintingClaim }>) { } function LaunchClaimCard({ claim }: Readonly<{ claim: MintingClaim }>) { - const { claim: manifoldClaim } = useDropForgeManifoldClaim(claim.claim_id); + const { claim: manifoldClaim, isFetching: isManifoldClaimFetching } = + useDropForgeManifoldClaim(claim.claim_id); const primaryStatus = getClaimPrimaryStatus({ claim, manifoldClaim: manifoldClaim ?? null, + isManifoldClaimFetching, }); return ( @@ -381,7 +383,10 @@ function LaunchClaimCard({ claim }: Readonly<{ claim: MintingClaim }>) { diff --git a/components/drop-forge/drop-forge-status.helpers.ts b/components/drop-forge/drop-forge-status.helpers.ts index 54730927e3..44f5ae83e3 100644 --- a/components/drop-forge/drop-forge-status.helpers.ts +++ b/components/drop-forge/drop-forge-status.helpers.ts @@ -5,6 +5,7 @@ import type { ManifoldClaim } from "@/hooks/useManifoldClaim"; type ClaimPrimaryStatusKey = | "draft" | "publishing" + | "checking_onchain" | "published" | "pending_initialization_missing_info" | "pending_initialization" @@ -28,7 +29,7 @@ export interface ClaimPrimaryStatus { interface ClaimArweaveSectionStatus { key: "not_published" | "publishing" | "published"; - label: "Not Published" | "Publishing…" | "Published"; + label: "Not Published" | "Publishing" | "Published"; tone: ClaimPrimaryStatusTone; reason?: string; } @@ -54,9 +55,11 @@ export function getPrimaryStatusPillClassName( export function getClaimPrimaryStatus({ claim, manifoldClaim, + isManifoldClaimFetching = false, }: { claim: MintingClaim; manifoldClaim?: Pick | null; + isManifoldClaimFetching?: boolean; }): ClaimPrimaryStatus { const initializedOnchain = !!manifoldClaim?.instanceId; const localMetadata = claim.metadata_location?.trim() ?? ""; @@ -70,7 +73,7 @@ export function getClaimPrimaryStatus({ if (claim.media_uploading === true) { return { key: "publishing", - label: "Publishing…", + label: "Publishing", tone: "pending", reason: "Uploading media and metadata to Arweave now", }; @@ -85,6 +88,20 @@ export function getClaimPrimaryStatus({ }; } + if ( + !isCraftContext && + isManifoldClaimFetching && + manifoldClaim == null && + hasLocalMetadata + ) { + return { + key: "checking_onchain", + label: "Checking Onchain", + tone: "pending", + reason: "Loading current onchain claim state", + }; + } + if (hasLocalMetadata && !initializedOnchain && !missingLaunchInfo) { return isCraftContext ? { @@ -142,7 +159,7 @@ export function getClaimArweaveSectionStatus( if (claim.media_uploading === true) { return { key: "publishing", - label: "Publishing…", + label: "Publishing", tone: "pending", reason: "Uploading media and metadata to Arweave now", }; diff --git a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx index a202ec0273..a1b1794b63 100644 --- a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx +++ b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx @@ -10,9 +10,11 @@ import { getClaimPrimaryStatus } from "@/components/drop-forge/drop-forge-status import ClaimTransactionModal from "@/components/drop-forge/launch/ClaimTransactionModal"; import { buildSubscriptionAirdropSelection, + clampResearchTargetEditionSize, formatDateTimeLocalInput, getAnimationMimeType, getAutoSelectedLaunchPhase, + getDefaultResearchTargetEditionSize, getErrorMessage, getMediaTypeLabel, getRootForPhase, @@ -167,7 +169,7 @@ export default function DropForgeLaunchClaimPageClient({ () => Date.now() ); const [researchTargetEditionSize, setResearchTargetEditionSize] = - useState(310); + useState(() => getDefaultResearchTargetEditionSize(null)); const [phaseAllowlistWindows, setPhaseAllowlistWindows] = useState< Record >({}); @@ -210,6 +212,7 @@ export default function DropForgeLaunchClaimPageClient({ const lastErrorToastRef = useRef<{ message: string; ts: number } | null>( null ); + const syncedResearchTargetClaimIdRef = useRef(null); const onChainClaimFetchStartedAtRef = useRef(null); const onChainClaimSpinnerHideTimeoutRef = useRef { + syncedResearchTargetClaimIdRef.current = null; + setResearchTargetEditionSize(getDefaultResearchTargetEditionSize(null)); + }, [claimId]); + + useEffect(() => { + if (!claim) return; + + setResearchTargetEditionSize((current) => { + if (syncedResearchTargetClaimIdRef.current !== claimId) { + syncedResearchTargetClaimIdRef.current = claimId; + return getDefaultResearchTargetEditionSize(claim.edition_size); + } + + return clampResearchTargetEditionSize(current, claim.edition_size); + }); + }, [claim, claimId]); + useEffect(() => { setSelectedPhase(""); setIsPhaseSelectionManual(false); @@ -945,9 +970,13 @@ export default function DropForgeLaunchClaimPageClient({ const claimTxModalClosable = claimTxModal?.status === "success" || claimTxModal?.status === "error"; const totalMinted = Number(manifoldClaim?.total ?? 0); + const cappedResearchTargetEditionSize = clampResearchTargetEditionSize( + researchTargetEditionSize, + claim?.edition_size + ); const researchAirdropCount = Math.max( 0, - Math.trunc(researchTargetEditionSize) - totalMinted + cappedResearchTargetEditionSize - totalMinted ); const runMetadataLocationOnlyUpdate = useCallback(() => { @@ -1444,10 +1473,30 @@ export default function DropForgeLaunchClaimPageClient({ const handleResearchTargetEditionSizeChange = useCallback((value: string) => { const parsed = Number(value); + const editionSizeLimit = + typeof claim?.edition_size === "number" && claim.edition_size > 0 + ? Math.trunc(claim.edition_size) + : null; + if ( + editionSizeLimit != null && + Number.isFinite(parsed) && + parsed > editionSizeLimit + ) { + setResearchTargetEditionSize(editionSizeLimit); + setToast({ + message: `Target edition size cannot exceed edition size (${editionSizeLimit})`, + type: "error", + }); + return; + } + setResearchTargetEditionSize( - Number.isFinite(parsed) && parsed >= 0 ? Math.trunc(parsed) : 0 + clampResearchTargetEditionSize( + Number.isFinite(parsed) && parsed >= 0 ? parsed : 0, + claim?.edition_size + ) ); - }, []); + }, [claim?.edition_size, setToast]); const handleSelectedPhasePriceChange = useCallback( (value: string) => { @@ -1611,7 +1660,8 @@ export default function DropForgeLaunchClaimPageClient({ selectedPhase={selectedPhase} onSelectedPhaseChange={handleSelectedPhaseChange} totalMinted={totalMinted} - researchTargetEditionSize={researchTargetEditionSize} + researchTargetEditionSize={cappedResearchTargetEditionSize} + researchTargetEditionSizeMax={claim?.edition_size ?? null} onResearchTargetEditionSizeChange={ handleResearchTargetEditionSizeChange } diff --git a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.view.tsx b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.view.tsx index 941726b5ba..2379bd021b 100644 --- a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.view.tsx +++ b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.view.tsx @@ -134,6 +134,7 @@ interface DropForgeLaunchClaimPageViewProps { onSelectedPhaseChange: (value: LaunchPhaseKey) => void; totalMinted: number; researchTargetEditionSize: number; + researchTargetEditionSizeMax: number | null; onResearchTargetEditionSizeChange: (value: string) => void; researchAirdropCount: number; runResearchAirdropWrite: (mintingClaimAction: string | null) => void; @@ -482,7 +483,10 @@ function DropForgeLaunchClaimTimelineRow({ )} @@ -1128,6 +1132,7 @@ function DropForgeMetadataUpdateSection({ function DropForgeResearchAirdropSection({ totalMinted, researchTargetEditionSize, + researchTargetEditionSizeMax, onResearchTargetEditionSizeChange, claimWritePending, isInitialized, @@ -1139,6 +1144,7 @@ function DropForgeResearchAirdropSection({ }: Readonly<{ totalMinted: number; researchTargetEditionSize: number; + researchTargetEditionSizeMax: number | null; onResearchTargetEditionSizeChange: (value: string) => void; claimWritePending: boolean; isInitialized: boolean; @@ -1178,6 +1184,7 @@ function DropForgeResearchAirdropSection({ type="number" inputMode="numeric" min="0" + max={researchTargetEditionSizeMax ?? undefined} step="1" value={researchTargetEditionSize} onChange={(e) => onResearchTargetEditionSizeChange(e.target.value)} @@ -1455,6 +1462,7 @@ function DropForgePhaseSelectionSection({ isInitialized, totalMinted, researchTargetEditionSize, + researchTargetEditionSizeMax, onResearchTargetEditionSizeChange, claimWritePending, researchAirdropCount, @@ -1493,6 +1501,7 @@ function DropForgePhaseSelectionSection({ isInitialized: boolean; totalMinted: number; researchTargetEditionSize: number; + researchTargetEditionSizeMax: number | null; onResearchTargetEditionSizeChange: (value: string) => void; claimWritePending: boolean; researchAirdropCount: number; @@ -1628,6 +1637,7 @@ function DropForgePhaseSelectionSection({ void; totalMinted: number; researchTargetEditionSize: number; + researchTargetEditionSizeMax: number | null; onResearchTargetEditionSizeChange: (value: string) => void; researchAirdropCount: number; runResearchAirdropWrite: (mintingClaimAction: string | null) => void; @@ -1809,6 +1821,7 @@ function DropForgeLaunchClaimActionsSection({ isInitialized={isInitialized} totalMinted={totalMinted} researchTargetEditionSize={researchTargetEditionSize} + researchTargetEditionSizeMax={researchTargetEditionSizeMax} onResearchTargetEditionSizeChange={onResearchTargetEditionSizeChange} claimWritePending={claimWritePending} researchAirdropCount={researchAirdropCount} @@ -1877,6 +1890,7 @@ function DropForgeLaunchClaimContent({ onSelectedPhaseChange, totalMinted, researchTargetEditionSize, + researchTargetEditionSizeMax, onResearchTargetEditionSizeChange, researchAirdropCount, runResearchAirdropWrite, @@ -1957,6 +1971,7 @@ function DropForgeLaunchClaimContent({ onSelectedPhaseChange={onSelectedPhaseChange} totalMinted={totalMinted} researchTargetEditionSize={researchTargetEditionSize} + researchTargetEditionSizeMax={researchTargetEditionSizeMax} onResearchTargetEditionSizeChange={onResearchTargetEditionSizeChange} researchAirdropCount={researchAirdropCount} runResearchAirdropWrite={runResearchAirdropWrite} diff --git a/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts b/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts index c300bd82ae..2fc917d81f 100644 --- a/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts +++ b/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts @@ -176,6 +176,40 @@ export function getSubscriptionPhaseName(phaseKey: LaunchPhaseKey): string { return "Public Phase"; } +const DEFAULT_RESEARCH_TARGET_EDITION_SIZE = 310; + +function normalizeEditionSizeLimit( + editionSize: number | null | undefined +): number | null { + const normalized = Number(editionSize); + if (!Number.isFinite(normalized) || normalized <= 0) { + return null; + } + return Math.trunc(normalized); +} + +export function getDefaultResearchTargetEditionSize( + editionSize: number | null | undefined +): number { + const limit = normalizeEditionSizeLimit(editionSize); + return limit == null + ? DEFAULT_RESEARCH_TARGET_EDITION_SIZE + : Math.min(limit, DEFAULT_RESEARCH_TARGET_EDITION_SIZE); +} + +export function clampResearchTargetEditionSize( + targetEditionSize: number, + editionSize: number | null | undefined +): number { + const normalizedTarget = Number(targetEditionSize); + const safeTarget = + Number.isFinite(normalizedTarget) && normalizedTarget >= 0 + ? Math.trunc(normalizedTarget) + : 0; + const limit = normalizeEditionSizeLimit(editionSize); + return limit == null ? safeTarget : Math.min(safeTarget, limit); +} + export function getAutoSelectedLaunchPhase({ hasPublishedMetadata, isInitialized, From 22281a77c06195464494b23e63de829f0a9c92bd Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 12:07:12 +0200 Subject: [PATCH 2/8] WIP Signed-off-by: prxt6529 --- .../launch/DropForgeLaunchClaimPageClient.tsx | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx index a1b1794b63..6999788606 100644 --- a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx +++ b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx @@ -168,8 +168,9 @@ export default function DropForgeLaunchClaimPageClient({ const [initialPhaseSelectionNowMs, setInitialPhaseSelectionNowMs] = useState( () => Date.now() ); - const [researchTargetEditionSize, setResearchTargetEditionSize] = - useState(() => getDefaultResearchTargetEditionSize(null)); + const [researchTargetEditionSize, setResearchTargetEditionSize] = useState( + () => getDefaultResearchTargetEditionSize(null) + ); const [phaseAllowlistWindows, setPhaseAllowlistWindows] = useState< Record >({}); @@ -1471,32 +1472,35 @@ export default function DropForgeLaunchClaimPageClient({ setSelectedPhase(value); }, []); - const handleResearchTargetEditionSizeChange = useCallback((value: string) => { - const parsed = Number(value); - const editionSizeLimit = - typeof claim?.edition_size === "number" && claim.edition_size > 0 - ? Math.trunc(claim.edition_size) - : null; - if ( - editionSizeLimit != null && - Number.isFinite(parsed) && - parsed > editionSizeLimit - ) { - setResearchTargetEditionSize(editionSizeLimit); - setToast({ - message: `Target edition size cannot exceed edition size (${editionSizeLimit})`, - type: "error", - }); - return; - } + const handleResearchTargetEditionSizeChange = useCallback( + (value: string) => { + const parsed = Number(value); + const editionSizeLimit = + typeof claim?.edition_size === "number" && claim.edition_size > 0 + ? Math.trunc(claim.edition_size) + : null; + if ( + editionSizeLimit != null && + Number.isFinite(parsed) && + parsed > editionSizeLimit + ) { + setResearchTargetEditionSize(editionSizeLimit); + setToast({ + message: `Target edition size cannot exceed edition size (${editionSizeLimit})`, + type: "error", + }); + return; + } - setResearchTargetEditionSize( - clampResearchTargetEditionSize( - Number.isFinite(parsed) && parsed >= 0 ? parsed : 0, - claim?.edition_size - ) - ); - }, [claim?.edition_size, setToast]); + setResearchTargetEditionSize( + clampResearchTargetEditionSize( + Number.isFinite(parsed) && parsed >= 0 ? parsed : 0, + claim?.edition_size + ) + ); + }, + [claim?.edition_size, setToast] + ); const handleSelectedPhasePriceChange = useCallback( (value: string) => { From fe7a06e7c4ccd1bcb6fb34c6aac31a93c3fcdcc6 Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 13:14:39 +0200 Subject: [PATCH 3/8] WIP Signed-off-by: prxt6529 --- ...e-launch-claim-page-client.helpers.test.ts | 16 ++++++++ .../launch/DropForgeLaunchClaimPageClient.tsx | 38 +++++++++++++------ ...-forge-launch-claim-page-client.helpers.ts | 26 +++++++++---- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts b/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts index d33909b8df..8b659c2614 100644 --- a/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts +++ b/__tests__/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.test.ts @@ -2,6 +2,7 @@ import { clampResearchTargetEditionSize, getAutoSelectedLaunchPhase, getDefaultResearchTargetEditionSize, + getResearchTargetEditionSizeLimit, } from "@/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers"; describe("getAutoSelectedLaunchPhase", () => { @@ -146,11 +147,21 @@ describe("getAutoSelectedLaunchPhase", () => { }); describe("research target edition size helpers", () => { + it("uses the live onchain total max when it is available", () => { + expect(getResearchTargetEditionSizeLimit(400, 250)).toBe(250); + expect(getResearchTargetEditionSizeLimit(250, 400)).toBe(400); + }); + it("defaults to the lower of the claim edition size and 310", () => { expect(getDefaultResearchTargetEditionSize(305)).toBe(305); expect(getDefaultResearchTargetEditionSize(500)).toBe(310); }); + it("defaults to the onchain cap whenever it is available", () => { + expect(getDefaultResearchTargetEditionSize(500, 275)).toBe(275); + expect(getDefaultResearchTargetEditionSize(250, 400)).toBe(310); + }); + it("falls back to 310 when the claim edition size is missing", () => { expect(getDefaultResearchTargetEditionSize(null)).toBe(310); expect(getDefaultResearchTargetEditionSize(0)).toBe(310); @@ -161,6 +172,11 @@ describe("research target edition size helpers", () => { expect(clampResearchTargetEditionSize(304.8, 305)).toBe(304); }); + it("caps the research target at the live onchain total max", () => { + expect(clampResearchTargetEditionSize(310, 500, 275)).toBe(275); + expect(clampResearchTargetEditionSize(450, 250, 400)).toBe(400); + }); + it("keeps the research target uncapped when there is no edition size limit", () => { expect(clampResearchTargetEditionSize(310, null)).toBe(310); }); diff --git a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx index 6999788606..0f88e1191a 100644 --- a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx +++ b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx @@ -17,6 +17,7 @@ import { getDefaultResearchTargetEditionSize, getErrorMessage, getMediaTypeLabel, + getResearchTargetEditionSizeLimit, getRootForPhase, getSafeExternalUrl, getSubscriptionPhaseName, @@ -345,6 +346,10 @@ export default function DropForgeLaunchClaimPageClient({ const missingRequiredInfo = Boolean( claim && isMissingRequiredLaunchInfo(claim) ); + const researchTargetEditionSizeLimit = getResearchTargetEditionSizeLimit( + claim?.edition_size, + manifoldClaim?.totalMax + ); const primaryStatus = claim ? getClaimPrimaryStatus({ claim, @@ -374,12 +379,19 @@ export default function DropForgeLaunchClaimPageClient({ setResearchTargetEditionSize((current) => { if (syncedResearchTargetClaimIdRef.current !== claimId) { syncedResearchTargetClaimIdRef.current = claimId; - return getDefaultResearchTargetEditionSize(claim.edition_size); + return getDefaultResearchTargetEditionSize( + claim.edition_size, + manifoldClaim?.totalMax + ); } - return clampResearchTargetEditionSize(current, claim.edition_size); + return clampResearchTargetEditionSize( + current, + claim.edition_size, + manifoldClaim?.totalMax + ); }); - }, [claim, claimId]); + }, [claim, claimId, manifoldClaim?.totalMax]); useEffect(() => { setSelectedPhase(""); @@ -973,7 +985,8 @@ export default function DropForgeLaunchClaimPageClient({ const totalMinted = Number(manifoldClaim?.total ?? 0); const cappedResearchTargetEditionSize = clampResearchTargetEditionSize( researchTargetEditionSize, - claim?.edition_size + claim?.edition_size, + manifoldClaim?.totalMax ); const researchAirdropCount = Math.max( 0, @@ -1475,10 +1488,10 @@ export default function DropForgeLaunchClaimPageClient({ const handleResearchTargetEditionSizeChange = useCallback( (value: string) => { const parsed = Number(value); - const editionSizeLimit = - typeof claim?.edition_size === "number" && claim.edition_size > 0 - ? Math.trunc(claim.edition_size) - : null; + const editionSizeLimit = getResearchTargetEditionSizeLimit( + claim?.edition_size, + manifoldClaim?.totalMax + ); if ( editionSizeLimit != null && Number.isFinite(parsed) && @@ -1486,7 +1499,7 @@ export default function DropForgeLaunchClaimPageClient({ ) { setResearchTargetEditionSize(editionSizeLimit); setToast({ - message: `Target edition size cannot exceed edition size (${editionSizeLimit})`, + message: `Target edition size cannot exceed claim max (${editionSizeLimit})`, type: "error", }); return; @@ -1495,11 +1508,12 @@ export default function DropForgeLaunchClaimPageClient({ setResearchTargetEditionSize( clampResearchTargetEditionSize( Number.isFinite(parsed) && parsed >= 0 ? parsed : 0, - claim?.edition_size + claim?.edition_size, + manifoldClaim?.totalMax ) ); }, - [claim?.edition_size, setToast] + [claim?.edition_size, manifoldClaim?.totalMax, setToast] ); const handleSelectedPhasePriceChange = useCallback( @@ -1665,7 +1679,7 @@ export default function DropForgeLaunchClaimPageClient({ onSelectedPhaseChange={handleSelectedPhaseChange} totalMinted={totalMinted} researchTargetEditionSize={cappedResearchTargetEditionSize} - researchTargetEditionSizeMax={claim?.edition_size ?? null} + researchTargetEditionSizeMax={researchTargetEditionSizeLimit} onResearchTargetEditionSizeChange={ handleResearchTargetEditionSizeChange } diff --git a/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts b/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts index 2fc917d81f..b93b2759a3 100644 --- a/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts +++ b/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers.ts @@ -178,20 +178,31 @@ export function getSubscriptionPhaseName(phaseKey: LaunchPhaseKey): string { const DEFAULT_RESEARCH_TARGET_EDITION_SIZE = 310; -function normalizeEditionSizeLimit( - editionSize: number | null | undefined +function normalizePositiveIntegerLimit( + value: number | null | undefined ): number | null { - const normalized = Number(editionSize); + const normalized = Number(value); if (!Number.isFinite(normalized) || normalized <= 0) { return null; } return Math.trunc(normalized); } +export function getResearchTargetEditionSizeLimit( + editionSize: number | null | undefined, + onChainTotalMax?: number | null | undefined +): number | null { + const dbLimit = normalizePositiveIntegerLimit(editionSize); + const onChainLimit = normalizePositiveIntegerLimit(onChainTotalMax); + + return onChainLimit ?? dbLimit; +} + export function getDefaultResearchTargetEditionSize( - editionSize: number | null | undefined + editionSize: number | null | undefined, + onChainTotalMax?: number | null | undefined ): number { - const limit = normalizeEditionSizeLimit(editionSize); + const limit = getResearchTargetEditionSizeLimit(editionSize, onChainTotalMax); return limit == null ? DEFAULT_RESEARCH_TARGET_EDITION_SIZE : Math.min(limit, DEFAULT_RESEARCH_TARGET_EDITION_SIZE); @@ -199,14 +210,15 @@ export function getDefaultResearchTargetEditionSize( export function clampResearchTargetEditionSize( targetEditionSize: number, - editionSize: number | null | undefined + editionSize: number | null | undefined, + onChainTotalMax?: number | null | undefined ): number { const normalizedTarget = Number(targetEditionSize); const safeTarget = Number.isFinite(normalizedTarget) && normalizedTarget >= 0 ? Math.trunc(normalizedTarget) : 0; - const limit = normalizeEditionSizeLimit(editionSize); + const limit = getResearchTargetEditionSizeLimit(editionSize, onChainTotalMax); return limit == null ? safeTarget : Math.min(safeTarget, limit); } From fc9c85fd756dd7adbff779ed99252717744379af Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 13:32:22 +0200 Subject: [PATCH 4/8] WIP Signed-off-by: prxt6529 --- .../launch/DropForgeLaunchClaimPageClient.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx index 0f88e1191a..4924cb2a40 100644 --- a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx +++ b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx @@ -369,12 +369,18 @@ export default function DropForgeLaunchClaimPageClient({ }, [claimId]); useEffect(() => { - syncedResearchTargetClaimIdRef.current = null; - setResearchTargetEditionSize(getDefaultResearchTargetEditionSize(null)); - }, [claimId]); + const claimMatchesCurrentClaimId = claim?.claim_id === claimId; - useEffect(() => { - if (!claim) return; + if ( + syncedResearchTargetClaimIdRef.current !== claimId && + !claimMatchesCurrentClaimId + ) { + syncedResearchTargetClaimIdRef.current = null; + setResearchTargetEditionSize(getDefaultResearchTargetEditionSize(null)); + return; + } + + if (!claim || !claimMatchesCurrentClaimId) return; setResearchTargetEditionSize((current) => { if (syncedResearchTargetClaimIdRef.current !== claimId) { From dcdd67e99ab1078db3f54019f6575af42b190e1d Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 13:50:12 +0200 Subject: [PATCH 5/8] WIP Signed-off-by: prxt6529 --- .../drop-forge-status.helpers.test.ts | 83 +++++++++++ .../drop-forge/drop-forge-status.helpers.ts | 133 ++++++++++++------ 2 files changed, 176 insertions(+), 40 deletions(-) diff --git a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts index a0977589f8..52da446eea 100644 --- a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts +++ b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts @@ -49,6 +49,38 @@ describe("getClaimPrimaryStatus", () => { }); }); + it("returns draft when there is no metadata and nothing is initialized", () => { + expect( + getClaimPrimaryStatus({ + claim: { + ...baseClaim, + metadata_location: null, + }, + manifoldClaim: null, + }) + ).toMatchObject({ + key: "draft", + label: "Draft", + tone: "neutral", + }); + }); + + it("returns pending initialization missing info when launch fields are incomplete", () => { + mockedIsMissingRequiredLaunchInfo.mockReturnValue(true); + + expect( + getClaimPrimaryStatus({ + claim: baseClaim, + manifoldClaim: null, + isManifoldClaimFetching: false, + }) + ).toMatchObject({ + key: "pending_initialization_missing_info", + label: "Pending Initialization — Missing Info", + tone: "pending", + }); + }); + it("keeps craft claims in the published state", () => { expect(getClaimPrimaryStatus({ claim: baseClaim })).toMatchObject({ key: "published", @@ -56,4 +88,55 @@ describe("getClaimPrimaryStatus", () => { tone: "success", }); }); + + it("returns live when onchain and local metadata match", () => { + expect( + getClaimPrimaryStatus({ + claim: baseClaim, + manifoldClaim: { + instanceId: 123, + location: "ar://example", + }, + }) + ).toMatchObject({ + key: "live", + label: "Live", + tone: "success", + }); + }); + + it("returns live needs update when onchain metadata differs", () => { + expect( + getClaimPrimaryStatus({ + claim: baseClaim, + manifoldClaim: { + instanceId: 123, + location: "ar://older", + }, + }) + ).toMatchObject({ + key: "live_needs_update", + label: "Live — Needs Update", + tone: "update", + }); + }); + + it("returns diverged when initialized onchain without local metadata", () => { + expect( + getClaimPrimaryStatus({ + claim: { + ...baseClaim, + metadata_location: null, + }, + manifoldClaim: { + instanceId: 123, + location: "ar://example", + }, + }) + ).toMatchObject({ + key: "diverged", + label: "Diverged", + tone: "destructive", + }); + }); }); diff --git a/components/drop-forge/drop-forge-status.helpers.ts b/components/drop-forge/drop-forge-status.helpers.ts index 44f5ae83e3..73b478d8d6 100644 --- a/components/drop-forge/drop-forge-status.helpers.ts +++ b/components/drop-forge/drop-forge-status.helpers.ts @@ -52,33 +52,21 @@ export function getPrimaryStatusPillClassName( return "tw-bg-iron-700/30 tw-text-iron-300 tw-ring-iron-500/40"; } -export function getClaimPrimaryStatus({ - claim, +function getPreInitializationClaimStatus({ + hasLocalMetadata, + initializedOnchain, + isCraftContext, + isManifoldClaimFetching, manifoldClaim, - isManifoldClaimFetching = false, + missingLaunchInfo, }: { - claim: MintingClaim; + hasLocalMetadata: boolean; + initializedOnchain: boolean; + isCraftContext: boolean; + isManifoldClaimFetching: boolean; manifoldClaim?: Pick | null; - isManifoldClaimFetching?: boolean; -}): ClaimPrimaryStatus { - const initializedOnchain = !!manifoldClaim?.instanceId; - const localMetadata = claim.metadata_location?.trim() ?? ""; - const hasLocalMetadata = localMetadata.length > 0; - const onchainLocation = manifoldClaim?.location?.trim() ?? ""; - const chainMatchesLocal = - onchainLocation.length > 0 && onchainLocation === localMetadata; - const isCraftContext = manifoldClaim === undefined; - const missingLaunchInfo = isMissingRequiredLaunchInfo(claim); - - if (claim.media_uploading === true) { - return { - key: "publishing", - label: "Publishing", - tone: "pending", - reason: "Uploading media and metadata to Arweave now", - }; - } - + missingLaunchInfo: boolean; +}): ClaimPrimaryStatus | null { if (!hasLocalMetadata && !initializedOnchain) { return { key: "draft", @@ -103,22 +91,24 @@ export function getClaimPrimaryStatus({ } if (hasLocalMetadata && !initializedOnchain && !missingLaunchInfo) { - return isCraftContext - ? { - key: "published", - label: "Published", - tone: "success", - reason: "Published to Arweave and ready for onchain initialization", - } - : { - key: "pending_initialization", - label: "Pending Initialization", - tone: "pending", - reason: "Ready to initialize onchain", - }; + if (isCraftContext) { + return { + key: "published", + label: "Published", + tone: "success", + reason: "Published to Arweave and ready for onchain initialization", + }; + } + + return { + key: "pending_initialization", + label: "Pending Initialization", + tone: "pending", + reason: "Ready to initialize onchain", + }; } - if (!initializedOnchain && missingLaunchInfo === true) { + if (!initializedOnchain && missingLaunchInfo) { return { key: "pending_initialization_missing_info", label: "Pending Initialization — Missing Info", @@ -127,7 +117,19 @@ export function getClaimPrimaryStatus({ }; } - if (initializedOnchain && hasLocalMetadata && chainMatchesLocal === false) { + return null; +} + +function getInitializedClaimStatus({ + initializedOnchain, + hasLocalMetadata, + chainMatchesLocal, +}: { + initializedOnchain: boolean; + hasLocalMetadata: boolean; + chainMatchesLocal: boolean; +}): ClaimPrimaryStatus | null { + if (initializedOnchain && hasLocalMetadata && !chainMatchesLocal) { return { key: "live_needs_update", label: "Live — Needs Update", @@ -136,7 +138,7 @@ export function getClaimPrimaryStatus({ }; } - if (initializedOnchain && hasLocalMetadata && chainMatchesLocal === true) { + if (initializedOnchain && hasLocalMetadata && chainMatchesLocal) { return { key: "live", label: "Live", @@ -145,6 +147,57 @@ export function getClaimPrimaryStatus({ }; } + return null; +} + +export function getClaimPrimaryStatus({ + claim, + manifoldClaim, + isManifoldClaimFetching = false, +}: { + claim: MintingClaim; + manifoldClaim?: Pick | null; + isManifoldClaimFetching?: boolean; +}): ClaimPrimaryStatus { + const initializedOnchain = !!manifoldClaim?.instanceId; + const localMetadata = claim.metadata_location?.trim() ?? ""; + const hasLocalMetadata = localMetadata.length > 0; + const onchainLocation = manifoldClaim?.location?.trim() ?? ""; + const chainMatchesLocal = + onchainLocation.length > 0 && onchainLocation === localMetadata; + const isCraftContext = manifoldClaim === undefined; + const missingLaunchInfo = isMissingRequiredLaunchInfo(claim); + + if (claim.media_uploading === true) { + return { + key: "publishing", + label: "Publishing", + tone: "pending", + reason: "Uploading media and metadata to Arweave now", + }; + } + + const preInitializationStatus = getPreInitializationClaimStatus({ + hasLocalMetadata, + initializedOnchain, + isCraftContext, + isManifoldClaimFetching, + manifoldClaim, + missingLaunchInfo, + }); + if (preInitializationStatus) { + return preInitializationStatus; + } + + const initializedStatus = getInitializedClaimStatus({ + initializedOnchain, + hasLocalMetadata, + chainMatchesLocal, + }); + if (initializedStatus) { + return initializedStatus; + } + return { key: "diverged", label: "Diverged", From 461021026972c2e4c6a9d8cb411fdf43e9578fa2 Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 14:47:21 +0200 Subject: [PATCH 6/8] WIP Signed-off-by: prxt6529 --- .../drop-forge-status.helpers.test.ts | 10 ++++++++++ .../drop-forge/drop-forge-status.helpers.ts | 18 ++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts index 52da446eea..9732504e27 100644 --- a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts +++ b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts @@ -89,6 +89,16 @@ describe("getClaimPrimaryStatus", () => { }); }); + it("keeps craft claims published even when launch info is missing", () => { + mockedIsMissingRequiredLaunchInfo.mockReturnValue(true); + + expect(getClaimPrimaryStatus({ claim: baseClaim })).toMatchObject({ + key: "published", + label: "Published", + tone: "success", + }); + }); + it("returns live when onchain and local metadata match", () => { expect( getClaimPrimaryStatus({ diff --git a/components/drop-forge/drop-forge-status.helpers.ts b/components/drop-forge/drop-forge-status.helpers.ts index 73b478d8d6..e05eddce76 100644 --- a/components/drop-forge/drop-forge-status.helpers.ts +++ b/components/drop-forge/drop-forge-status.helpers.ts @@ -90,7 +90,7 @@ function getPreInitializationClaimStatus({ }; } - if (hasLocalMetadata && !initializedOnchain && !missingLaunchInfo) { + if (hasLocalMetadata && !initializedOnchain) { if (isCraftContext) { return { key: "published", @@ -100,15 +100,17 @@ function getPreInitializationClaimStatus({ }; } - return { - key: "pending_initialization", - label: "Pending Initialization", - tone: "pending", - reason: "Ready to initialize onchain", - }; + if (!missingLaunchInfo) { + return { + key: "pending_initialization", + label: "Pending Initialization", + tone: "pending", + reason: "Ready to initialize onchain", + }; + } } - if (!initializedOnchain && missingLaunchInfo) { + if (!initializedOnchain && missingLaunchInfo && !isCraftContext) { return { key: "pending_initialization_missing_info", label: "Pending Initialization — Missing Info", From 6912a79d20111f45003b068d361ed7c6d080f16b Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 15:08:18 +0200 Subject: [PATCH 7/8] WIP Signed-off-by: prxt6529 --- .../drop-forge-status.helpers.test.ts | 29 +++++++++++++++++-- .../DropForgeClaimsListPageClient.tsx | 3 +- .../craft/DropForgeCraftClaimPageClient.tsx | 2 +- .../drop-forge/drop-forge-status.helpers.ts | 5 ++-- .../launch/DropForgeLaunchClaimPageClient.tsx | 3 +- 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts index 9732504e27..1f7b60c2f1 100644 --- a/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts +++ b/__tests__/components/drop-forge/drop-forge-status.helpers.test.ts @@ -26,6 +26,7 @@ describe("getClaimPrimaryStatus", () => { getClaimPrimaryStatus({ claim: baseClaim, manifoldClaim: null, + isCraftContext: false, isManifoldClaimFetching: true, }) ).toMatchObject({ @@ -40,6 +41,7 @@ describe("getClaimPrimaryStatus", () => { getClaimPrimaryStatus({ claim: baseClaim, manifoldClaim: null, + isCraftContext: false, isManifoldClaimFetching: false, }) ).toMatchObject({ @@ -57,6 +59,7 @@ describe("getClaimPrimaryStatus", () => { metadata_location: null, }, manifoldClaim: null, + isCraftContext: false, }) ).toMatchObject({ key: "draft", @@ -72,6 +75,7 @@ describe("getClaimPrimaryStatus", () => { getClaimPrimaryStatus({ claim: baseClaim, manifoldClaim: null, + isCraftContext: false, isManifoldClaimFetching: false, }) ).toMatchObject({ @@ -82,7 +86,9 @@ describe("getClaimPrimaryStatus", () => { }); it("keeps craft claims in the published state", () => { - expect(getClaimPrimaryStatus({ claim: baseClaim })).toMatchObject({ + expect( + getClaimPrimaryStatus({ claim: baseClaim, isCraftContext: true }) + ).toMatchObject({ key: "published", label: "Published", tone: "success", @@ -92,13 +98,32 @@ describe("getClaimPrimaryStatus", () => { it("keeps craft claims published even when launch info is missing", () => { mockedIsMissingRequiredLaunchInfo.mockReturnValue(true); - expect(getClaimPrimaryStatus({ claim: baseClaim })).toMatchObject({ + expect( + getClaimPrimaryStatus({ claim: baseClaim, isCraftContext: true }) + ).toMatchObject({ key: "published", label: "Published", tone: "success", }); }); + it("treats instanceId 0 as initialized onchain", () => { + expect( + getClaimPrimaryStatus({ + claim: baseClaim, + manifoldClaim: { + instanceId: 0, + location: "ar://example", + }, + isCraftContext: false, + }) + ).toMatchObject({ + key: "live", + label: "Live", + tone: "success", + }); + }); + it("returns live when onchain and local metadata match", () => { expect( getClaimPrimaryStatus({ diff --git a/components/drop-forge/DropForgeClaimsListPageClient.tsx b/components/drop-forge/DropForgeClaimsListPageClient.tsx index da9b396082..78eedddb0b 100644 --- a/components/drop-forge/DropForgeClaimsListPageClient.tsx +++ b/components/drop-forge/DropForgeClaimsListPageClient.tsx @@ -345,7 +345,7 @@ function ClaimCardContent({ } function CraftClaimCard({ claim }: Readonly<{ claim: MintingClaim }>) { - const primaryStatus = getClaimPrimaryStatus({ claim }); + const primaryStatus = getClaimPrimaryStatus({ claim, isCraftContext: true }); return ( ) { const primaryStatus = getClaimPrimaryStatus({ claim, manifoldClaim: manifoldClaim ?? null, + isCraftContext: false, isManifoldClaimFetching, }); diff --git a/components/drop-forge/craft/DropForgeCraftClaimPageClient.tsx b/components/drop-forge/craft/DropForgeCraftClaimPageClient.tsx index fe2b61f12c..f7b9e11520 100644 --- a/components/drop-forge/craft/DropForgeCraftClaimPageClient.tsx +++ b/components/drop-forge/craft/DropForgeCraftClaimPageClient.tsx @@ -1665,7 +1665,7 @@ function ArweaveSection({ const { setToast } = useAuth(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const primaryStatus = getClaimPrimaryStatus({ claim }); + const primaryStatus = getClaimPrimaryStatus({ claim, isCraftContext: true }); const isPublishing = primaryStatus.key === "publishing"; const isDraft = primaryStatus.key === "draft"; const canPublish = isDraft && !hasPendingChanges && !loading && !isPublishing; diff --git a/components/drop-forge/drop-forge-status.helpers.ts b/components/drop-forge/drop-forge-status.helpers.ts index e05eddce76..eae76323af 100644 --- a/components/drop-forge/drop-forge-status.helpers.ts +++ b/components/drop-forge/drop-forge-status.helpers.ts @@ -155,19 +155,20 @@ function getInitializedClaimStatus({ export function getClaimPrimaryStatus({ claim, manifoldClaim, + isCraftContext = false, isManifoldClaimFetching = false, }: { claim: MintingClaim; manifoldClaim?: Pick | null; + isCraftContext?: boolean; isManifoldClaimFetching?: boolean; }): ClaimPrimaryStatus { - const initializedOnchain = !!manifoldClaim?.instanceId; + const initializedOnchain = manifoldClaim?.instanceId != null; const localMetadata = claim.metadata_location?.trim() ?? ""; const hasLocalMetadata = localMetadata.length > 0; const onchainLocation = manifoldClaim?.location?.trim() ?? ""; const chainMatchesLocal = onchainLocation.length > 0 && onchainLocation === localMetadata; - const isCraftContext = manifoldClaim === undefined; const missingLaunchInfo = isMissingRequiredLaunchInfo(claim); if (claim.media_uploading === true) { diff --git a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx index 4924cb2a40..d3b88de10a 100644 --- a/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx +++ b/components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx @@ -341,7 +341,7 @@ export default function DropForgeLaunchClaimPageClient({ showErrorToast, ]); - const isInitialized = Boolean(manifoldClaim?.instanceId); + const isInitialized = manifoldClaim?.instanceId != null; const hasPublishedMetadata = Boolean(claim?.metadata_location != null); const missingRequiredInfo = Boolean( claim && isMissingRequiredLaunchInfo(claim) @@ -354,6 +354,7 @@ export default function DropForgeLaunchClaimPageClient({ ? getClaimPrimaryStatus({ claim, manifoldClaim: manifoldClaim ?? null, + isCraftContext: false, isManifoldClaimFetching: onChainClaimFetching, }) : null; From d872a7743093128bce3210b08fd00547e46789d9 Mon Sep 17 00:00:00 2001 From: prxt6529 Date: Thu, 26 Mar 2026 16:02:56 +0200 Subject: [PATCH 8/8] WIP Signed-off-by: prxt6529 --- components/drop-forge/drop-forge-status.helpers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/drop-forge/drop-forge-status.helpers.ts b/components/drop-forge/drop-forge-status.helpers.ts index eae76323af..cdc25ad584 100644 --- a/components/drop-forge/drop-forge-status.helpers.ts +++ b/components/drop-forge/drop-forge-status.helpers.ts @@ -163,6 +163,7 @@ export function getClaimPrimaryStatus({ isCraftContext?: boolean; isManifoldClaimFetching?: boolean; }): ClaimPrimaryStatus { + const resolvedManifoldClaim = manifoldClaim ?? null; const initializedOnchain = manifoldClaim?.instanceId != null; const localMetadata = claim.metadata_location?.trim() ?? ""; const hasLocalMetadata = localMetadata.length > 0; @@ -185,7 +186,7 @@ export function getClaimPrimaryStatus({ initializedOnchain, isCraftContext, isManifoldClaimFetching, - manifoldClaim, + manifoldClaim: resolvedManifoldClaim, missingLaunchInfo, }); if (preInitializationStatus) {