Skip to content

Comments

feat(#1782): implemented privy updateEmail#1832

Open
evgenibir wants to merge 2 commits intomainfrom
feat/1782-Updating-email-on-profile-should-update-login-change
Open

feat(#1782): implemented privy updateEmail#1832
evgenibir wants to merge 2 commits intomainfrom
feat/1782-Updating-email-on-profile-should-update-login-change

Conversation

@evgenibir
Copy link
Member

@evgenibir evgenibir commented Nov 27, 2025

Summary by CodeRabbit

  • New Features
    • Enhanced email management in profile settings with authentication-based restrictions. Users with certain account types will now see informational guidance explaining when email changes are unavailable.

✏️ Tip: You can customize this high-level summary in your review settings.

@evgenibir evgenibir force-pushed the feat/1782-Updating-email-on-profile-should-update-login-change branch from 5b66e2e to 57a442e Compare November 27, 2025 09:40
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 27, 2025

Walkthrough

This PR integrates Privy-based email handling into the profile edit flow. The profile edit page now detects email changes using Privy's user state, queues pending form data when an email change is identified, and uses Privy's updateEmail API to process the change asynchronously. The EditPersonSection component is enhanced with a cannotChangeEmail prop to disable email editing and display usage restrictions.

Changes

Cohort / File(s) Summary
Privy Email Integration
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx
Imports useUpdateAccount and usePrivy hooks; computes email state variables (hasEmailAccount, currentPrivyEmail, hasOAuthAccount, cannotChangeEmail) from Privy user data; introduces pendingFormDataRef to buffer form data during email updates; sets up updateEmail handler with onSuccess callback to apply edits and navigate, and onError callback to retry with cleanup; enhances onEdit to detect email changes and conditionally route through Privy's updateEmail or standard editProfile flow.
Email Change Constraints UI
packages/epics/src/people/components/edit-person-section.tsx
Adds optional cannotChangeEmail prop to component signature (default: false); updates email input disabled condition to include cannotChangeEmail; renders informational warning block when cannotChangeEmail is true to explain email change restrictions.

Sequence Diagram

sequenceDiagram
    actor User
    participant Page as Profile Edit Page
    participant Privy as Privy Auth Service
    participant API as Backend API
    participant DB as Database

    User->>Page: Edit profile with email change
    Page->>Page: Detect email differs from currentPrivyEmail
    Page->>Page: Store form data in pendingFormDataRef
    Page->>Privy: Call updateEmail()
    activate Privy
    Privy->>Privy: Validate & process email update
    alt Email update succeeds
        Privy-->>Page: onSuccess callback
        Page->>API: editProfile with pending data
        API->>DB: Update profile
        API-->>Page: Success response
        Page->>Page: Navigate to /profile
    else Email update fails
        Privy-->>Page: onError callback
        Page->>API: editProfile with pending data (retry)
        API->>DB: Update profile
        API-->>Page: Success response
        Page->>Page: Navigate to /profile
    end
    deactivate Privy
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Profile edit page: Verify async flow correctness for Privy email updates, state derivation logic, and error handling paths
  • EditPersonSection: Confirm prop interface change is backward compatible and warning UI renders only when appropriate
  • Integration: Ensure cannotChangeEmail state is correctly derived and passed through the component hierarchy

Possibly related PRs

Suggested reviewers

  • sergey3bv
  • plitzenberger

Poem

🐰 Hopping through Privy's secure email gates,
Pending forms buffered while auth awaits,
Google and OAuth harmonize so tight,
Email constraints dance in guiding light,
Profile edits flow—now Privy-approved and 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 accurately describes the main change: implementing Privy's updateEmail functionality for profile email updates.
✨ 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/1782-Updating-email-on-profile-should-update-login-change

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 (3)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (3)

55-57: Use console.error for error logging.

Errors caught in try/catch blocks should be logged with console.error for proper severity and dev tools filtering.

         } catch (err) {
-          console.log(err);
+          console.error('Error editing profile after email update:', err);
           pendingFormDataRef.current = null;
         }

72-73: Use console.error for consistency.

Same as above — prefer console.error for caught exceptions.

             .catch((err) => {
-              console.log(err);
+              console.error('Error editing profile:', err);
             })

102-104: Use console.error for caught exceptions.

     } catch (err) {
-      console.log(err);
+      console.error('Error in onEdit:', err);
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86a46ae and 57a442e.

📒 Files selected for processing (1)
  • apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (2 hunks)
⏰ 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). (1)
  • GitHub Check: deploy-preview
🔇 Additional comments (1)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)

95-97: The review comment is incorrect. The updateEmail() call is correct as written.

According to Privy's official documentation, updateEmail() takes no parameters and is designed to open a modal where the user enters and verifies their new email address. The code correctly follows the Privy pattern:

  1. updateEmail() is called with no arguments (line 97) ✓
  2. The onSuccess callback (lines 49-60) handles the post-update flow
  3. The pendingFormDataRef pattern properly stores the form data for use after email verification succeeds

The suggested fix in the review comment (updateEmail({ email: newEmail })) would be incorrect and would not work with the Privy SDK.

Likely an incorrect or invalid review comment.


if (emailChanged) {
pendingFormDataRef.current = data;
updateEmail();
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't updateEmail return Promise, too?

@evgenibir evgenibir force-pushed the feat/1782-Updating-email-on-profile-should-update-login-change branch from 57a442e to 6ee9411 Compare November 27, 2025 13:04
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: 1

♻️ Duplicate comments (1)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)

20-21: useUpdateAccount and updateEmail usage matches Privy API; no await needed

According to Privy’s docs, useUpdateAccount returns { updateEmail: () => void, updatePhone: () => void }, and updateEmail is a void function that just opens the modal (not a Promise), while onSuccess / onError are invoked via callbacks. (docs.privy.io)

Your usage:

  • Destructuring updateEmail from useUpdateAccount({ onSuccess, onError }).
  • Not awaiting updateEmail() in onEdit.
  • Treating onSuccess / onError as the async boundaries for calling editProfile and routing.

is aligned with the documented hook contract and resolves the prior concern about updateEmail returning a Promise.

Also applies to: 50-87

🧹 Nitpick comments (1)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)

89-105: Consider preserving non‑email edits when Privy update fails for other reasons

When emailChanged is true, you stash data in pendingFormDataRef and rely on the useUpdateAccount callbacks to call editProfile. If onError fires with an error that doesn’t match the two specific substrings, you simply clear pendingFormDataRef and never persist the pending profile changes.

This means users who attempted an email change and hit a generic Privy error will lose all non‑email edits unless they re‑submit the form.

Consider either:

  • Still calling editProfile(pendingFormDataRef.current) for generic errors (perhaps after showing a message that the email itself was not updated), or
  • Surfacing an explicit error to the user and leaving pendingFormDataRef intact so they can retry, rather than silently dropping it.

Also applies to: 50-87

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57a442e and 6ee9411.

📒 Files selected for processing (2)
  • apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (3 hunks)
  • packages/epics/src/people/components/edit-person-section.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-03-19T12:11:00.004Z
Learnt from: plitzenberger
Repo: hypha-dao/hypha-web PR: 486
File: apps/web/src/app/[lang]/@aside/(.)profile/edit/loading.tsx:21-21
Timestamp: 2025-03-19T12:11:00.004Z
Learning: In the EditPersonSection component, successfulEditCallback is an optional prop that should be invoked using optional chaining (successfulEditCallback?.()) when it's used.

Applied to files:

  • packages/epics/src/people/components/edit-person-section.tsx
📚 Learning: 2025-03-21T11:15:37.961Z
Learnt from: plitzenberger
Repo: hypha-dao/hypha-web PR: 486
File: apps/web/src/app/[lang]/profile/edit/page.tsx:17-24
Timestamp: 2025-03-21T11:15:37.961Z
Learning: In React forms with validation schemas like Zod, required fields that shouldn't be edited by users (like IDs) should be included as hidden form fields rather than checked conditionally in submission handlers. This ensures proper form validation and prevents invalid submissions.

Applied to files:

  • packages/epics/src/people/components/edit-person-section.tsx
🧬 Code graph analysis (2)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)
packages/core/src/people/validation.ts (2)
  • schemaEditPersonWeb2 (145-145)
  • editPersonFiles (83-122)
packages/epics/src/people/components/edit-person-section.tsx (1)
apps/hypha-migration/hypha-react-demo/src/components/HyphaWallet.jsx (1)
  • isLoading (7-7)
⏰ 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). (1)
  • GitHub Check: deploy-preview
🔇 Additional comments (1)
packages/epics/src/people/components/edit-person-section.tsx (1)

44-52: Email lock-out prop and UI wiring look consistent

The cannotChangeEmail prop, its defaulting in the props destructuring, and its use to both disable the email Input and show the explanatory block are all consistent and backward‑compatible with existing callers. No functional issues spotted here.

Also applies to: 56-64, 247-248, 259-270

Comment on lines +36 to +43
const { user: privyUser, authenticated } = usePrivy();

const hasEmailAccount = !!privyUser?.email?.address;
const currentPrivyEmail = privyUser?.email?.address;

const hasOAuthAccount = !!(privyUser?.google || privyUser?.apple);
const cannotChangeEmail =
authenticated && hasOAuthAccount && !hasEmailAccount;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Align cannotChangeEmail gating with OAuth provider‑agnostic copy

cannotChangeEmail is computed as:

const hasOAuthAccount = !!(privyUser?.google || privyUser?.apple);
const cannotChangeEmail = authenticated && hasOAuthAccount && !hasEmailAccount;

and then passed to EditPersonSection. For Apple SSO users (where privyUser?.apple is truthy but privyUser?.google is not), this flag will also be true, but the explanatory text rendered in EditPersonSection currently talks only about “Google SSO” and “Google Account settings”.

To avoid confusing Apple‑SSO users, consider either:

  • Making the message in EditPersonSection provider‑agnostic, or
  • Passing down more specific context (e.g., which OAuth provider is in use) so the message can be tailored appropriately.

Also applies to: 128-136

@evgenibir evgenibir force-pushed the feat/1782-Updating-email-on-profile-should-update-login-change branch from 6ee9411 to 75f0cf4 Compare November 28, 2025 07:14
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/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)

36-43: Consider gating Privy‑derived flags on ready to avoid transient misclassification

Privy recommends checking ready before relying on authenticated/user. Right now hasEmailAccount, emailChanged, and cannotChangeEmail don’t consider ready, so on initial render a logged‑in user could briefly be treated as having no email account (or as unauthenticated) while Privy initializes. That’s a small edge case, but easy to harden by including ready in these conditions.

Example refactor:

-  const { user: privyUser, authenticated } = usePrivy();
+  const { user: privyUser, authenticated, ready } = usePrivy();

   const hasEmailAccount = !!privyUser?.email?.address;
   const currentPrivyEmail = privyUser?.email?.address;

   const hasOAuthAccount = !!(privyUser?.google || privyUser?.apple);
-  const cannotChangeEmail =
-    authenticated && hasOAuthAccount && !hasEmailAccount;
+  const cannotChangeEmail =
+    ready && authenticated && hasOAuthAccount && !hasEmailAccount;
...
-      const emailChanged =
-        authenticated &&
-        hasEmailAccount &&
-        currentPrivyEmail &&
-        newEmail &&
-        newEmail !== currentPrivyEmail;
+      const emailChanged =
+        ready &&
+        authenticated &&
+        hasEmailAccount &&
+        currentPrivyEmail &&
+        newEmail &&
+        newEmail !== currentPrivyEmail;

Also applies to: 91-97, 135-135

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ee9411 and 75f0cf4.

📒 Files selected for processing (2)
  • apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (3 hunks)
  • packages/epics/src/people/components/edit-person-section.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/epics/src/people/components/edit-person-section.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)
packages/core/src/people/validation.ts (2)
  • schemaEditPersonWeb2 (145-145)
  • editPersonFiles (83-122)
⏰ 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). (1)
  • GitHub Check: deploy-preview
🔇 Additional comments (1)
apps/web/src/app/[lang]/profile/[personSlug]/@aside/edit/page.tsx (1)

20-20: Privy updateEmail flow + pendingFormDataRef usage look correct (no await needed)

Per Privy’s useUpdateAccount docs, updateEmail is a () => void that opens the modal and drives completion via the supplied onSuccess/onError callbacks, not a Promise‑returning function. Calling updateEmail() without await in onEdit is therefore correct, and the pattern of stashing EditPersonFormData in pendingFormDataRef and only invoking editProfile from those callbacks gives you the intended “update Privy email first, then persist profile” behavior.

You also clear pendingFormDataRef in all code paths (success, handled error, and unhandled error), so there’s no obvious risk of stale data leaking into a later submission. From a correctness standpoint this async flow looks good as implemented.

Also applies to: 50-87, 99-105

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