Skip to content

Conversation

binu-baiju
Copy link

@binu-baiju binu-baiju commented Aug 29, 2025

Description

Fixed two critical bugs in the Onlook web application that were affecting user experience:

  1. GitHub Profile Picture Bug: Users' GitHub profile pictures were not displaying correctly, showing only initials ("bb") instead of their actual GitHub avatar after OAuth login.

  2. Recent Projects Refresh Bug: The recent projects list was not updating when users accessed projects, meaning recently opened projects did not move to the top of the list as expected.

Root Causes Identified:

Solutions Implemented:

Related Issues

Fixes GitHub profile picture display issue
Fixes recent projects list refresh issue

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Release
  • Refactor
  • Other (please describe):

Testing

Manual Testing Performed:

GitHub Profile Pictures:

  1. Logged in using GitHub OAuth
  2. Verified profile picture displays correctly (actual GitHub avatar, not initials)
  3. Tested both fresh login and existing user scenarios

Recent Projects Refresh:

  1. Created test projects with different timestamps (seeded database)
  2. Opened an older project from the list ("Project A - Old")
  3. Navigated back to projects page
  4. Verified the accessed project moved to the top of recent projects list
  5. Confirmed UI updates immediately without manual refresh

Environment:

  • Local development setup with Supabase backend
  • Windows 10 with Docker Desktop
  • Tested in Chrome browser

Screenshots (if applicable)

Additional Notes

Technical Details:

  • Applied bun format for code formatting consistency
  • Ran bun lint to ensure code quality standards
  • Used targeted, minimal changes to avoid unnecessary side effects
  • Both fixes are backward compatible and don't affect existing functionality

Development Process:

  • Set up complete local development environment including Supabase
  • Reproduced both bugs systematically before implementing fixes
  • Tested fixes thoroughly in local environment
  • Used proper Git workflow with focused commits

Files Modified:

  • apps/web/client/src/server/api/routers/user/user.ts (2 lines changed)
  • apps/web/client/src/server/api/routers/project/project.ts (added trackAccess mutation)
  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (integrated tracking)

Both issues were affecting real user experience, and these fixes provide immediate value to the Onlook community.


Important

Fix GitHub avatar display and project list refresh, update Supabase config and database URL.

  • Behavior:
    • Fix GitHub profile picture display by mapping authUser.user_metadata.avatar_url in user.ts.
    • Implement trackAccess mutation in project.ts to update project timestamps and refresh UI cache.
  • Configuration:
    • Change Supabase ports in config.toml.
    • Update DEFAULT_DATABASE_URL in drizzle.config.ts to new port.
  • Misc:
    • Minor code formatting changes in onlook-preload-script.js.

This description was created by Ellipsis for 1189d05. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • “Recent projects” now updates automatically when you open a project.
  • Bug Fixes

    • User avatars load more reliably by reading the correct profile image field.
  • Chores

    • Local development: Supabase now starts excluding certain auxiliary services.
    • Local ports and the default DB URL used by migrations were updated.
    • Update local OAuth callback URLs and environment configs as needed.

…perty

- Fixed camelCase vs snake_case mismatch in user metadata
- GitHub OAuth provides avatar_url, not avatarUrl
- Resolves profile pictures showing only initials instead of actual images
Copy link

vercel bot commented Aug 29, 2025

@binu-baiju is attempting to deploy a commit to the Onlook Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

coderabbitai bot commented Aug 29, 2025

Walkthrough

Updates local Supabase startup to exclude mailpit, logflare, and vector; changes Supabase ports and OAuth redirect URIs; updates Drizzle default DB port. Adds a protected trackAccess mutation and client hook to record project access and refresh recent projects. Reads avatar URL from snake_case user_metadata.avatar_url.

Changes

Cohort / File(s) Summary of changes
Supabase runtime & DB config
apps/backend/package.json, apps/backend/supabase/config.toml, packages/db/drizzle.config.ts
Start script now runs supabase start --exclude mailpit,logflare,vector. Supabase ports updated (api -> 55321, db -> 55432, studio -> 55323) and OAuth redirect URIs adjusted. Drizzle default DB URL port changed to 127.0.0.1:55432.
Project access tracking (API + client)
apps/web/client/src/server/api/routers/project/project.ts, apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
Added protected trackAccess mutation validating membership and updating projects.updatedAt; client hook calls trackAccess on project load and invalidates project list to refresh recent projects.
User avatar metadata mapping
apps/web/client/src/server/api/routers/user/user.ts
Avatar URL sourcing switched to authUser.user_metadata.avatar_url (with fallback to user.avatarUrl) in get and upsert paths.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Client as Web Client
  participant API as projectRouter
  participant DB as Database

  User->>Client: Open project page
  Client->>API: trackAccess({ projectId })
  note right of API: protectedProcedure verifies membership
  API->>DB: UPDATE projects SET updatedAt = now() WHERE id = projectId
  DB-->>API: OK
  API-->>Client: { success: true }
  note right of Client: invalidate project list cache -> refetch recent projects
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I thump my paws at ports anew,
Supabase hops to 55321 and 55432 too.
I track the trails where projects play,
Avatars shimmer from metadata's bay.
A carrot cheer — quick tests, then away! 🥕🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share
🪧 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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (6)
apps/web/client/src/server/api/routers/user/user.ts (1)

25-26: Harden avatar derivation with broader fallbacks

Some IdPs store avatar under different keys. Add a short fallback chain to be resilient across providers.

Apply this diff:

-            avatarUrl: user.avatarUrl ?? authUser.user_metadata.avatar_url,
+            avatarUrl:
+                user.avatarUrl
+                ?? authUser.user_metadata?.avatar_url
+                ?? authUser.user_metadata?.avatarUrl
+                ?? authUser.user_metadata?.picture
+                ?? null,
-                avatarUrl: input.avatarUrl ?? authUser.user_metadata.avatar_url,
+                avatarUrl:
+                    input.avatarUrl
+                    ?? authUser.user_metadata?.avatar_url
+                    ?? authUser.user_metadata?.avatarUrl
+                    ?? authUser.user_metadata?.picture
+                    ?? null,

Also applies to: 59-60

apps/backend/supabase/config.toml (1)

10-15: Consider adding 127.0.0.1 app URLs for local dev flexibility

If you sometimes run the web app at 127.0.0.1:3000 (not localhost), add it to allowed redirects to avoid blocked post-auth redirects.

Apply this diff:

 additional_redirect_urls = [
     "http://localhost:3000",
     "http://localhost:3000/auth/callback",
+    "http://127.0.0.1:3000",
+    "http://127.0.0.1:3000/auth/callback",
 ]
apps/backend/package.json (1)

5-5: Excluding logflare/vector may conflict with analytics settings

You’re starting Supabase without logflare/vector, while analytics.enabled = true remains in config.toml. Validate local startup health and decide whether to:

  • keep exclude flags and set [analytics] enabled = false, or
  • include the services during supabase start.
apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1)

39-44: Surface mutation errors to the user

Add onError to toast/log failures so access-tracking issues aren’t silent.

Apply this diff:

-    const { mutateAsync: trackProjectAccess } = api.project.trackAccess.useMutation({
-        onSuccess: () => {
+    const { mutateAsync: trackProjectAccess } = api.project.trackAccess.useMutation({
+        onSuccess: () => {
             // Invalidate project list to refresh recent projects
             apiUtils.project.list.invalidate();
-        },
+        },
+        onError: (err) => {
+            console.error('Failed to track project access', err);
+            toast.message('Could not update recent projects', {
+                description: err instanceof Error ? err.message : 'Unknown error',
+            });
+        },
     });
apps/web/client/src/server/api/routers/project/project.ts (2)

164-181: Sort in SQL by last access (or updatedAt) to reduce app-side work and align semantics.

Move sorting to the query and, if adopting lastAccessedAt, order by it (fallback to projects.updatedAt).

Example (Drizzle-style; adjust imports for desc/sql):

-            const fetchedUserProjects = await ctx.db.query.userProjects.findMany({
-                where: input?.excludeProjectId ? and(
-                    eq(userProjects.userId, ctx.user.id),
-                    ne(userProjects.projectId, input.excludeProjectId),
-                ) : eq(userProjects.userId, ctx.user.id),
-                with: {
-                    project: true,
-                },
-                limit: input?.limit,
-            });
-            return fetchedUserProjects.map((userProject) => toProject(userProject.project)).sort((a, b) => new Date(b.metadata.updatedAt).getTime() - new Date(a.metadata.updatedAt).getTime());
+            const rows = await ctx.db
+                .select()
+                .from(userProjects)
+                .leftJoin(projects, eq(userProjects.projectId, projects.id))
+                .where(
+                    input?.excludeProjectId
+                        ? and(eq(userProjects.userId, ctx.user.id), ne(userProjects.projectId, input.excludeProjectId))
+                        : eq(userProjects.userId, ctx.user.id),
+                )
+                // If lastAccessedAt exists, prefer it; otherwise order by projects.updatedAt
+                .orderBy(sql`COALESCE(${userProjects.lastAccessedAt}, ${projects.updatedAt}) DESC`)
+                .limit(input?.limit ?? undefined);
+            return rows.map((r) => toProject(r.projects));

Additional imports (top of file):

import { desc, sql } from 'drizzle-orm';

406-412: Track access per user instead of mutating project.updatedAt
updatedAt should reflect content changes; using it for “recent” access affects all members and conflates semantics. Add a lastAccessedAt timestamp to your user_projects table and update that here:

await ctx.db
  .update(userProjects)
  .set({ lastAccessedAt: new Date() })
  .where(and(
    eq(userProjects.userId, ctx.user.id),
    eq(userProjects.projectId, input.projectId),
  ));

You’ll need to add the lastAccessedAt column in the Drizzle schema and create a migration.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1f6e025 and 1189d05.

📒 Files selected for processing (6)
  • apps/backend/package.json (1 hunks)
  • apps/backend/supabase/config.toml (2 hunks)
  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1 hunks)
  • apps/web/client/src/server/api/routers/project/project.ts (1 hunks)
  • apps/web/client/src/server/api/routers/user/user.ts (2 hunks)
  • packages/db/drizzle.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1)
apps/web/client/src/trpc/react.tsx (1)
  • api (23-23)
apps/web/client/src/server/api/routers/project/project.ts (2)
apps/web/client/src/server/api/trpc.ts (1)
  • protectedProcedure (130-149)
packages/db/src/schema/project/project.ts (1)
  • projects (12-31)
🔇 Additional comments (5)
apps/web/client/src/server/api/routers/user/user.ts (1)

25-26: Good fix: use snake_case avatar_url from Supabase metadata

This resolves the GitHub avatar mismatch where avatarUrl was undefined.

apps/backend/supabase/config.toml (2)

5-5: Ports alignment looks consistent with Drizzle config

API=55321, DB=55432, Studio=55323. Matches packages/db/drizzle.config.ts.

Also applies to: 19-19, 22-22


28-29: Verify OAuth app callback URLs are updated to 127.0.0.1:55321

Ensure GitHub and Google developer console apps include the exact callback:

A mismatch will block local sign-in.

Also applies to: 34-35

packages/db/drizzle.config.ts (1)

3-3: Drizzle fallback DB URL updated to 55432 — matches Supabase DB port

This keeps local migrations consistent with the new Supabase config.

apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1)

39-44: Nice: track access + invalidate list to refresh recents

This should surface the accessed project at the top without manual refresh.

Comment on lines +51 to +54
// Track project access to update "recent projects" list
trackProjectAccess({ projectId: project.id }).catch(console.error);
}
}, [project]);
}, [project, trackProjectAccess]);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Guard against duplicate tracking on re-renders

Depending on the identity stability of trackProjectAccess, the effect can fire more than once per project. Guard with a ref keyed by project.id and narrow the dependency to the project id.

Apply this diff in the nearest appropriate scopes:

+import { useEffect, useRef, useState } from 'react';
-    useEffect(() => {
-        if (project) {
-            startSandbox(project);
-            editorEngine.screenshot.lastScreenshotAt = project.metadata.updatedPreviewImgAt;
-            // Track project access to update "recent projects" list
-            trackProjectAccess({ projectId: project.id }).catch(console.error);
-        }
-    }, [project, trackProjectAccess]);
+    const lastTrackedProjectIdRef = useRef<string | null>(null);
+    useEffect(() => {
+        if (project) {
+            startSandbox(project);
+            editorEngine.screenshot.lastScreenshotAt = project.metadata.updatedPreviewImgAt;
+            // Track project access to update "recent projects" list (once per project id)
+            if (lastTrackedProjectIdRef.current !== project.id) {
+                lastTrackedProjectIdRef.current = project.id;
+                void trackProjectAccess({ projectId: project.id }).catch(console.error);
+            }
+        }
+    }, [project?.id]);
📝 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
// Track project access to update "recent projects" list
trackProjectAccess({ projectId: project.id }).catch(console.error);
}
}, [project]);
}, [project, trackProjectAccess]);
// At the top of the file, ensure you have React hooks imported:
import React, { useEffect, useRef, useState } from 'react';
// …inside your component…
- useEffect(() => {
- if (project) {
- startSandbox(project);
- editorEngine.screenshot.lastScreenshotAt = project.metadata.updatedPreviewImgAt;
- // Track project access to update "recent projects" list
- trackProjectAccess({ projectId: project.id }).catch(console.error);
- }
// Ref to remember which project ID we've already tracked
const lastTrackedProjectIdRef = useRef<string | null>(null);
useEffect(() => {
if (project) {
startSandbox(project);
editorEngine.screenshot.lastScreenshotAt = project.metadata.updatedPreviewImgAt;
// Track project access to update "recent projects" list (once per project id)
if (lastTrackedProjectIdRef.current !== project.id) {
lastTrackedProjectIdRef.current = project.id;
void trackProjectAccess({ projectId: project.id }).catch(console.error);
}
}
}, [project?.id]);

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/web/client/src/server/api/routers/project/project.ts (1)

407-430: IDOR closed with membership check + UUID validation; consider atomic update/returning and access-specific timestamp.

Well done on adding protectedProcedure, z.string().uuid(), and the membership check. To harden further and improve UX semantics:

  • Make the update atomic and verify one row changed (handles race where project is deleted between check and update) and use DB time to avoid clock skew.
  • Optional: avoid overloading updatedAt for “recently accessed.” Add a lastAccessedAt column and sort by it to prevent confusing modification history.

Apply within these lines:

-            await ctx.db.update(projects).set({
-                updatedAt: new Date(),
-            }).where(eq(projects.id, input.projectId));
-            
-            return { success: true };
+            const updated = await ctx.db
+                .update(projects)
+                .set({ updatedAt: sql`now()` })
+                .where(eq(projects.id, input.projectId))
+                .returning({ id: projects.id });
+
+            if (updated.length === 0) {
+                // Project was likely removed between membership check and update
+                throw new TRPCError({ code: 'NOT_FOUND' });
+            }
+            return { success: true };

Outside the selected range, add sql to the drizzle import:

import { and, eq, ne, sql } from 'drizzle-orm';

If you opt for a dedicated access timestamp later, I can propose the migration + list query change.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1189d05 and ffd79bc.

📒 Files selected for processing (1)
  • apps/web/client/src/server/api/routers/project/project.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/client/src/server/api/routers/project/project.ts (3)
apps/web/client/src/server/api/trpc.ts (1)
  • protectedProcedure (130-149)
packages/db/src/schema/user/user-project.ts (1)
  • userProjects (10-23)
packages/db/src/schema/project/project.ts (1)
  • projects (12-31)
🔇 Additional comments (1)
apps/web/client/src/server/api/routers/project/project.ts (1)

3-3: Good import.

Importing TRPCError is appropriate and used below.

displayName: user.displayName ?? displayName,
email: user.email ?? authUser.email,
avatarUrl: user.avatarUrl ?? authUser.user_metadata.avatarUrl,
avatarUrl: user.avatarUrl ?? authUser.user_metadata.avatar_url,
Copy link
Contributor

Choose a reason for hiding this comment

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

@binu-baiju Thanks for the PR! There seems to be a lot of your personal config changes. I think this is the only thing we need. Could you remove the rest? Thanks

Copy link
Author

Choose a reason for hiding this comment

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

@Kitenite Sure

Copy link
Author

Choose a reason for hiding this comment

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

@Kitenite New PR :#2778

@binu-baiju binu-baiju closed this Aug 31, 2025
@binu-baiju
Copy link
Author

Closing this PR to create a cleaner version without config changes as requested by @Kitenite.

New clean PR: [https://github.com//pull/2778]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants