Skip to content

Comments

Simplify merge#969

Merged
elie222 merged 7 commits intomainfrom
feat/simplify-merge
Nov 14, 2025
Merged

Simplify merge#969
elie222 merged 7 commits intomainfrom
feat/simplify-merge

Conversation

@elie222
Copy link
Owner

@elie222 elie222 commented Nov 14, 2025

Summary by CodeRabbit

  • New Features

    • Referral dialog now supports scrolling and improved responsive layout across screen sizes.
  • Bug Fixes

    • Premium card now displays correctly when subscription data is unavailable.
    • Video card properly hidden on mobile devices.
  • Style

    • Updated billing message text.
    • Refined referral card display and action buttons.
    • Adjusted grid layouts for improved mobile responsiveness.
  • Chores

    • Removed account merge confirmation dialog.
    • Simplified OAuth linking flow.
    • Version bumped to v2.19.2.

@vercel
Copy link

vercel bot commented Nov 14, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
inbox-zero Ready Ready Preview Nov 14, 2025 3:26pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 14, 2025

Walkthrough

This pull request removes the action parameter from OAuth account-linking flows across Google and Outlook providers, simplifying the authorization state generation and account linking logic to direct account merging instead of action-based branching. Supporting UI and component updates include renaming and generalizing the premium card component and adjusting responsive layouts.

Changes

Cohort / File(s) Summary
OAuth Google/Outlook Auth-URL Simplification
apps/web/app/api/google/linking/auth-url/route.ts, apps/web/app/api/outlook/linking/auth-url/route.ts
Removed action parameter dependency and validation; getAuthUrl() now accepts only userId and generates OAuth state solely from userId. Zod validation import removed from Google route.
OAuth Google/Outlook Callback Handling
apps/web/app/api/google/linking/callback/route.ts, apps/web/app/api/outlook/linking/callback/route.ts
Removed action field from OAuth validation destructuring and from handleAccountLinking() call payload, reducing input object parameters.
Account Linking Logic Refactor
apps/web/utils/oauth/account-linking.ts
Removed action field from AccountLinkingParams interface; eliminated action-based branching and redirect flows; introduced direct account merge logic with warnings for conflicts (e.g., already_linked_to_self, account_already_exists_use_merge).
OAuth Callback Validation
apps/web/utils/oauth/callback-validation.ts
Removed action field from ValidationResult success type and decoded state type; updated return value to exclude action.
OAuth Validation Tests
apps/web/utils/oauth/callback-validation.test.ts, apps/web/utils/oauth/account-linking.test.ts
Removed action: "auto" mock setup and result assertions; removed test case for redirect-on-auto flow; renamed and simplified merge-confirmation test expectations.
Premium Card Generalization
apps/web/components/PremiumCard.tsx
Renamed exported component from PremiumExpiredCard to PremiumCard; removed early null guard; added fallback "Upgrade to Premium" messaging when premium data is absent; introduced isNewUser logic to conditionally set button text and href (Upgrade/Reactivate paths).
Premium Card Import Updates
apps/web/app/(landing)/components/page.tsx, apps/web/components/SideNav.tsx
Updated imports and usages from PremiumExpiredCard to PremiumCard with corresponding path changes.
AddAccount UI Updates
apps/web/app/(app)/accounts/AddAccount.tsx
Removed action=auto query parameter from Google/Microsoft auth-URL fetch; updated billing message text and styling (TypographyP with text-muted-foreground).
Accounts Page Cleanup
apps/web/app/(app)/accounts/page.tsx
Removed useState import and AlertDialog-related imports; deleted MergeConfirmationDialog component and all related state/effects/API calls; adjusted grid layout from md:grid-cols-2 lg:grid-cols-3 to lg:grid-cols-2 xl:grid-cols-3.
ReferralDialog Layout Refactor
apps/web/components/ReferralDialog.tsx
Added max-height with vertical overflow to DialogContent; reduced spacing (space-y-8space-y-6 sm:space-y-8); reworked referral code card from two-column layout to compact rounded card with horizontal overflow and monospace display; adjusted skeleton and typography sizing for responsiveness.
VideoCard Responsive Visibility
apps/web/components/VideoCard.tsx
Added responsive hidden/visible breakpoint: Dialog container now hidden on small screens and shown on medium+ screens (hidden md:flex).
Version Bump
version.txt
Updated version from v2.19.0 to v2.19.2.

Sequence Diagram(s)

sequenceDiagram
    participant Client as AddAccount Client
    participant AuthURL as OAuth Auth-URL Endpoint
    participant Cookie as Cookie Storage
    participant Callback as OAuth Callback
    participant Validation as Validation Logic
    participant Linking as Account Linking
    
    Client->>AuthURL: GET /api/{provider}/linking/auth-url<br/>(userId only)
    AuthURL->>AuthURL: generateState(userId)
    AuthURL->>AuthURL: getAuthUrl({ userId })
    AuthURL->>Cookie: Set OAuth state cookie
    AuthURL-->>Client: Return auth URL
    
    Client->>Callback: OAuth provider redirects to callback<br/>with code & state
    Callback->>Validation: validateOAuthCallback(state, code)
    Validation->>Validation: Decode state (userId, nonce)
    Validation-->>Callback: { targetUserId, code }
    
    Callback->>Linking: handleAccountLinking({ targetUserId, code })
    alt Account exists for same user
        Linking-->>Callback: { type: "continue_create" }
    else Account exists for different user
        Linking-->>Callback: { type: "merge", sourceUserId, sourceAccountId }
    else No existing account
        Linking-->>Callback: { type: "continue_create" }
    end
Loading
sequenceDiagram
    participant Store as Premium Data Store
    participant Card as PremiumCard Component
    participant UI as UI Render
    
    Store->>Card: premium data (may be undefined)
    Card->>Card: hasNoSubscription check
    alt Premium data exists
        Card->>Card: Extract renewal date
        Card->>UI: Render expired card (Reactivate)
    else Premium data missing
        Card->>Card: Determine if new user<br/>(no subscription data)
        alt New user
            Card->>UI: Render upgrade message<br/>(Upgrade to Premium)
        else Existing user
            Card->>UI: Render reactivate message<br/>(Reactivate)
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Multiple heterogeneous changes: OAuth simplification across different provider routes, account linking logic refactoring, and several UI component updates require separate reasoning for each area.
  • API signature changes: handleAccountLinking(), ValidationResult type, and AccountLinkingParams interface changes are breaking changes affecting multiple call sites.
  • Logic density in core files: apps/web/utils/oauth/account-linking.ts contains non-trivial conditional logic changes that warrant careful review to ensure merge flows function correctly and error handling remains intact.
  • Component refactoring complexity: PremiumCard.tsx logic changes (null guards, isNewUser branching, dynamic button text/href) require verification against test coverage and UI behavior across different states.
  • Test coverage: Review test removals in account-linking.test.ts to ensure removed tests represent truly obsolete scenarios and not oversight.

Possibly related PRs

  • Premium expired sidebar card #816: Adds and exports the PremiumExpiredCard component that this PR refactors into PremiumCard with generalized logic—directly related refactoring continuation.
  • fix: Fix microsoft account link for new accounts #731: Introduces the action parameter and action-based OAuth branching logic that this PR removes—opposing changes addressing the same feature area.
  • Fix up account merging #967: Modifies OAuth account-linking flow (auth-url/callback routes, validation, and linking logic) to add action-aware flows—this PR reverses those changes.

Poem

🐰 The action parameter hops away,
OAuth flows now simpler today,
Direct merges replace the old dance,
Premium cards don a new stance,
Layouts shift on responsive sight—
Refactored and shining bright! ✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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 'Simplify merge' directly matches the primary objective of the PR: removing action parameter handling and streamlining account linking flow, thereby simplifying the merge logic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/simplify-merge

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.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
apps/web/components/PremiumCard.tsx (1)

46-70: Suggest extracting duplicate logic into a helper function.

The logic for determining if a user has no subscription appears twice (lines 51-53 and lines 60-64). Additionally, similar logic is repeated at lines 116-120 for isNewUser.

Consider extracting this into a helper function:

+const hasNoSubscriptionHistory = (premium: PremiumData | null | undefined) => {
+  if (!premium) return true;
+  return (
+    !premium.stripeSubscriptionStatus &&
+    !premium.stripeSubscriptionId &&
+    !premium.lemonSqueezySubscriptionId
+  );
+};
+
 const getSubscriptionMessage = () => {
   const UPGRADE_MESSAGE = {
     title: "Upgrade to Premium",
     description: "Upgrade to Premium to enable your AI email assistant.",
   };

-  if (!premium) {
+  if (hasNoSubscriptionHistory(premium)) {
     return UPGRADE_MESSAGE;
   }

   const status = premium.stripeSubscriptionStatus;
   const hasLemonSqueezyExpired =
     lemonSqueezyRenewsAt && lemonSqueezyRenewsAt < new Date();

-  // Check if user never had a subscription
-  const hasNoSubscription =
-    !status &&
-    !premium.stripeSubscriptionId &&
-    !premium.lemonSqueezySubscriptionId;
-
-  if (!premium || hasNoSubscription) {
-    return {
-      title: "Upgrade to Premium",
-      description: "Upgrade to Premium to enable your AI email assistant.",
-    };
-  }

Then reuse the helper for isNewUser at line 116:

-const isNewUser =
-  !premium ||
-  (!premium.stripeSubscriptionStatus &&
-    !premium.stripeSubscriptionId &&
-    !premium.lemonSqueezySubscriptionId);
+const isNewUser = hasNoSubscriptionHistory(premium);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c74679b and e3ac0bb.

📒 Files selected for processing (16)
  • apps/web/app/(app)/accounts/AddAccount.tsx (2 hunks)
  • apps/web/app/(app)/accounts/page.tsx (2 hunks)
  • apps/web/app/(landing)/components/page.tsx (1 hunks)
  • apps/web/app/api/google/linking/auth-url/route.ts (2 hunks)
  • apps/web/app/api/google/linking/callback/route.ts (1 hunks)
  • apps/web/app/api/outlook/linking/auth-url/route.ts (2 hunks)
  • apps/web/app/api/outlook/linking/callback/route.ts (1 hunks)
  • apps/web/components/PremiumCard.tsx (5 hunks)
  • apps/web/components/ReferralDialog.tsx (4 hunks)
  • apps/web/components/SideNav.tsx (2 hunks)
  • apps/web/components/VideoCard.tsx (1 hunks)
  • apps/web/utils/oauth/account-linking.test.ts (1 hunks)
  • apps/web/utils/oauth/account-linking.ts (2 hunks)
  • apps/web/utils/oauth/callback-validation.test.ts (0 hunks)
  • apps/web/utils/oauth/callback-validation.ts (0 hunks)
  • version.txt (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/utils/oauth/callback-validation.test.ts
  • apps/web/utils/oauth/callback-validation.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.
📚 Learning: 2025-07-08T13:14:07.449Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 537
File: apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx:30-34
Timestamp: 2025-07-08T13:14:07.449Z
Learning: The clean onboarding page in apps/web/app/(app)/[emailAccountId]/clean/onboarding/page.tsx is intentionally Gmail-specific and should show an error for non-Google email accounts rather than attempting to support multiple providers.

Applied to files:

  • apps/web/utils/oauth/account-linking.test.ts
  • apps/web/app/(app)/accounts/AddAccount.tsx
  • apps/web/app/(app)/accounts/page.tsx
  • apps/web/utils/oauth/account-linking.ts
📚 Learning: 2025-06-05T09:49:12.168Z
Learnt from: elie222
Repo: elie222/inbox-zero PR: 485
File: apps/web/app/(landing)/login/page.tsx:41-43
Timestamp: 2025-06-05T09:49:12.168Z
Learning: In Next.js App Router, components that use the `useSearchParams` hook require a Suspense boundary to handle the asynchronous nature of search parameter access. The Suspense wrapper is necessary and should not be removed when a component uses useSearchParams.

Applied to files:

  • apps/web/app/(app)/accounts/page.tsx
📚 Learning: 2024-08-23T11:37:26.779Z
Learnt from: aryanprince
Repo: elie222/inbox-zero PR: 210
File: apps/web/app/(app)/stats/NewsletterModal.tsx:2-4
Timestamp: 2024-08-23T11:37:26.779Z
Learning: `MoreDropdown` is a React component and `useUnsubscribeButton` is a custom React hook, and they should not be imported using `import type`.

Applied to files:

  • apps/web/app/(app)/accounts/page.tsx
🧬 Code graph analysis (5)
apps/web/app/api/google/linking/auth-url/route.ts (2)
apps/web/utils/gmail/client.ts (1)
  • getLinkingOAuth2Client (40-46)
apps/web/utils/oauth/state.ts (1)
  • generateOAuthState (10-18)
apps/web/app/api/outlook/linking/auth-url/route.ts (1)
apps/web/utils/oauth/state.ts (1)
  • generateOAuthState (10-18)
apps/web/app/(app)/accounts/AddAccount.tsx (1)
apps/web/components/Typography.tsx (1)
  • TypographyP (129-129)
apps/web/components/SideNav.tsx (1)
apps/web/components/PremiumCard.tsx (1)
  • PremiumCard (213-232)
apps/web/components/PremiumCard.tsx (1)
apps/web/utils/premium/index.ts (1)
  • isPremium (14-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: test
🔇 Additional comments (24)
version.txt (1)

1-1: LGTM!

Version bump is appropriate for the changes in this PR.

apps/web/components/VideoCard.tsx (1)

109-109: LGTM!

The responsive behavior appropriately hides the video thumbnail on small screens while keeping the "Watch Video" button accessible, improving mobile UX.

apps/web/components/ReferralDialog.tsx (3)

31-31: LGTM!

Adding scrollable overflow ensures the referral dialog content remains accessible on smaller viewports.


99-108: LGTM!

Responsive typography and spacing adjustments improve readability across different screen sizes.


121-127: LGTM!

The overflow-x-auto and break-all handling ensures long referral URLs display correctly without breaking the layout.

apps/web/app/(app)/accounts/AddAccount.tsx (2)

22-25: LGTM!

The simplification from POST with action parameter to GET aligns with the PR objective to remove action-based OAuth flows. The cleaner API surface reduces complexity.


86-87: Minor text improvement.

Changing "each additional account" to "each account" is clearer and more accurate, as all accounts are billed, not just additional ones.

apps/web/components/SideNav.tsx (1)

60-60: LGTM!

Clean refactor updating to the renamed PremiumCard component.

Also applies to: 235-235

apps/web/app/(landing)/components/page.tsx (1)

41-41: LGTM!

Import path correctly updated to reference the renamed PremiumCard module.

apps/web/app/(app)/accounts/page.tsx (2)

6-6: LGTM!

Cleanup of unused useState import after MergeConfirmationDialog removal.


38-38: Note the responsive layout change.

The grid now displays:

  • 1 column until lg breakpoint (1024px)
  • 2 columns from lg to xl (1024-1280px)
  • 3 columns at xl+ (1280px+)

Previously it went multi-column earlier at md (768px). This may provide better spacing on tablets but delays the multi-column view.

apps/web/app/api/outlook/linking/callback/route.ts (1)

36-36: Verification confirmed - changes are consistent across both OAuth callback routes.

The Google callback route at line 35 shows the same destructuring pattern: const { targetUserId, code } = validation; with the action parameter removed. The handleAccountLinking call also does not include the action parameter, confirming that both the Outlook and Google linking callbacks have been updated consistently with the PR objective.

apps/web/app/api/google/linking/auth-url/route.ts (1)

13-26: LGTM! Clean simplification of OAuth state generation.

The removal of the action parameter streamlines the authorization flow. The state now derives solely from userId, which simplifies the OAuth flow and reduces complexity.

apps/web/app/api/google/linking/callback/route.ts (2)

33-33: LGTM! Correctly updated to remove action from validation.

The destructuring now matches the updated validateOAuthCallback return type, which no longer includes the action field.


93-102: LGTM! Simplified account linking flow.

The handleAccountLinking call no longer passes the action parameter, relying instead on the function's internal logic to determine the appropriate flow (merge vs. create). The handling of the result types (redirect, continue_create, and merge) remains comprehensive.

apps/web/app/api/outlook/linking/auth-url/route.ts (2)

12-19: LGTM! Consistent simplification with Google OAuth flow.

The removal of the action parameter aligns with the Google linking flow, providing consistency across OAuth providers. State generation is now streamlined to derive from userId only.


27-31: Good addition: OAuth state cookie is now properly set.

This ensures the state is available for validation during the callback, matching the pattern used in the Google linking flow.

apps/web/utils/oauth/account-linking.test.ts (1)

75-92: LGTM! Test correctly reflects new direct merge behavior.

The test has been appropriately updated to expect a direct merge result (type: "merge") instead of a redirect to a confirmation flow. This aligns with the simplified account linking logic that no longer requires action-based branching.

apps/web/components/PremiumCard.tsx (3)

32-41: LGTM! Robust null handling for premium data.

The use of optional chaining ensures the component gracefully handles missing premium data without throwing errors.


116-123: LGTM! Clear differentiation between new and existing users.

The logic correctly determines whether to show an "Upgrade" or "Reactivate" flow based on subscription history, improving the user experience.


213-213: No remaining references to old component name detected.

The search for PremiumExpiredCard found no direct matches in the codebase. The search results show PremiumExpiredCardContent—a separate component—which appears to be an internal sub-component and is distinct from the renamed public export PremiumCard. This indicates all references to the old component name have been successfully updated or removed.

apps/web/utils/oauth/account-linking.ts (3)

6-15: Public API change: Removed action parameter from AccountLinkingParams.

The interface has been simplified by removing the action: "auto" | "merge_confirmed" field. This is a breaking change that affects all callers of handleAccountLinking.


46-65: Excellent addition: Prevents duplicate email account creation.

This new logic checks if an email account already exists for a different user before attempting to create a new one, preventing conflicts. The error message "account_already_exists_use_merge" clearly guides the user to use the merge flow instead.


86-96: LGTM! Simplified and more direct merge flow.

The function now proceeds directly to merge when an account exists for a different user, removing the need for action-based branching. The logging clearly documents the merge decision.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 16 files

Prompt for AI agents (all 1 issues)

Understand the root cause of the following 1 issues and fix them.


<file name="apps/web/app/(app)/accounts/page.tsx">

<violation number="1" location="apps/web/app/(app)/accounts/page.tsx:6">
Removing the merge confirmation dialog removes the entire confirm-merge flow that reacts to the ?confirm_merge/provider/email query params and issues the merge_confirmed redirect, so users can no longer finalize an account merge.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

import Link from "next/link";
import { Trash2, ArrowRight, BotIcon } from "lucide-react";
import { useEffect, useState } from "react";
import { useEffect } from "react";
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 14, 2025

Choose a reason for hiding this comment

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

Removing the merge confirmation dialog removes the entire confirm-merge flow that reacts to the ?confirm_merge/provider/email query params and issues the merge_confirmed redirect, so users can no longer finalize an account merge.

Prompt for AI agents
Address the following comment on apps/web/app/(app)/accounts/page.tsx at line 6:

<comment>Removing the merge confirmation dialog removes the entire confirm-merge flow that reacts to the ?confirm_merge/provider/email query params and issues the merge_confirmed redirect, so users can no longer finalize an account merge.</comment>

<file context>
@@ -3,7 +3,7 @@
 import Link from &quot;next/link&quot;;
 import { Trash2, ArrowRight, BotIcon } from &quot;lucide-react&quot;;
-import { useEffect, useState } from &quot;react&quot;;
+import { useEffect } from &quot;react&quot;;
 import { useSearchParams, useRouter, usePathname } from &quot;next/navigation&quot;;
 import { ConfirmDialog } from &quot;@/components/ConfirmDialog&quot;;
</file context>
Fix with Cubic

@elie222 elie222 merged commit 9cba411 into main Nov 14, 2025
18 checks passed
@elie222 elie222 deleted the feat/simplify-merge branch November 14, 2025 15:34
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.

1 participant