Skip to content

feat: add secure folder share links - #54

Open
bmdavis419 wants to merge 4 commits into
mainfrom
agent/folder-share-links
Open

feat: add secure folder share links#54
bmdavis419 wants to merge 4 commits into
mainfrom
agent/folder-share-links

Conversation

@bmdavis419

@bmdavis419 bmdavis419 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add one durable, revocable share link for any folder in the project tree.
  • Let link recipients browse that folder and all current descendant folders, then watch ready current-version videos and read their comment threads.
  • Keep share contents dynamic: moving an item into the subtree adds it; moving it out removes it; moving the shared root preserves the link.
  • Add a dashboard share dialog for creating, copying, and revoking folder links.

Implementation

The reimplementation follows this rollout plan:

  1. Model a stable folder link separately from short-lived viewer grants.
  2. Revalidate the link, root, ancestry, team boundary, deletion state, and subtree membership on every public read.
  3. Expose intentionally small public DTOs and signed Mux playback rather than leaking internal storage, team, Clerk, or raw Mux identifiers.
  4. Build a noindex public folder browser with pagination, prewarming, breadcrumb navigation, and retry states.
  5. Integrate link lifecycle with folder moves, recursive deletion, team deletion, and scheduled grant cleanup.
  6. Cover security and lifecycle behavior with focused Convex tests plus prewarm unit tests.

Security and lifecycle

  • Only team members or higher can create, inspect, or revoke a link.
  • The durable URL token is exchanged for a 24-hour access grant (matching video shares); every protected request still reloads and validates the durable link, so revocation is instant regardless of grant lifetime.
  • Traversal is bounded and cycle-safe, and rejects cross-team or deleting ancestry.
  • Folder deletion is tombstoned before asynchronous cleanup so descendant shares stop working immediately.
  • Revocation immediately invalidates outstanding grants; physical grant cleanup is batched asynchronously.
  • Grant issuance and playback claims are rate-limited globally and per durable link and video as appropriate.
  • Playback and posters use signed, one-hour Mux URLs, including legacy rows that only have a playback ID, and refresh before expiry without losing playback position.
  • Public responses omit team IDs, Clerk IDs, storage keys, raw Mux IDs, public video IDs, and avatars.

Data model

Yes, this touches the data model:

  • Add folderShareLinks for the stable one-link-per-folder relationship.
  • Add folderShareAccessGrants for short-lived viewer sessions.
  • Add optional projects.deletionStartedAt to make asynchronous deletion immediately visible to authorization checks.
  • Add active-folder-name and ready-current-video indexes for paginated public browsing.

All additions are backward-compatible and require no backfill.

Validation

  • bun run check
  • 64 unit tests passed
  • 54 Convex tests passed
  • Focused folder-share suite: 16 tests passed
  • git diff --check
  • Independent correctness/security and UX/performance reviews
  • Final independent backend/security and frontend/runtime re-audits: no findings

Origin and credit

This is a fresh reimplementation inspired by PR #21, originally proposed and implemented by @Engm4nn. Thank you to @Engm4nn for the project-sharing concept and the first pass.

No code was cherry-picked from #21; this was rewritten from current main around folder-level sharing, nested navigation, current lifecycle behavior, and stronger public-boundary security.

Note

Add public folder share links with time-limited access grants and video playback

  • Adds a new /folder-share/$token route where recipients can browse shared project folders and watch videos without an account.
  • Members with upload permission see a 'Share folder' button in the project dashboard that opens a FolderShareDialog to create, copy, or revoke a durable share link.
  • Access is gated by short-lived grant tokens (folderShareAccessGrants) issued via folderShares.issueAccessGrant, with layered rate limiting and an hourly sweep cron for expired grants.
  • Video playback uses Mux signed URLs (1-hour sessions with embedded resolution/thumbnail JWT claims) fetched via videoActions.getFolderSharedPlaybackSession; the player auto-refreshes before expiry.
  • Comments are surfaced read-only in a one-level threaded view; replies to replies are now rejected across all comment creation mutations.
  • Folder deletion immediately marks the root with deletionStartedAt, removes its share link, and invalidates active grants; team deletion also cleans up share links.
  • Risk: schema additions (folderShareLinks, folderShareAccessGrants, deletionStartedAt on projects, new indexes) require a Convex migration deployment before the UI changes take effect.

Macroscope summarized c622d37.

Summary by CodeRabbit

  • New Features

    • Added folder sharing with durable links, access grants, and permission-aware sharing controls.
    • Added a public shared-folder experience with browsing, pagination, breadcrumbs, comments, and video playback.
    • Added video playback sessions with signed URLs, refresh handling, and rate limiting.
    • Added options to create, copy, and revoke folder share links.
    • Added automatic cleanup for expired or invalidated sharing access.
  • Bug Fixes

    • Improved folder deletion, movement, comment reply validation, and shared-content access boundaries.
    • Enhanced protection of sensitive sharing and playback information.

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
lawn Ready Ready Preview, Comment Jul 17, 2026 10:50pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds durable folder sharing with expiring grants, scoped folder/video browsing, signed playback, dashboard sharing controls, public routing, deletion invalidation, and extensive Convex tests.

Changes

Folder Sharing Feature

Layer / File(s) Summary
Schema, routing, and prewarm contracts
convex/schema.ts, app/routes/folder-share.$token.tsx, app/routeTree.gen.ts, src/lib/routes.ts, src/lib/convexRouteData.ts
Adds folder-share tables and indexes, the public route, URL construction, route prewarming keys, and sensitive-token redaction.
Folder-share access and public reads
convex/folderShares.ts, convex/folderShareAccess.ts, convex/comments.ts, convex/folderShares.vitest.ts
Implements durable links, expiring grants, scoped folder/video queries, public comment shaping, playback claims, rate limits, and lifecycle tests.
Move, deletion, and cleanup invalidation
convex/projects.ts, convex/teams.ts, convex/crons.ts
Blocks moves through deleting paths, invalidates shares during project/team deletion, and sweeps expired grants hourly.
Signed shared playback sessions
convex/mux.ts, convex/videoActions.ts, src/lib/muxUrls.test.ts
Creates signed playback and thumbnail sessions, resolves legacy playback IDs, applies Mux parameters, and handles rate limiting.
Share management UI integration
app/routes/dashboard/-project.tsx, src/components/projects/FolderShareDialog.tsx
Adds controlled share-link creation, copying, revocation, and dashboard access checks.
Shared route data and playback experience
app/routes/-folder-share.data.ts, app/routes/-folder-share.tsx, src/components/video-player/VideoPlayer.tsx
Adds shared browsing, pagination, intent prewarming, playback refresh, comments, breadcrumbs, and player readiness reporting.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProjectDashboard
  participant FolderShareDialog
  participant folderShares
  participant FolderSharePage
  participant getFolderSharedPlaybackSession
  participant VideoPlayer

  ProjectDashboard->>FolderShareDialog: open folder sharing
  FolderShareDialog->>folderShares: create or revoke durable link
  FolderSharePage->>folderShares: issue access grant
  FolderSharePage->>folderShares: load folders, videos, and comments
  FolderSharePage->>getFolderSharedPlaybackSession: claim and sign video playback
  getFolderSharedPlaybackSession-->>FolderSharePage: signed URLs and expiry
  FolderSharePage->>VideoPlayer: attach signed playback
  VideoPlayer-->>FolderSharePage: report readiness
Loading

Possibly related PRs

  • pingdotgg/lawn#34: Overlaps with nested-folder move, remove, and deletion logic in convex/projects.ts.
  • pingdotgg/lawn#49: Overlaps with the VideoPlayer props and playback event handling.
  • pingdotgg/lawn#67: Overlaps with the shared playback and VideoPlayer integration.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.12% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding secure folder share links.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/folder-share-links

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

Comment thread convex/folderShares.ts
Comment thread convex/folderShares.ts
Comment thread convex/folderShares.ts
Comment thread app/routes/-folder-share.tsx
Comment thread convex/folderShares.ts Outdated
Comment thread app/routes/dashboard/-project.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
convex/folderShareAccess.ts (1)

48-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share the grant-token length as one constant to avoid generation/validation drift.

The generated token length is hardcoded as 40 here, while resolveSharedFolder/resolveSharedVideo in convex/folderShares.ts reject tokens whose length !== FOLDER_SHARE_GRANT_TOKEN_LENGTH (also 40, but defined independently). If either value is changed without the other, every issued grant would fail the length check and all shared reads would silently resolve to null — breaking access with no error. Define the length once and reuse it in both modules.

♻️ Proposed fix
 export const FOLDER_SHARE_ACCESS_GRANT_TTL_MS = 60 * 60 * 1000;
 export const FOLDER_SHARE_ANCESTRY_WALK_LIMIT = 12;
+export const FOLDER_SHARE_GRANT_TOKEN_LENGTH = 40;
   const token = await generateUniqueToken(
-    40,
+    FOLDER_SHARE_GRANT_TOKEN_LENGTH,
     async (candidate) =>

Then in convex/folderShares.ts, import and drop the local duplicate:

 import {
   FOLDER_SHARE_ANCESTRY_WALK_LIMIT,
+  FOLDER_SHARE_GRANT_TOKEN_LENGTH,
   findFolderShareLinkByToken,
   ...
 } from "./folderShareAccess";
-const FOLDER_SHARE_GRANT_TOKEN_LENGTH = 40;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@convex/folderShareAccess.ts` around lines 48 - 72, The token length is
duplicated between issueFolderShareAccessGrant and the length check in
resolveSharedFolder/resolveSharedVideo, so unify it into a single shared
constant to prevent drift. Define or reuse FOLDER_SHARE_GRANT_TOKEN_LENGTH as
the source of truth in convex/folderShareAccess.ts for generateUniqueToken and
import that same constant into convex/folderShares.ts, removing the local
duplicate so both issuance and validation stay aligned.
app/routes/-folder-share.data.ts (1)

87-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate auto-load-more effect logic.

The folders (Lines 87-98) and videos (Lines 100-111) auto-load effects are structurally identical modulo the collection name and the load/status/ref identifiers. Extracting a small shared helper (e.g. useAutoLoadMore({ status, folderKey, collection, pageSize, memory, countRef, loadMore })) would remove the duplication and reduce the risk of the two copies drifting apart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/-folder-share.data.ts` around lines 87 - 111, The folders and
videos auto-load-more effects are duplicated in the same component and should be
consolidated. Extract the shared logic from the two useEffect blocks into a
small helper or reusable hook in the same module, parameterized by folderKey,
status, pageSize, the pagination memory ref, the restored count ref, and the
loadMore callback. Keep the behavior identical for both folders and videos while
removing the near-copy/paste logic.
src/lib/convexRouteData.test.ts (1)

33-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for the actually risky path.

Both new tests only exercise the custom-dedupeKey path. Per the concern raised in convexRouteData.ts (makeRouteQuerySpec/prewarmSpecs), the unprotected path is the default key (no dedupeKey) with sensitive args — that's the one that currently leaks into console.warn. A test asserting that scenario is not yet safe (or asserting it is fixed, once addressed) would give real regression coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/convexRouteData.test.ts` around lines 33 - 68, The current tests only
cover the custom dedupeKey path, but the risky case is the default key path in
makeRouteQuerySpec/prewarmSpecs when no dedupeKey is provided and sensitive args
are used. Add a test that exercises this default path with a bearer-like
argument and verifies whether the warning from prewarmSpecs/console.warn does
not leak the sensitive value (or documents the current leak if that path is
still unfixed). Use the existing symbols makeRouteQuerySpec, prewarmSpecs, and
resetPrewarmDedupeForTests to keep the test aligned with the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/routes/-folder-share.tsx`:
- Around line 297-314: The shared acquireGrant flow currently treats every
failure as fatal by setting grantError, which causes ShareUnavailable to replace
the entire page even when a background renewal fails. Update acquireGrant and
its callers so only the initial grant fetch can hard-fail the view, while
periodic renewals surface a non-blocking error state or retry without clearing
already loaded folder/video content. Use the existing grantRequestPendingRef,
setGrantError, and grantToken handling to distinguish initial load from renewal,
and ensure the render path that shows ShareUnavailable is only triggered for the
true bootstrap failure case.
- Around line 512-521: The breadcrumb SharedFolderLink usages currently rely on
hover:underline only, which leaves links underlined only on hover instead of by
default. Update both breadcrumb link instances in the folder share route,
including the folder-view breadcrumb, so SharedFolderLink always has an
underline while keeping the existing hover styling. Use the SharedFolderLink
component in the breadcrumb render blocks as the place to apply the class
change.
- Around line 321-330: The expiry timer in the folder-share flow is clearing the
grant state, which forces the UI to reset and re-bootstrap playback when the
token expires. Update the effect around the grant expiry handling in the
folder-share route so the timeout does not call setGrantToken(null) or otherwise
trigger the full-screen loading path; instead, keep the existing session active
and only refresh or renew the grant state in place. Use the
grantExpiresAt/grantToken effect and the loadPlayback path as the main places to
adjust.

In `@src/lib/convexRouteData.ts`:
- Around line 21-32: The default key generation in makeRouteQuerySpec still
serializes raw args via buildQueryKey(getFunctionName(query), args), so those
values can leak through console.warn even when redactErrorDetails is enabled.
Change the RouteQuerySpec construction so redaction is explicit for callers
without a dedupeKey, or replace the fallback key with a non-sensitive identifier
that does not embed args. Update makeRouteQuerySpec and any callers that rely on
the default key behavior to ensure prewarm failures never log sensitive request
data.

---

Nitpick comments:
In `@app/routes/-folder-share.data.ts`:
- Around line 87-111: The folders and videos auto-load-more effects are
duplicated in the same component and should be consolidated. Extract the shared
logic from the two useEffect blocks into a small helper or reusable hook in the
same module, parameterized by folderKey, status, pageSize, the pagination memory
ref, the restored count ref, and the loadMore callback. Keep the behavior
identical for both folders and videos while removing the near-copy/paste logic.

In `@convex/folderShareAccess.ts`:
- Around line 48-72: The token length is duplicated between
issueFolderShareAccessGrant and the length check in
resolveSharedFolder/resolveSharedVideo, so unify it into a single shared
constant to prevent drift. Define or reuse FOLDER_SHARE_GRANT_TOKEN_LENGTH as
the source of truth in convex/folderShareAccess.ts for generateUniqueToken and
import that same constant into convex/folderShares.ts, removing the local
duplicate so both issuance and validation stay aligned.

In `@src/lib/convexRouteData.test.ts`:
- Around line 33-68: The current tests only cover the custom dedupeKey path, but
the risky case is the default key path in makeRouteQuerySpec/prewarmSpecs when
no dedupeKey is provided and sensitive args are used. Add a test that exercises
this default path with a bearer-like argument and verifies whether the warning
from prewarmSpecs/console.warn does not leak the sensitive value (or documents
the current leak if that path is still unfixed). Use the existing symbols
makeRouteQuerySpec, prewarmSpecs, and resetPrewarmDedupeForTests to keep the
test aligned with the implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9916d080-1da4-4e84-b3bb-c0b5d9241c38

📥 Commits

Reviewing files that changed from the base of the PR and between fc6b979 and dff37a2.

⛔ Files ignored due to path filters (1)
  • convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (22)
  • app/routeTree.gen.ts
  • app/routes/-folder-share.data.ts
  • app/routes/-folder-share.tsx
  • app/routes/dashboard/-project.tsx
  • app/routes/folder-share.$token.tsx
  • convex/crons.ts
  • convex/folderShareAccess.ts
  • convex/folderShares.ts
  • convex/folderShares.vitest.ts
  • convex/mux.ts
  • convex/projects.ts
  • convex/schema.ts
  • convex/teams.ts
  • convex/videoActions.ts
  • src/components/projects/FolderShareDialog.tsx
  • src/components/video-player/VideoPlayer.tsx
  • src/lib/convexRouteData.test.ts
  • src/lib/convexRouteData.ts
  • src/lib/folderShareNavigation.test.ts
  • src/lib/folderShareNavigation.ts
  • src/lib/routes.test.ts
  • src/lib/routes.ts

Comment thread app/routes/-folder-share.tsx
Comment thread app/routes/-folder-share.tsx Outdated
Comment thread app/routes/-folder-share.tsx
Comment thread src/lib/convexRouteData.ts
Comment thread app/routes/-folder-share.tsx Outdated
Comment thread convex/folderShares.ts
Comment thread convex/videoActions.ts
Comment thread app/routes/-folder-share.tsx
Align the folder share system with the existing single-video share
patterns instead of introducing parallel machinery:

- Use the same 24h access-grant TTL as video shares and drop the
  in-place renewal mutation, its three rate-limit buckets, and the
  client renewal/retention loop. Revocation stays instant because every
  public read re-loads the durable link.
- Remove the bespoke focus-restore and pagination-memory navigation
  layer; no other page in the app restores focus or page depth.
- Remove the shadow first-page queries that duplicated each
  usePaginatedQuery subscription during initial load.
- Drop the unused RouteQuerySpec.redactErrorDetails field and document
  why buildQueryKey fingerprints serialized args.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
app/routes/-folder-share.tsx (2)

249-273: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Discard grant responses from obsolete route tokens.

Resetting grantRequestPendingRef allows the new token request to overlap the old one, but either response can subsequently set grantToken. If the old request resolves last, the page queries and displays the previous share under the new URL.

Proposed request-sequence guard
 const grantRequestPendingRef = useRef(false);
+const grantRequestSequenceRef = useRef(0);

 useEffect(() => {
+  grantRequestSequenceRef.current += 1;
   setGrantToken(null);
   setHasAttemptedGrant(false);
   setGrantError(false);
   grantRequestPendingRef.current = false;
 }, [token]);

 const acquireGrant = useCallback(async () => {
   if (grantRequestPendingRef.current) return;
+  const requestSequence = grantRequestSequenceRef.current + 1;
+  grantRequestSequenceRef.current = requestSequence;
   grantRequestPendingRef.current = true;
   setHasAttemptedGrant(true);
   setGrantError(false);
   try {
     const result = await issueAccessGrant({ token });
+    if (requestSequence !== grantRequestSequenceRef.current) return;
     if (result.ok && result.grantToken) {
       setGrantToken(result.grantToken);
     } else {
       setGrantError(true);
     }
   } catch {
-    setGrantError(true);
+    if (requestSequence === grantRequestSequenceRef.current) {
+      setGrantError(true);
+    }
   } finally {
-    grantRequestPendingRef.current = false;
+    if (requestSequence === grantRequestSequenceRef.current) {
+      grantRequestPendingRef.current = false;
+    }
   }
 }, [issueAccessGrant, token]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/-folder-share.tsx` around lines 249 - 273, Guard grant responses
in acquireGrant against the route token that initiated the request, so results
from obsolete tokens cannot update grantToken or grantError after token changes.
Capture the current token/request sequence before issueAccessGrant and apply
state updates only when it still matches the active token, while preserving the
existing pending reset and error behavior.

447-449: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Underline the remaining plain navigation links by default.

The two lawn. links and footer link are only visually emphasized through hover color. Add underline to their base classes.

As per coding guidelines, “Links should use underlines, not color-only differentiation” and “Do not hide information behind hover states.”

Also applies to: 633-635, 768-772

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/-folder-share.tsx` around lines 447 - 449, Update the Link
elements containing the “lawn.” branding and the footer link to include the
underline utility in their base className, covering the instances near the shown
Link and the additional locations noted in the comment. Preserve their existing
hover styling and navigation targets.

Source: Coding guidelines

🧹 Nitpick comments (2)
src/lib/convexRouteData.ts (1)

27-29: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Canonicalize arguments before fingerprinting.

JSON.stringify is insertion-order-sensitive, so equivalent argument objects with different key order produce different fingerprints and bypass prewarmSpecs deduplication. Use a canonical serializer that recursively sorts object keys, and add a reversed-key-order test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/convexRouteData.ts` around lines 27 - 29, Update buildQueryKey to
canonicalize convexToJson arguments before fingerprinting by recursively sorting
object keys, ensuring equivalent objects with different insertion order produce
the same serialized representation. Add a test covering reversed key order and
confirming prewarmSpecs deduplication.
app/routes/-folder-share.tsx (1)

88-111: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove decorative shadows and the video-card gradient.

The folder card, video card, and player frame are primary UI elements but use several shadow-* classes; Line 144 also adds a radial gradient. Increase the folder card’s p-4 to the prescribed generous padding while adjusting these styles.

As per coding guidelines, “Do not use gradients or shadows in primary UI elements” and “Use generous padding (p-6 to p-8) for spacing.”

Also applies to: 129-168, 503-540

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/routes/-folder-share.tsx` around lines 88 - 111, Update SharedFolderCard
and the related video card and player frame sections to remove all shadow-*
classes and the radial gradient near the video card. Increase the folder card
padding from p-4 to the prescribed generous spacing, using p-6 or p-8, while
preserving the existing layout and hover/focus behavior otherwise.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/routes/-folder-share.tsx`:
- Around line 249-273: Guard grant responses in acquireGrant against the route
token that initiated the request, so results from obsolete tokens cannot update
grantToken or grantError after token changes. Capture the current token/request
sequence before issueAccessGrant and apply state updates only when it still
matches the active token, while preserving the existing pending reset and error
behavior.
- Around line 447-449: Update the Link elements containing the “lawn.” branding
and the footer link to include the underline utility in their base className,
covering the instances near the shown Link and the additional locations noted in
the comment. Preserve their existing hover styling and navigation targets.

---

Nitpick comments:
In `@app/routes/-folder-share.tsx`:
- Around line 88-111: Update SharedFolderCard and the related video card and
player frame sections to remove all shadow-* classes and the radial gradient
near the video card. Increase the folder card padding from p-4 to the prescribed
generous spacing, using p-6 or p-8, while preserving the existing layout and
hover/focus behavior otherwise.

In `@src/lib/convexRouteData.ts`:
- Around line 27-29: Update buildQueryKey to canonicalize convexToJson arguments
before fingerprinting by recursively sorting object keys, ensuring equivalent
objects with different insertion order produce the same serialized
representation. Add a test covering reversed key order and confirming
prewarmSpecs deduplication.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f06d0b78-c039-41d8-ae35-988ce83ef59b

📥 Commits

Reviewing files that changed from the base of the PR and between 87ca159 and c622d37.

📒 Files selected for processing (7)
  • app/routes/-folder-share.data.ts
  • app/routes/-folder-share.tsx
  • convex/folderShareAccess.ts
  • convex/folderShares.ts
  • convex/folderShares.vitest.ts
  • src/lib/convexRouteData.test.ts
  • src/lib/convexRouteData.ts
💤 Files with no reviewable changes (3)
  • src/lib/convexRouteData.test.ts
  • convex/folderShares.ts
  • convex/folderShares.vitest.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • convex/folderShareAccess.ts

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant