Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions __tests__/components/drop-forge/drop-forge-status.helpers.test.ts
Original file line number Diff line number Diff line change
@@ -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",
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { getAutoSelectedLaunchPhase } from "@/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers";
import {
clampResearchTargetEditionSize,
getAutoSelectedLaunchPhase,
getDefaultResearchTargetEditionSize,
getResearchTargetEditionSizeLimit,
} from "@/components/drop-forge/launch/drop-forge-launch-claim-page-client.helpers";

describe("getAutoSelectedLaunchPhase", () => {
const phases = [
Expand Down Expand Up @@ -140,3 +145,39 @@ describe("getAutoSelectedLaunchPhase", () => {
).toBe("phase1");
});
});

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);
});

it("caps the research target at the claim edition size", () => {
expect(clampResearchTargetEditionSize(310, 305)).toBe(305);
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);
});
});
9 changes: 7 additions & 2 deletions components/drop-forge/DropForgeClaimsListPageClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -381,7 +383,10 @@ function LaunchClaimCard({ claim }: Readonly<{ claim: MintingClaim }>) {
<DropForgeStatusPill
className={getPrimaryStatusPillClassName(primaryStatus.tone)}
label={primaryStatus.label}
showLoader={primaryStatus.key === "publishing"}
showLoader={
primaryStatus.key === "publishing" ||
primaryStatus.key === "checking_onchain"
}
tooltipText={primaryStatus.reason ?? ""}
/>
</div>
Expand Down
23 changes: 20 additions & 3 deletions components/drop-forge/drop-forge-status.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
type ClaimPrimaryStatusKey =
| "draft"
| "publishing"
| "checking_onchain"
| "published"
| "pending_initialization_missing_info"
| "pending_initialization"
Expand All @@ -28,7 +29,7 @@

interface ClaimArweaveSectionStatus {
key: "not_published" | "publishing" | "published";
label: "Not Published" | "Publishing" | "Published";
label: "Not Published" | "Publishing" | "Published";
tone: ClaimPrimaryStatusTone;
reason?: string;
}
Expand All @@ -51,12 +52,14 @@
return "tw-bg-iron-700/30 tw-text-iron-300 tw-ring-iron-500/40";
}

export function getClaimPrimaryStatus({

Check failure on line 55 in components/drop-forge/drop-forge-status.helpers.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=6529-Collections_6529seize-frontend&issues=AZ0puPKeOTMil6mNcZym&open=AZ0puPKeOTMil6mNcZym&pullRequest=2170
claim,
manifoldClaim,
isManifoldClaimFetching = false,
}: {
claim: MintingClaim;
manifoldClaim?: Pick<ManifoldClaim, "instanceId" | "location"> | null;
isManifoldClaimFetching?: boolean;
}): ClaimPrimaryStatus {
const initializedOnchain = !!manifoldClaim?.instanceId;
const localMetadata = claim.metadata_location?.trim() ?? "";
Expand All @@ -70,7 +73,7 @@
if (claim.media_uploading === true) {
return {
key: "publishing",
label: "Publishing",
label: "Publishing",
tone: "pending",
reason: "Uploading media and metadata to Arweave now",
};
Expand All @@ -85,6 +88,20 @@
};
}

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
? {
Expand Down Expand Up @@ -142,7 +159,7 @@
if (claim.media_uploading === true) {
return {
key: "publishing",
label: "Publishing",
label: "Publishing",
tone: "pending",
reason: "Uploading media and metadata to Arweave now",
};
Expand Down
90 changes: 79 additions & 11 deletions components/drop-forge/launch/DropForgeLaunchClaimPageClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ 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,
getResearchTargetEditionSizeLimit,
getRootForPhase,
getSafeExternalUrl,
getSubscriptionPhaseName,
Expand Down Expand Up @@ -166,8 +169,9 @@ export default function DropForgeLaunchClaimPageClient({
const [initialPhaseSelectionNowMs, setInitialPhaseSelectionNowMs] = useState(
() => Date.now()
);
const [researchTargetEditionSize, setResearchTargetEditionSize] =
useState(310);
const [researchTargetEditionSize, setResearchTargetEditionSize] = useState(
() => getDefaultResearchTargetEditionSize(null)
);
const [phaseAllowlistWindows, setPhaseAllowlistWindows] = useState<
Record<string, { start: string; end: string }>
>({});
Expand Down Expand Up @@ -210,6 +214,7 @@ export default function DropForgeLaunchClaimPageClient({
const lastErrorToastRef = useRef<{ message: string; ts: number } | null>(
null
);
const syncedResearchTargetClaimIdRef = useRef<number | null>(null);
const onChainClaimFetchStartedAtRef = useRef<number | null>(null);
const onChainClaimSpinnerHideTimeoutRef = useRef<ReturnType<
typeof setTimeout
Expand Down Expand Up @@ -341,8 +346,16 @@ export default function DropForgeLaunchClaimPageClient({
const missingRequiredInfo = Boolean(
claim && isMissingRequiredLaunchInfo(claim)
);
const researchTargetEditionSizeLimit = getResearchTargetEditionSizeLimit(
claim?.edition_size,
manifoldClaim?.totalMax
);
const primaryStatus = claim
? getClaimPrimaryStatus({ claim, manifoldClaim: manifoldClaim ?? null })
? getClaimPrimaryStatus({
claim,
manifoldClaim: manifoldClaim ?? null,
isManifoldClaimFetching: onChainClaimFetching,
})
: null;
const hasImage = Boolean(claim?.image_url);
const hasAnimation = Boolean(claim?.animation_url);
Expand All @@ -355,6 +368,31 @@ export default function DropForgeLaunchClaimPageClient({
setActiveMediaTab("image");
}, [claimId]);

useEffect(() => {
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,
manifoldClaim?.totalMax
);
}

return clampResearchTargetEditionSize(
current,
claim.edition_size,
manifoldClaim?.totalMax
);
});
}, [claim, claimId, manifoldClaim?.totalMax]);
Comment thread
prxt6529 marked this conversation as resolved.

useEffect(() => {
setSelectedPhase("");
setIsPhaseSelectionManual(false);
Expand Down Expand Up @@ -945,9 +983,14 @@ 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,
manifoldClaim?.totalMax
);
const researchAirdropCount = Math.max(
0,
Math.trunc(researchTargetEditionSize) - totalMinted
cappedResearchTargetEditionSize - totalMinted
);
Comment thread
prxt6529 marked this conversation as resolved.

const runMetadataLocationOnlyUpdate = useCallback(() => {
Expand Down Expand Up @@ -1442,12 +1485,36 @@ export default function DropForgeLaunchClaimPageClient({
setSelectedPhase(value);
}, []);

const handleResearchTargetEditionSizeChange = useCallback((value: string) => {
const parsed = Number(value);
setResearchTargetEditionSize(
Number.isFinite(parsed) && parsed >= 0 ? Math.trunc(parsed) : 0
);
}, []);
const handleResearchTargetEditionSizeChange = useCallback(
(value: string) => {
const parsed = Number(value);
const editionSizeLimit = getResearchTargetEditionSizeLimit(
claim?.edition_size,
manifoldClaim?.totalMax
);
if (
editionSizeLimit != null &&
Number.isFinite(parsed) &&
parsed > editionSizeLimit
) {
setResearchTargetEditionSize(editionSizeLimit);
setToast({
message: `Target edition size cannot exceed claim max (${editionSizeLimit})`,
type: "error",
});
return;
}

setResearchTargetEditionSize(
clampResearchTargetEditionSize(
Number.isFinite(parsed) && parsed >= 0 ? parsed : 0,
claim?.edition_size,
manifoldClaim?.totalMax
)
);
},
[claim?.edition_size, manifoldClaim?.totalMax, setToast]
);

const handleSelectedPhasePriceChange = useCallback(
(value: string) => {
Expand Down Expand Up @@ -1611,7 +1678,8 @@ export default function DropForgeLaunchClaimPageClient({
selectedPhase={selectedPhase}
onSelectedPhaseChange={handleSelectedPhaseChange}
totalMinted={totalMinted}
researchTargetEditionSize={researchTargetEditionSize}
researchTargetEditionSize={cappedResearchTargetEditionSize}
researchTargetEditionSizeMax={researchTargetEditionSizeLimit}
onResearchTargetEditionSizeChange={
handleResearchTargetEditionSizeChange
}
Expand Down
Loading
Loading