Skip to content

Subscribe Next Mint#2203

Merged
prxt6529 merged 11 commits intomainfrom
subscribe-next-mint-2
Apr 2, 2026
Merged

Subscribe Next Mint#2203
prxt6529 merged 11 commits intomainfrom
subscribe-next-mint-2

Conversation

@prxt6529
Copy link
Copy Markdown
Collaborator

@prxt6529 prxt6529 commented Apr 2, 2026

Summary by CodeRabbit

  • New Features

    • Added a reusable ArtistPill UI used across the Now Minting header and artist displays.
    • Introduced a subscription row component and a Next-Mint subscription widget integrated into the Now Minting section and user subscriptions list.
  • Tests

    • Added test coverage for the Next-Mint subscription widget validating render and behavior across profile/session states.

prxt6529 added 3 commits April 2, 2026 15:53
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 2, 2026

Warning

Rate limit exceeded

@prxt6529 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 51 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 51 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 859e2cdf-4a53-4cc5-8210-e67baf3adea7

📥 Commits

Reviewing files that changed from the base of the PR and between e92cf02 and 85da3fa.

📒 Files selected for processing (2)
  • components/home/now-minting/LatestDropNextMintSubscribe.tsx
  • components/user/subscriptions/MemeSubscriptionRow.tsx
📝 Walkthrough

Walkthrough

Adds subscription UI and behavior for upcoming Memes mints, introduces an ArtistPill UI component, moves per-subscription row logic into a reusable MemeSubscriptionRow, updates NowMinting components to use ArtistPill, and adds tests for LatestDropNextMintSubscribe. All changes are component additions/refactors and tests only.

Changes

Cohort / File(s) Summary
Tests
__tests__/components/home/LatestDropNextMintSubscribe.test.tsx
New Jest/RTL test suite mocking useQuery, child components, and calendar helpers to validate rendering with/without connected profile, subscription details, and proxy session.
Artist UI
components/home/now-minting/ArtistPill.tsx
New client component rendering artist pill with optional avatar & profile link; resolves avatar from prop or identity hook.
Now-minting Sections
components/home/now-minting/LatestDropNextMintSection.tsx, components/home/now-minting/NowMintingHeader.tsx
Replaced local pill implementations with ArtistPill; LatestDropNextMintSection now includes LatestDropNextMintSubscribe. Minor MediaTypeBadge prop tweak.
Subscription orchestration
components/home/now-minting/LatestDropNextMintSubscribe.tsx
New client component that derives profileKey/tokenId, queries subscription details & status via React Query, synthesizes NFTSubscription and renders MemeSubscriptionRow when data available.
Subscription row UI/logic
components/user/subscriptions/MemeSubscriptionRow.tsx
New, standalone row component handling subscription toggle, count updates, API POSTs, request blocking during minting, toasts, and cache invalidation; supports compact variant and optional final-subscription fetch when first.
List refactor
components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx
Removed inline SubscriptionRow implementation (≈284 LOC) and delegates per-row rendering to new MemeSubscriptionRow; cleaned up imports.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant LatestDrop as LatestDropNextMintSubscribe
    participant ReactQuery as React Query
    participant API as Server API
    participant Row as MemeSubscriptionRow
    participant Auth as Auth Context

    LatestDrop->>ReactQuery: query(details) / query(status)
    ReactQuery->>API: GET /subscriptions/consolidation/details/{profileKey}
    ReactQuery->>API: GET /subscriptions/consolidation/upcoming-memes/{tokenId}/{profileKey}
    API-->>ReactQuery: details & status
    ReactQuery-->>LatestDrop: data
    LatestDrop->>Row: render(subscription, eligibility, minting_today, refresh)
    User->>Row: toggle subscribe / change count
    Row->>Auth: requestAuth()
    Auth-->>Row: auth context
    Row->>API: POST /subscriptions/{profileKey}/subscription
    Row->>API: POST /subscriptions/{profileKey}/subscription-count
    API-->>Row: success/error
    Row->>ReactQuery: invalidate/refetch subscription queries
    Row->>User: show toast / update UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ragnep
  • simo6529

Poem

🐰✨ New pills and tidy rows I bring,
I hop through code and softly sing,
Subscriptions click and counts align,
The memedrop blooms — a neat design,
Puff of carrot, tests all green!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Subscribe Next Mint' directly aligns with the main changes: adding subscription functionality for the next mint via new LatestDropNextMintSubscribe component and refactored ArtistPill component.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch subscribe-next-mint-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

prxt6529 added 5 commits April 2, 2026 16:35
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
@prxt6529
Copy link
Copy Markdown
Collaborator Author

prxt6529 commented Apr 2, 2026

@CodeRabbit review

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
components/home/now-minting/ArtistPill.tsx (1)

21-26: Gate the identity lookup behind a real lookup key.

Line 22 always calls useIdentity, but components/home/now-minting/NowMintingHeader.tsx now renders a name-only pill with no profileHandle, and components/home/now-minting/LatestDropNextMintSection.tsx already passes pfp directly. That means this shared pill either asks the hook to resolve "" or repeats a fetch whose result is ignored. Please skip the lookup unless a real profileHandle is present and the avatar is actually needed.

Please confirm that useIdentity explicitly no-ops on empty handleOrWallet values before relying on this path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/home/now-minting/ArtistPill.tsx` around lines 21 - 26, The
ArtistPill currently always calls useIdentity with handleOrWallet: profileHandle
?? "" which triggers unnecessary lookups; change the component to only call
useIdentity when a real profileHandle exists and the avatar is needed (e.g.,
when pfp is not provided), and derive resolvedPfp accordingly (pfp ??
profile?.pfp ?? null) only when the hook was invoked; additionally, verify
and/or update the useIdentity implementation so it explicitly no-ops (returns
null/idle state) when handleOrWallet is empty to avoid accidental fetches —
adjust ArtistPill to gate calling useIdentity by a truthy profileHandle and
avatar requirement and rely on pfp prop when present (affecting components like
NowMintingHeader and LatestDropNextMintSection that may pass no handle or pass
pfp directly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/home/now-minting/LatestDropNextMintSubscribe.tsx`:
- Around line 30-31: The component LatestDropNextMintSubscribe currently freezes
tokenId by calling useMemo(() => getCanonicalNextMintNumber(), []) which can
become stale across auth changes; remove the empty-deps memoization or include
the auth-derived dependencies (e.g., connectedProfile, activeProfileProxy) so
tokenId is recomputed when auth state changes, and update any places using
tokenId (the query key that includes tokenId) to rely on the fresh value; also
add a unit/test that simulates changing connectedProfile/activeProfileProxy and
asserts the component re-derives getCanonicalNextMintNumber() and uses the
updated tokenId for fetching/mutations.

In `@components/user/subscriptions/MemeSubscriptionRow.tsx`:
- Around line 82-91: The row can get stuck because setIsSubmitting(true) is set
before calling requestAuth() and any rejection from requestAuth() is not caught;
update both submit and handleUpdateSubscriptionCount to ensure isSubmitting is
cleared on exceptions by wrapping the await requestAuth() call in a try/catch
(or enclosing the auth + subsequent logic in a try/finally) so that on error you
call setIsSubmitting(false) before returning or rethrowing; reference the submit
and handleUpdateSubscriptionCount functions and ensure the error path always
clears isSubmitting.
- Around line 57-61: The component MemeSubscriptionRow forces selectedCount back
to 1 when props.eligibilityCount is 0, which leaves an empty <select> and an
enabled toggle; change the logic in the useEffect(s) that currently do
setSelectedCount(Math.max(1, props.eligibilityCount)) (the block referencing
selectedCount and props.eligibilityCount) to handle eligibilityCount === 0
explicitly by setting selectedCount to 0 instead. Also update any UI/toggle
enablement checks (the toggle control and any handlers that initiate
subscription changes) to disable the toggle and prevent subscription attempts
when props.eligibilityCount === 0, and ensure the submit/change handler (the
function that performs the subscription update) guards against selectedCount ===
0 before proceeding. Apply the same fix pattern to the other similar blocks
mentioned (the ranges around 197-229 and 333-368) so the component consistently
treats eligibilityCount === 0 as ineligible and disables actions.

---

Nitpick comments:
In `@components/home/now-minting/ArtistPill.tsx`:
- Around line 21-26: The ArtistPill currently always calls useIdentity with
handleOrWallet: profileHandle ?? "" which triggers unnecessary lookups; change
the component to only call useIdentity when a real profileHandle exists and the
avatar is needed (e.g., when pfp is not provided), and derive resolvedPfp
accordingly (pfp ?? profile?.pfp ?? null) only when the hook was invoked;
additionally, verify and/or update the useIdentity implementation so it
explicitly no-ops (returns null/idle state) when handleOrWallet is empty to
avoid accidental fetches — adjust ArtistPill to gate calling useIdentity by a
truthy profileHandle and avatar requirement and rely on pfp prop when present
(affecting components like NowMintingHeader and LatestDropNextMintSection that
may pass no handle or pass pfp directly).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3e8790a3-e874-40bf-bd49-433bbdd388e1

📥 Commits

Reviewing files that changed from the base of the PR and between cae9920 and d26e86e.

📒 Files selected for processing (7)
  • __tests__/components/home/LatestDropNextMintSubscribe.test.tsx
  • components/home/now-minting/ArtistPill.tsx
  • components/home/now-minting/LatestDropNextMintSection.tsx
  • components/home/now-minting/LatestDropNextMintSubscribe.tsx
  • components/home/now-minting/NowMintingHeader.tsx
  • components/user/subscriptions/MemeSubscriptionRow.tsx
  • components/user/subscriptions/UserPageSubscriptionsUpcoming.tsx

Comment thread components/home/now-minting/LatestDropNextMintSubscribe.tsx
Comment thread components/user/subscriptions/MemeSubscriptionRow.tsx
Comment thread components/user/subscriptions/MemeSubscriptionRow.tsx Outdated
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Signed-off-by: prxt6529 <prxt@6529.io>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
components/user/subscriptions/MemeSubscriptionRow.tsx (2)

91-107: ⚠️ Potential issue | 🟠 Major

Keep isSubmitting recoverable if auth rejects.

Lines 95-99 and 150-154 still run requestAuth() outside the try/finally. If it rejects instead of returning { success: false }, isSubmitting never clears, the spinner stays up, and Line 202 also leaks that rejection through the fire-and-forget count update.

💡 Suggested fix
  const submit = async (): Promise<void> => {
    if (isSubmitting || props.minting_today) {
      return;
    }
-    setIsSubmitting(true);
-    const { success } = await requestAuth();
-    if (!success) {
-      setIsSubmitting(false);
-      return;
-    }
-    const subscribe = !subscribed;
     interface SubscribeBody {
       contract: string;
       token_id: number;
       subscribed: boolean;
     }
     try {
+      setIsSubmitting(true);
+      const { success } = await requestAuth();
+      if (!success) {
+        return;
+      }
+      const subscribe = !subscribed;
       const response = await commonApiPost<SubscribeBody, SubscribeBody>({

Apply the same structure to handleUpdateSubscriptionCount().

Also applies to: 144-161, 199-202

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/subscriptions/MemeSubscriptionRow.tsx` around lines 91 - 107,
The submit function currently sets isSubmitting before calling requestAuth()
outside a try/finally, so if requestAuth throws the spinner never clears; update
submit to call requestAuth() inside a try block and ensure
setIsSubmitting(false) is executed in a finally so isSubmitting is always reset
on success, failure, or thrown error; do the same for
handleUpdateSubscriptionCount() (and any other spots around lines noted) so
requestAuth() and any fire-and-forget updates are wrapped with
try/catch/finally, catch/log errors appropriately and ensure setIsSubmitting is
cleared in the finally block while preserving the existing subscribed toggle and
side-effect behavior.

61-65: ⚠️ Potential issue | 🟠 Major

Handle eligibilityCount === 0 as an ineligible state.

Line 63 still clamps back to 1 when there are zero eligible slots, so renderCountSelector() can still render a controlled <select> with no options. Since the caller forwards 0 unchanged, and both Toggle instances plus submit() still allow the unsubscribed path, this component can still attempt a subscribe mutation with no legal quantity.

💡 Suggested fix
  useEffect(() => {
    if (selectedCount > props.eligibilityCount) {
-      setSelectedCount(Math.max(1, props.eligibilityCount));
+      setSelectedCount(
+        props.eligibilityCount === 0 ? 0 : props.eligibilityCount
+      );
    }
  }, [props.eligibilityCount, selectedCount]);

  const submit = async (): Promise<void> => {
-    if (isSubmitting || props.minting_today) {
+    if (
+      isSubmitting ||
+      props.minting_today ||
+      (!subscribed && props.eligibilityCount < 1)
+    ) {
      return;
    }
  const renderCountSelector = ({
    selectClassName,
    disableWhenSingleOption,
  }: {
    selectClassName: string;
    disableWhenSingleOption: boolean;
  }) => {
-    if (!subscribed) {
+    if (!subscribed || props.eligibilityCount < 1) {
      return null;
    }

Mirror the same (!subscribed && props.eligibilityCount < 1) guard in both Toggle.disabled expressions.

Also applies to: 91-99, 205-237, 249-255, 354-360

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/user/subscriptions/MemeSubscriptionRow.tsx` around lines 61 - 65,
The useEffect that clamps selectedCount currently forces a minimum of 1 even
when props.eligibilityCount === 0, enabling renderCountSelector to render a
controlled <select> with no options and allowing subscribe paths; update the
clamp in the useEffect (references: useEffect, selectedCount, setSelectedCount,
props.eligibilityCount) to allow selectedCount to become 0 when eligibilityCount
is 0 (e.g., clamp to Math.max(0, props.eligibilityCount)), and also add the same
ineligibility guard to both Toggle.disabled expressions (references:
Toggle.disabled, subscribed, props.eligibilityCount) so the toggles are disabled
when (!subscribed && props.eligibilityCount < 1); apply the same pattern at the
other affected blocks noted in the comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/user/subscriptions/MemeSubscriptionRow.tsx`:
- Around line 150-155: The optimistic update to selectedCount is happening
before authentication and isn't rolled back when requestAuth() returns {
success: false }; locate the calls to setSelectedCount / selectedCount updates
in MemeSubscriptionRow (the places that call setIsSubmitting(true) then await
requestAuth()) and either move the optimistic setSelectedCount so it happens
only after requestAuth() succeeds, or if you prefer to keep it optimistic,
restore subscribedCount/selectedCount by calling
setSelectedCount(subscribedCount) before returning when requestAuth() fails;
ensure both occurrences (the two spots where setSelectedCount is updated around
requestAuth()) are corrected and setIsSubmitting(false) is still called on the
failure path.

---

Duplicate comments:
In `@components/user/subscriptions/MemeSubscriptionRow.tsx`:
- Around line 91-107: The submit function currently sets isSubmitting before
calling requestAuth() outside a try/finally, so if requestAuth throws the
spinner never clears; update submit to call requestAuth() inside a try block and
ensure setIsSubmitting(false) is executed in a finally so isSubmitting is always
reset on success, failure, or thrown error; do the same for
handleUpdateSubscriptionCount() (and any other spots around lines noted) so
requestAuth() and any fire-and-forget updates are wrapped with
try/catch/finally, catch/log errors appropriately and ensure setIsSubmitting is
cleared in the finally block while preserving the existing subscribed toggle and
side-effect behavior.
- Around line 61-65: The useEffect that clamps selectedCount currently forces a
minimum of 1 even when props.eligibilityCount === 0, enabling
renderCountSelector to render a controlled <select> with no options and allowing
subscribe paths; update the clamp in the useEffect (references: useEffect,
selectedCount, setSelectedCount, props.eligibilityCount) to allow selectedCount
to become 0 when eligibilityCount is 0 (e.g., clamp to Math.max(0,
props.eligibilityCount)), and also add the same ineligibility guard to both
Toggle.disabled expressions (references: Toggle.disabled, subscribed,
props.eligibilityCount) so the toggles are disabled when (!subscribed &&
props.eligibilityCount < 1); apply the same pattern at the other affected blocks
noted in the comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3b21787a-fcc8-47b8-bfbf-6b2275ddd341

📥 Commits

Reviewing files that changed from the base of the PR and between d26e86e and e92cf02.

📒 Files selected for processing (1)
  • components/user/subscriptions/MemeSubscriptionRow.tsx

Comment thread components/user/subscriptions/MemeSubscriptionRow.tsx Outdated
prxt6529 added 2 commits April 2, 2026 17:30
Signed-off-by: prxt6529 <prxt@6529.io>
Signed-off-by: prxt6529 <prxt@6529.io>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Apr 2, 2026

@prxt6529 prxt6529 merged commit e718dee into main Apr 2, 2026
8 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Apr 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants