Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Privy Passkeys #1511

Merged
merged 12 commits into from
Jan 22, 2025
Merged

feat: Privy Passkeys #1511

merged 12 commits into from
Jan 22, 2025

Conversation

alexrisch
Copy link
Contributor

@alexrisch alexrisch commented Jan 13, 2025

Added support for privy passkey login
Added support for smart contract wallets provided by privy Updated app entitlements
Updated builds for new env variables
Updated packages for better support
Fixed crash with app theme
Removed noisy logs

Summary by CodeRabbit

Based on the comprehensive summary of changes, here are the concise release notes:

Release Notes

  • New Features

    • Added passkey authentication for account creation and login
    • Introduced new onboarding flow with contact card setup
    • Enhanced user profile management with "real" and "rando" modes
    • Improved notification settings configuration
  • User Experience

    • Streamlined account creation process
    • Added more flexible profile creation options
    • Implemented theme-aware onboarding screens
    • Enhanced avatar and username generation
  • Technical Improvements

    • Updated Privy and XMTP integration
    • Refactored navigation and authentication flows
    • Added new design system components
    • Improved localization support
  • Platform Support

    • Enhanced iOS and Android compatibility
    • Added platform-specific styling and icon support

@alexrisch alexrisch requested a review from a team as a code owner January 13, 2025 15:44
Copy link
Contributor

coderabbitai bot commented Jan 13, 2025

Walkthrough

This pull request introduces comprehensive changes across the application's onboarding and configuration processes. The modifications primarily focus on restructuring the onboarding flow, updating workflow configurations, and enhancing authentication mechanisms. Key changes include replacing user profile screens with a new contact card screen, updating navigation logic, modifying environment configurations, and introducing new components for a more streamlined user experience.

Changes

File/Group Change Summary
.github/workflows/ Updated build and release workflow configurations, added environment variables
App.tsx Modified PrivyProvider configuration, added SmartWalletsProvider
config.ts Updated Privy configuration, added environment-specific domain settings
navigation/ Restructured onboarding and new account navigation flows
features/onboarding/ Added new components, hooks, and screens for contact card and passkey authentication
screens/ Removed old user profile and notifications screens, added new contact card and welcome screens
design-system/ Updated icon and text styling, added new icon and spacing options

Sequence Diagram

sequenceDiagram
    participant User
    participant WelcomeScreen
    participant ContactCardScreen
    participant AuthService
    
    User->>WelcomeScreen: Starts onboarding
    WelcomeScreen->>AuthService: Authenticate with Passkey
    AuthService-->>WelcomeScreen: Authentication successful
    WelcomeScreen->>ContactCardScreen: Navigate
    ContactCardScreen->>User: Prompt to create profile
    User->>ContactCardScreen: Enter profile details
    ContactCardScreen->>AuthService: Save profile
    AuthService-->>ContactCardScreen: Profile saved
    ContactCardScreen->>User: Onboarding complete
Loading

Possibly related PRs

Suggested reviewers

  • technoplato

Poem

🐰 A Rabbit's Onboarding Tale 🚀

Through passkeys and wallets we dance,
A contact card born of pure chance,
Workflows sing, configurations gleam,
Onboarding's now a seamless dream!

Hop, hop, hooray! 🎉

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

github-actions bot commented Jan 13, 2025

Performance Comparison Report

  • Current: 8c4dd3d - 2025-01-22 07:20:16Z
  • Baseline: main (03d90e8) - 2025-01-22 07:19:07Z

Significant Changes To Duration

There are no entries

Meaningless Changes To Duration

Show entries
Name Type Duration Count
Avatar Image 10 runs render 0.6 ms → 0.5 ms (-0.1 ms, -16.7%) 🟢 1 → 1
Avatar Image 50 runs render 0.7 ms → 0.7 ms (-0.0 ms, -2.9%) 1 → 1
Empty Avatar 10 runs render 1.0 ms → 1.0 ms 1 → 1
Empty Avatar 50 runs render 0.9 ms → 0.9 ms 1 → 1
Text Component with color prop - 10 runs render 0.5 ms → 0.4 ms (-0.1 ms, -20.0%) 🟢 1 → 1
Text Component with default props - 10 runs render 1.0 ms → 1.0 ms 1 → 1
Text Component with translation key - 10 runs render 0.6 ms → 0.4 ms (-0.2 ms, -33.3%) 🟢 1 → 1
Text Component with weight and size - 10 runs render 0.4 ms → 0.6 ms (+0.2 ms, +50.0%) 🔴 1 → 1
Show details
Name Type Duration Count
Avatar Image 10 runs render Baseline
Mean: 0.6 ms
Stdev: 0.5 ms (86.1%)
Runs: 0 0 1 0 1 1 0 1 1 1
Warmup runs: 1

Current
Mean: 0.5 ms
Stdev: 0.5 ms (105.4%)
Runs: 0 0 1 0 1 0 1 0 1 1
Warmup runs: 1
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:
Avatar Image 50 runs render Baseline
Mean: 0.7 ms
Stdev: 0.5 ms (69.3%)
Runs: 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 0 1 1 1 1 0 1 1 0 1 1 1 1 0 1 0 0 0 0 1 1 1 0 0 1 1 0
Warmup runs: 1

Current
Mean: 0.7 ms
Stdev: 0.5 ms (72.5%)
Runs: 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 0 1 1 0 1 0 1 1 0 1 1 1 0 0 0 0 1 0 1 1 1 0 1 1 0 1 0 0 1 1 1 1 1 1 1
Warmup runs: 1
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Render issues:
Empty Avatar 10 runs render Baseline
Mean: 1.0 ms
Stdev: 0.0 ms (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Warmup runs: 2

Current
Mean: 1.0 ms
Stdev: 0.0 ms (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Warmup runs: 1
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:
Empty Avatar 50 runs render Baseline
Mean: 0.9 ms
Stdev: 0.4 ms (40.8%)
Runs: 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1
Warmup runs: 1

Current
Mean: 0.9 ms
Stdev: 0.4 ms (40.8%)
Runs: 1 1 1 1 0 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0
Warmup runs: 1
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
Render issues:
Text Component with color prop - 10 runs render Baseline
Mean: 0.5 ms
Stdev: 0.5 ms (105.4%)
Runs: 0 0 0 1 1 1 1 1 0 0
Warmup runs: 1

Current
Mean: 0.4 ms
Stdev: 0.5 ms (129.1%)
Runs: 0 1 1 0 0 1 0 0 1 0
Warmup runs: 1
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:
Text Component with default props - 10 runs render Baseline
Mean: 1.0 ms
Stdev: 0.5 ms (47.1%)
Runs: 1 2 1 1 1 1 1 1 0 1
Warmup runs: 3

Current
Mean: 1.0 ms
Stdev: 0.0 ms (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Warmup runs: 3
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:
Text Component with translation key - 10 runs render Baseline
Mean: 0.6 ms
Stdev: 0.5 ms (86.1%)
Runs: 0 1 1 1 1 1 0 1 0 0
Warmup runs: 0

Current
Mean: 0.4 ms
Stdev: 0.5 ms (129.1%)
Runs: 1 0 0 1 1 0 0 0 1 0
Warmup runs: 1
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:
Text Component with weight and size - 10 runs render Baseline
Mean: 0.4 ms
Stdev: 0.5 ms (129.1%)
Runs: 0 0 1 0 1 1 1 0 0 0
Warmup runs: 0

Current
Mean: 0.6 ms
Stdev: 0.5 ms (86.1%)
Runs: 1 1 1 0 0 0 1 1 0 1
Warmup runs: 0
Baseline
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Current
Mean: 1
Stdev: 0 (0.0%)
Runs: 1 1 1 1 1 1 1 1 1 1
Render issues:

Render Count Changes

There are no entries

Render Issues

There are no entries

Added Scenarios

There are no entries

Removed Scenarios

There are no entries

Generated by 🚫 dangerJS against ebd5140

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: 11

🧹 Nitpick comments (16)
components/TransactionPreview/TransactionPreview.tsx (1)

Line range hint 177-189: Improve error handling type safety.

The error handling uses string concatenation and type 'any'. This could be improved for better type safety and error reporting.

Consider this improvement:

-    } catch (e: any) {
-      if (`${e}`.includes("User rejected the request")) {
+    } catch (error: unknown) {
+      if (error instanceof Error && error.message.includes("User rejected the request")) {
         setTxState({ status: "pending" });
       } else {
-        logger.error(e);
-        let txError = e?.message;
-        if (!txError) {
-          try {
-            txError = JSON.stringify(e);
-          } catch {}
-        }
-        if (!txError) txError = e;
-        setTxState({ status: "failure", error: `${txError}` });
+        logger.error(error);
+        const errorMessage = error instanceof Error 
+          ? error.message 
+          : typeof error === 'object' 
+            ? JSON.stringify(error) 
+            : String(error);
+        setTxState({ status: "failure", error: errorMessage });
       }
features/onboarding/Privy/usePrivySmartWalletConnection.ts (1)

46-54: Handle embedded wallet creation errors appropriately

There's a TODO comment on line 52 indicating that error handling needs to be improved for embeddedWallet.create(). Consider implementing better error handling to provide users with meaningful feedback.

Do you want me to generate error handling code or open a GitHub issue to track this task?

utils/xmtpRN/signer.ts (1)

23-38: Consider making wallet type configurable.

The implementation looks good, but hardcoding walletType: () => "SCW" might be too restrictive. Consider making it configurable through a parameter, similar to the ethersSignerToXmtpSigner function.

-export const viemAccountToXmtpSigner = (account: ViemAccount): XmtpSigner => ({
+export const viemAccountToXmtpSigner = (
+  account: ViemAccount,
+  isSCW?: boolean
+): XmtpSigner => ({
   getAddress: () => Promise.resolve(account.address),
   getChainId: () => account.chainId,
   getBlockNumber: () => undefined,
-  walletType: () => "SCW",
+  walletType: () => (isSCW ? "SCW" : "EOA"),
   signMessage: async (message: string) => {
features/onboarding/passkey/useCreatePasskey.ts (2)

52-54: Consider using typed errors.

Instead of using any type for the error, consider creating a custom error type or using unknown for better type safety.

-    } catch (e: any) {
-      setError(e?.message ?? "Error creating Passkey account");
+    } catch (e: unknown) {
+      const errorMessage = e instanceof Error ? e.message : "Error creating Passkey account";
+      setError(errorMessage);

50-51: Consider extracting status strings as constants.

Status strings like "Account created - Waiting for smart wallet" should be extracted into constants to maintain consistency and ease updates.

features/onboarding/passkey/useLoginWithPasskey.ts (1)

36-57: Consider extracting common logic.

The implementation is solid, but there's significant code duplication with useCreatePasskey. Consider extracting common patterns into shared utilities:

  • Error handling logic
  • Loading state management
  • User session handling

Example shared utility:

type PasskeyOperation = {
  operation: () => Promise<{ user: { id: string } | null }>;
  errorMessage: string;
  successMessage: string;
};

const executePasskeyOperation = async ({
  operation,
  errorMessage,
  successMessage,
  setLoading,
  setError,
  setStatusString,
  setPrivyAccountId,
  logout,
  privyUser
}: PasskeyOperation & Dependencies) => {
  try {
    setLoading(true);
    if (privyUser) {
      await logout();
    }
    const { user } = await operation();
    if (!user) {
      throw new Error("No account operation completed");
    }
    setStatusString(successMessage);
    setPrivyAccountId(user.id);
  } catch (e: unknown) {
    const error = e instanceof Error ? e.message : errorMessage;
    setError(error);
    captureErrorWithToast(e);
  } finally {
    setLoading(false);
  }
};

Also applies to: 67-70

features/onboarding/passkey/passkeyAuthStore.tsx (1)

4-9: Consider making essential props required

The store props are all marked as optional, which could lead to runtime issues if not properly handled. Consider making essential props like loading required to enforce proper initialization.

 type IPasskeyAuthStoreProps = {
-  loading?: boolean;
+  loading: boolean;
   error?: string;
   statusString?: string;
   account?: string;
 };
navigation/OnboardingNavigator.tsx (1)

53-56: Consider adding screen options for consistency

The new OnboardingPasskey screen is missing screen options that are present in other screens (e.g., headerShown). Consider adding consistent screen options.

         <OnboardingNativeStack.Screen
+          options={{
+            headerShown: false,
+          }}
           name="OnboardingPasskey"
           component={OnboardingPasskeyScreen}
         />
utils/xmtpRN/signIn.ts (2)

74-121: Consider refactoring to reduce code duplication.

The createXmtpClientFromViemAccount function is almost identical to createXmtpClientFromSigner. Consider extracting the common logic into a shared helper function to improve maintainability.

+async function createXmtpClientCommon(
+  options: any,
+  inboxId: string,
+  tempDirectory: string,
+  createClient: () => Promise<any>,
+  onInstallationRevoked: () => Promise<void>
+) {
+  await copyDatabasesToTemporaryDirectory(tempDirectory, inboxId);
+  const client = await createClient();
+  if (client.inboxId !== inboxId) {
+    throw new Error("Inbox ids don't match");
+  }
+  const installationValid = await isClientInstallationValid(client);
+  if (!installationValid) {
+    await client.dropLocalDatabaseConnection();
+    await deleteLibXmtpDatabaseForInboxId(inboxId);
+    onInstallationRevoked();
+    return;
+  }
+  await client.dropLocalDatabaseConnection();
+  await moveTemporaryDatabasesToDatabaseDirectory(tempDirectory, client.inboxId);
+}

 export const createXmtpClientFromViemAccount = async (
   account: ViemAccount,
   onInstallationRevoked: () => Promise<void>,
   preAuthenticateToInboxCallback?: () => Promise<void>
 ) => {
   const tempDirectory = await createTemporaryDirectory();
   const dbEncryptionKey = await getDbEncryptionKey();
   const options = {
     env,
     enableV3: true,
     dbDirectory: tempDirectory,
     dbEncryptionKey,
   };
   const inboxId = await getInboxId(await account.address);
-  await copyDatabasesToTemporaryDirectory(tempDirectory, inboxId);
-  logger.debug("Instantiating client from signer");
-  const client = await Client.create(viemAccountToXmtpSigner(account), {
-    ...options,
-    preAuthenticateToInboxCallback,
-  });
-  if (client.inboxId !== inboxId) {
-    throw new Error("Inbox ids don't match");
-  }
-  const installationValid = await isClientInstallationValid(client);
-  if (!installationValid) {
-    await client.dropLocalDatabaseConnection();
-    await deleteLibXmtpDatabaseForInboxId(inboxId);
-    onInstallationRevoked();
-    return;
-  }
-  await client.dropLocalDatabaseConnection();
-  await moveTemporaryDatabasesToDatabaseDirectory(tempDirectory, client.inboxId);
+  await createXmtpClientCommon(
+    options,
+    inboxId,
+    tempDirectory,
+    () => Client.create(viemAccountToXmtpSigner(account), {
+      ...options,
+      preAuthenticateToInboxCallback,
+    }),
+    onInstallationRevoked
+  );
 };

91-91: Update log message to match the account type.

The log message indicates "Instantiating client from signer" but we're using a Viem account.

-  logger.debug("Instantiating client from signer");
+  logger.debug("Instantiating client from Viem account");
screens/NewAccount/new-account-passkey-screen.tsx (3)

111-114: Use i18n for the login button text.

The login button text is hardcoded. For consistency and internationalization support, use the translation system.

-        text={"Login with passkey"}
+        text={translate("passkey.loginButton")}

98-103: Enhance account display format.

The account address display could be improved by truncating the address and adding proper spacing.

-        <Text preset="bodyBold">
-          Account created:
-          <Text preset="body">{account}</Text>
+        <Text preset="bodyBold" style={{ marginBottom: 10 }}>
+          Account created:{" "}
+          <Text preset="body">
+            {account.slice(0, 6)}...{account.slice(-4)}
+          </Text>
         </Text>

56-63: Improve error handling specificity.

The error handling could be more specific to help users understand and resolve issues.

   const handleError = useCallback(
     (error: Error) => {
       logger.error(error);
-      setError(error.message);
+      if (error.message.includes("not supported")) {
+        setError(translate("passkey.browser_not_supported"));
+      } else if (error.message.includes("cancelled")) {
+        setError(translate("passkey.operation_cancelled"));
+      } else {
+        setError(translate("passkey.generic_error"));
+      }
       captureErrorWithToast(error);
     },
     [setError]
   );
components/Onboarding/init-xmtp-client.ts (1)

63-64: Improve error message specificity.

The error message could be more specific about the missing Viem account.

-    throw new Error("No signer");
+    throw new Error("No Viem account provided for XMTP client initialization");
App.tsx (1)

154-154: Extract inline style to StyleSheet.

Move the inline style { flex: 1 } to the StyleSheet for better maintainability.

-<GestureHandlerRootView style={{ flex: 1 }}>
+<GestureHandlerRootView style={styles.gestureHandler}>

 const useStyles = () => {
   return useMemo(
     () =>
       StyleSheet.create({
         safe: {
           flex: 1,
         },
+        gestureHandler: {
+          flex: 1,
+        },
       }),
     []
   );
 };
🧰 Tools
🪛 GitHub Check: lint

[warning] 154-154:
Inline style: { flex: 1 }

theme/useAppTheme.ts (1)

154-154: LGTM: Improved theme management.

The changes simplify theme handling by:

  1. Removing navigation theme dependency
  2. Directly using system color scheme
  3. Streamlining theme context computation

Consider adding a comment explaining the rationale behind using controlledTheme over navTheme for better maintainability:

+  // Using system color scheme directly instead of navigation theme for simpler theme management
   const controlledTheme = useColorScheme();

Also applies to: 180-180, 189-190, 212-212, 215-215

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2bbdb97 and 7032edd.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (32)
  • .github/workflows/build-app-release.yml (2 hunks)
  • .github/workflows/build-internal.yml (1 hunks)
  • App.tsx (2 hunks)
  • app.config.ts (1 hunks)
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx (1 hunks)
  • components/Onboarding/init-xmtp-client.ts (2 hunks)
  • components/TransactionPreview/TransactionPreview.tsx (2 hunks)
  • config.ts (6 hunks)
  • features/onboarding/Privy/usePrivySmartWalletConnection.ts (1 hunks)
  • features/onboarding/passkey/passkey.constants.ts (1 hunks)
  • features/onboarding/passkey/passkeyAuthStore.tsx (1 hunks)
  • features/onboarding/passkey/useCreatePasskey.ts (1 hunks)
  • features/onboarding/passkey/useLoginWithPasskey.ts (1 hunks)
  • i18n/translations/en.ts (1 hunks)
  • ios/Converse/Converse.entitlements (1 hunks)
  • metro.config.js (2 hunks)
  • navigation/OnboardingNavigator.tsx (3 hunks)
  • package.json (6 hunks)
  • screens/Main.tsx (0 hunks)
  • screens/Navigation/Navigation.tsx (5 hunks)
  • screens/NewAccount/NewAccountScreen.tsx (2 hunks)
  • screens/NewAccount/new-account-passkey-screen.tsx (1 hunks)
  • screens/Onboarding/OnboardingGetStartedScreen.tsx (2 hunks)
  • screens/Onboarding/onboarding-passkey-screen.tsx (1 hunks)
  • scripts/build/ios/preview.js (1 hunks)
  • scripts/build/ios/production.js (1 hunks)
  • theme/useAppTheme.ts (4 hunks)
  • tsconfig.json (1 hunks)
  • utils/events.ts (1 hunks)
  • utils/navigation.ts (1 hunks)
  • utils/xmtpRN/signIn.ts (2 hunks)
  • utils/xmtpRN/signer.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • screens/Main.tsx
🧰 Additional context used
🪛 GitHub Check: lint
App.tsx

[warning] 154-154:
Inline style: { flex: 1 }

config.ts

[warning] 39-39:
Prefer named exports

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build-with-eas
  • GitHub Check: build-with-eas
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: performance-test
🔇 Additional comments (31)
utils/events.ts (1)

6-6: Verify type compatibility between viem and thirdweb TransactionReceipt.

The migration from thirdweb to viem for TransactionReceipt type could impact existing event handlers. Let's verify the type compatibility.

✅ Verification successful

✓ TransactionReceipt type migration is consistent

The change from thirdweb to viem TransactionReceipt is consistently applied across all usage locations (utils/events.ts and components/TransactionPreview/TransactionPreview.tsx) with compatible type usage patterns maintained.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for TransactionReceipt usage and potential type mismatches

# Find all files using TransactionReceipt
echo "Files importing TransactionReceipt:"
rg -l "TransactionReceipt"

# Check for potential type mismatches in event handlers
echo "\nEvent handlers using transactionResult:"
rg "transactionResult.*receipt.*" -A 2

Length of output: 435


Script:

#!/bin/bash
# Check implementation details and previous import source

# Examine TransactionPreview component
echo "TransactionPreview implementation:"
ast-grep --pattern 'interface Props {
  $$$
}'  components/TransactionPreview/TransactionPreview.tsx

# Check previous version of events.ts
echo "\nPrevious import in events.ts:"
git show HEAD^:utils/events.ts | rg "TransactionReceipt"

# Check type usage in both files
echo "\nType usage in TransactionPreview:"
rg "TransactionReceipt" components/TransactionPreview/TransactionPreview.tsx -A 2

Length of output: 1041

features/onboarding/Privy/usePrivySmartWalletConnection.ts (1)

31-34: Confirm the necessity of logging out on mount

The useEffect on lines 31-34 logs out the user when the component mounts. Is this intentional? This could cause the user to be logged out unexpectedly when this hook is used.

features/onboarding/passkey/passkey.constants.ts (1)

3-4: LGTM!

The constants RPID and RELYING_PARTY are correctly defined and exported.

ios/Converse/Converse.entitlements (1)

11-11: LGTM!

The added entry <string>webcredentials:dev.converse.xyz</string> correctly adds the web credentials domain for the development environment.

utils/xmtpRN/signer.ts (1)

17-21: LGTM! Well-structured type definition.

The ViemAccount type correctly defines the required properties for Viem account integration.

metro.config.js (2)

9-19: LGTM! Well-implemented package resolution.

The function correctly handles Privy package resolution while maintaining compatibility with other packages.


Line range hint 39-48: Verify package exports compatibility.

The configuration enables package exports for Privy modules. Ensure this doesn't affect other packages that might not be compatible with this feature.

✅ Verification successful

Package exports feature is safely scoped to Privy packages only

The implementation of resolveRequestWithPackageExports explicitly enables package exports only for @privy-io/* packages, leaving all other packages unaffected. This scoped approach ensures compatibility and prevents any impact on other dependencies.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for package.json files with "exports" field
rg -l '"exports"' '**/package.json'

Length of output: 93


Script:

#!/bin/bash
# Find package.json files and check for exports field
echo "=== Checking for package.json files with exports field ==="
fd package.json --type f --exec rg -l '"exports"' {}

echo -e "\n=== Checking main package.json for dependencies ==="
fd package.json --max-depth 1 --type f --exec cat {}

echo -e "\n=== Checking for import/require statements ==="
rg -g '!*.json' -g '!*.md' 'require\(|import .* from' -A 1

Length of output: 68831


Script:

#!/bin/bash
# Check Privy packages for exports field
echo "=== Checking Privy packages for exports field ==="
fd package.json -p node_modules/@privy-io --type f --exec rg -l '"exports"' {}

echo -e "\n=== Looking for package exports related errors ==="
rg -i "package.+exports?.+(error|issue|fail|invalid)" --type ts --type js --type log

echo -e "\n=== Checking resolveRequestWithPackageExports implementation ==="
rg -p "resolveRequestWithPackageExports" --type ts --type js -A 5

Length of output: 1322

features/onboarding/passkey/useLoginWithPasskey.ts (1)

43-46: Verify error handling for missing user.

The error handling for a missing user is good, but verify if there are specific error codes or messages from Privy that should be handled differently.

✅ Verification successful

Error handling implementation is appropriate

The current implementation properly handles both specific cases (missing user) and generic errors with user-friendly messages and error tracking. No additional Privy-specific error codes were found that would require different handling.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Privy error handling patterns in the codebase
rg -A 3 "catch.*privy" 

Length of output: 24


Script:

#!/bin/bash
# Search for Privy imports and error handling patterns
echo "=== Privy imports ==="
rg "from.*privy" -A 2

echo -e "\n=== Error handling in auth files ==="
fd -e ts -e tsx . | grep -i "auth\|login" | xargs rg "catch|throw" -A 2

echo -e "\n=== Error constants and types ==="
rg "type.*Error|interface.*Error|enum.*Error" -A 2

Length of output: 15647

features/onboarding/passkey/passkeyAuthStore.tsx (1)

62-64: Add selector type validation

The usePasskeyAuthStoreContext hook accepts any selector without type validation. Consider adding runtime validation for the selected value.

scripts/build/ios/production.js (1)

93-97: LGTM: Associated domain configuration for passkeys

The addition of the webcredentials domain is correctly configured for the production environment, which is necessary for passkey authentication support.

Verify the domain configuration matches across environments:

✅ Verification successful

✅ Associated domains configuration is consistent

The webcredentials domain configuration correctly transitions from:

  • dev.converse.xyz → preview.converse.xyz (in preview)
  • dev.converse.xyz → converse.xyz (in production)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check consistency of webcredentials domain configuration
rg -l "webcredentials:(dev|preview|)\.?converse\.xyz" scripts/build/ios/

Length of output: 135


Script:

#!/bin/bash
# Display the domain configuration context from both files
echo "=== Production configuration ==="
rg -B2 -A2 "webcredentials:" scripts/build/ios/production.js

echo -e "\n=== Preview configuration ==="
rg -B2 -A2 "webcredentials:" scripts/build/ios/preview.js

Length of output: 724

config.ts (2)

80-80: LGTM: Consistent domain configuration

The appDomain configuration is correctly set up across all environments (dev, preview, prod) and aligns with the iOS entitlements configuration.

Also applies to: 100-100, 136-136


39-40: Verify Privy configuration completeness

The Privy configuration is properly structured with clientId from environment variables. However, ensure all required Privy configuration parameters are included.

Check for any missing Privy configuration parameters:

Also applies to: 120-121

✅ Verification successful

Privy configuration is complete and correctly implemented

The current Privy configuration with clientId and defaultChain parameters is sufficient for the implemented features, including SMS authentication, passkey support, smart wallets, and embedded wallets functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Privy-related configuration patterns in similar projects
rg "PRIVY_" .env.example || echo "No .env.example file found"

Length of output: 140


Script:

#!/bin/bash
# Search for Privy-related files and patterns
echo "=== Searching for Privy-related files ==="
fd -i privy

echo -e "\n=== Searching for Privy imports and configurations ==="
rg -i "from ['\"](.*privy|@privy-io)" -A 3

echo -e "\n=== Searching for Privy configuration objects ==="
ast-grep --pattern 'const $_ = {
  $$$
  clientId: $_,
  $$$
}'

Length of output: 14791

🧰 Tools
🪛 GitHub Check: lint

[warning] 39-39:
Prefer named exports

screens/Onboarding/OnboardingGetStartedScreen.tsx (2)

9-22: LGTM: Import path standardization

The switch to absolute imports improves code maintainability and follows better module resolution patterns.


62-66: Verify passkey implementation completeness

The passkey authentication option is properly integrated into the onboarding flow. However, ensure the OnboardingPasskey screen is properly implemented with appropriate error handling.

Check for the existence and implementation of the OnboardingPasskey screen:

✅ Verification successful

OnboardingPasskey screen implementation verified

The OnboardingPasskey screen is properly implemented with comprehensive error handling:

  • Uses error capture and toast notifications
  • Manages loading and status states
  • Properly integrated with navigation
  • Wrapped with necessary authentication providers
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for OnboardingPasskey screen implementation
ast-grep --pattern 'export function OnboardingPasskeyScreen() {
  $$$
}'

Length of output: 74


Script:

#!/bin/bash
# Find files containing OnboardingPasskey
fd -t f "OnboardingPasskey"

# Search for OnboardingPasskey references in code
rg "OnboardingPasskey" -A 5

# Try alternative component definition patterns
ast-grep --pattern 'const OnboardingPasskey = $$$'
ast-grep --pattern 'export const OnboardingPasskey = $$$'

Length of output: 3655


Script:

#!/bin/bash
# Check Content component and error handling in passkey screen
rg -A 15 "function Content" screens/Onboarding/onboarding-passkey-screen.tsx

# Look for error handling patterns
rg "catch|error|Error|try" screens/Onboarding/onboarding-passkey-screen.tsx

Length of output: 1002

scripts/build/ios/preview.js (1)

120-124: LGTM: Preview environment domain configuration

The webcredentials domain is correctly configured for the preview environment, maintaining consistency with the production setup.

Verify domain configuration consistency:

✅ Verification successful

Domain configuration verified: Preview and production environments are properly aligned

The domain configurations maintain consistency across environments while correctly using environment-specific domains:

  • Production: dev.converse.xyz → converse.xyz
  • Preview: dev.converse.xyz → preview.converse.xyz
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Compare domain configurations across environments
echo "Checking domain configuration consistency across environments..."
rg -A 1 "webcredentials:" scripts/build/ios/{preview,production}.js

Length of output: 587

App.tsx (2)

142-147: LGTM: Privy configuration looks good.

The PrivyProvider is properly configured with the required props for passkey support: clientId and supportedChains.


148-165: Provider structure is well-organized.

The SmartWalletsProvider is correctly positioned within the provider tree, maintaining proper context hierarchy.

🧰 Tools
🪛 GitHub Check: lint

[warning] 154-154:
Inline style: { flex: 1 }

app.config.ts (1)

228-228: LGTM: Required plugin for passkey support.

The addition of "expo-secure-store" plugin is necessary for secure storage of passkey credentials.

components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx (1)

27-37: LGTM: Well-structured passkey item generator.

The new function follows the established pattern and maintains consistency with other item generators.

screens/Navigation/Navigation.tsx (4)

49-50: LGTM: Clean imports for new passkey screens.

The imports follow the established pattern and are correctly placed with other screen imports.


65-65: LGTM: Well-typed navigation parameters.

The new screen parameters are properly typed as undefined, consistent with other similar screens.

Also applies to: 76-76


171-174: LGTM: Proper screen registration in SignedInNavigation.

The NewAccountPasskey screen is correctly placed in the modal group, maintaining consistency with other account-related screens.


220-223: LGTM: Proper screen registration in SignedOutNavigation.

The OnboardingPasskey screen is correctly placed in the auth/onboarding group with appropriate screen options inherited from the group configuration.

i18n/translations/en.ts (1)

107-111: LGTM: Clear and consistent translations for passkey features.

The translations are well-organized in a dedicated section and provide clear, user-friendly messages for the passkey functionality.

tsconfig.json (1)

7-7: Verify compatibility with existing imports.

The change to "Bundler" moduleResolution is good for modern bundlers, but verify that it doesn't break any existing imports, especially in:

  1. Dynamic imports
  2. Package exports
  3. Path aliases

Run this check to verify import compatibility:

✅ Verification successful

moduleResolution "Bundler" change is safe to apply

The change to "Bundler" moduleResolution is compatible with the project's:

  • Path alias usage (@/, @components/, etc.)
  • Package imports
  • Expo toolchain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for potential import issues with new moduleResolution
# Look for any import statements that might be affected

# Search for dynamic imports
echo "Checking dynamic imports..."
rg "import\(.*\)" --type ts --type tsx

# Search for package.json exports field usage
echo "Checking package.json exports..."
fd package.json --exec jq -r '.exports // empty'

# Search for path alias usage
echo "Checking path alias usage..."
rg "from ['\"]@/" --type ts --type tsx

Length of output: 397


Script:

#!/bin/bash
# First check tsconfig configuration
echo "Checking tsconfig.json configuration..."
cat tsconfig.json

# Then check for various import patterns
echo -e "\nChecking import patterns..."
# Using -t for file type and checking both .ts and .tsx
rg -t ts "import .* from ['\"].*['\"]"

# Check for dynamic imports
echo -e "\nChecking dynamic imports..."
rg -t ts "import\(['\"].*['\"]\)"

# Check for path aliases (if any)
echo -e "\nChecking path alias usage..."
rg -t ts "from ['\"]@"

Length of output: 71068

.github/workflows/build-internal.yml (1)

67-67: LGTM: Proper environment variable configuration.

The EXPO_PUBLIC_PRIVY_CLIENT_ID is correctly added:

  • Uses EXPO_PUBLIC_ prefix for Expo compatibility
  • Properly sourced from repository secrets
  • Added in the correct location in env file creation
.github/workflows/build-app-release.yml (2)

Line range hint 9-13: LGTM! Removal of scheduled triggers

The removal of scheduled triggers aligns with the current release schedule, making the workflow more maintainable by relying on manual triggers.


49-49: LGTM! Addition of Privy environment variable

The EXPO_PUBLIC_PRIVY_CLIENT_ID environment variable has been correctly added to both Android and iOS build configurations, which is necessary for the Privy passkey integration.

Also applies to: 93-93

package.json (3)

151-151: LGTM! New dependencies align with PR objectives

The addition of new dependencies supports the PR objectives:

  • react-native-passkeys (v0.3.1) for passkey authentication
  • permissionless (v0.2.25) for smart contract wallet support
  • react-native-qrcode-styled (v0.3.3) for QR code functionality

Also applies to: 174-174, 177-177


56-57: Verify migration steps for Privy SDK major version update

The update from @privy-io/expo v0.16.2 to v0.36.2 is a significant version jump that may include breaking changes.

Run this script to check for migration guides and breaking changes:


111-111: Verify compatibility of updated Expo packages

Multiple Expo packages have been updated to newer patch versions. While these are minor updates, we should verify compatibility.

Run this script to check for any reported issues:

Also applies to: 117-117, 119-119, 130-130, 135-135, 141-141

✅ Verification successful

Expo package updates are compatible

The updated packages are patch version changes (x.y.Z) which only include backwards-compatible bug fixes. No compatibility issues or bugs were found for these specific versions in the Expo repository.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any known issues with the updated Expo packages
gh api graphql -f query='
{
  search(query: "repo:expo/expo is:issue state:open label:bug", type: ISSUE, first: 10) {
    nodes {
      ... on Issue {
        title
        url
        labels(first: 5) {
          nodes {
            name
          }
        }
      }
    }
  }
}'

Length of output: 959


Script:

#!/bin/bash
# Get all expo-related dependencies and their versions
echo "=== Expo Dependencies ==="
cat package.json | jq -r '.dependencies | with_entries(select(.key | startswith("expo"))) | to_entries | .[] | "\(.key): \(.value)"'

echo -e "\n=== Checking for version-specific issues ==="
# Search for issues mentioning specific versions
gh api graphql -f query='
{
  search(query: "repo:expo/expo is:issue state:open label:bug \"6.0.2\"", type: ISSUE, first: 5) {
    nodes {
      ... on Issue {
        title
        url
        body
      }
    }
  }
}'

Length of output: 1382

components/TransactionPreview/TransactionPreview.tsx Outdated Show resolved Hide resolved
components/TransactionPreview/TransactionPreview.tsx Outdated Show resolved Hide resolved
Comment on lines 46 to 63
if (!creatingEmbeddedWallet.current) {
creatingEmbeddedWallet.current = true;
onStatusChange("Creating embedded wallet");
logger.debug("[Privy connection] Creating embedded wallet");
embeddedWallet
.create()
// TODO: Handle better error
.catch(ensureErrorHandler(onConnectionError));
}
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

Reset creatingEmbeddedWallet.current flag on error

In the embedded wallet creation logic, if embeddedWallet.create() fails, creatingEmbeddedWallet.current remains true, preventing further attempts to create the wallet. Consider resetting creatingEmbeddedWallet.current to false in the error handler to allow retries.

Apply this diff to reset the flag on error:

 embeddedWallet
   .create()
   // TODO: Handle better error
   .catch((error) => {
+    creatingEmbeddedWallet.current = false;
     ensureErrorHandler(onConnectionError)(error);
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!creatingEmbeddedWallet.current) {
creatingEmbeddedWallet.current = true;
onStatusChange("Creating embedded wallet");
logger.debug("[Privy connection] Creating embedded wallet");
embeddedWallet
.create()
// TODO: Handle better error
.catch(ensureErrorHandler(onConnectionError));
}
if (!creatingEmbeddedWallet.current) {
creatingEmbeddedWallet.current = true;
onStatusChange("Creating embedded wallet");
logger.debug("[Privy connection] Creating embedded wallet");
embeddedWallet
.create()
// TODO: Handle better error
.catch((error) => {
creatingEmbeddedWallet.current = false;
ensureErrorHandler(onConnectionError)(error);
});
}

features/onboarding/Privy/usePrivySmartWalletConnection.ts Outdated Show resolved Hide resolved
features/onboarding/passkey/passkeyAuthStore.tsx Outdated Show resolved Hide resolved
utils/navigation.ts Outdated Show resolved Hide resolved
screens/Onboarding/onboarding-passkey-screen.tsx Outdated Show resolved Hide resolved
Comment on lines 36 to 82
const Content = memo(function Content() {
const router = useRouter();
// Passkey Store Hooks
const loading = usePasskeyAuthStoreContext((state) => state.loading);

const statusString = usePasskeyAuthStoreContext(
(state) => state.statusString
);

const account = usePasskeyAuthStoreContext((state) => state.account);

const setError = usePasskeyAuthStoreContext((state) => state.setError);

const setStatusString = usePasskeyAuthStoreContext(
(state) => state.setStatusString
);
const { createPasskey: handleCreateAccountWithPasskey } = useCreatePasskey();

const { loginWithPasskey: handleLoginWithPasskey } = useLoginWithPasskey();

const handleError = useCallback(
(error: Error) => {
logger.error(error);
setError(error.message);
captureErrorWithToast(error);
},
[setError]
);

usePrivySmartWalletConnection({
onConnectionDone: () => {
if (isMissingConverseProfile()) {
router.navigate("OnboardingUserProfile");
} else if (needToShowNotificationsPermissions()) {
router.navigate("OnboardingNotifications");
} else {
setAuthStatus("signedIn");
}
},
onConnectionError: handleError,
onStatusChange: setStatusString,
});

const showDebug = useCallback(() => {
converseEventEmitter.emit("showDebugMenu");
}, []);

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Create a shared hook for passkey functionality.

The passkey-related logic is duplicated between screens. Consider extracting it into a custom hook.

Create a new shared hook:

// hooks/usePasskeyAuth.ts
export const usePasskeyAuth = (onSuccess: () => void) => {
  const router = useRouter();
  const loading = usePasskeyAuthStoreContext((state) => state.loading);
  const statusString = usePasskeyAuthStoreContext((state) => state.statusString);
  const account = usePasskeyAuthStoreContext((state) => state.account);
  const setError = usePasskeyAuthStoreContext((state) => state.setError);
  const setStatusString = usePasskeyAuthStoreContext((state) => state.setStatusString);

  const handleError = useCallback(
    (error: Error) => {
      logger.error(error);
      setError(error.message);
      captureErrorWithToast(error);
    },
    [setError]
  );

  usePrivySmartWalletConnection({
    onConnectionDone: onSuccess,
    onConnectionError: handleError,
    onStatusChange: setStatusString,
  });

  const showDebug = useCallback(() => {
    converseEventEmitter.emit("showDebugMenu");
  }, []);

  return {
    loading,
    statusString,
    account,
    handleError,
    showDebug,
  };
};

components/Onboarding/init-xmtp-client.ts Show resolved Hide resolved
config.ts Outdated Show resolved Hide resolved
@alexrisch alexrisch force-pushed the ar/privy-passkey branch 2 times, most recently from 5c1e7ed to 003d806 Compare January 14, 2025 00:30
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)
app.json (1)

Line range hint 1-12: Consider documenting version update strategy.

Given that both iOS and Android versions have significant increments, it would be helpful to document the versioning strategy in the repository (e.g., in CONTRIBUTING.md) to help maintainers understand when and why version numbers should be incremented.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 74f5069 and 003d806.

📒 Files selected for processing (1)
  • app.json (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (2)
app.json (2)

9-9: Verify Android version code increment.

The Android versionCode has been incremented by 11 versions (from 257 to 268). While this is valid, please ensure this significant jump in version codes is intentional and aligns with your versioning strategy.

Run this script to check the version history and ensure proper version alignment:


6-6: Verify iOS build number increment.

The iOS build number has been incremented by 9 versions (from 58 to 67). While this is valid, please ensure this significant jump in build numbers is intentional and aligns with your versioning strategy.

Run this script to check the version history and ensure no builds are skipped:

✅ Verification successful

Build number increment is part of automated CI/CD process

The increment from 58 to 67 is valid as it's managed by the automated build system (EAS) and dedicated increment scripts. The project uses proper version management with automated PRs for version updates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check version history in git to ensure no builds are skipped
# and verify the version alignment with release tags

# Check git history for build number changes
git log -p app.json | grep -B 5 -A 5 "buildNumber"

# List tags to verify version alignment
git tag --sort=-v:refname | head -n 5

Length of output: 39


Script:

#!/bin/bash
# Search for buildNumber patterns and version configurations
rg -A 2 -B 2 '"buildNumber"' --type json

# Look for version-related files
fd -e json -e yaml -e yml | xargs rg -l 'version|build'

# Check for CI configuration
fd -e yml -e yaml . .github/

Length of output: 1029


Script:

#!/bin/bash
# Check EAS configuration
cat eas.json

# Examine build workflows
cat .github/workflows/build-app-release.yml
cat .github/workflows/build-internal.yml

Length of output: 13177

@technoplato
Copy link
Collaborator

before we super review this @alexrisch is this at the level of

a) its a poc - shit is going to suck but it works and demos privy
b) ready to merge functionality and happy with the way the code looks/is organized etc
c) something else

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: 4

♻️ Duplicate comments (5)
features/onboarding/passkey/passkeyAuthStore.tsx (1)

50-52: 🛠️ Refactor suggestion

Improve error handling in setLoading

The setLoading implementation clears errors when loading is true but not when it's false. This asymmetric error handling could lead to stale error states.

features/onboarding/Privy/usePrivySmartWalletConnection.ts (2)

46-54: ⚠️ Potential issue

Reset creatingEmbeddedWallet.current flag on error

The embedded wallet creation logic should reset the flag on error to allow retries.


68-101: ⚠️ Potential issue

Reset initializingXmtp.current flag on error

The XMTP initialization logic should reset the flag on error to allow retries.

config.ts (1)

39-40: ⚠️ Potential issue

Verify chain configuration consistency.

The code imports mainnet base chain but uses Base Sepolia's chain ID (0x14a34) in the default config. This mismatch needs to be resolved.

Apply this diff to fix the chain ID:

-    transactionChainId: "0x14a34", // Base Sepolia
+    transactionChainId: "0x2105", // Base Mainnet
🧰 Tools
🪛 GitHub Check: lint

[warning] 39-39:
Prefer named exports

components/Onboarding/init-xmtp-client.ts (1)

55-91: 🛠️ Refactor suggestion

Reduce code duplication in XMTP client initialization.

The initialization logic is duplicated between initXmtpClient and initXmtpClientFromViemAccount.

This issue was previously identified in a past review. The suggested refactor to extract common initialization logic remains valid.

🧹 Nitpick comments (7)
features/onboarding/passkey/useCreatePasskey.ts (2)

37-58: Add retry mechanism for passkey creation

The handleCreateAccountWithPasskey function doesn't handle temporary failures that might occur during passkey creation. Consider implementing a retry mechanism with exponential backoff for transient errors.

 const handleCreateAccountWithPasskey = useCallback(async () => {
+  const maxRetries = 3;
+  const baseDelay = 1000;
+  let retryCount = 0;
   try {
     setLoading(true);
     if (privyUser) {
       await logout();
     }
-    const { user } = await signupWithPasskey({
-      relyingParty: RELYING_PARTY,
-    });
+    while (retryCount < maxRetries) {
+      try {
+        const { user } = await signupWithPasskey({
+          relyingParty: RELYING_PARTY,
+        });
+        if (!user) {
+          throw new Error("No account created from Passkey");
+        }
+        setStatusString("Account created - Waiting for smart wallet");
+        setPrivyAccountId(user.id);
+        return;
+      } catch (e) {
+        retryCount++;
+        if (retryCount === maxRetries) throw e;
+        await new Promise(resolve => setTimeout(resolve, baseDelay * Math.pow(2, retryCount)));
+      }
+    }
+  } catch (e: any) {
     if (!user) {
       throw new Error("No account created from Passkey");
     }

52-54: Improve error handling specificity

The catch block uses a generic error message. Consider handling specific error types differently to provide more helpful error messages to users.

-    } catch (e: any) {
-      setError(e?.message ?? "Error creating Passkey account");
-      captureErrorWithToast(e);
+    } catch (e: any) {
+      let errorMessage = "Error creating Passkey account";
+      if (e instanceof PrivyError) {
+        errorMessage = "Failed to create Privy account: " + e.message;
+      } else if (e instanceof PasskeyError) {
+        errorMessage = "Failed to create Passkey: " + e.message;
+      }
+      setError(errorMessage);
+      captureErrorWithToast(e);
features/onboarding/passkey/passkeyAuthStore.tsx (1)

26-38: Add display name to PasskeyAuthStoreProvider

The provider component is memoized but missing a display name, which can make debugging more difficult.

 export const PasskeyAuthStoreProvider = memo(
   ({ children, ...props }: IPasskeyAuthStoreProviderProps) => {
     const storeRef = useRef<IPasskeyAuthStore>();
     if (!storeRef.current) {
       storeRef.current = createPasskeyAuthStore(props);
     }
     return (
       <PasskeyAuthStoreContext.Provider value={storeRef.current}>
         {children}
       </PasskeyAuthStoreContext.Provider>
     );
   }
-);
+, { displayName: 'PasskeyAuthStoreProvider' });
screens/NewAccount/new-account-passkey-screen.tsx (1)

69-79: Add loading state during navigation

The onConnectionDone callback navigates immediately without showing a loading state, which might lead to a jarring user experience.

   usePrivySmartWalletConnection({
     onConnectionDone: () => {
+      setStatusString("Finalizing setup...");
       if (isMissingConverseProfile()) {
-        router.navigate("NewAccountUserProfile");
+        setTimeout(() => router.navigate("NewAccountUserProfile"), 500);
       } else {
-        router.popTo("Chats");
+        setTimeout(() => router.popTo("Chats"), 500);
       }
     },
     onConnectionError: handleError,
     onStatusChange: setStatusString,
   });
screens/Onboarding/onboarding-passkey-screen.tsx (1)

26-34: Extract providers into a shared HOC.

The provider wrapping pattern is duplicated across passkey-related screens. Consider creating a shared HOC to reduce duplication.

// components/Passkey/PasskeyAuthProviders.tsx
export const withPasskeyAuth = (WrappedComponent: React.ComponentType) => {
  return function WithPasskeyAuthWrapper(props: any) {
    return (
      <PrivyAuthStoreProvider>
        <PasskeyAuthStoreProvider>
          <WrappedComponent {...props} />
        </PasskeyAuthStoreProvider>
      </PrivyAuthStoreProvider>
    );
  };
};
App.tsx (1)

170-170: Extract inline style to StyleSheet.

Move the inline style to the StyleSheet definition for better performance and maintainability.

-<GestureHandlerRootView style={{ flex: 1 }}>
+<GestureHandlerRootView style={styles.gestureHandler}>

const useStyles = () => {
  return useMemo(
    () =>
      StyleSheet.create({
        safe: {
          flex: 1,
        },
+       gestureHandler: {
+         flex: 1,
+       },
      }),
    []
  );
};
🧰 Tools
🪛 GitHub Check: lint

[warning] 170-170:
Inline style: { flex: 1 }

package.json (1)

Line range hint 1-275: Consider adding resolutions field for security.

Given the large number of dependencies and the security-critical nature of wallet/passkey functionality, consider adding a resolutions field to enforce minimum versions of shared dependencies.

Add this section to package.json:

+ "resolutions": {
+   "@babel/traverse": "^7.25.3",
+   "axios": "^1.6.0",
+   "crypto-js": "^4.2.0"
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 923ada6 and 3d3c926.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (31)
  • .github/workflows/build-app-release.yml (2 hunks)
  • .github/workflows/build-internal.yml (1 hunks)
  • App.tsx (2 hunks)
  • app.config.ts (1 hunks)
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx (1 hunks)
  • components/Onboarding/init-xmtp-client.ts (2 hunks)
  • components/TransactionPreview/TransactionPreview.tsx (2 hunks)
  • config.ts (6 hunks)
  • features/onboarding/Privy/usePrivySmartWalletConnection.ts (1 hunks)
  • features/onboarding/passkey/passkey.constants.ts (1 hunks)
  • features/onboarding/passkey/passkeyAuthStore.tsx (1 hunks)
  • features/onboarding/passkey/useCreatePasskey.ts (1 hunks)
  • features/onboarding/passkey/useLoginWithPasskey.ts (1 hunks)
  • i18n/translations/en.ts (1 hunks)
  • ios/Converse/Converse.entitlements (1 hunks)
  • metro.config.js (2 hunks)
  • navigation/OnboardingNavigator.tsx (3 hunks)
  • package.json (6 hunks)
  • screens/Main.tsx (0 hunks)
  • screens/Navigation/Navigation.tsx (5 hunks)
  • screens/NewAccount/NewAccountScreen.tsx (2 hunks)
  • screens/NewAccount/new-account-passkey-screen.tsx (1 hunks)
  • screens/Onboarding/OnboardingGetStartedScreen.tsx (2 hunks)
  • screens/Onboarding/onboarding-passkey-screen.tsx (1 hunks)
  • scripts/build/ios/preview.js (1 hunks)
  • scripts/build/ios/production.js (1 hunks)
  • tsconfig.json (1 hunks)
  • utils/events.ts (1 hunks)
  • utils/navigation.ts (1 hunks)
  • utils/xmtpRN/signIn.ts (2 hunks)
  • utils/xmtpRN/signer.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • screens/Main.tsx
🚧 Files skipped from review as they are similar to previous changes (16)
  • scripts/build/ios/preview.js
  • scripts/build/ios/production.js
  • features/onboarding/passkey/passkey.constants.ts
  • utils/navigation.ts
  • tsconfig.json
  • app.config.ts
  • i18n/translations/en.ts
  • .github/workflows/build-internal.yml
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx
  • navigation/OnboardingNavigator.tsx
  • metro.config.js
  • screens/NewAccount/NewAccountScreen.tsx
  • utils/events.ts
  • components/TransactionPreview/TransactionPreview.tsx
  • screens/Navigation/Navigation.tsx
  • ios/Converse/Converse.entitlements
🧰 Additional context used
🪛 GitHub Check: lint
config.ts

[warning] 39-39:
Prefer named exports

App.tsx

[warning] 170-170:
Inline style: { flex: 1 }

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (15)
features/onboarding/passkey/useLoginWithPasskey.ts (2)

36-57: Add retry mechanism for passkey login

Similar to passkey creation, the login process should handle temporary failures with a retry mechanism.


51-54: Improve error handling specificity

The error handling can be improved by providing more specific error messages based on the error type.

screens/Onboarding/onboarding-passkey-screen.tsx (2)

36-82: Extract passkey authentication logic into a custom hook.

The passkey-related logic should be extracted into a reusable hook to improve maintainability and reusability.


83-112: Extract shared passkey components.

The UI components are duplicated between passkey screens. Consider extracting them into shared components.

config.ts (1)

80-80: LGTM: Domain configuration is consistent.

The appDomain configuration is correctly set for each environment.

Also applies to: 100-100, 136-136

screens/Onboarding/OnboardingGetStartedScreen.tsx (1)

62-66: LGTM: Passkey integration is properly implemented.

The passkey option is correctly integrated into the onboarding flow.

App.tsx (1)

158-163: LGTM: Privy configuration is complete.

The PrivyProvider is correctly configured with all required props.

.github/workflows/build-app-release.yml (1)

53-53: LGTM: Environment configuration is consistent.

The Privy client ID is correctly added to both Android and iOS build configurations.

Also applies to: 101-101

package.json (3)

110-111: Verify Expo SDK compatibility with updated packages.

Multiple Expo packages have been updated to patch versions:

  • expo-apple-authentication: 7.1.3
  • expo-application: 6.0.2
  • expo-constants: 17.0.4
  • expo-crypto: 14.0.2
  • expo-linking: 7.0.4
  • expo-secure-store: 14.0.1
  • expo-web-browser: 14.0.2

These updates seem to be patch-level changes, but we should verify they're all compatible with the project's Expo SDK version (52.0.0).

Also applies to: 117-119, 130-130, 135-135, 141-141

✅ Verification successful

All updated Expo packages are compatible with SDK 52.0.0

The patch-level updates are safe to use as all packages specify flexible version requirements (*) in their peer dependencies, indicating they're designed to work with any Expo SDK version.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check Expo SDK compatibility

# Get the expo SDK version from package.json
EXPO_VERSION=$(jq -r '.dependencies.expo' package.json | tr -d '^~')

# Check if all updated packages are compatible with this Expo version
for pkg in "expo-apple-authentication" "expo-application" "expo-constants" "expo-crypto" "expo-linking" "expo-secure-store" "expo-web-browser"; do
  echo "Checking $pkg..."
  curl -s "https://registry.npmjs.org/$pkg" | jq -r ".versions[] | select(.peerDependencies.expo == \"$EXPO_VERSION\")"
done

Length of output: 2404


Script:

#!/bin/bash
# Description: Check specific Expo package versions compatibility

declare -A versions=(
  ["expo-apple-authentication"]="7.1.3"
  ["expo-application"]="6.0.2"
  ["expo-constants"]="17.0.4"
  ["expo-crypto"]="14.0.2"
  ["expo-linking"]="7.0.4"
  ["expo-secure-store"]="14.0.1"
  ["expo-web-browser"]="14.0.2"
)

for pkg in "${!versions[@]}"; do
  version="${versions[$pkg]}"
  echo "Checking $pkg@$version..."
  curl -s "https://registry.npmjs.org/$pkg/$version" | jq '{
    name: .name,
    version: .version,
    peerDependencies: .peerDependencies
  }'
done

Length of output: 3010


56-57: Verify compatibility with the major version upgrade of @privy-io/expo.

The significant version jump from ^0.16.2 to 0.36.2 requires careful verification:

  1. Breaking changes might affect existing Privy integration
  2. New native extensions package might require additional setup

Run this script to check for breaking changes:

✅ Verification successful

The Privy SDK upgrade appears compatible with the current implementation

The codebase correctly implements all critical Privy SDK features including:

  • Proper provider setup with required configuration
  • Passkey authentication using latest patterns
  • Smart wallet integration with correct provider nesting
  • Native extensions package properly included
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check breaking changes in @privy-io/expo changelog

# Get changelog or release notes
gh api repos/privy-io/expo/releases | jq -r '.[].body' | grep -i "breaking"

# Check if native extensions are properly configured
rg -g "app.{json,config.js}" "expo-native-extensions|@privy-io/expo-native-extensions" 

Length of output: 254


Script:

#!/bin/bash
# Search for Privy SDK usage and configuration
echo "=== Privy SDK Usage ==="
rg -l "@privy-io/expo"

echo -e "\n=== App Configuration ==="
fd "app.config" -e js -e json -x cat {}

echo -e "\n=== Native Module Setup ==="
rg -g "*.{ts,js,tsx,jsx}" "registerNativeModule|nativeExtensions|PrivyNativeExtensions"

Length of output: 691


Script:

#!/bin/bash
# Check SDK initialization and implementation
echo "=== App.tsx Privy Setup ==="
rg -A 10 "PrivyProvider|createPrivyClient" App.tsx

echo -e "\n=== Authentication Implementation ==="
cat features/onboarding/passkey/useLoginWithPasskey.ts
cat features/onboarding/Privy/usePrivyConnection.ts

echo -e "\n=== Plugin Configuration ==="
rg -g "*.{json,js}" "plugins.*privy|expo-native-extensions" -A 5

Length of output: 6729


151-151: Verify blockchain dependencies compatibility.

New blockchain-related packages have been added:

  • permissionless: 0.2.25
  • viem: 2.22.7
  • wagmi: 2.5.11

These packages are commonly used together but version compatibility should be verified.

Also applies to: 206-207

✅ Verification successful

Blockchain dependencies are compatible

The package versions are compatible:

  • wagmi 2.5.11 requires viem 2.x
  • permissionless 0.2.25 requires viem ^2.0.0
  • viem 2.22.7 satisfies both requirements

Integration patterns in the codebase look correct, with blockchain functionality properly organized in configuration, wallet connection, and transaction handling components.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check blockchain package compatibility

# Check peer dependencies
for pkg in "permissionless" "viem" "wagmi"; do
  echo "Checking $pkg peer dependencies..."
  curl -s "https://registry.npmjs.org/$pkg" | jq '.versions | to_entries[] | select(.key | contains("2")) | .value.peerDependencies'
done

# Look for existing blockchain configurations
rg -g "*.{ts,js}" "wagmi|viem|permissionless"

Length of output: 66802


Script:

#!/bin/bash
# Retry checking wagmi peer dependencies and usage patterns
echo "Checking wagmi@2.5.11 peer dependencies..."
curl -s "https://registry.npmjs.org/wagmi/2.5.11" | jq '.peerDependencies'

echo -e "\nChecking for blockchain package usage patterns..."
rg --type ts --type js -l 'wagmi|viem|permissionless' || true

Length of output: 641

utils/xmtpRN/signer.ts (3)

17-21: LGTM! Well-defined type interface.

The ViemAccount type correctly defines the minimal required properties for XMTP signing functionality.


28-37: LGTM! Proper error handling and logging.

The message signing implementation includes appropriate error handling and logging.


27-27: Verify wallet type determination.

The wallet type is hardcoded as "SCW" (Smart Contract Wallet). This might not be accurate for all Viem accounts.

Consider adding a parameter to determine the wallet type:

-  walletType: () => "SCW",
+  walletType: () => account.isSmartContract ? "SCW" : "EOA",

Run this script to verify wallet type handling in the codebase:

components/Onboarding/init-xmtp-client.ts (1)

63-65: 🛠️ Refactor suggestion

Fix misleading error message.

The error message "No signer" is incorrect for a Viem account.

-    throw new Error("No signer");
+    throw new Error("No Viem account provided");

Likely invalid or redundant comment.

Comment on lines 81 to 97
const viemAccount: ViemAccount = {
address: smartAccount.address,
chainId:
smartAccount.client.chain?.id ?? config.privy.defaultChain.id,
signMessage: async ({ message }) => {
return smartAccount.signMessage({ message });
},
};
await initXmtpClientFromViemAccount({
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add timeout for smart wallet operations

The smart wallet operations don't have a timeout, which could lead to hanging operations if the network is slow or unresponsive.

+      const TIMEOUT_MS = 30000;
+      const timeoutPromise = new Promise((_, reject) => 
+        setTimeout(() => reject(new Error('Smart wallet operation timed out')), TIMEOUT_MS)
+      );
+
       const viemAccount: ViemAccount = {
         address: smartAccount.address,
         chainId:
           smartAccount.client.chain?.id ?? config.privy.defaultChain.id,
         signMessage: async ({ message }) => {
-          return smartAccount.signMessage({ message });
+          return Promise.race([
+            smartAccount.signMessage({ message }),
+            timeoutPromise
+          ]);
         },
       };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const viemAccount: ViemAccount = {
address: smartAccount.address,
chainId:
smartAccount.client.chain?.id ?? config.privy.defaultChain.id,
signMessage: async ({ message }) => {
return smartAccount.signMessage({ message });
},
};
await initXmtpClientFromViemAccount({
const TIMEOUT_MS = 30000;
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Smart wallet operation timed out')), TIMEOUT_MS)
);
const viemAccount: ViemAccount = {
address: smartAccount.address,
chainId:
smartAccount.client.chain?.id ?? config.privy.defaultChain.id,
signMessage: async ({ message }) => {
return Promise.race([
smartAccount.signMessage({ message }),
timeoutPromise
]);
},
};
await initXmtpClientFromViemAccount({

screens/NewAccount/new-account-passkey-screen.tsx Outdated Show resolved Hide resolved
package.json Show resolved Hide resolved
Comment on lines 74 to 121
export const createXmtpClientFromViemAccount = async (
account: ViemAccount,
onInstallationRevoked: () => Promise<void>,
preAuthenticateToInboxCallback?: () => Promise<void>
) => {
const tempDirectory = await createTemporaryDirectory();
const dbEncryptionKey = await getDbEncryptionKey();

const options = {
env,
enableV3: true,
dbDirectory: tempDirectory,
dbEncryptionKey,
};
const inboxId = await getInboxId(await account.address);
await copyDatabasesToTemporaryDirectory(tempDirectory, inboxId);

logger.debug("Instantiating client from signer");

const client = await Client.create(viemAccountToXmtpSigner(account), {
...options,
preAuthenticateToInboxCallback,
});

if (client.inboxId !== inboxId) {
throw new Error("Inbox ids don't match");
}

// In case we're logging with an existing libxmtp database, make sure
// the installation has not already been revoked
const installationValid = await isClientInstallationValid(client);
if (!installationValid) {
await client.dropLocalDatabaseConnection();
await deleteLibXmtpDatabaseForInboxId(inboxId);
onInstallationRevoked();
return;
}

logger.debug("Instantiated client from signer");
// This Client is only be used to extract the key, we can disconnect
// it to prevent locks happening during Onboarding
await client.dropLocalDatabaseConnection();
await moveTemporaryDatabasesToDatabaseDirectory(
tempDirectory,
client.inboxId
);
logger.debug("Dropped client databases");
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reduce code duplication with the existing client creation function.

The new createXmtpClientFromViemAccount function duplicates most of the logic from createXmtpClientFromSigner. Consider extracting the common initialization logic into a shared function.

Apply this refactor to reduce duplication:

+async function createXmtpClientCommon(
+  options: {
+    env: string;
+    enableV3: boolean;
+    dbDirectory: string;
+    dbEncryptionKey: string;
+  },
+  inboxId: string,
+  createClient: () => Promise<Client>,
+  onInstallationRevoked: () => Promise<void>
+) {
+  await copyDatabasesToTemporaryDirectory(options.dbDirectory, inboxId);
+
+  logger.debug("Instantiating client");
+  const client = await createClient();
+
+  if (client.inboxId !== inboxId) {
+    throw new Error("Inbox ids don't match");
+  }
+
+  const installationValid = await isClientInstallationValid(client);
+  if (!installationValid) {
+    await client.dropLocalDatabaseConnection();
+    await deleteLibXmtpDatabaseForInboxId(inboxId);
+    onInstallationRevoked();
+    return;
+  }
+
+  logger.debug("Instantiated client");
+  await client.dropLocalDatabaseConnection();
+  await moveTemporaryDatabasesToDatabaseDirectory(
+    options.dbDirectory,
+    client.inboxId
+  );
+  logger.debug("Dropped client databases");
+}

 export const createXmtpClientFromViemAccount = async (
   account: ViemAccount,
   onInstallationRevoked: () => Promise<void>,
   preAuthenticateToInboxCallback?: () => Promise<void>
 ) => {
   const tempDirectory = await createTemporaryDirectory();
   const dbEncryptionKey = await getDbEncryptionKey();
   const options = {
     env,
     enableV3: true,
     dbDirectory: tempDirectory,
     dbEncryptionKey,
   };
   const inboxId = await getInboxId(await account.address);
-  await copyDatabasesToTemporaryDirectory(tempDirectory, inboxId);
-
-  logger.debug("Instantiating client from signer");
-
-  const client = await Client.create(viemAccountToXmtpSigner(account), {
-    ...options,
-    preAuthenticateToInboxCallback,
-  });
-
-  if (client.inboxId !== inboxId) {
-    throw new Error("Inbox ids don't match");
-  }
-
-  const installationValid = await isClientInstallationValid(client);
-  if (!installationValid) {
-    await client.dropLocalDatabaseConnection();
-    await deleteLibXmtpDatabaseForInboxId(inboxId);
-    onInstallationRevoked();
-    return;
-  }
-
-  logger.debug("Instantiated client from signer");
-  await client.dropLocalDatabaseConnection();
-  await moveTemporaryDatabasesToDatabaseDirectory(
-    tempDirectory,
-    client.inboxId
-  );
-  logger.debug("Dropped client databases");
+  await createXmtpClientCommon(
+    options,
+    inboxId,
+    () => Client.create(viemAccountToXmtpSigner(account), {
+      ...options,
+      preAuthenticateToInboxCallback,
+    }),
+    onInstallationRevoked
+  );
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const createXmtpClientFromViemAccount = async (
account: ViemAccount,
onInstallationRevoked: () => Promise<void>,
preAuthenticateToInboxCallback?: () => Promise<void>
) => {
const tempDirectory = await createTemporaryDirectory();
const dbEncryptionKey = await getDbEncryptionKey();
const options = {
env,
enableV3: true,
dbDirectory: tempDirectory,
dbEncryptionKey,
};
const inboxId = await getInboxId(await account.address);
await copyDatabasesToTemporaryDirectory(tempDirectory, inboxId);
logger.debug("Instantiating client from signer");
const client = await Client.create(viemAccountToXmtpSigner(account), {
...options,
preAuthenticateToInboxCallback,
});
if (client.inboxId !== inboxId) {
throw new Error("Inbox ids don't match");
}
// In case we're logging with an existing libxmtp database, make sure
// the installation has not already been revoked
const installationValid = await isClientInstallationValid(client);
if (!installationValid) {
await client.dropLocalDatabaseConnection();
await deleteLibXmtpDatabaseForInboxId(inboxId);
onInstallationRevoked();
return;
}
logger.debug("Instantiated client from signer");
// This Client is only be used to extract the key, we can disconnect
// it to prevent locks happening during Onboarding
await client.dropLocalDatabaseConnection();
await moveTemporaryDatabasesToDatabaseDirectory(
tempDirectory,
client.inboxId
);
logger.debug("Dropped client databases");
};
async function createXmtpClientCommon(
options: {
env: string;
enableV3: boolean;
dbDirectory: string;
dbEncryptionKey: string;
},
inboxId: string,
createClient: () => Promise<Client>,
onInstallationRevoked: () => Promise<void>
) {
await copyDatabasesToTemporaryDirectory(options.dbDirectory, inboxId);
logger.debug("Instantiating client");
const client = await createClient();
if (client.inboxId !== inboxId) {
throw new Error("Inbox ids don't match");
}
const installationValid = await isClientInstallationValid(client);
if (!installationValid) {
await client.dropLocalDatabaseConnection();
await deleteLibXmtpDatabaseForInboxId(inboxId);
onInstallationRevoked();
return;
}
logger.debug("Instantiated client");
await client.dropLocalDatabaseConnection();
await moveTemporaryDatabasesToDatabaseDirectory(
options.dbDirectory,
client.inboxId
);
logger.debug("Dropped client databases");
}
export const createXmtpClientFromViemAccount = async (
account: ViemAccount,
onInstallationRevoked: () => Promise<void>,
preAuthenticateToInboxCallback?: () => Promise<void>
) => {
const tempDirectory = await createTemporaryDirectory();
const dbEncryptionKey = await getDbEncryptionKey();
const options = {
env,
enableV3: true,
dbDirectory: tempDirectory,
dbEncryptionKey,
};
const inboxId = await getInboxId(await account.address);
await createXmtpClientCommon(
options,
inboxId,
() => Client.create(viemAccountToXmtpSigner(account), {
...options,
preAuthenticateToInboxCallback,
}),
onInstallationRevoked
);
};

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: 24

🔭 Outside diff range comments (3)
features/onboarding/screens/OnboardingPrivateKeyScreen.tsx (1)

Line range hint 31-49: Critical: Implement secure private key handling.

The current implementation has several security vulnerabilities in handling private keys:

  1. Private keys are stored in plain text state
  2. No secure memory clearing after use
  3. No input masking
  4. Potential exposure through clipboard operations

Recommended security improvements:

 export function OnboardingPrivateKeyScreen({...}) {
-  const [privateKey, setPrivateKey] = useState("");
+  const [privateKey, setPrivateKey] = useState<string>("");
+
+  // Clear private key from memory when component unmounts
+  useEffect(() => {
+    return () => {
+      setPrivateKey("");
+    };
+  }, []);

   const handlePressConnect = useCallback(async () => {
     try {
       const trimmedPrivateKey = privateKey.trim();
       if (!trimmedPrivateKey) return;
       await loginWithPrivateKey(trimmedPrivateKey);
+      // Clear private key from memory after use
+      setPrivateKey("");
       if (isMissingConverseProfile()) {
         router.navigate("OnboardingContactCard");
       }
     // ...
   }
 }

 export const PrivateKeyInput = ({ value, onChange }: IPrivateKeyInputProps) => {
   return (
     <TextInput
       multiline
       textAlignVertical="top"
+      secureTextEntry={true}
+      contextMenuHidden={true}
+      autoCorrect={false}
+      autoCapitalize="none"
       style={styles.textInput}
       // ...
     />
   );
 };

Additionally, consider:

  1. Using secure storage (e.g., Keychain) for temporary private key storage
  2. Implementing a secure input method that prevents screen recording
  3. Adding clipboard warnings and auto-clear functionality

Also applies to: 115-146

screens/NewAccount/NewAccountConnectWalletScreen.tsx (1)

Line range hint 18-27: Add missing router dependency to useCallback.

The callback uses router.navigate but router is not included in the dependency array.

      },
-      // eslint-disable-next-line react-hooks/exhaustive-deps
-      []
+      [router]
    );
features/onboarding/screens/OnboardingConnectWalletScreen.tsx (1)

Line range hint 26-34: Add missing router dependency to handleDoneConnecting callback.

Similar to NewAccountConnectWalletScreen, the router dependency is missing.

      },
-      // eslint-disable-next-line react-hooks/exhaustive-deps
-      []
+      [router]
    );
♻️ Duplicate comments (5)
utils/xmtpRN/signIn.ts (1)

74-121: 🛠️ Refactor suggestion

Reduce code duplication with existing client creation function.

The implementation duplicates most of the logic from createXmtpClientFromSigner. Consider extracting the common initialization logic into a shared function.

Additionally, the log message on line 91 states "Instantiating client from signer" which is incorrect as we're using a Viem account.

-  logger.debug("Instantiating client from signer");
+  logger.debug("Instantiating client from Viem account");
components/Onboarding/init-xmtp-client.ts (1)

55-91: 🛠️ Refactor suggestion

Reduce code duplication in XMTP client initialization.

The initXmtpClientFromViemAccount function shares significant logic with initXmtpClient. Consider extracting the common initialization logic.

features/onboarding/Privy/usePrivySmartWalletConnection.ts (3)

81-89: 🛠️ Refactor suggestion

Add timeout for smart wallet operations.

The smart wallet operations don't have a timeout, which could lead to hanging operations if the network is slow or unresponsive.

Apply this diff to add a timeout:

+      const TIMEOUT_MS = 30000;
+      const timeoutPromise = new Promise((_, reject) => 
+        setTimeout(() => reject(new Error('Smart wallet operation timed out')), TIMEOUT_MS)
+      );
+
       const viemAccount: ViemAccount = {
         address: smartAccount.address,
         chainId:
           smartAccount.client.chain?.id ?? config.privy.defaultChain.id,
         signMessage: async ({ message }) => {
-          return smartAccount.signMessage({ message });
+          return Promise.race([
+            smartAccount.signMessage({ message }),
+            timeoutPromise
+          ]);
         },
       };
🧰 Tools
🪛 GitHub Check: tsc

[failure] 84-84:
Property 'defaultChain' does not exist on type '{ appId: string; clientId: string; } | { readonly appId: string; readonly clientId: string; }'.

🪛 GitHub Actions: Typescript CI

[error] 84-84: Property 'defaultChain' does not exist on type '{ appId: string; clientId: string; } | { readonly appId: string; readonly clientId: string; }'


46-54: ⚠️ Potential issue

Reset creatingEmbeddedWallet.current flag on error.

In the embedded wallet creation logic, if embeddedWallet.create() fails, creatingEmbeddedWallet.current remains true, preventing further attempts to create the wallet.

Apply this diff to reset the flag on error:

 embeddedWallet
   .create()
   // TODO: Handle better error
-  .catch(ensureErrorHandler(onConnectionError));
+  .catch((error) => {
+    creatingEmbeddedWallet.current = false;
+    ensureErrorHandler(onConnectionError)(error);
+  });

95-97: ⚠️ Potential issue

Reset initializingXmtp.current flag on error.

In the XMTP initialization logic, if initialization fails, initializingXmtp.current remains true, preventing further attempts to initialize XMTP.

Apply this diff to reset the flag on error:

 } catch (error) {
+  initializingXmtp.current = false;
   onConnectionError(ensureError(error));
 }
🧹 Nitpick comments (38)
features/onboarding/components/onboarding-subtitle.tsx (2)

1-3: Consider organizing imports by external/internal dependencies.

While the imports are well-structured, consider grouping them by:

  1. External dependencies (react, react-native)
  2. Internal dependencies (@/design-system)
+import { memo } from "react";
+import { TextStyle } from "react-native";
+
 import { AnimatedText, IAnimatedTextProps } from "@/design-system/Text";
-import { memo } from "react";
-import { TextStyle } from "react-native";

9-14: Add JSDoc documentation and consider explicit prop handling.

While the implementation is solid, consider:

  1. Adding JSDoc documentation to describe the component's purpose and props
  2. Explicitly listing commonly used props instead of spreading all props
+/**
+ * A reusable subtitle component for onboarding screens with animation support.
+ * @param props - Extended AnimatedText props for customization
+ * @param props.style - Additional styles to apply to the text
+ * @param props.children - The text content to display
+ */
 export const OnboardingSubtitle = memo(function OnboardingSubtitle(
   props: IAnimatedTextProps
 ) {
   const { style, ...rest } = props;
   return <AnimatedText size="sm" style={[$defaultStyle, style]} {...rest} />;
 });
components/Onboarding/init-xmtp-client.ts (1)

63-65: Fix incorrect error message.

The error message should reflect that we're checking for a Viem account, not a signer.

-    throw new Error("No signer");
+    throw new Error("No Viem account provided");
features/onboarding/screens/OnboardingPrivateKeyScreen.tsx (1)

18-27: Consider using path aliases for improved maintainability.

The deep nesting in import paths (../../../) could be simplified using path aliases, similar to how @components and @i18n are used in other imports. This would make the imports more maintainable and less prone to errors when files are moved.

Example transformation:

-import { OnboardingPictoTitleSubtitle } from "../../../components/Onboarding/OnboardingPictoTitleSubtitle";
+import { OnboardingPictoTitleSubtitle } from "@components/Onboarding/OnboardingPictoTitleSubtitle";
features/onboarding/screens/OnboardingEphemeraScreen.tsx (2)

12-23: Consider using path aliases for deep imports.

The deep relative paths ("../../../") can be fragile and harder to maintain. Consider using TypeScript path aliases to make imports more maintainable and less prone to breaking during file moves.

Example configuration in tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@onboarding/*": ["features/onboarding/*"],
      "@components/*": ["components/*"],
      "@design-system/*": ["design-system/*"],
      "@screens/*": ["screens/*"]
    }
  }
}

Then imports could be simplified to:

import { OnboardingPictoTitleSubtitle } from "@components/Onboarding/OnboardingPictoTitleSubtitle";
import { Text } from "@design-system/Text/Text";

42-42: Document the navigation flow change.

The navigation target has been changed from "OnboardingUserProfile" to "OnboardingContactCard". While this appears to be part of a broader restructuring of the onboarding flow, it would be helpful to document why this change was made to maintain clarity for future developers.

Consider adding a comment explaining the navigation flow:

+ // Navigate to contact card creation after wallet generation
+ // as part of the new Privy passkey onboarding flow
router.navigate("OnboardingContactCard");
design-system/IconButton/IconButton.tsx (1)

20-23: Add explicit type for the ref parameter.

While the implementation is correct, the ref parameter should be explicitly typed for better type safety.

 export const IconButton = React.forwardRef(function IconButton(
   props: IIconButtonProps,
-  ref
+  ref: React.ForwardedRef<any>
 ) {
design-system/IconButton/IconButton.styles.ts (1)

51-54: Use theme spacing system for xl size dimensions.

The hard-coded values (56) break the pattern of using the theme's spacing system. Consider using theme spacing values for consistency.

       xl: {
-        width: 56,
-        height: 56,
+        width: spacing.xxxl,
+        height: spacing.xxxl,
       },
theme/timing.ts (1)

6-8: Maintain consistent naming convention across timing constants.

The new timing constants introduce inconsistent naming patterns:

  • xSlow and xxSlow use a different prefix style than existing constants
  • "3xSlow" as a string literal key breaks the camelCase pattern and requires string indexing

Consider using one of these consistent approaches:

-  xSlow: 1000,
-  xxSlow: 2000,
-  "3xSlow": 3000,
+  // Option 1: Descriptive naming (matches existing pattern)
+  verySlow: 1000,
+  extraSlow: 2000,
+  ultraslow: 3000,
+  // Option 2: Numeric prefix
+  slow1x: 1000,
+  slow2x: 2000,
+  slow3x: 3000,
features/onboarding/constants/animationConstants.ts (2)

3-3: Consider type safety for timing constant access.

Using string literal indexing with timing["3xSlow"] is fragile and could break if the key is renamed.

Consider using a type-safe reference:

-export const ONBOARDING_ENTERING_DURATION = timing["3xSlow"];
+export const ONBOARDING_ENTERING_DURATION = timing.slow3x; // after renaming in timing.ts

5-9: Document the purpose of animation delays and consider timing intervals.

The enum defines small incremental delays (50ms differences) which might not be perceptibly different to users.

Consider:

  1. Adding documentation explaining the purpose of these specific timings
  2. Using more distinct intervals for noticeable staging effects
+/**
+ * Defines sequential animation delays for onboarding elements.
+ * Each stage is delayed to create a staggered entrance effect.
+ */
 export enum ONBOARDING_ENTERING_DELAY {
-  FIRST = 200,
-  SECOND = 250,
-  THIRD = 300,
+  FIRST = 200,
+  SECOND = 400,  // More distinct delay
+  THIRD = 600,   // More distinct delay
 }
features/onboarding/passkey/passkeyAuthStore.tsx (2)

26-38: Add display name for better debugging.

While the Provider implementation is solid, adding a display name would improve debugging experience.

export const PasskeyAuthStoreProvider = memo(
  ({ children, ...props }: IPasskeyAuthStoreProviderProps) => {
    const storeRef = useRef<IPasskeyAuthStore>();
    if (!storeRef.current) {
      storeRef.current = createPasskeyAuthStore(props);
    }
    return (
      <PasskeyAuthStoreContext.Provider value={storeRef.current}>
        {children}
      </PasskeyAuthStoreContext.Provider>
    );
  }
);
+PasskeyAuthStoreProvider.displayName = 'PasskeyAuthStoreProvider';

56-56: Consider secure data cleanup in reset.

Since this store handles sensitive authentication data, consider implementing a more thorough cleanup in the reset method to ensure all sensitive data is properly cleared.

-    reset: () => set(DEFAULT_PROPS),
+    reset: () => {
+      // Ensure sensitive data is properly cleared
+      set({
+        ...DEFAULT_PROPS,
+        account: undefined,  // Explicitly clear sensitive data
+        statusString: undefined,
+      });
+    },
design-system/Icon/Icon.tsx (1)

71-72: LGTM! Platform-appropriate icon mappings with suggestion.

The mappings are well-chosen for iOS:

  • Using "faceid" for biometric is appropriate
  • Using "camera.fill" maintains iOS style consistency

However, consider extracting platform-specific icon names into a shared configuration to make platform differences more explicit and maintainable.

Example approach:

// platformIcons.ts
export const platformSpecificIcons = {
  biometric: Platform.select({
    ios: 'faceid',
    android: 'fingerprint'
  }),
  camera: Platform.select({
    ios: 'camera.fill',
    android: 'camera'
  })
};
features/onboarding/types/onboarding.types.ts (2)

1-5: Consider adding validation constraints to ProfileType.

The type could benefit from additional constraints and documentation:

  • Username length limits
  • Allowed character patterns
  • Avatar URL format

Consider adding JSDoc comments and using branded types:

+/**
+ * Represents a user profile with optional avatar and display name.
+ * @property username - Must be 3-30 characters, alphanumeric with underscores
+ * @property avatar - Optional URL to user's avatar image
+ * @property displayName - Optional human-readable name
+ */
 export type ProfileType = {
-  avatar?: string;
+  avatar?: `https://${string}` | `data:image/${string}`;
   username: string;
   displayName?: string;
 };

7-18: Add specific error types for better error handling.

The current error type could be more specific about possible error scenarios.

Consider using an enum for error types:

+export enum ProfileError {
+  USERNAME_TAKEN = "USERNAME_TAKEN",
+  INVALID_USERNAME = "INVALID_USERNAME",
+  INVALID_AVATAR_URL = "INVALID_AVATAR_URL",
+  SERVER_ERROR = "SERVER_ERROR"
+}

 type CreateOrUpdateProfileError = {
   success: false;
-  errorMessage: string;
+  errorCode: ProfileError;
+  errorMessage: string;
 };
features/onboarding/components/onboarding-icon-button.tsx (2)

10-15: Add accessibility props and improve memoization.

The component could benefit from better accessibility support and memoization optimization.

Consider these improvements:

+import { useMemo } from "react";

 export const OnboardingIconButton = memo(function OnboardingIconButton(
-  props: IIconButtonProps
+  props: IIconButtonProps & { accessibilityLabel?: string }
 ) {
-  const { style, ...rest } = props;
+  const { style, accessibilityLabel, ...rest } = props;
+  const combinedStyle = useMemo(
+    () => [$defaultStyle, style],
+    [style]
+  );
-  return <IconButton style={[$defaultStyle, style]} {...rest} />;
+  return (
+    <IconButton
+      style={combinedStyle}
+      accessibilityRole="button"
+      accessibilityLabel={accessibilityLabel}
+      {...rest}
+    />
+  );
 });

6-8: Consider making default style configurable.

The hardcoded default style might not be suitable for all use cases.

Consider making it configurable:

+export const ONBOARDING_ICON_BUTTON_STYLES = {
+  default: {
+    alignSelf: "center",
+  },
+} as const;

-const $defaultStyle: ViewStyle = {
-  alignSelf: "center",
-};
+const $defaultStyle: ViewStyle = ONBOARDING_ICON_BUTTON_STYLES.default;
features/onboarding/components/onboarding-title.tsx (1)

5-7: Consider using theme spacing for text alignment.

For consistency with the theme system, consider defining the text alignment in a themed style function similar to other components in the PR.

-const $defaultStyle: TextStyle = {
+const $defaultStyle: ThemedStyle<TextStyle> = () => ({
   textAlign: "center",
-};
+});
theme/spacing.ts (1)

9-9: Consider documenting the use case for this spacing value.

While the value fits the progression, adding a comment about its intended use case would help maintain consistency across the application.

+  /** Used for fine-tuned spacing in onboarding components */
   "3xs": 3,
features/onboarding/components/onboarding-footer-text.tsx (1)

12-24: Add JSDoc documentation for the component and its props.

While the implementation is solid, adding documentation would improve maintainability.

+/**
+ * Renders footer text in onboarding screens with consistent styling and animations.
+ * @param props - Extended AnimatedText props for customization
+ */
export const OnboardingFooterText = memo(function OnboardingFooterText(
features/onboarding/components/onboarding-contact-card-provider.tsx (1)

8-27: LGTM! Clean implementation of theme context.

Good use of memoization and clear documentation. The implementation effectively handles theme inversion without prop drilling.

Consider adding prop-types validation for additional type safety in development:

+import PropTypes from 'prop-types';
 export const OnboardingContactCardThemeProvider = memo(
   function OnboardingContactCardThemeProvider({
     children,
   }: {
     children: React.ReactNode;
   }) {
     // ... implementation ...
   }
 );
+OnboardingContactCardThemeProvider.propTypes = {
+  children: PropTypes.node.isRequired,
+};
features/onboarding/components/onboarding-notification-row.tsx (1)

17-30: Extract styles to a separate styles object.

Consider moving all styles to a single styles object for better maintainability.

-const $switchContainer: ViewStyle = {
-  flex: 1,
-  justifyContent: "flex-end",
-};
-
-const $container: ThemedStyle<ViewStyle> = ({ spacing }) => ({
-  marginVertical: spacing.xs,
-  marginHorizontal: spacing.xxs,
-});
-
-const $flex2: ViewStyle = {
-  flex: 2,
-};

+const styles = {
+  switchContainer: {
+    flex: 1,
+    justifyContent: "flex-end",
+  } as ViewStyle,
+  
+  container: ({ spacing }: Theme) => ({
+    marginVertical: spacing.xs,
+    marginHorizontal: spacing.xxs,
+  }) as ThemedStyle<ViewStyle>,
+  
+  content: {
+    flex: 2,
+  } as ViewStyle,
+};
features/onboarding/hooks/useProfile.ts (2)

23-26: Consider using optional chaining for safer property access.

The current implementation could throw if currentUserUsername is undefined.

-  const usernameWithoutSuffix = currentUserUsername?.name?.replace(
-    config.usernameSuffix,
-    ""
-  );
+  const usernameWithoutSuffix = currentUserUsername?.name?.replace?.(
+    config.usernameSuffix,
+    ""
+  ) ?? "";

37-45: Consider memoizing the profile state initialization.

The profile state initialization logic could be memoized to prevent unnecessary recalculations.

+  const initialProfile = useMemo(() => ({
+    username: ephemeralAccount
+      ? defaultEphemeralUsername
+      : usernameWithoutSuffix || "",
+    avatar: currentUserUsername?.avatar || "",
+    displayName: ephemeralAccount
+      ? defaultEphemeralDisplayName
+      : currentUserUsername?.displayName || "",
+  }), [
+    ephemeralAccount,
+    defaultEphemeralUsername,
+    usernameWithoutSuffix,
+    currentUserUsername?.avatar,
+    defaultEphemeralDisplayName,
+    currentUserUsername?.displayName
+  ]);
-  const [profile, setProfile] = useState<ProfileType>({
-    username: ephemeralAccount
-      ? defaultEphemeralUsername
-      : usernameWithoutSuffix || "",
-    avatar: currentUserUsername?.avatar || "",
-    displayName: ephemeralAccount
-      ? defaultEphemeralDisplayName
-      : currentUserUsername?.displayName || "",
-  });
+  const [profile, setProfile] = useState<ProfileType>(initialProfile);
screens/NewAccount/NewAccountConnectWalletScreen.tsx (1)

Line range hint 29-34: Add error type definition and improve error handling.

The error handling could be more robust with proper error typing and user feedback.

-    const handleErrorConnecting = useCallback(
-      (arg: { error: Error }) => {
-        router.goBack();
-      },
+    const handleErrorConnecting = useCallback(
+      ({ error }: { error: Error }) => {
+        logger.error("[NewAccount] Wallet connection failed:", error);
+        router.goBack();
+        // Consider showing a user-friendly error message
+      },
features/onboarding/screens/OnboardingConnectWalletScreen.tsx (1)

Line range hint 36-41: Consider adding user feedback for connection errors.

While the error is logged, the user experience could be improved with visual feedback.

    const handleErrorConnecting = useCallback((arg: { error: Error }) => {
      logger.debug("[Onboarding] Error connecting wallet", arg.error);
+     // Consider showing a toast or alert to inform the user
      router.goBack();
-      // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, []);
+    }, [router]);
features/onboarding/hooks/useAddPfp.ts (1)

34-62: Consider adding loading state during media operations.

Media operations can take time. Adding a loading state would improve user experience.

+ const [isLoading, setIsLoading] = useState(false);
  const addPFP = useCallback(() => {
    const showOptions = () =>
      showActionSheetWithOptions(
        {
          options: [
            translate("userProfile.mediaOptions.takePhoto"),
            translate("userProfile.mediaOptions.chooseFromLibrary"),
            translate("userProfile.mediaOptions.cancel"),
          ],
          cancelButtonIndex: 2,
+         disabled: isLoading,
          ...actionSheetColors(colorScheme),
        },
        async (selectedIndex?: number) => {
+         setIsLoading(true);
          try {
            switch (selectedIndex) {
              case 0:
                await openCamera();
                break;
              case 1:
                await pickMedia();
                break;
              default:
                break;
            }
+         } finally {
+           setIsLoading(false);
+         }
        }
      );
    executeAfterKeyboardClosed(showOptions);
-  }, [colorScheme, openCamera, pickMedia]);
+  }, [colorScheme, openCamera, pickMedia, isLoading]);

- return { addPFP, asset };
+ return { addPFP, asset, isLoading };
features/onboarding/components/onboarding-footer.tsx (1)

41-46: Consider adding keyboard dismiss on scroll.

The keyboard handling is implemented well, but consider adding keyboard dismiss functionality when the user scrolls for better UX.

utils/str.ts (1)

44-46: Consider renaming the function and reducing code duplication.

  1. The term "Rando" seems informal for production code. Consider a more professional name like formatRandomDisplayName.
  2. This function shares logic with formatEphemeralDisplayName. Consider refactoring to reuse code.
-export const formatRandoDisplayName = (address: string) =>
-  humanize(address.slice(2, 42), 2, " ", true);
+export const formatRandomDisplayName = (address: string) =>
+  formatEphemeralDisplayName(address);
screens/NewAccount/NewAccountEphemeraScreen.tsx (1)

19-19: Improve error handling and user feedback.

While the error is tracked with Sentry, consider providing user feedback when wallet generation fails.

 } catch (error) {
   sentryTrackError(error);
+  // Show error toast or alert to inform user
+  Alert.alert(
+    translate("errors.title"),
+    translate("errors.walletGeneration")
+  );
 }

Also applies to: 38-38

features/onboarding/components/onboarding-contact-card.tsx (2)

63-65: Add loading state for PFP upload.

The avatar press handler should show a loading state during PFP upload.

+const [isUploading, setIsUploading] = useState(false);
+
+const handlePFPUpload = useCallback(async () => {
+  setIsUploading(true);
+  try {
+    await addPFP();
+  } finally {
+    setIsUploading(false);
+  }
+}, [addPFP]);

-<Pressable disabled={!editable} onPress={addPFP}>
+<Pressable disabled={!editable || isUploading} onPress={handlePFPUpload}>
   <OnboardingEditableAvatar 
     uri={pfpUri} 
     name={displayName}
+    isLoading={isUploading}
   />
 </Pressable>

51-62: Enhance accessibility support.

Add appropriate accessibility labels and roles to improve the component's usability for screen readers.

 <AnimatedVStack
+  accessible={true}
+  accessibilityRole="form"
+  accessibilityLabel={translate("onboarding.contactCard.accessibilityLabel")}
   style={{
     marginVertical: spacing.lg,
     padding: spacing.lg,
features/onboarding/hooks/useCreateOrUpdateProfileInfo.ts (2)

49-51: Remove debug logging in production code.

The debug log contains what appears to be a development identifier "[OnboardingContactCardScreen]5" which should be removed or made more descriptive.

-        logger.debug(
-          `[OnboardingContactCardScreen]5 ${profile.username.length} + ${profile.username}`
-        );
+        logger.debug(
+          `[useCreateOrUpdateProfileInfo] Invalid username length: ${profile.username.length}, username: ${profile.username}`
+        );

107-125: Improve error handling granularity.

The error handling is too generic. Different types of errors should be handled differently to provide better user feedback.

       try {
         await claimProfile({
           account: address,
           profile: { ...profile, avatar: publicAvatar },
         });
         await invalidateProfileSocialsQuery(address);
         return { success: true };
       } catch (e: any) {
         logger.error(e, { context: "UserProfile: claiming and refreshing" });
+        let errorMessage = "An unknown error occurred";
+        if (e?.response?.status === 409) {
+          errorMessage = "Profile already exists";
+        } else if (e?.response?.status === 413) {
+          errorMessage = "Profile data too large";
+        } else if (e?.response?.data?.message) {
+          errorMessage = e.response.data.message;
+        }
-        setErrorMessage(
-          e?.response?.data?.message || "An unknown error occurred"
-        );
+        setErrorMessage(errorMessage);
         setLoading(false);
         return {
           success: false,
-          errorMessage:
-            e?.response?.data?.message || "An unknown error occurred",
+          errorMessage,
         };
       }
screens/NewAccount/new-account-screen.tsx (1)

72-74: Remove or enhance debug logging.

The debug log for status change lacks meaningful context about what the status represents.

-      logger.debug("[NewAccountWelcomeScreenContent] onStatusChange", status);
+      logger.debug("[NewAccountWelcomeScreenContent] Connection status changed", {
+        status,
+        timestamp: new Date().toISOString(),
+      });
features/onboarding/screens/onboarding-notifications-screen.tsx (1)

100-107: Consider consolidating disabled notification rows.

The code has multiple OnboardingNotificationRow components with identical disabled states, which could be refactored to reduce duplication.

Consider creating a configuration array:

const NOTIFICATION_TYPES = [
  {
    title: "onboarding.notifications.mentionsOnly",
    description: "onboarding.notifications.mentionsOnlyDescription",
  },
  {
    title: "onboarding.notifications.cash",
    description: "onboarding.notifications.cashDescription",
  },
];

Then map over it:

{NOTIFICATION_TYPES.map(({ title, description }) => (
  <OnboardingNotificationRow
    key={title}
    title={translate(title)}
    description={translate(description)}
    disabled
    value={false}
    onToggle={() => {}}
  />
))}

Also applies to: 108-123

App.tsx (1)

176-176: Consider extracting inline styles to StyleSheet.

The inline style { flex: 1 } should be moved to the StyleSheet definition for better performance and maintainability.

const useStyles = () => {
  return useMemo(
    () =>
      StyleSheet.create({
        safe: {
          flex: 1,
        },
+       root: {
+         flex: 1,
+       },
      }),
    []
  );
};

Then update the component:

-<GestureHandlerRootView style={{ flex: 1 }}>
+<GestureHandlerRootView style={styles.root}>
🧰 Tools
🪛 GitHub Check: lint

[warning] 176-176:
Inline style: { flex: 1 }

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d3c926 and 3140e3a.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (81)
  • .github/workflows/build-app-release.yml (2 hunks)
  • .github/workflows/build-internal.yml (1 hunks)
  • App.tsx (3 hunks)
  • app.config.ts (1 hunks)
  • components/Avatar.tsx (2 hunks)
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx (1 hunks)
  • components/Onboarding/init-xmtp-client.ts (2 hunks)
  • components/TransactionPreview/TransactionPreview.tsx (2 hunks)
  • components/__tests__/Avatar.test.tsx (1 hunks)
  • config.ts (5 hunks)
  • design-system/Icon/Icon.android.tsx (1 hunks)
  • design-system/Icon/Icon.tsx (1 hunks)
  • design-system/Icon/Icon.types.ts (1 hunks)
  • design-system/IconButton/IconButton.props.ts (1 hunks)
  • design-system/IconButton/IconButton.styles.ts (3 hunks)
  • design-system/IconButton/IconButton.tsx (3 hunks)
  • design-system/IconButton/animated-icon-button.tsx (1 hunks)
  • design-system/Text/Text.styles.ts (1 hunks)
  • features/conversation-list/conversation-list.screen-header.tsx (3 hunks)
  • features/conversation/conversation-message/conversation-message-content-types/conversation-message-simple-text.tsx (1 hunks)
  • features/onboarding/Privy/usePrivySmartWalletConnection.ts (1 hunks)
  • features/onboarding/components/onboarding-contact-card-provider.tsx (1 hunks)
  • features/onboarding/components/onboarding-contact-card.tsx (1 hunks)
  • features/onboarding/components/onboarding-editable-avatar.tsx (1 hunks)
  • features/onboarding/components/onboarding-footer-text.tsx (1 hunks)
  • features/onboarding/components/onboarding-footer.tsx (1 hunks)
  • features/onboarding/components/onboarding-icon-button.tsx (1 hunks)
  • features/onboarding/components/onboarding-notification-row.tsx (1 hunks)
  • features/onboarding/components/onboarding-subtitle.tsx (1 hunks)
  • features/onboarding/components/onboarding-title.tsx (1 hunks)
  • features/onboarding/constants/animationConstants.ts (1 hunks)
  • features/onboarding/hooks/useAddPfp.ts (1 hunks)
  • features/onboarding/hooks/useCreateOrUpdateProfileInfo.ts (1 hunks)
  • features/onboarding/hooks/useProfile.ts (1 hunks)
  • features/onboarding/passkey/passkey.constants.ts (1 hunks)
  • features/onboarding/passkey/passkeyAuthStore.tsx (1 hunks)
  • features/onboarding/passkey/useCreatePasskey.ts (1 hunks)
  • features/onboarding/passkey/useLoginWithPasskey.ts (1 hunks)
  • features/onboarding/screens/OnboardingConnectWalletScreen.tsx (2 hunks)
  • features/onboarding/screens/OnboardingEphemeraScreen.tsx (2 hunks)
  • features/onboarding/screens/OnboardingPrivateKeyScreen.tsx (2 hunks)
  • features/onboarding/screens/OnboardingPrivyScreen.tsx (2 hunks)
  • features/onboarding/screens/onboarding-contact-card-screen.tsx (1 hunks)
  • features/onboarding/screens/onboarding-notifications-screen.tsx (1 hunks)
  • features/onboarding/screens/onboarding-weclome-screen.tsx (1 hunks)
  • features/onboarding/types/onboarding.types.ts (1 hunks)
  • features/onboarding/utils/formatRandomUserName.ts (1 hunks)
  • i18n/translations/en.ts (2 hunks)
  • i18n/translations/fr.ts (1 hunks)
  • ios/Converse/Converse.entitlements (1 hunks)
  • metro.config.js (2 hunks)
  • navigation/OnboardingNavigator.tsx (2 hunks)
  • package.json (6 hunks)
  • screens/Main.tsx (0 hunks)
  • screens/Navigation/Navigation.tsx (6 hunks)
  • screens/NewAccount/NewAccountConnectWalletScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountEphemeraScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountPrivateKeyScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountPrivyScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountScreen.tsx (0 hunks)
  • screens/NewAccount/NewAccountUserProfileScreen.tsx (0 hunks)
  • screens/NewAccount/new-account-contact-card-screen.tsx (1 hunks)
  • screens/NewAccount/new-account-screen.tsx (1 hunks)
  • screens/Onboarding/OnboardingGetStartedScreen.tsx (0 hunks)
  • screens/Onboarding/OnboardingNotificationsScreen.tsx (0 hunks)
  • screens/Onboarding/OnboardingUserProfileScreen.tsx (0 hunks)
  • screens/UserProfileScreen.tsx (3 hunks)
  • scripts/build/ios/preview.js (1 hunks)
  • scripts/build/ios/production.js (1 hunks)
  • theme/border-radius.ts (1 hunks)
  • theme/spacing.ts (1 hunks)
  • theme/timing.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • utils/api/profiles.ts (1 hunks)
  • utils/events.ts (1 hunks)
  • utils/getCapitalizedLettersForAvatar.ts (1 hunks)
  • utils/getFirstLetterForAvatar.ts (0 hunks)
  • utils/navigation.ts (1 hunks)
  • utils/str.ts (1 hunks)
  • utils/xmtpRN/signIn.ts (2 hunks)
  • utils/xmtpRN/signer.ts (2 hunks)
💤 Files with no reviewable changes (7)
  • screens/Main.tsx
  • utils/getFirstLetterForAvatar.ts
  • screens/NewAccount/NewAccountScreen.tsx
  • screens/Onboarding/OnboardingNotificationsScreen.tsx
  • screens/Onboarding/OnboardingGetStartedScreen.tsx
  • screens/NewAccount/NewAccountUserProfileScreen.tsx
  • screens/Onboarding/OnboardingUserProfileScreen.tsx
🚧 Files skipped from review as they are similar to previous changes (17)
  • tsconfig.json
  • utils/navigation.ts
  • scripts/build/ios/preview.js
  • ios/Converse/Converse.entitlements
  • features/onboarding/passkey/passkey.constants.ts
  • app.config.ts
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx
  • metro.config.js
  • utils/xmtpRN/signer.ts
  • scripts/build/ios/production.js
  • utils/events.ts
  • components/TransactionPreview/TransactionPreview.tsx
  • .github/workflows/build-internal.yml
  • features/onboarding/passkey/useCreatePasskey.ts
  • features/onboarding/passkey/useLoginWithPasskey.ts
  • screens/Navigation/Navigation.tsx
  • package.json
🧰 Additional context used
🪛 GitHub Check: lint
App.tsx

[warning] 176-176:
Inline style: { flex: 1 }

config.ts

[warning] 39-39:
Prefer named exports

🪛 GitHub Check: tsc
features/onboarding/Privy/usePrivySmartWalletConnection.ts

[failure] 84-84:
Property 'defaultChain' does not exist on type '{ appId: string; clientId: string; } | { readonly appId: string; readonly clientId: string; }'.

🪛 GitHub Actions: Typescript CI
features/onboarding/Privy/usePrivySmartWalletConnection.ts

[error] 84-84: Property 'defaultChain' does not exist on type '{ appId: string; clientId: string; } | { readonly appId: string; readonly clientId: string; }'

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (42)
features/onboarding/components/onboarding-subtitle.tsx (2)

5-7: Well-structured style implementation!

Good practices observed:

  • Using $ prefix for style constants
  • Proper TypeScript typing with TextStyle
  • Clear, single-responsibility style object

1-14: Overall excellent implementation!

The component is well-structured, properly typed, and follows React best practices. The use of memo for performance optimization and the clean separation of styles demonstrate good engineering practices.

features/onboarding/screens/OnboardingPrivyScreen.tsx (2)

6-19: LGTM! Import paths properly reorganized.

The import path updates reflect a better organized directory structure with clear separation of components, features, and utilities.


37-37: Verify navigation flow consistency across onboarding screens.

The navigation target has been updated to "OnboardingContactCard". Let's ensure this change is consistent with other onboarding screens.

✅ Verification successful

Navigation flow is consistent across onboarding screens

All onboarding screens correctly navigate to "OnboardingContactCard" when the Converse profile is missing.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check navigation targets in other onboarding screens
# Expected: All screens should navigate to "OnboardingContactCard" when profile is missing

# Search for navigation patterns in onboarding screens
rg -A 2 'isMissingConverseProfile\(\).*\{' features/onboarding/screens/

Length of output: 1366

utils/xmtpRN/signIn.ts (1)

12-17: LGTM! Imports are properly organized.

The new imports for Viem integration are correctly added and organized.

components/Onboarding/init-xmtp-client.ts (2)

9-20: LGTM! Imports are properly organized.

The imports have been updated to use absolute paths and include necessary new functionality.


73-91: LGTM! Proper error handling and cleanup.

The implementation maintains security best practices with:

  • Proper validation of required parameters
  • Comprehensive error handling
  • Cleanup on failure using logoutAccount
features/onboarding/screens/OnboardingPrivateKeyScreen.tsx (1)

43-43: Navigation flow update looks good.

The navigation to "OnboardingContactCard" aligns with the PR's objective of updating the onboarding workflow. The change is properly integrated into the existing conditional flow and includes error handling.

features/onboarding/screens/OnboardingEphemeraScreen.tsx (1)

41-43: Verify navigation consistency across onboarding screens.

Let's ensure that the navigation flow changes are consistent across all onboarding-related screens.

✅ Verification successful

Navigation changes are consistent across onboarding screens

All onboarding screens consistently use the centralized isMissingConverseProfile check and navigate to "OnboardingContactCard" when needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check navigation patterns in onboarding screens
# Expected: All relevant screens should navigate to "OnboardingContactCard" when profile is missing

# Search for navigation patterns in onboarding screens
echo "Checking navigation patterns in onboarding screens..."
rg -A 2 'router\.navigate.*OnboardingContactCard|router\.navigate.*OnboardingUserProfile' 'features/onboarding'

# Search for isMissingConverseProfile usage
echo "Checking profile check usage..."
rg 'isMissingConverseProfile' 'features/onboarding'

Length of output: 2513

design-system/IconButton/animated-icon-button.tsx (1)

1-8: Clean implementation of animated button wrapper!

The implementation is well-structured, properly typed, and follows the single responsibility principle by cleanly wrapping the existing IconButton with animation capabilities.

design-system/IconButton/IconButton.props.ts (1)

10-10: LGTM! Clean type extension.

The addition of the "xl" size option maintains consistency with the existing type pattern.

design-system/IconButton/IconButton.tsx (1)

125-125: LGTM! Proper ref forwarding to Icon component.

The ref is correctly forwarded to the Icon component.

design-system/IconButton/IconButton.styles.ts (1)

138-140: Review xl icon size scaling.

The xl size is using the same fontSize as lg (spacing.lg). Consider if xl icons should scale larger for better visual hierarchy.

features/onboarding/constants/animationConstants.ts (1)

1-9: Verify animation timing in the onboarding flow.

Since this is part of the Privy Passkeys feature, ensure these animation timings provide a smooth user experience during the authentication flow.

Let's check for other animation-related constants that might need coordination:

✅ Verification successful

Animation timing implementation is consistent and well-structured

The animation constants are properly implemented and consistently used across the onboarding flow. The sequential delays (200ms, 250ms, 300ms) create a smooth cascading effect, while the consistent duration ensures visual harmony throughout the authentication experience.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other animation timing constants in the onboarding feature
rg -t typescript "timing|duration|delay" features/onboarding/

Length of output: 98


Script:

#!/bin/bash
# Search for animation-related constants and timing usage
rg -t ts -t tsx "timing\[|duration|delay|animation" features/

# Find files importing these constants
rg -t ts -t tsx "from.*animationConstants"

# Check usage of timing object across the codebase
rg -t ts -t tsx "timing\[.+\]"

Length of output: 225


Script:

#!/bin/bash
# Search for animation-related constants and timing usage without file type restrictions
rg "timing\[|ONBOARDING_ENTERING" 

# Find animation-related patterns
rg "duration|delay|animation" features/onboarding/

# Find imports of timing object
rg "import.*timing.*from.*theme"

Length of output: 7764

features/conversation/conversation-message/conversation-message-content-types/conversation-message-simple-text.tsx (1)

36-36: LGTM! Correctly updated to use the new text size.

The change maintains the original visual appearance while adopting the new naming convention.

features/onboarding/passkey/passkeyAuthStore.tsx (4)

4-22: Well-structured type definitions!

The type definitions provide good type safety and follow TypeScript best practices. The separation between props and state is clean and logical.


50-51: Improve error handling in setLoading

The setLoading implementation clears errors when loading is true but not when it's false. This asymmetric error handling could lead to stale error states.

    setLoading: (loading) =>
-      loading ? set({ loading, error: undefined }) : set({ loading: false }),
+      set({ loading, error: undefined }),

60-76: Well-implemented hooks with proper error handling!

The hooks follow React best practices, provide descriptive error messages, and maintain type safety.


52-53: Review the automatic clearing of statusString in setError.

The current implementation of setError automatically clears statusString. Consider whether this is always the desired behavior, as there might be cases where you want to preserve the status while showing an error.

✅ Verification successful

The automatic clearing of statusString in setError is the correct behavior

The current implementation ensures clean state transitions by preventing the simultaneous display of both error and status messages. This is particularly important in authentication flows where showing outdated status messages alongside errors could be misleading to users.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of setError to verify if clearing statusString is always appropriate
rg -A 5 "setError" --type typescript

Length of output: 71


Script:

#!/bin/bash
# Search for setError in TypeScript and TypeScript React files
rg "setError" -t ts -t tsx -A 5

# Search for statusString to understand its usage patterns
rg "statusString" -t ts -t tsx -A 5

# Use ast-grep to find the store's structure
ast-grep --pattern 'create<$_>($_)({
  $$$
  setError: $_,
  $$$
})'

Length of output: 194


Script:

#!/bin/bash
# Find relevant files first
fd "passkey.*\.(ts|tsx)$"

# Search for setError and statusString usage
rg "setError" -B 2 -A 2

# Search for statusString patterns
rg "statusString" -B 2 -A 2

# Search for store creation pattern
rg "create.*\({" -B 1 -A 10

Length of output: 11244

design-system/Icon/Icon.types.ts (1)

68-69: LGTM! New icons align with passkey feature.

The addition of "biometric" and "camera" icons aligns well with the PR objectives for implementing Privy passkey login support.

design-system/Icon/Icon.android.tsx (1)

74-75: LGTM! Platform-appropriate icon mappings.

The mappings are well-chosen:

  • Using "fingerprint" for biometric is appropriate for Android
  • Using "camera" maintains consistency with the icon name
features/onboarding/components/onboarding-title.tsx (1)

9-21: LGTM! Well-structured component with good practices.

The component follows React best practices with proper memoization, type safety, and clean composition.

theme/border-radius.ts (1)

6-6: LGTM! Consistent with existing theme pattern.

The new lg size follows the established progression and maintains type safety.

features/onboarding/components/onboarding-footer-text.tsx (1)

6-10: LGTM! Well-structured themed styles.

The themed style implementation properly uses the spacing system and maintains consistency.

screens/NewAccount/NewAccountPrivyScreen.tsx (2)

16-16: LGTM! Import path update aligns with feature reorganization.

The import path change for isMissingConverseProfile reflects proper organization of onboarding-related features.


34-34: Verify navigation flow change impact.

The navigation target has been changed from UserProfile to ContactCard. This appears to be part of a larger refactor of the onboarding flow.

Let's verify the navigation flow changes across the codebase:

✅ Verification successful

Navigation flow changes are consistent

The navigation target change from UserProfile to ContactCard has been properly implemented across all relevant screens. The old UserProfile name only remains in an internal function name which doesn't affect the navigation flow.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other navigation changes to ensure consistency
rg -A 2 "router\.navigate\(\"NewAccountContactCard\"\)"

# Search for any remaining references to the old navigation target
rg "NewAccountUserProfile"

Length of output: 1164


Script:

#!/bin/bash
# Check the contact card screen implementation
cat screens/NewAccount/new-account-contact-card-screen.tsx

# Search for any navigation calls to UserProfile
rg "router\.navigate\(\"NewAccountUserProfile\"\)"

Length of output: 9036

components/Avatar.tsx (1)

5-5: LGTM! Enhanced avatar initials display.

The switch to getCapitalizedLettersForAvatar improves user representation by showing multiple initials instead of just the first letter.

Also applies to: 25-25

features/onboarding/components/onboarding-footer.tsx (1)

36-39: Remove debug menu trigger before production.

The comment "don't anticipate this will be here long" suggests this is temporary debug code. Consider removing the debug menu trigger before merging to production.

Let's check for other debug-related code:

screens/NewAccount/NewAccountPrivateKeyScreen.tsx (2)

11-15: LGTM! Import reorganization aligns with feature structure.

The import path changes properly organize onboarding-related features and screens.


32-32: Navigation change consistent with new flow.

The navigation target change to ContactCard aligns with the changes in NewAccountPrivyScreen.tsx, maintaining consistency in the onboarding flow.

utils/api/profiles.ts (1)

8-8: Verify consistent usage of ProfileType import across the codebase.

The import path change reflects a code reorganization moving types to the features directory. Let's ensure all files using ProfileType are updated.

✅ Verification successful

Import path change for ProfileType is consistently applied

All files importing ProfileType are using the new path @/features/onboarding/types/onboarding.types, and the type is properly defined in its new location. No remaining references to the old import path were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining imports from the old path
rg "from.*@/screens/Onboarding/OnboardingUserProfileScreen.*ProfileType"

# Search for consistency in new imports
rg "from.*@/features/onboarding/types/onboarding\.types.*ProfileType"

Length of output: 146


Script:

#!/bin/bash
# Find all occurrences of ProfileType
rg "ProfileType" -A 2

# Find type definition
ast-grep --pattern 'type ProfileType = {
  $$$
}'

# Find interface definition (alternative syntax)
ast-grep --pattern 'interface ProfileType {
  $$$
}'

Length of output: 3420

features/onboarding/hooks/useCreateOrUpdateProfileInfo.ts (1)

76-105: 🛠️ Refactor suggestion

Enhance avatar handling with type checking and size limits.

The avatar handling logic needs additional validation:

  1. No type checking for the avatar input
  2. No file size limits
  3. No error handling for the compression process
       let publicAvatar = "";
       if (profile.avatar) {
+        const MAX_AVATAR_SIZE = 5 * 1024 * 1024; // 5MB
+        if (profile.avatar instanceof File && profile.avatar.size > MAX_AVATAR_SIZE) {
+          setErrorMessage(translate("userProfile.errors.avatarTooLarge"));
+          return {
+            success: false,
+            errorMessage: translate("userProfile.errors.avatarTooLarge"),
+          };
+        }
         if (profile.avatar.startsWith("https://")) {
           publicAvatar = profile.avatar;
         } else {
-          const resizedImage = await compressAndResizeImage(
-            profile.avatar,
-            true
-          );
+          try {
+            const resizedImage = await compressAndResizeImage(
+              profile.avatar,
+              true
+            );
+            if (!resizedImage?.uri) {
+              throw new Error("Failed to compress image");
+            }
             publicAvatar = await uploadFile({
               account: address,
               filePath: resizedImage.uri,
               contentType: "image/jpeg",
             });
+          } catch (e: any) {
+            logger.error(e, { context: "UserProfile: compressing profile pic" });
+            setErrorMessage("Failed to process image");
+            setLoading(false);
+            return {
+              success: false,
+              errorMessage: "Failed to process image",
+            };
+          }
features/conversation-list/conversation-list.screen-header.tsx (1)

84-84: Verify avatar loading performance.

The usePreferredAvatarUri hook is called in the render cycle. Consider implementing loading states and error handling for avatar loading failures.

-  const avatarUri = usePreferredAvatarUri(currentAccount!);
+  const { uri: avatarUri, loading: avatarLoading, error: avatarError } = usePreferredAvatarUri(currentAccount!);

   // In the Avatar component:
   <Avatar
     name={preferredName}
     uri={avatarUri}
     size={theme.avatarSize.sm}
+    loading={avatarLoading}
+    onError={() => logger.warn("Failed to load avatar", { account: currentAccount })}
   />

Also applies to: 116-120

screens/NewAccount/new-account-contact-card-screen.tsx (1)

127-129: Inconsistent navigation logic between real and rando flows.

The navigation logic differs between real and rando profiles:

  • Real profiles: Checks for notifications permissions
  • Rando profiles: Always navigates to "Chats"

This inconsistency might lead to a confusing user experience.

Let's verify if this is intentional:

Also applies to: 146-152

✅ Verification successful

Navigation logic difference is intentional

The different navigation flows are by design:

  • Real profiles require proper onboarding including notification setup as part of the user experience
  • Rando profiles are meant to be lightweight temporary accounts with streamlined creation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for similar navigation patterns
rg -A 5 'navigation\.popTo\("Chats"\)' 
rg -A 5 'needToShowNotificationsPermissions'

Length of output: 9453

i18n/translations/fr.ts (1)

3-42: LGTM! Well-structured and complete French translations.

The translations are well-organized, maintain consistency with English translations, and follow proper French grammar and punctuation.

config.ts (1)

Line range hint 42-44: Resolve chain ID inconsistency.

The default config uses Base Sepolia (0x14a34) while the prod config uses Base Mainnet (0x2105). This inconsistency could lead to confusion and potential issues.

Run this script to check for any remaining references to Base Sepolia:

Also applies to: 122-124

🧰 Tools
🪛 GitHub Check: lint

[warning] 39-39:
Prefer named exports

.github/workflows/build-app-release.yml (2)

Line range hint 9-13: LGTM! Removed scheduled triggers to match current release schedule.

The removal of scheduled triggers aligns with the PR objectives to update builds.


53-53: LGTM! Properly configured Privy client ID for both platforms.

The environment variable EXPO_PUBLIC_PRIVY_CLIENT_ID is correctly added to both Android and iOS builds, using GitHub's secure secrets mechanism.

Also applies to: 101-101

App.tsx (2)

13-13: LGTM! Added smart wallet support and updated theme imports.

The changes properly integrate Privy's smart wallet functionality and update theme imports to fix the crash mentioned in the PR objectives.

Also applies to: 19-19


164-187: LGTM! Properly configured providers with correct nesting order.

The providers are correctly configured and nested in a logical order:

  1. PrivyProvider with required clientId and supportedChains
  2. SmartWalletsProvider for smart wallet support
  3. Remaining providers maintaining their original order
🧰 Tools
🪛 GitHub Check: lint

[warning] 176-176:
Inline style: { flex: 1 }

i18n/translations/en.ts (2)

3-39: LGTM! Well-structured onboarding translations.

The new onboarding section is comprehensive and well-organized, with clear and user-friendly messages for:

  • Welcome screen
  • Contact card setup
  • Notification preferences

144-148: LGTM! Added required passkey translations.

The passkey section includes all necessary strings for the new Privy passkey login feature.

Comment on lines 182 to 225
(theme: Theme) => // TODO: fix once we fixed IconProps
// : Partial<IIconProps>
{
const { colors, spacing } = theme;

if (action === "primary") {
switch (variant) {
case "fill":
props.color = colors.text.inverted.primary;
break;

case "outline":
case "subtle":
case "ghost":
props.color = colors.text.primary;
break;

default:
break;
}
}
const props: any =
// :Partial<IIconProps>
{};

return props;
// Set icon size
const sizeMap = {
sm: spacing.xs,
md: spacing.sm,
lg: spacing.lg,
xl: spacing.lg,
};

props.size = sizeMap[size];

if (disabled && variant !== "fill") {
props.color = colors.text.tertiary;
return props;
}

if (action === "primary") {
switch (variant) {
case "fill":
props.color = colors.text.inverted.primary;
break;

case "outline":
case "subtle":
case "ghost":
props.color = colors.text.primary;
break;

default:
break;
}
}

return props;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Address TODO comments and improve typing.

The implementation has multiple TODO comments and uses any type. Consider:

  1. Fixing the IconProps type as mentioned in the TODO
  2. Removing the commented-out type annotations
  3. Replacing any with proper typing
-  (theme: Theme) => // TODO: fix once we fixed IconProps
-  // : Partial<IIconProps>
+  (theme: Theme): IconProps => 
   {
     const { colors, spacing } = theme;
 
-    const props: any =
-      // :Partial<IIconProps>
-      {};
+    const props: Partial<IconProps> = {};

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +7 to +8
["3xl"]: { fontSize: 48, lineHeight: 60 } satisfies TextStyle,
xxl: { fontSize: 40, lineHeight: 44 } satisfies TextStyle,
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Update onboarding title component to use new size

The text size changes will affect the onboarding title component. Since "xxl" is now smaller (40/44 instead of 48/60), you should update features/onboarding/components/onboarding-title.tsx to use the new "3xl" size if you want to maintain the current visual dimensions.

🔗 Analysis chain

Verify all existing "xxl" usages across the codebase.

The text size changes look good and follow common design system practices. However, since "xxl" now has different dimensions (40/44 instead of 48/60), we should ensure all existing usages are updated appropriately.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any remaining usages of textSizeStyles.xxl that might need updating
rg -l 'textSizeStyles\.xxl|textSizeStyles\[.xxl.\]' --type typescript

Length of output: 106


Script:

#!/bin/bash
# Search for "xxl" text size usages in TypeScript and JavaScript files
rg -t ts -t tsx -t js -t jsx -A 2 '(textSizeStyles\.xxl|textSizeStyles\[.xxl.\]|size=["\']xxl["\']|size:\s*["\']xxl["\'])'

Length of output: 116


Script:

#!/bin/bash
# Search for direct property access of xxl
echo "=== Searching for textSizeStyles.xxl ==="
rg -t ts -t tsx -t js -t jsx 'textSizeStyles\.xxl'

echo -e "\n=== Searching for bracket notation access of xxl ==="
rg -t ts -t tsx -t js -t jsx 'textSizeStyles\[.(xxl|"xxl"|'\''xxl'\'').\]'

echo -e "\n=== Searching for size prop with xxl ==="
rg -t ts -t tsx -t js -t jsx 'size=["\x27]xxl["\x27]'

Length of output: 578


Script:

#!/bin/bash
# Find all TypeScript/JavaScript files and search for xxl usages
fd -e ts -e tsx -e js -e jsx | xargs rg -l '(textSizeStyles\.xxl|textSizeStyles\[.xxl.\]|size=["\x27]xxl["\x27]|size:\s*["\x27]xxl["\x27])'

Length of output: 193


Script:

#!/bin/bash
# View the usage context of xxl in the onboarding title component
rg -A 3 -B 3 '(textSizeStyles\.xxl|textSizeStyles\[.xxl.\]|size=["\x27]xxl["\x27]|size:\s*["\x27]xxl["\x27])' features/onboarding/components/onboarding-title.tsx

Length of output: 293

design-system/Icon/Icon.types.ts Show resolved Hide resolved
Comment on lines 3 to 9
export const formatRandomUserName = (displayName: string) => {
const randomUserGuid = v4().replace(/-/g, "").slice(0, 30);
const profileUserName = (
displayName?.replace(/\s+/g, "") + randomUserGuid
).slice(0, 30);
return profileUserName;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and handle edge cases.

The function needs defensive programming to handle edge cases:

  • displayName could be undefined, null, or empty string
  • Current implementation might generate usernames shorter than expected if displayName is empty

Consider this implementation:

 export const formatRandomUserName = (displayName: string) => {
+  if (!displayName?.trim()) {
+    const randomId = v4().replace(/-/g, "").slice(0, 30);
+    return `user${randomId}`.slice(0, 30);
+  }
   const randomUserGuid = v4().replace(/-/g, "").slice(0, 30);
   const profileUserName = (
     displayName?.replace(/\s+/g, "") + randomUserGuid
   ).slice(0, 30);
   return profileUserName;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const formatRandomUserName = (displayName: string) => {
const randomUserGuid = v4().replace(/-/g, "").slice(0, 30);
const profileUserName = (
displayName?.replace(/\s+/g, "") + randomUserGuid
).slice(0, 30);
return profileUserName;
};
export const formatRandomUserName = (displayName: string) => {
if (!displayName?.trim()) {
const randomId = v4().replace(/-/g, "").slice(0, 30);
return `user${randomId}`.slice(0, 30);
}
const randomUserGuid = v4().replace(/-/g, "").slice(0, 30);
const profileUserName = (
displayName?.replace(/\s+/g, "") + randomUserGuid
).slice(0, 30);
return profileUserName;
};

utils/getCapitalizedLettersForAvatar.ts Outdated Show resolved Hide resolved
Comment on lines +107 to +106
const randomUsername = uuidv4().replace(/-/g, "").slice(0, 30);
logger.debug(
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance username generation security and error handling.

The random username generation using UUID could be improved:

  1. Using UUID for usernames might create unnecessarily long strings
  2. The error handling in handleRandoContinue could be more specific

Consider this implementation:

+const generateRandomUsername = (length: number = 12): string => {
+  return Math.random()
+    .toString(36)
+    .substring(2, 2 + length);
+};

 const handleRandoContinue = useCallback(async () => {
   try {
-    const randomUsername = uuidv4().replace(/-/g, "").slice(0, 30);
+    const randomUsername = generateRandomUsername();
     logger.debug(
       "[OnboardingContactCardScreen] handleRandoContinue",
       randoDisplayName
     );
     const { success } = await createOrUpdateProfile({
       profile: {
         displayName: randoDisplayName,
         username: randomUsername,
       },
     });
     // ... rest of the code
   } catch (error) {
-    handleError(error as Error);
+    if (error instanceof Error) {
+      handleError(error);
+    } else {
+      handleError(new Error('Unknown error during profile creation'));
+    }
   }
 }, [randoDisplayName, createOrUpdateProfile, navigation, handleError]);

Also applies to: 110-132

Comment on lines 72 to 246
} catch (error) {
handleError(error as Error);
}
}, [createOrUpdateProfile, profile, router, handleError, asset?.uri]);

const handleContinue = useCallback(() => {
logger.debug("[NewAccountContactCardScreen] handleContinue", type);
if (type === "real") {
handleRealContinue();
} else {
handleRandoContinue();
}
}, [type, handleRealContinue, handleRandoContinue]);

return (
<Screen
preset="scroll"
contentContainerStyle={$flex1}
safeAreaEdges={["bottom"]}
>
<Center style={$centerContainerStyle}>
<VStack style={$flex1}>
{type === "real" ? (
<OnboardingTitle entering={titleAnimation} size={"xl"}>
{translate("onboarding.contactCard.title")}
</OnboardingTitle>
) : (
<OnboardingTitle entering={titleAnimation} size={"xl"}>
{translate("onboarding.contactCard.randoTitle")}
</OnboardingTitle>
)}
{type === "real" ? (
<OnboardingSubtitle
style={themed($subtitleStyle)}
entering={subtitleAnimation}
>
{translate("onboarding.contactCard.subtitle")}
</OnboardingSubtitle>
) : (
<OnboardingSubtitle
style={themed($subtitleStyle)}
entering={subtitleAnimation}
>
{translate("onboarding.contactCard.randoSubtitle")}
</OnboardingSubtitle>
)}
<OnboardingContactCardThemeProvider>
{type === "real" ? (
<OnboardingContactCard
addPFP={addPFP}
pfpUri={asset?.uri}
displayName={profile.displayName}
setDisplayName={(displayName) =>
setProfile({ ...profile, displayName })
}
/>
) : (
<OnboardingContactCard
editable={false}
addPFP={() => {}}
pfpUri={undefined}
displayName={randoDisplayName}
setDisplayName={() => {}}
/>
)}
</OnboardingContactCardThemeProvider>

<AnimatedVStack
entering={animation
.fadeInDownSlow()
.delay(ONBOARDING_ENTERING_DELAY.THIRD)
.duration(ONBOARDING_ENTERING_DURATION)}
>
<AnimatedText style={$subtextStyle} color={"secondary"}>
{type === "real"
? translate("onboarding.contactCard.body")
: translate("onboarding.contactCard.randoBody")}
</AnimatedText>
<Pressable onPress={toggleType}>
<AnimatedText
style={$subtextPressableStyle}
color={"secondary"}
>
{type === "real"
? translate("onboarding.contactCard.bodyPressable")
: translate("onboarding.contactCard.randoPressable")}
</AnimatedText>
</Pressable>
</AnimatedVStack>
</VStack>
</Center>
<OnboardingFooter
text={translate("onboarding.contactCard.continue")}
iconName="chevron.right"
onPress={handleContinue}
disabled={loading || (type === "real" && !profile.displayName)}
/>
</Screen>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider extracting shared logic to avoid code duplication.

This component shares significant code with OnboardingContactCardScreen. Consider creating a shared hook or component to reduce duplication.

Create a custom hook:

function useContactCardLogic({
  onSuccess,
  navigation,
}: {
  onSuccess: (type: "real" | "rando") => void;
  navigation: any;
}) {
  const [type, setType] = useState<"real" | "rando">("real");
  // ... shared state and handlers

  return {
    type,
    toggleType,
    handleContinue: () => {
      if (type === "real") {
        handleRealContinue();
      } else {
        handleRandoContinue();
      }
    },
    // ... other shared logic
  };
}

Then use it in both components:

function NewAccountContactCardScreen() {
  const logic = useContactCardLogic({
    onSuccess: (type) => {
      if (type === "real") {
        // Navigate to notifications
      } else {
        navigation.popTo("Chats");
      }
    },
    navigation,
  });
  // ... render UI using logic
}

navigation/OnboardingNavigator.tsx Outdated Show resolved Hide resolved
features/onboarding/Privy/usePrivySmartWalletConnection.ts Outdated Show resolved Hide resolved
config.ts Show resolved Hide resolved
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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54a00fa and b7a553e.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • app.json (1 hunks)
  • package.json (8 hunks)
  • polyfills.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • app.json
  • package.json
🔇 Additional comments (1)
polyfills.ts (1)

1-4: Verify if the import order is correct for Privy integration.

The comment states "DO NOT CHANGE THE ORDER OF THESE IMPORTS" and the new import has been placed in the first group. We should verify if this placement is correct for Privy Passkeys functionality.

Let's check Privy's documentation and usage patterns:

✅ Verification successful

The import order is correct for Privy integration

The current order is appropriate as WalletConnect's compatibility layer should be loaded first, followed by more generic polyfills like fast-text-encoding. This ordering ensures core functionality is available before other polyfills are applied.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Privy-related polyfill requirements
rg -i "privy.*polyfill|polyfill.*privy" -g "*.md" -g "package.json"

# Look for any initialization or setup patterns in Privy-related code
ast-grep --pattern 'import { Privy $_ } from "$_"'

Length of output: 120


Script:

#!/bin/bash
# Search for WalletConnect polyfill requirements
rg -i "walletconnect.*polyfill|polyfill.*walletconnect" -g "*.md" -g "*.ts" -g "*.js"

# Look for any React Native polyfill patterns
rg -i "react.?native.*polyfill|polyfill.*react.?native" -g "*.md" -g "*.ts" -g "*.js"

# Search for any setup or initialization files
fd -e ts -e js -e md -g "*setup*" -g "*init*" -g "*config*"

# Check package.json for related dependencies
cat package.json | jq '.dependencies, .devDependencies'

Length of output: 8477

polyfills.ts Outdated Show resolved Hide resolved
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)
App.tsx (3)

8-10: Document Privy's development requirements more formally.

The comment indicates a specific requirement for Privy to work, but it's not clear why this is needed or what the implications are. Consider:

  1. Adding this information to the project's README or development documentation
  2. Explaining why --no-dev --minify flags are required
  3. Documenting any potential impact on the development workflow

Would you like me to help create a documentation section for Privy integration requirements?


171-171: Consider future chain support requirements.

Currently, only the Base chain is supported (supportedChains={[base]}). Consider:

  1. Whether other chains will be needed in the future
  2. Making the supported chains list configurable through environment variables
  3. Adding documentation about supported chains

Would you like me to propose a more flexible chain configuration approach?


173-189: Document provider hierarchy dependencies.

The nesting order of providers (PrivyProvider > SmartWalletsProvider > ThirdwebProvider) is crucial for proper functionality. Consider adding documentation that explains:

  1. Why this specific order is required
  2. Dependencies between providers
  3. Potential issues if the order is changed

Would you like me to help create a provider architecture documentation section?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7a553e and 5b5111d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • App.tsx (3 hunks)
  • app.json (1 hunks)
  • package.json (7 hunks)
  • polyfills.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • app.json
  • polyfills.ts
  • package.json
🔇 Additional comments (1)
App.tsx (1)

167-172: Verify secure handling of Privy credentials.

The clientId is being passed through config. Ensure that:

  1. The environment variable PRIVY_CLIENT_ID is properly set in CI/CD
  2. The value is not exposed in client-side logs
  3. Error handling exists for missing credentials

Let's verify the environment variable handling:

✅ Verification successful

Privy credentials are properly handled

The implementation follows secure patterns:

  • Uses Expo's public environment variables convention
  • Configured correctly in CI/CD workflows
  • No unsafe logging detected
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if PRIVY_CLIENT_ID is properly configured in workflow files
rg -l "PRIVY_CLIENT_ID" .github/workflows/
# Check for potential credential logging
rg -i "privy.*client.*id" -g "!*.md" -g "!*.env*"

Length of output: 352

@alexrisch alexrisch force-pushed the ar/privy-passkey branch 2 times, most recently from e75436c to b2c158f Compare January 17, 2025 17:51
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: 4

🔭 Outside diff range comments (1)
navigation/OnboardingNavigator.tsx (1)

Line range hint 15-28: Remove unused screen type from OnboardingParamList.

The OnboardingGetStarted screen type is defined but its corresponding screen component has been removed from the navigator. This could lead to runtime errors if navigation to this screen is attempted.

Apply this diff to remove the unused type:

type OnboardingParamList = {
  OnboardingWelcome: undefined;
  OnboardingContactCard: undefined;

-  OnboardingGetStarted: undefined;
  OnboardingPrivy: undefined;
  OnboardingConnectWallet: {
    address: string;
  };
  OnboardingNotifications: undefined;
  OnboardingPrivateKey: undefined;
  OnboardingEphemeral: undefined;
};
♻️ Duplicate comments (2)
utils/xmtpRN/signIn.ts (1)

74-121: 🛠️ Refactor suggestion

Reduce code duplication with existing client creation function.

The implementation is correct but duplicates most of the logic from createXmtpClientFromSigner. A previous review already suggested extracting the common initialization logic into a shared function.

Please refer to the previous review comment for the suggested refactoring approach that extracts the common initialization logic into a shared function.

components/Onboarding/init-xmtp-client.ts (1)

55-91: 🛠️ Refactor suggestion

Reduce code duplication in XMTP client initialization.

The implementation duplicates significant logic from initXmtpClient. A previous review already suggested extracting the common initialization logic.

Please refer to the previous review comment for the suggested refactoring approach that extracts the common initialization logic into a shared function.

🧹 Nitpick comments (24)
i18n/translations/fr.ts (2)

14-15: Consider combining split text for better maintainability.

The bodyPressable text is unnecessarily split across two lines. Consider combining them into a single line:

-      bodyPressable:
-        "ou optez pour le mode Rando pour plus de confidentialité.",
+      bodyPressable: "ou optez pour le mode Rando pour plus de confidentialité.",

31-32: Consider using more formal language for consistency.

The phrase "Me notifier" is more casual. Consider using a more formal alternative that matches the tone of the rest of the UI:

-        "Me notifier lorsque mes contacts approuvés m'envoient des messages",
+        "Être notifié(e) lorsque mes contacts approuvés m'envoient des messages",
utils/xmtpRN/signIn.ts (1)

91-91: Update log message to reflect Viem account usage.

The log message incorrectly states "from signer" when it's actually creating from a Viem account.

-  logger.debug("Instantiating client from signer");
+  logger.debug("Instantiating client from Viem account");
components/Onboarding/init-xmtp-client.ts (1)

63-65: Fix error message to reference Viem account.

The error message incorrectly refers to a signer instead of a Viem account.

-    throw new Error("No signer");
+    throw new Error("No Viem account provided");
design-system/IconButton/IconButton.styles.ts (2)

138-140: Maintain size progression for "xl" icons.

The "xl" size uses the same fontSize (spacing.lg) as "md" size, breaking the size progression pattern. Consider using a larger fontSize for "xl" to maintain visual hierarchy.

   xl: {
-    fontSize: spacing.lg,
+    fontSize: spacing.xxl,
   },

192-197: Maintain size progression for icon size mapping.

The "xl" size uses the same value (spacing.lg) as "lg" size in the size mapping. This breaks the visual progression of icon sizes.

   const sizeMap = {
     sm: spacing.xs,
     md: spacing.sm,
     lg: spacing.lg,
-    xl: spacing.lg,
+    xl: spacing.xl,
   };
features/onboarding/components/onboarding-editable-avatar.tsx (2)

13-19: Consider using theme border radius constant.

The style implementation looks good, but consider using a theme constant for the border radius instead of a magic number.

 const $placeholderStyle: ThemedStyle<ViewStyle> = (theme) => ({
-  borderRadius: 9999,
+  borderRadius: theme.borderRadius.full, // or similar theme constant
   width: theme.avatarSize.xxl,
   height: theme.avatarSize.xxl,
   backgroundColor: theme.colors.fill.minimal,
   borderWidth: theme.borderWidth.sm,
 });

21-24: Add JSDoc documentation.

Add documentation to describe the component's purpose, props, and usage examples.

+/**
+ * A component that renders an editable avatar for the onboarding flow.
+ * Shows a camera placeholder when no image is provided.
+ *
+ * @param {string} [uri] - The URI of the avatar image
+ * @param {string} [name] - The name to use for the avatar fallback
+ *
+ * @example
+ * <OnboardingEditableAvatar
+ *   uri="https://example.com/avatar.jpg"
+ *   name="John Doe"
+ * />
+ */
 export const OnboardingEditableAvatar = ({
screens/Navigation/Navigation.tsx (1)

209-218: Consider adding screen transition animations for a smoother UX.

The new screens have headerShown: false, but no explicit transition animations are defined. Consider adding custom transitions for a more polished user experience.

 options={{
   headerShown: false,
+  animation: 'slide_from_right',
+  animationDuration: 200,
 }}

Also applies to: 277-283

navigation/OnboardingNavigator.tsx (1)

7-13: Standardize import path styles.

The import statements mix relative (../features/) and absolute (@/features/) paths. Consider standardizing to absolute imports for better maintainability.

Apply this diff to standardize the imports:

-import { OnboardingConnectWalletScreen } from "../features/onboarding/screens/OnboardingConnectWalletScreen";
-import { OnboardingEphemeraScreen } from "../features/onboarding/screens/OnboardingEphemeraScreen";
-import { OnboardingNotificationsScreen } from "../features/onboarding/screens/onboarding-notifications-screen";
-import { OnboardingPrivateKeyScreen } from "../features/onboarding/screens/OnboardingPrivateKeyScreen";
-import { OnboardingPrivyScreen } from "../features/onboarding/screens/OnboardingPrivyScreen";
+import { OnboardingConnectWalletScreen } from "@/features/onboarding/screens/OnboardingConnectWalletScreen";
+import { OnboardingEphemeraScreen } from "@/features/onboarding/screens/OnboardingEphemeraScreen";
+import { OnboardingNotificationsScreen } from "@/features/onboarding/screens/onboarding-notifications-screen";
+import { OnboardingPrivateKeyScreen } from "@/features/onboarding/screens/OnboardingPrivateKeyScreen";
+import { OnboardingPrivyScreen } from "@/features/onboarding/screens/OnboardingPrivyScreen";
features/onboarding/passkey/useLoginWithPasskey.ts (2)

1-31: Consider consolidating authentication stores.

The hook currently depends on two separate stores (PasskeyAuthStore and PrivyAuthStore) which could lead to synchronization issues and increased complexity. Consider consolidating authentication-related state into a single store for better maintainability.


36-65: Improve error handling specificity.

The catch block uses a generic any type for the error. Consider:

  1. Using a more specific error type
  2. Adding specific error handling for different failure scenarios (e.g., network errors, invalid credentials)
-    } catch (e: any) {
+    } catch (e) {
+      if (e instanceof PrivyError) {
+        setError(e.message);
+      } else if (e instanceof NetworkError) {
+        setError("Network connection failed");
+      } else {
+        setError("Unknown error occurred during login");
+      }
       captureErrorWithToast(e);
     }
features/onboarding/passkey/passkeyAuthStore.tsx (1)

56-56: Consider granular reset functionality.

The current reset() implementation resets all state to default values. Consider adding more granular reset functions for specific state subsets or allowing selective state reset.

-    reset: () => set(DEFAULT_PROPS),
+    reset: (keys?: (keyof IPasskeyAuthStoreProps)[]) => 
+      set(keys ? 
+        keys.reduce((acc, key) => ({ ...acc, [key]: DEFAULT_PROPS[key] }), {}) : 
+        DEFAULT_PROPS
+      ),
screens/NewAccount/new-account-screen.tsx (3)

63-87: Enhance error handling and logging.

  1. Consider adding more context to error messages for better debugging
  2. Debug logging should be conditionally enabled only in development
 const handleError = useCallback(
   (error: Error) => {
     logger.error(error);
-    setError(error.message);
+    setError(`Account creation failed: ${error.message}`);
     captureErrorWithToast(error);
   },
   [setError]
 );

 const onStatusChange = useCallback((status: string) => {
-  logger.debug("[NewAccountWelcomeScreenContent] onStatusChange", status);
+  if (__DEV__) {
+    logger.debug("[NewAccountWelcomeScreenContent] onStatusChange", status);
+  }
 }, []);

88-93: Add retry mechanism for connection failures.

Consider implementing a retry mechanism for failed wallet connections to improve reliability.

const MAX_RETRIES = 3;
const RETRY_DELAY = 1000;

const onConnectionError = useCallback(
  async (error: Error, retryCount = 0) => {
    if (retryCount < MAX_RETRIES) {
      setTimeout(() => {
        // Retry connection
        handleCreateAccountWithPasskey().catch(e => 
          onConnectionError(e, retryCount + 1)
        );
      }, RETRY_DELAY * (retryCount + 1));
    } else {
      handleError(error);
    }
  },
  [handleError, handleCreateAccountWithPasskey]
);

131-136: Enhance loading state feedback.

Consider adding a loading indicator or progress feedback during the connection process to improve user experience.

 <OnboardingFooter
   text={translate("onboarding.welcome.createContactCard")}
   iconName="biometric"
   onPress={handleCreateAccountWithPasskey}
   disabled={loading}
+  loading={loading}
+  loadingText={translate("onboarding.welcome.connecting")}
 />
features/onboarding/screens/OnboardingPrivyScreen.tsx (1)

6-19: Consider using path aliases to simplify import statements

The updated import paths use multiple ../ segments, which can make the code harder to read and maintain. Configuring path aliases can simplify these imports and enhance code readability.

For example, you can set up path aliases in your tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"],
      "@data/*": ["src/data/*"],
      "@features/*": ["src/features/*"],
      "@navigation/*": ["src/navigation/*"],
      "@styles/*": ["src/styles/*"]
    }
  }
}

Then, update your import statements:

-import { OnboardingPictoTitleSubtitle } from "../../../components/Onboarding/OnboardingPictoTitleSubtitle";
+import { OnboardingPictoTitleSubtitle } from "@components/Onboarding/OnboardingPictoTitleSubtitle";
screens/UserProfileScreen.tsx (2)

Line range hint 104-111: Add visual feedback during profile updates.

Consider showing a loading state or progress indicator when the profile is being updated.

 <Button
   variant="text"
+  loading={loading}
   title={
     profile?.avatar
       ? translate("userProfile.buttons.changeProfilePicture")
       : translate("userProfile.buttons.addProfilePicture")
   }
   onPress={addPFP}
 />

157-235: Enhance style organization and color management.

Consider these improvements:

  1. Extract platform-specific styles into separate objects
  2. Define color constants for better maintainability
const COLORS = {
  danger: dangerColor,
  text: {
    primary: textPrimaryColor,
    secondary: textSecondaryColor,
  },
  separator: itemSeparatorColor,
} as const;

const platformStyles = {
  android: {
    profileInput: {
      paddingHorizontal: 16,
      borderWidth: 1,
      borderRadius: 4,
    },
    // ... other Android styles
  },
  ios: {
    // ... iOS styles
  },
} as const;
features/onboarding/screens/onboarding-notifications-screen.tsx (5)

29-32: Consider using theme-based spacing.

Instead of hardcoding the margin value, consider using theme-based spacing for better maintainability and consistency across the app.

 const $screenContainer: ViewStyle = {
   flex: 1,
-  marginHorizontal: 16,
+  marginHorizontal: theme.spacing.md, // or appropriate theme spacing value
 };

57-61: Document Android-specific behavior.

The Android-specific handling needs better documentation to explain the workflow and potential edge cases.

-      // Android 13 always show denied first but sometimes
-      // it will still show the popup. If not, go to Settings!
+      // On Android 13+, the system initially returns "denied" status even when
+      // the permission dialog hasn't been shown yet. We need to:
+      // 1. Check if it's a genuine denial (user clicked "Deny")
+      // 2. Open system settings if the user needs to manually enable permissions
+      // Reference: [Add link to Android documentation or relevant issue]

72-76: Consider persisting notification preferences.

The isEssentialsEnabled state is not persisted. Consider saving this preference to ensure it survives app restarts.


108-123: Improve UX for disabled notification options.

The disabled notification options (mentionsOnly and cash) might confuse users. Consider:

  1. Adding tooltips or explanations for why they're disabled
  2. Adding a "Coming Soon" badge
  3. Removing them if they're not yet ready for production

85-92: Extract inline styles to constants.

Move inline styles to named constants for better maintainability:

+const $avatarContainer: ViewStyle = {
+  marginHorizontal: 8,
+};
+
+const $subtitleContainer: ViewStyle = {
+  marginTop: 16,
+  marginBottom: 24,
+};

 <Avatar
-  style={{ marginHorizontal: 8 }}
+  style={$avatarContainer}
   size={theme.avatarSize.sm}
   uri={avatarUri}
   name={displayName}
 />

 <OnboardingSubtitle
-  style={{ marginTop: 16, marginBottom: 24 }}
+  style={$subtitleContainer}
 >

Also applies to: 97-99

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b5111d and b2c158f.

⛔ Files ignored due to path filters (2)
  • ios/Podfile.lock is excluded by !**/*.lock
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (81)
  • .github/workflows/build-app-release.yml (2 hunks)
  • .github/workflows/build-internal.yml (1 hunks)
  • App.tsx (3 hunks)
  • app.config.ts (1 hunks)
  • components/Avatar.tsx (2 hunks)
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx (1 hunks)
  • components/Onboarding/init-xmtp-client.ts (2 hunks)
  • components/TransactionPreview/TransactionPreview.tsx (2 hunks)
  • components/__tests__/Avatar.test.tsx (1 hunks)
  • config.ts (5 hunks)
  • design-system/Icon/Icon.android.tsx (1 hunks)
  • design-system/Icon/Icon.tsx (1 hunks)
  • design-system/Icon/Icon.types.ts (1 hunks)
  • design-system/IconButton/IconButton.props.ts (1 hunks)
  • design-system/IconButton/IconButton.styles.ts (3 hunks)
  • design-system/IconButton/IconButton.tsx (3 hunks)
  • design-system/IconButton/animated-icon-button.tsx (1 hunks)
  • design-system/Text/Text.styles.ts (1 hunks)
  • features/conversation/conversation-message/conversation-message-content-types/conversation-message-simple-text.tsx (1 hunks)
  • features/onboarding/Privy/usePrivySmartWalletConnection.ts (1 hunks)
  • features/onboarding/components/onboarding-contact-card-provider.tsx (1 hunks)
  • features/onboarding/components/onboarding-contact-card.tsx (1 hunks)
  • features/onboarding/components/onboarding-editable-avatar.tsx (1 hunks)
  • features/onboarding/components/onboarding-footer-text.tsx (1 hunks)
  • features/onboarding/components/onboarding-footer.tsx (1 hunks)
  • features/onboarding/components/onboarding-icon-button.tsx (1 hunks)
  • features/onboarding/components/onboarding-notification-row.tsx (1 hunks)
  • features/onboarding/components/onboarding-subtitle.tsx (1 hunks)
  • features/onboarding/components/onboarding-title.tsx (1 hunks)
  • features/onboarding/constants/animationConstants.ts (1 hunks)
  • features/onboarding/hooks/useAddPfp.ts (1 hunks)
  • features/onboarding/hooks/useCreateOrUpdateProfileInfo.ts (1 hunks)
  • features/onboarding/hooks/useProfile.ts (1 hunks)
  • features/onboarding/passkey/passkey.constants.ts (1 hunks)
  • features/onboarding/passkey/passkeyAuthStore.tsx (1 hunks)
  • features/onboarding/passkey/useCreatePasskey.ts (1 hunks)
  • features/onboarding/passkey/useLoginWithPasskey.ts (1 hunks)
  • features/onboarding/screens/OnboardingConnectWalletScreen.tsx (2 hunks)
  • features/onboarding/screens/OnboardingEphemeraScreen.tsx (2 hunks)
  • features/onboarding/screens/OnboardingPrivateKeyScreen.tsx (2 hunks)
  • features/onboarding/screens/OnboardingPrivyScreen.tsx (2 hunks)
  • features/onboarding/screens/onboarding-contact-card-screen.tsx (1 hunks)
  • features/onboarding/screens/onboarding-notifications-screen.tsx (1 hunks)
  • features/onboarding/screens/onboarding-weclome-screen.tsx (1 hunks)
  • features/onboarding/types/onboarding.types.ts (1 hunks)
  • features/onboarding/utils/formatRandomUserName.ts (1 hunks)
  • i18n/translations/en.ts (2 hunks)
  • i18n/translations/fr.ts (1 hunks)
  • ios/Converse/Converse.entitlements (1 hunks)
  • metro.config.js (2 hunks)
  • navigation/OnboardingNavigator.tsx (2 hunks)
  • package.json (7 hunks)
  • polyfills.ts (1 hunks)
  • screens/Main.tsx (0 hunks)
  • screens/Navigation/Navigation.tsx (6 hunks)
  • screens/NewAccount/NewAccountConnectWalletScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountEphemeraScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountPrivateKeyScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountPrivyScreen.tsx (2 hunks)
  • screens/NewAccount/NewAccountScreen.tsx (0 hunks)
  • screens/NewAccount/NewAccountUserProfileScreen.tsx (0 hunks)
  • screens/NewAccount/new-account-contact-card-screen.tsx (1 hunks)
  • screens/NewAccount/new-account-screen.tsx (1 hunks)
  • screens/Onboarding/OnboardingGetStartedScreen.tsx (0 hunks)
  • screens/Onboarding/OnboardingNotificationsScreen.tsx (0 hunks)
  • screens/Onboarding/OnboardingUserProfileScreen.tsx (0 hunks)
  • screens/UserProfileScreen.tsx (3 hunks)
  • scripts/build/ios/preview.js (1 hunks)
  • scripts/build/ios/production.js (1 hunks)
  • theme/border-radius.ts (1 hunks)
  • theme/spacing.ts (1 hunks)
  • theme/timing.ts (1 hunks)
  • tsconfig.json (1 hunks)
  • utils/api/profiles.ts (1 hunks)
  • utils/events.ts (1 hunks)
  • utils/getCapitalizedLettersForAvatar.ts (1 hunks)
  • utils/getFirstLetterForAvatar.ts (0 hunks)
  • utils/navigation.ts (1 hunks)
  • utils/str.ts (1 hunks)
  • utils/xmtpRN/signIn.ts (2 hunks)
  • utils/xmtpRN/signer.ts (2 hunks)
💤 Files with no reviewable changes (7)
  • screens/Main.tsx
  • screens/NewAccount/NewAccountScreen.tsx
  • utils/getFirstLetterForAvatar.ts
  • screens/NewAccount/NewAccountUserProfileScreen.tsx
  • screens/Onboarding/OnboardingGetStartedScreen.tsx
  • screens/Onboarding/OnboardingUserProfileScreen.tsx
  • screens/Onboarding/OnboardingNotificationsScreen.tsx
🚧 Files skipped from review as they are similar to previous changes (57)
  • theme/border-radius.ts
  • tsconfig.json
  • theme/spacing.ts
  • features/conversation/conversation-message/conversation-message-content-types/conversation-message-simple-text.tsx
  • utils/api/profiles.ts
  • design-system/Icon/Icon.tsx
  • features/onboarding/components/onboarding-icon-button.tsx
  • components/tests/Avatar.test.tsx
  • design-system/IconButton/IconButton.props.ts
  • design-system/Icon/Icon.types.ts
  • ios/Converse/Converse.entitlements
  • features/onboarding/types/onboarding.types.ts
  • utils/xmtpRN/signer.ts
  • features/onboarding/components/onboarding-title.tsx
  • features/onboarding/components/onboarding-contact-card-provider.tsx
  • scripts/build/ios/preview.js
  • features/onboarding/screens/OnboardingConnectWalletScreen.tsx
  • design-system/IconButton/animated-icon-button.tsx
  • features/onboarding/constants/animationConstants.ts
  • features/onboarding/passkey/passkey.constants.ts
  • .github/workflows/build-internal.yml
  • features/onboarding/utils/formatRandomUserName.ts
  • utils/str.ts
  • theme/timing.ts
  • design-system/Icon/Icon.android.tsx
  • polyfills.ts
  • components/Onboarding/ConnectViaWallet/ConnectViaWalletTableViewItems.tsx
  • design-system/IconButton/IconButton.tsx
  • components/Avatar.tsx
  • screens/NewAccount/NewAccountConnectWalletScreen.tsx
  • metro.config.js
  • utils/events.ts
  • utils/getCapitalizedLettersForAvatar.ts
  • features/onboarding/components/onboarding-footer-text.tsx
  • screens/NewAccount/NewAccountPrivyScreen.tsx
  • design-system/Text/Text.styles.ts
  • features/onboarding/components/onboarding-footer.tsx
  • features/onboarding/hooks/useProfile.ts
  • features/onboarding/screens/onboarding-contact-card-screen.tsx
  • .github/workflows/build-app-release.yml
  • features/onboarding/screens/OnboardingEphemeraScreen.tsx
  • features/onboarding/components/onboarding-notification-row.tsx
  • features/onboarding/hooks/useCreateOrUpdateProfileInfo.ts
  • components/TransactionPreview/TransactionPreview.tsx
  • screens/NewAccount/NewAccountPrivateKeyScreen.tsx
  • app.config.ts
  • features/onboarding/Privy/usePrivySmartWalletConnection.ts
  • features/onboarding/components/onboarding-contact-card.tsx
  • features/onboarding/hooks/useAddPfp.ts
  • features/onboarding/passkey/useCreatePasskey.ts
  • package.json
  • features/onboarding/screens/OnboardingPrivateKeyScreen.tsx
  • utils/navigation.ts
  • i18n/translations/en.ts
  • features/onboarding/components/onboarding-subtitle.tsx
  • scripts/build/ios/production.js
  • features/onboarding/screens/onboarding-weclome-screen.tsx
🧰 Additional context used
🪛 GitHub Check: lint
App.tsx

[warning] 179-179:
Inline style: { flex: 1 }

config.ts

[warning] 39-39:
Prefer named exports

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (32)
i18n/translations/fr.ts (2)

3-43: LGTM! Well-structured and grammatically correct French translations.

The new onboarding section is well-organized with proper nesting and complete translations for all UI elements. The French translations are grammatically correct and maintain a consistent tone throughout the section.


Line range hint 1-999: LGTM! Complete and well-structured translations.

The French translations file is well-organized, complete, and maintains proper hierarchical structure. All the features mentioned in the AI summary are properly translated, and the translations maintain consistency in tone and style throughout the file.

utils/xmtpRN/signIn.ts (1)

12-17: LGTM! Import changes align with new Viem account support.

The new imports for ViemAccount and viemAccountToXmtpSigner are appropriately added to support the new functionality.

components/Onboarding/init-xmtp-client.ts (2)

9-20: LGTM! Import organization and new dependencies.

The imports are well-organized using absolute paths, and the new dependencies for Viem account support are correctly added.


73-90: LGTM! Error handling and logging implementation.

The error handling follows the established pattern, including proper cleanup with logoutAccount and error logging.

design-system/IconButton/IconButton.styles.ts (1)

182-190: Address TODO comments and improve typing.

The implementation has multiple TODO comments and uses any type. Consider:

  1. Fixing the IconProps type as mentioned in the TODO
  2. Removing the commented-out type annotations
  3. Replacing any with proper typing
-  (
-    theme: Theme // TODO: fix once we fixed IconProps
-  ) => // : Partial<IIconProps>
+  (theme: Theme): Partial<IconProps> =>
   {
     const { colors, spacing } = theme;
 
-    const props: any =
-      // :Partial<IIconProps>
-      {};
+    const props: Partial<IconProps> = {};
features/onboarding/components/onboarding-editable-avatar.tsx (2)

1-11: LGTM! Clean imports and well-defined types.

The imports are well-organized and the type definition is clear with proper optional props.


21-43: ⚠️ Potential issue

Add missing functionality for editable avatar.

The component is named "Editable" but lacks the following essential features:

  1. Press handler for editing
  2. Loading state during image upload
  3. Error handling for invalid URIs
  4. Accessibility support

Here's how to implement these features:

 export const OnboardingEditableAvatar = ({
   uri,
   name,
+  onEdit,
+  isLoading,
 }: IOnboardingEditableAvatarProps) => {
   const { theme, themed } = useAppTheme();
+  const [hasError, setHasError] = useState(false);
+
   if (!uri && !name) {
     return (
-      <Center style={themed($placeholderStyle)}>
+      <Pressable
+        onPress={onEdit}
+        accessibilityRole="button"
+        accessibilityLabel="Add profile picture"
+        accessibilityHint="Opens image picker"
+      >
+        <Center style={themed($placeholderStyle)}>
+          {isLoading ? (
+            <ActivityIndicator color={theme.colors.text.primary} />
+          ) : (
             <Icon
               icon="camera"
               size={theme.iconSize.md}
               color={theme.colors.text.primary}
             />
+          )}
         </Center>
+      </Pressable>
     );
   }

   return (
-    <VStack style={{ width: theme.avatarSize.xxl }}>
+    <Pressable
+      onPress={onEdit}
+      accessibilityRole="button"
+      accessibilityLabel={`Edit profile picture${name ? ` for ${name}` : ''}`}
+      accessibilityHint="Opens image picker"
+    >
+      <VStack style={{ width: theme.avatarSize.xxl }}>
         <Avatar
-          uri={uri}
+          uri={hasError ? undefined : uri}
           name={name}
           size={theme.avatarSize.xxl}
+          onError={() => setHasError(true)}
         />
+        {isLoading && (
+          <Center
+            style={[
+              themed($placeholderStyle),
+              { position: 'absolute', top: 0, left: 0 }
+            ]}
+          >
+            <ActivityIndicator color={theme.colors.text.primary} />
+          </Center>
+        )}
       </VStack>
+    </Pressable>
   );
 };

Don't forget to update the type definition:

 type IOnboardingEditableAvatarProps = {
   uri?: string;
   name?: string;
+  onEdit: () => void;
+  isLoading?: boolean;
 };

Likely invalid or redundant comment.

screens/Navigation/Navigation.tsx (2)

47-49: LGTM! Clean organization of new screen imports.

The new screen imports are well-organized and follow the established pattern.


55-57: LGTM! Navigation params are properly typed.

The new route definitions are correctly added to the NavigationParamList type.

Also applies to: 69-69

screens/NewAccount/NewAccountEphemeraScreen.tsx (1)

19-19: LGTM! Navigation flow updated correctly.

The import path and navigation target have been properly updated to reflect the new screen structure.

Also applies to: 38-38

screens/NewAccount/new-account-contact-card-screen.tsx (1)

1-247: Consider extracting shared logic to avoid code duplication.

This component shares significant code with OnboardingContactCardScreen. Consider creating a shared hook or component to reduce duplication.

navigation/OnboardingNavigator.tsx (2)

12-12: Fix typo in import path.

The import path contains a typo: onboarding-weclome-screen should be onboarding-welcome-screen.


50-70: Verify the onboarding flow sequence.

The screen order has been modified with the introduction of new screens. Please confirm that this sequence aligns with the intended user experience for Privy passkey login:

  1. Welcome
  2. Contact Card
  3. Notifications
  4. Privy
  5. Connect Wallet
  6. Private Key
  7. Ephemeral

Let's check for any potential navigation dead-ends or circular references:

✅ Verification successful

Navigation flow implementation verified successfully

The screen sequence in the navigator matches the implemented navigation flow, with proper transitions between screens. The Welcome screen correctly navigates to Contact Card, and all screens have the necessary navigation capabilities implemented.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check navigation patterns in onboarding screens

# Look for navigation.navigate calls to verify the flow
echo "Checking navigation patterns in onboarding screens..."
rg -A 2 "navigation\.navigate\([\'\"]Onboarding" --type typescript

# Look for potential dead-ends (screens without outgoing navigation)
echo "Checking for potential dead-ends..."
for file in $(fd "Onboarding.*Screen\.tsx$"); do
  if ! rg -q "navigation\.navigate" "$file"; then
    echo "Potential dead-end in $file"
  fi
done

Length of output: 1648


Script:

#!/bin/bash
# Search for navigation patterns with corrected regex
echo "Checking navigation patterns..."
rg "navigation\.(navigate|replace|reset|goBack)" --type typescript features/onboarding/

# Check for navigation-related imports and custom hooks
echo -e "\nChecking navigation imports and hooks..."
rg "import.*navigation" --type typescript features/onboarding/

# Look for button components that might handle navigation
echo -e "\nChecking for button components..."
rg "(Button|Pressable|TouchableOpacity|onPress)" --type typescript features/onboarding/

Length of output: 602


Script:

#!/bin/bash
# Search for navigation patterns in all relevant files
echo "Checking navigation patterns..."
rg "navigation\.(navigate|replace|reset|goBack)" features/onboarding/screens/

# Check for navigation-related imports and hooks
echo -e "\nChecking navigation imports and hooks..."
rg "import.*navigation" features/onboarding/screens/

# Look for button components and onPress handlers
echo -e "\nChecking for interactive components..."
rg "(Button|Pressable|TouchableOpacity)" features/onboarding/screens/

# Look for specific screen transitions
echo -e "\nChecking screen transitions..."
rg "Onboarding[A-Za-z]+Screen" features/onboarding/screens/ -A 2 -B 2

Length of output: 10260

features/onboarding/passkey/useLoginWithPasskey.ts (2)

32-34: LGTM!

The effect correctly synchronizes the login state with the UI.


67-70: LGTM!

The hook's return value is correctly implemented and well-documented.

features/onboarding/passkey/passkeyAuthStore.tsx (2)

50-51: Improve error handling in setLoading.

The setLoading implementation clears errors when loading is true but not when it's false. This asymmetric error handling could lead to stale error states.


60-76: LGTM!

The hook implementations follow React context best practices with proper error handling.

screens/NewAccount/new-account-screen.tsx (1)

43-51: Reduce code duplication with OnboardingWelcomeScreen.

The NewAccountScreen and its content component are nearly identical to OnboardingWelcomeScreen. Consider extracting the common functionality into a shared component.

features/onboarding/screens/OnboardingPrivyScreen.tsx (1)

37-37: Navigation update aligns with onboarding flow changes

The navigation target is correctly updated to "OnboardingContactCard", aligning with the restructured onboarding process to enhance user experience.

screens/UserProfileScreen.tsx (3)

Line range hint 3-28: LGTM! Well-organized imports.

The imports are properly organized and use consistent path conventions.


156-156: Improve type safety in useUserProfileStyles.

The function uses any types for its parameters, which could lead to runtime errors.


Line range hint 89-102: Add validation for avatar URI and loading state.

The avatar handling lacks input validation and loading state feedback:

  1. Validate the avatar URI format and size before setting it in the profile
  2. Show a loading indicator during avatar upload
 <Pressable onPress={addPFP}>
   <Avatar
     uri={profile?.avatar || asset?.uri}
     style={styles.avatar}
+    loading={asset?.loading}
     name={profile.displayName || profile.username}
   />
 </Pressable>
features/onboarding/screens/onboarding-notifications-screen.tsx (3)

1-28: LGTM! Well-organized imports.

The imports are logically grouped and all dependencies are properly declared.


52-70: Improve error handling in handleEnableNotifications.

The error handling needs enhancement as previously suggested.


34-40: Add runtime validation for currentAccount.

The non-null assertion (!) on currentAccount could lead to runtime errors if the account is unexpectedly null. Consider adding a runtime check or error boundary.

-  const currentAccount = useCurrentAccount()!;
+  const currentAccount = useCurrentAccount();
+  if (!currentAccount) {
+    // Handle the null case appropriately
+    return null; // or show an error state
+  }
config.ts (3)

39-39: Remove duplicated privy config in prod environment.

The privy configuration is duplicated between defaultConfig and prod environment.

🧰 Tools
🪛 GitHub Check: lint

[warning] 39-39:
Prefer named exports


79-79: Verify domain configuration consistency.

The new appDomain values match their respective websiteDomain values. This is good for consistency, but please ensure:

  1. DNS records are properly configured for all domains
  2. SSL certificates are valid

Also applies to: 99-99, 134-134


Line range hint 42-44: Verify chain configuration for production readiness.

The configuration properly distinguishes between Base Sepolia (testnet) for development and Base Mainnet for production. However, please ensure:

  1. All smart contract interactions are tested on Base Mainnet
  2. Contract addresses are verified on both chains

Also applies to: 122-124

✅ Verification successful

Chain configuration and contract addresses verified successfully

Both USDC contract addresses are properly deployed and operational on their respective networks:

  • Base Sepolia (testnet): 0x036CbD53842c5426634e7929541eC2318f3dCF7e
  • Base Mainnet: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify contract addresses on both chains
echo "Checking Base Sepolia USDC contract..."
curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0x036CbD53842c5426634e7929541eC2318f3dCF7e", "latest"],"id":1}' "https://sepolia.base.org" | jq .result

echo "Checking Base Mainnet USDC contract..."
curl -s -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "latest"],"id":1}' "https://mainnet.base.org" | jq .result

Length of output: 7913

🧰 Tools
🪛 GitHub Check: lint

[warning] 39-39:
Prefer named exports

App.tsx (3)

7-10: Document minification requirement in build documentation.

The comment indicates a critical requirement for Privy to work with minification. This should be documented in the build process.

Would you like me to help create a documentation PR or issue to properly document this requirement?


167-190: Provider configuration and nesting looks good!

The provider hierarchy is correct:

  1. PrivyProvider with all required props (appId, clientId, storage, supportedChains)
  2. SmartWalletsProvider nested within PrivyProvider
  3. Remaining providers maintain their original order
🧰 Tools
🪛 GitHub Check: lint

[warning] 179-179:
Inline style: { flex: 1 }


55-55: Verify chain configuration consistency with config.ts.

The Base chain configuration in PrivyProvider should match the chain configuration in config.ts based on the environment.

Also applies to: 171-171

✅ Verification successful

Chain configuration is properly configured

The chain configuration is correctly implemented with environment-specific settings:

  • Development/Preview environments use Base Sepolia (0x14a34)
  • Production environment uses Base Mainnet (0x2105)

The base chain import in App.tsx is consistent with this setup as it's used in the PrivyProvider configuration.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for chain configurations to ensure consistency
echo "Checking chain configurations..."
rg -A 2 "transactionChainId|supportedChains"

Length of output: 2435


Script:

#!/bin/bash
# Check base chain usage and environment configuration
echo "Checking base chain configuration..."
rg -A 5 "import.*base.*from.*viem/chains"

echo -e "\nChecking environment-based configuration..."
rg -A 10 "export const config = {" config.ts

Length of output: 1191


Script:

#!/bin/bash
# Check configuration structure with fixed regex
echo "Checking config structure..."
rg -A 10 "export const config =" config.ts

echo -e "\nChecking environment variables usage..."
rg -B 2 -A 2 "process\.env\." config.ts

Length of output: 1874


Script:

#!/bin/bash
# Get complete config structure and chain selection logic
echo "Checking complete config file..."
cat config.ts

echo -e "\nChecking chain-related conditionals..."
rg -B 5 -A 5 "transactionChainId.*Base"

Length of output: 5746

design-system/IconButton/IconButton.styles.ts Outdated Show resolved Hide resolved

const handleRandoContinue = useCallback(async () => {
try {
const randomUsername = uuidv4().replace(/-/g, "").slice(0, 30);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve random username generation.

The current random username generation methods could be more robust:

  1. UUID-based generation might create usernames that are hard to read
  2. Using display name for username generation could lead to collisions
+const generateRandomUsername = (base?: string) => {
+  const randomSuffix = Math.random().toString(36).substring(2, 8);
+  if (base) {
+    const sanitized = base.toLowerCase().replace(/[^a-z0-9]/g, '');
+    return `${sanitized}_${randomSuffix}`.slice(0, 30);
+  }
+  return `user_${randomSuffix}`;
+};

-const randomUsername = uuidv4().replace(/-/g, "").slice(0, 30);
+const randomUsername = generateRandomUsername();

-const profileUserName = formatRandomUserName(profile.displayName ?? "");
+const profileUserName = generateRandomUsername(profile.displayName);

Also applies to: 133-133

Comment on lines 93 to 102
const handleError = useCallback((error: Error) => {
logger.error(error);
captureErrorWithToast(error);
}, []);

useEffect(() => {
if (errorMessage) {
handleError(new Error(errorMessage));
}
}, [errorMessage, handleError]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling with specific error types.

The current error handling is too generic. Consider creating specific error types for different failure scenarios.

+type ProfileError = {
+  code: 'VALIDATION_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
+  message: string;
+};

-const handleError = useCallback((error: Error) => {
+const handleError = useCallback((error: Error | ProfileError) => {
   logger.error(error);
-  captureErrorWithToast(error);
+  if ('code' in error) {
+    captureErrorWithToast(error.message);
+  } else {
+    captureErrorWithToast('An unexpected error occurred');
+  }
 }, []);

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 131 to 150
const handleRealContinue = useCallback(async () => {
try {
const profileUserName = formatRandomUserName(profile.displayName ?? "");
const newProfile: ProfileType = {
...profile,
username: profileUserName,
avatar: asset?.uri,
};
const { success } = await createOrUpdateProfile({
profile: newProfile,
});
if (success) {
navigation.popTo("Chats");
} else {
throw new Error("Failed to create or update profile");
}
} catch (error) {
handleError(error as Error);
}
}, [profile, asset?.uri, createOrUpdateProfile, navigation, handleError]);
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

Add input validation for profile data.

The profile update logic lacks validation for the display name and username format.

 const handleRealContinue = useCallback(async () => {
   try {
+    if (!profile.displayName?.trim()) {
+      throw { code: 'VALIDATION_ERROR', message: 'Display name is required' };
+    }
+    if (profile.displayName.length > 50) {
+      throw { code: 'VALIDATION_ERROR', message: 'Display name is too long' };
+    }
     const profileUserName = formatRandomUserName(profile.displayName ?? "");
+    if (profileUserName.length < 3 || profileUserName.length > 30) {
+      throw { code: 'VALIDATION_ERROR', message: 'Invalid username length' };
+    }
     const newProfile: ProfileType = {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleRealContinue = useCallback(async () => {
try {
const profileUserName = formatRandomUserName(profile.displayName ?? "");
const newProfile: ProfileType = {
...profile,
username: profileUserName,
avatar: asset?.uri,
};
const { success } = await createOrUpdateProfile({
profile: newProfile,
});
if (success) {
navigation.popTo("Chats");
} else {
throw new Error("Failed to create or update profile");
}
} catch (error) {
handleError(error as Error);
}
}, [profile, asset?.uri, createOrUpdateProfile, navigation, handleError]);
const handleRealContinue = useCallback(async () => {
try {
if (!profile.displayName?.trim()) {
throw { code: 'VALIDATION_ERROR', message: 'Display name is required' };
}
if (profile.displayName.length > 50) {
throw { code: 'VALIDATION_ERROR', message: 'Display name is too long' };
}
const profileUserName = formatRandomUserName(profile.displayName ?? "");
if (profileUserName.length < 3 || profileUserName.length > 30) {
throw { code: 'VALIDATION_ERROR', message: 'Invalid username length' };
}
const newProfile: ProfileType = {
...profile,
username: profileUserName,
avatar: asset?.uri,
};
const { success } = await createOrUpdateProfile({
profile: newProfile,
});
if (success) {
navigation.popTo("Chats");
} else {
throw new Error("Failed to create or update profile");
}
} catch (error) {
handleError(error as Error);
}
}, [profile, asset?.uri, createOrUpdateProfile, navigation, handleError]);

Copy link
Collaborator

@technoplato technoplato left a comment

Choose a reason for hiding this comment

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

I'm a big fan of getting this on but let's not consider it dev done until we've cleaned up a little!

config.ts Outdated
@@ -77,6 +76,7 @@ const ENV = {
appCheckDebugToken: isAndroid
? process.env.EXPO_PUBLIC_FIREBASE_APP_CHECK_DEBUG_TOKEN_ANDROID
: process.env.EXPO_PUBLIC_FIREBASE_APP_CHECK_DEBUG_TOKEN_IOS,
appDomain: "dev.converse.xyz",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Did this fix anything?

design-system/IconButton/IconButton.tsx Show resolved Hide resolved
features/onboarding/Privy/usePrivySmartWalletConnection.ts Outdated Show resolved Hide resolved
const initializingXmtp = useRef(false);

// Let's make sure we start with a clean state
useEffect(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could clean up a lot of this state if we utilize React Query here. It would be a little odd in the sense that we would have to pass the client from usePrivy to the hook, since Privy doesn't look like it exposes any imperative APIs.

But it would take care of cleaning up a lot of the management around whether or not we're currently creating and we would get the status changes for free and catching errors would be taken care of for free. Then, on success, we could initialize XMTP.

I'm happy to take a look at this with you to clean things up once you've got a final iteration on Privy and we've consolidated and agreed upon using it, which it seems like we already have.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Another spot this would clean up: #1511 (comment)

Copy link
Collaborator

Choose a reason for hiding this comment

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

I believe this is another spot. We react query would help us out and simplifying a lot of this code, and we would just have to extract the imperative function of actually updating or creating the profile info.

@@ -0,0 +1,4 @@
import config from "@config";

export const RPID = config.appDomain;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you maybe just add a link to documentation on what this is

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should delete tie this and redo when we're adding transactions

Copy link
Collaborator

Choose a reason for hiding this comment

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

#1511 (comment)

We are moving away from this pattern and I think this would go away completely if we use react query from managing these async operations

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we could use react-query instead of this store

const { logout, user: privyUser } = usePrivy();

const { signupWithPasskey, state: signupState } = useSignupWithPasskey();

Copy link
Collaborator

Choose a reason for hiding this comment

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

@@ -25,7 +25,7 @@ export const OnboardingConnectWalletScreen = memo(

const handleDoneConnecting = useCallback(() => {
if (isMissingConverseProfile()) {
router.navigate("OnboardingUserProfile");
router.navigate("OnboardingContactCard");
Copy link
Collaborator

Choose a reason for hiding this comment

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

I feel like renaming this route to OnboardingCreateContactCard makes more sense because that's what the route screen is doing.

metro.config.js Show resolved Hide resolved
tsconfig.json Show resolved Hide resolved
Copy link
Collaborator

@thierryskoda thierryskoda left a comment

Choose a reason for hiding this comment

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

  • Make sure to name all files with lower-kebab.
  • Please add comments in places that it's hard to understand why.
  • I saw @technoplato saying we could use react-query at some places. I think I agree.

@@ -49,6 +52,44 @@ export async function initXmtpClient(args: {
}
}

export async function initXmtpClientFromViemAccount(args: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I thought we wouldn't need this anymore. Because all identity would be from Passkeys/Privy and then we would only connect external wallet to this account.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to create an XMTP signer in some way, it's expecting some way to do the signing
The signing is then handled within the app and doesn't need to move out, but it still needs something to authorize a signature

const { themeScheme, setThemeContextOverride, ThemeProvider } =
useThemeProvider();

const invertedTheme = themeScheme === "light" ? "dark" : "light";
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have stuff like theme.colors.fill.inverted.light etc... no?

@@ -0,0 +1,114 @@
import { AnimatedVStack, VStack } from "@/design-system/VStack";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not using Louis' contact-card.tsx ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wasn't merged at the time, that card would also need to be updated to support editing as well, happy to make the update since it's been merged

Comment on lines 27 to 29
const $flex2: ViewStyle = {
flex: 2,
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

Prefer $textsContainer or something like that

@@ -0,0 +1,9 @@
import { timing } from "@/theme/timing";
Copy link
Collaborator

Choose a reason for hiding this comment

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

lowercase kebab style name 🙏

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we could use react-query instead of this store

Comment on lines 46 to 51
const $flex1: ViewStyle = {
flex: 1,
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

we have something you can use in theme/styles.ts for those utility style


const handleError = useCallback(
(error: Error) => {
logger.error(error);
Copy link
Collaborator

Choose a reason for hiding this comment

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

don't need if we have captureErrorWithToast no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah was during debugging, but it was because we didn't log to sentry outside of dev

Added support for privy passkey login
Added support for smart contract wallets provided by privy
Updated app entitlements
Updated builds for new env variables
Updated packages for better support
Fixed crash with app theme
Removed noisy logs
Updated onboarding and new account experience

Still a number of files to delete but there will be a bunch of conflicts so trying to avoid that for now
Privy for some reason requires to be imported as *
use theme animations
Remove transaction frames work
@alexrisch alexrisch merged commit a506922 into main Jan 22, 2025
11 checks passed
@alexrisch alexrisch deleted the ar/privy-passkey branch January 22, 2025 08:18
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.

4 participants