Skip to content

Conversation

@binu-baiju
Copy link

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

  • Add trackAccess mutation with proper authorization checks
  • Integrate project access tracking to refresh recent projects list

Closes #2760

Description

Fixed critical bug in the Onlook web application affecting user experience:

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. Added trackAccess mutation with proper authorization checks to update project timestamps when accessed.

Related Issues

Closes #2760

Type of Change

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

Testing

Manual Testing Performed:

Recent Projects Refresh:

  • Opened an older project from the list
  • Navigated back to projects page
  • Verified the accessed project moved to the top of recent projects list
  • Confirmed UI updates immediately without manual refresh

Environment: Local development setup with Supabase backend, Windows 11 with Docker Desktop, tested in Chrome browser

Screenshots (if applicable)

None required - functional bug fix

Additional Notes

  • Applied proper authorization checks to prevent unauthorized project access
  • Used targeted, minimal changes to avoid unnecessary side effects
  • Backward compatible and doesn't affect existing functionality
  • Security-focused implementation with user validation in trackAccess mutation

Summary by CodeRabbit

  • New Features
    • Added project access tracking to record when a project is opened.
    • Client now triggers access tracking automatically when a project loads (fire-and-forget).
    • Implemented permission validation so only authorized users can record project access.

@vercel
Copy link

vercel bot commented Aug 31, 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.

@coderabbitai
Copy link

coderabbitai bot commented Aug 31, 2025

Walkthrough

Adds a server mutation to record project access (validates membership and updates updatedAt) and a client-side hook call that invokes this mutation and invalidates the project list cache when a project is opened.

Changes

Cohort / File(s) Summary
Server: project access mutation
apps/web/client/src/server/api/routers/project/project.ts
Added trackAccess protected mutation that validates the user's membership for projectId, updates the project's updatedAt timestamp on success, and throws TRPCError FORBIDDEN for unauthorized access.
Client: hook invocation & cache invalidation
apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
Added api.project.trackAccess.useMutation usage, exposed mutateAsync as trackProjectAccess, called trackProjectAccess({ projectId }) inside a useEffect (fire-and-forget .catch(console.error)), and added the mutation to the effect dependency array.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Hook as useStartProject
    participant Client as TRPC Client
    participant Server as project.trackAccess
    participant DB as Database
    participant Cache as Query Cache

    User->>Hook: Open project
    Hook->>Client: trackProjectAccess(projectId)
    Client->>Server: RPC trackAccess(input: { projectId })
    Server->>DB: Check userProjects for membership
    alt membership exists
        Server->>DB: Update project.updatedAt
        DB-->>Server: ok
        Server-->>Client: { success: true }
        Client->>Cache: invalidate api.project.list
        Cache->>Cache: refetch latest projects
    else no membership
        Server-->>Client: TRPCError FORBIDDEN
        Client->>Client: .catch(console.error) (fire-and-forget)
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐇 I hopped in, nudged the timestamp bright,
A tiny thump to mark the night.
I whisper "access" to the log,
The list refreshes through the fog.
Newest hops now gleam in sight. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Linked Issues Check ⚠️ Warning Issue #2760 describes two distinct problems: (1) GitHub profile pictures not loading for GitHub-authenticated users, and (2) the recent projects list not updating when projects are accessed. The PR claims to close this issue but only implements a solution for problem (2)—the trackAccess mutation updates project timestamps to refresh the recent projects list. The GitHub avatar loading issue remains completely unaddressed in this changeset, meaning the PR does not fully satisfy the requirements of the linked issue it claims to close.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The pull request title "fix: resolve recent projects refresh issues when accessed" clearly and specifically describes the main change: adding functionality to update the recent projects list when projects are accessed. The title is concise, uses standard commit conventions, and accurately reflects the core objective without unnecessary noise or vague terms. A reviewer can immediately understand that this PR addresses the recent projects refresh bug.
Out of Scope Changes Check ✅ Passed The code changes are tightly focused and directly address the stated objective of fixing the recent projects refresh issue. The additions include a new trackAccess mutation in the project router with proper authorization checks and integration into the use-start-project hook to trigger access tracking. All modifications are related to the project access tracking functionality and do not introduce unrelated refactoring, tooling changes, or feature additions outside the scope of refreshing recent projects.
Description Check ✅ Passed The pull request description follows the repository template structure well, including all major sections: Description, Related Issues, Type of Change, Testing, Screenshots, and Additional Notes. The author provides clear context about the bug being fixed, explains the solution approach, specifies the exact testing performed with environment details, and includes relevant security notes. The description contains substantive information rather than generic placeholders.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • 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

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

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 (4)
apps/web/client/src/server/api/routers/project/project.ts (3)

426-431: Avoid conflating “last accessed” with project.updatedAt (per-user recency).

Updating projects.updatedAt on access will:

  • Reorder the project for all members, not just the accessing user.
  • Overload “updatedAt” (mutations vs. access), which can ripple into any feature relying on it.

Recommended direction (follow-up, requires migration):

  • Add lastAccessedAt timestamp to user_projects (per user, per project).
  • Update that field here; sort recent lists by user_projects.lastAccessedAt.

If the column existed, this block would look like:

- await ctx.db.update(projects).set({
-     updatedAt: new Date(),
- }).where(eq(projects.id, input.projectId));
+ await ctx.db.update(userProjects).set({
+   // use DB time if available to avoid clock skew (see next comment)
+   lastAccessedAt: new Date(),
+ }).where(and(
+   eq(userProjects.userId, ctx.user.id),
+   eq(userProjects.projectId, input.projectId),
+ ));

Happy to provide a minimal Drizzle migration and list() query update if you want to pursue this in this PR or a follow-up.


426-429: Prefer database time to avoid clock skew (minor).

Using application time can drift across servers. If you have drizzle’s sql available, consider delegating to the DB.

-import { and, eq, ne } from 'drizzle-orm';
+import { and, eq, ne, sql } from 'drizzle-orm';- await ctx.db.update(projects).set({
-   updatedAt: new Date(),
- }).where(eq(projects.id, input.projectId));
+ await ctx.db.update(projects).set({
+   updatedAt: sql`now()`,
+ }).where(eq(projects.id, input.projectId));

165-182: Guard against limit-before-sort if future callers use limit
Our search didn’t find any calls to project.list passing a limit, so this issue isn’t triggered today. If you add a limit later, either remove it from the DB query and slice after sorting in code (Option A) or, preferably, add an orderBy on updatedAt in SQL and keep your limit (Option B).

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

50-54: Surface failures via onError instead of per-call .catch (minor UX).

Centralizing error handling on the mutation keeps effects clean and makes failures visible to users.

-const { mutateAsync: trackProjectAccess } = api.project.trackAccess.useMutation({
-    onSuccess: () => {
-        // Invalidate project list to refresh recent projects
-        apiUtils.project.list.invalidate();
-    },
-});
+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.error('Failed to record recent activity', {
+      description: err instanceof Error ? err.message : 'Unknown error',
+    });
+  },
+});- trackProjectAccess({ projectId: project.id }).catch(console.error);
+ void trackProjectAccess({ projectId: project.id });
📜 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 914b4b0 and 360f9fd.

📒 Files selected for processing (2)
  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1 hunks)
  • apps/web/client/src/server/api/routers/project/project.ts (2 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 (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 (2)
apps/web/client/src/server/api/routers/project/project.ts (1)

408-424: Access control: solid membership check and correct error semantics.

Verifying membership in userProjects before mutating state and throwing TRPCError(FORBIDDEN) is the right call. This prevents probing and keeps error semantics tight under protectedProcedure.

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

39-44: Good integration: fire-and-forget mutation with list invalidation.

Hook wiring looks correct; onSuccess invalidation will refresh the recent projects list as intended.

@binu-baiju binu-baiju changed the title fix: resolve GitHub profile pictures and recent projects refresh issues fix: resolve recent projects refresh issues when accessed Aug 31, 2025
binu-baiju and others added 2 commits October 21, 2025 11:48
- Fix GitHub profile picture display by using correct avatar_url property
- Add trackAccess mutation with proper authorization checks
- Integrate project access tracking to refresh recent projects list

Closes onlook-dev#2760
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: 1

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

41-45: Consider extracting the mutation options to avoid potential re-creation.

The inline options object is recreated on every render. While tRPC typically handles this correctly, extracting the options or using useCallback for the onSuccess handler ensures the mutation hook remains stable and avoids potential edge cases.

Apply this diff to extract the callback:

+    const handleTrackAccessSuccess = useCallback(() => {
+        apiUtils.project.list.invalidate();
+    }, [apiUtils]);
+
-    const { mutateAsync: trackProjectAccess } = api.project.trackAccess.useMutation({
-        onSuccess: () => {
-            apiUtils.project.list.invalidate();
-        },
-    });
+    const { mutateAsync: trackProjectAccess } = api.project.trackAccess.useMutation({
+        onSuccess: handleTrackAccessSuccess,
+    });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 587db4c and 1028bfd.

📒 Files selected for processing (1)
  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
apps/web/client/src/app/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/app/**/*.tsx: Default to Server Components; add 'use client' when using events, state/effects, browser APIs, or client‑only libraries
Do not use process.env in client code; import env from @/env instead

Avoid hardcoded user-facing text; use next-intl messages/hooks

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
apps/web/client/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.{ts,tsx}: Use path aliases @/* and ~/* for imports that map to apps/web/client/src/*
Avoid hardcoded user-facing text; use next-intl messages/hooks instead

Use path aliases @/* and ~/* for imports mapping to src/*

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
apps/web/client/src/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable references across renders
Keep the active MobX store in a useRef and perform async cleanup with setTimeout(() => storeRef.current?.clear(), 0) to avoid route-change races
Avoid useMemo for creating MobX store instances
Avoid putting the MobX store instance in effect dependency arrays if it causes loops; split concerns by domain

apps/web/client/src/**/*.tsx: Create MobX store instances with useState(() => new Store()) for stable identities across renders
Keep the active MobX store in a useRef and clean up asynchronously with setTimeout(() => storeRef.current?.clear(), 0)
Do not use useMemo to create MobX stores
Avoid placing MobX store instances in effect dependency arrays if it causes loops; split concerns instead
observer components must be client components; place a single client boundary at the feature entry; child observers need not repeat 'use client'

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not use the any type unless necessary

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
apps/web/client/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Default to Server Components; add 'use client' only when using events, state/effects, browser APIs, or client-only libs

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
{apps,packages}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Avoid using the any type unless absolutely necessary

Files:

  • apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx
🧬 Code graph analysis (1)
apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1)
apps/web/client/src/trpc/react.tsx (1)
  • api (23-23)
🔇 Additional comments (1)
apps/web/client/src/app/project/[id]/_hooks/use-start-project.tsx (1)

62-64: Remove trackProjectAccess from the dependency array or memoize the mutation to prevent excessive re-renders.

The mutateAsync function from useMutation is not referentially stable, meaning the mutation object recreates on each render. Including trackProjectAccess in the dependency array causes the effect to run on every render rather than only when projectId changes, triggering redundant tracking mutations.

Recommended fixes:

  1. Remove trackProjectAccess from deps and rely only on editorEngine.projectId (if mutation stability is not critical for this effect)
  2. Wrap the effect logic in useCallback to memoize the tracking call
  3. Move the mutation to a stable reference using useRef and async cleanup pattern

The current fire-and-forget error handling also silently swallows errors; consider logging them for debugging tracking issues.

[mandatory_refactors_recommended]

Comment on lines +62 to +64
useEffect(() => {
trackProjectAccess({ projectId: editorEngine.projectId }).catch(console.error);
}, [editorEngine.projectId, trackProjectAccess]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fire-and-forget error handling silences all failures.

The .catch(console.error) pattern silently swallows errors from the tracking mutation. While this may be acceptable for non-critical tracking, consider whether authorization failures, network errors, or invalid project IDs should be handled more explicitly (e.g., with a toast notification for unexpected errors).

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.

[bug] Profile pics don't seem to load-in + latest projects in editor aren't latest

2 participants