Skip to content

Conversation

@ameer2468
Copy link
Contributor

@ameer2468 ameer2468 commented Sep 2, 2025

This PR: we now cache Rive folders for a much better experience, no more constant initial loading for all folders art when you open the new folder dialog.

Summary by CodeRabbit

  • New Features

    • Interactive folder previews in New Folder and Subfolder dialogs: hover to open/close animations.
    • Dialog visuals now use dynamic animated asset files so each folder instance can receive its own animation.
    • Theme-aware updates ensure previews adapt when theme/color changes.
  • Refactor

    • Centralized per-instance animation handling for more reliable controls.
    • Layout adjustments for consistent scaling and crisper folder animations.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 2, 2025

Warning

Rate limit exceeded

@ameer2468 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 7 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between fc50cef and 2b45260.

📒 Files selected for processing (2)
  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (5 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (7 hunks)

Walkthrough

Replaces static Rive asset usage with an injectable RiveFile across folder components, adds layout options, updates forwardRef signatures, and integrates Rive into NewFolderDialog and SubfolderDialog. Dialogs load a shared Rive file, convert folder options to functions that accept riveFile, centralize per-option refs, clone elements with refs, and wire hover handlers to play/stop animations.

Changes

Cohort / File(s) Summary
Folder components: dynamic RiveFile + layout
apps/web/app/(org)/dashboard/caps/components/Folders.tsx
Switch from static src to injected riveFile prop for NormalFolder/BlueFolder/RedFolder/YellowFolder; update forwardRef signatures to accept `{ riveFile: RiveFile
NewFolderDialog: Rive integration and centralized refs
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
Import RiveFile and useRiveFile and load /rive/dashboard.riv; refactor FolderOptions so each option exposes component(riveFile) returning an element; initialize a centralized folderRefs map (one ref per option); render options via option.component(riveFile) and React.cloneElement to attach per-option refs; change map keys to rive-${option.value}; add onMouseEnter/onMouseLeave that stop/play folder-open/folder-close; update types and pass riveFile into folder components.
SubfolderDialog: Rive integration and ref centralization
apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx
Add RiveFile and useRiveFile, load /rive/dashboard.riv and pass riveFile into option components; convert FolderOptions to function components (riveFile) => <ColorFolder riveFile={riveFile} />; introduce typed folderRefs map initialized via createRef for each option; render via option.component(riveFile) and React.cloneElement with per-option refs; use keys rive-<value>; wire hover handlers to call stop()/`play("folder-open"

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant D as Dialog (New/Subfolder)
  participant RF as useRiveFile
  participant F as Folder Component
  participant R as Rive Runtime

  U->>D: Open dialog
  D->>RF: useRiveFile({ src: "/rive/dashboard.riv" })
  RF-->>D: riveFile (or undefined)
  loop For each option
    D->>F: component(riveFile)
    F->>R: useRive({ riveFile, layout: Fit.Contain })
    note right of F #e6f7ff: Forward ref exposes play/stop
    D->>D: store ref in folderRefs[option.value]
  end

  U->>D: Hover option
  D->>D: folderRefs[value].current.play("folder-open")
  D->>R: Rive animation runs

  U->>D: Mouse leave
  D->>D: folderRefs[value].current.play("folder-close")
  D->>R: Rive animation runs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I twitch my whiskers, files in tow,
A Rive hops in, ready for show.
Refs in a row, they open and close,
Tiny animations where the dashboard glows.
Thump-thump—rabbit cheers, and off I go! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cache-folders-rive

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

🧹 Nitpick comments (6)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (3)

13-13: Use a type-only import for RiveFile to avoid bundling runtime code.
This also lets you drop the unsafe cast later.

-import { RiveFile, useRiveFile } from "@rive-app/react-canvas";
+import type { RiveFile } from "@rive-app/react-canvas";
+import { useRiveFile } from "@rive-app/react-canvas";

33-35: Model options with component constructors instead of lambdas to remove cloneElement/ref friction.
Storing the component itself simplifies ref typing and avoids extra element cloning.

-  component: (rivefile: RiveFile | undefined) => (
-    <NormalFolder riveFile={rivefile} />
-  ),
+  component: NormalFolder,

Repeat for Blue/Red/Yellow. Then render with:

- {React.cloneElement(option.component(riveFile), { ref: folderRefs.current[option.value] })}
+ {React.createElement(option.component, {
+   riveFile,
+   ref: folderRefs.current[option.value],
+ })}

And tighten the option type:

const FolderOptions = [
  { value: "normal", label: "Normal", component: NormalFolder },
  // ...
] as const satisfies readonly {
  value: "blue" | "red" | "yellow" | "normal";
  label: string;
  component: React.ForwardRefExoticComponent<
    { riveFile: RiveFile | undefined } & React.RefAttributes<FolderHandle>
  >;
}[];

Also applies to: 40-42, 47-49, 54-56


133-185: Use stable keys and remove the unnecessary type cast.

  • Prefer a semantic key for stability.
  • You already typed component to accept undefined; no need to force-cast.
- key={`rive-instance-${idx.toString()}`}
+ key={`rive-instance-${option.value}`}
- {React.cloneElement(
-   option.component(riveFile as RiveFile),
-   { ref: folderRefs.current[option.value] },
- )}
+ {React.cloneElement(
+   option.component(riveFile),
+   { ref: folderRefs.current[option.value] },
+ )}

If you apply the earlier “constructor” refactor, switch to createElement as shown there.

apps/web/app/(org)/dashboard/caps/components/Folders.tsx (3)

3-3: Make RiveFile a type-only import to keep the client bundle lean.

-import { Fit, Layout, RiveFile, useRive } from "@rive-app/react-canvas";
+import type { RiveFile } from "@rive-app/react-canvas";
+import { Fit, Layout, useRive } from "@rive-app/react-canvas";

50-62: Reduce duplication with a small factory for color-specific folders.
All three components are identical except the artboard. A factory improves maintainability.

Example (outside selected lines):

function makeFolder(artboard: string) {
  return React.forwardRef<FolderHandle, { riveFile: RiveFile | undefined }>(
    (props, ref) => {
      const { rive, RiveComponent } = useRive({
        riveFile: props.riveFile,
        artboard,
        animations: "idle",
        autoplay: false,
        layout: new Layout({ fit: Fit.Contain }),
      });
      useImperativeHandle(ref, () => ({
        play: (name) => { if (rive) rive.play(name); },
        stop: () => { if (rive) rive.stop(); },
      }), [rive]);
      return <RiveComponent className="w-[50px] h-[50px]" />;
    },
  );
}

export const BlueFolder = makeFolder("folder-blue");
export const RedFolder = makeFolder("folder-red");
export const YellowFolder = makeFolder("folder-yellow");

Also applies to: 82-94, 114-126


166-173: Consider injecting riveFile into AllFolders for consistency and caching.
To fully realize the “shared/cached Rive file” goal, mirror the other components and accept a riveFile prop, falling back to src only if undefined.

Sketch:

-interface AllFoldersProps {
-  color: "normal" | "blue" | "red" | "yellow";
-  className?: string;
-}
+interface AllFoldersProps {
+  color: "normal" | "blue" | "red" | "yellow";
+  className?: string;
+  riveFile?: RiveFile;
+}
...
-const { rive, RiveComponent: AllFoldersRive } = useRive({ src: "/rive/dashboard.riv", artboard, ... });
+const { rive, RiveComponent: AllFoldersRive } = useRive({
+  riveFile: props.riveFile,
+  src: props.riveFile ? undefined : "/rive/dashboard.riv",
+  artboard,
+  animations: "idle",
+  autoplay: false,
+  layout: new Layout({ fit: Fit.Contain }),
+});
📜 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 57c45f2 and bd83186.

📒 Files selected for processing (2)
  • apps/web/app/(org)/dashboard/caps/components/Folders.tsx (5 hunks)
  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for client-side server state and data fetching in the web app
Mutations should call Server Actions and perform precise cache updates with setQueryData/setQueriesData, avoiding broad invalidations
Prefer Server Components for initial data and pass initialData to client components for React Query hydration

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folders.tsx
{apps/web,packages/ui}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{apps/web,packages/ui}/**/*.{ts,tsx}: Use Tailwind CSS exclusively for styling in the web app and shared React UI components
Component naming: React components in PascalCase; hooks in camelCase starting with 'use'

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folders.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; prefer shared types from packages

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
  • apps/web/app/(org)/dashboard/caps/components/Folders.tsx
🧬 Code graph analysis (2)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (1)
apps/web/app/(org)/dashboard/caps/components/Folders.tsx (4)
  • NormalFolder (12-48)
  • BlueFolder (50-80)
  • RedFolder (82-112)
  • YellowFolder (114-144)
apps/web/app/(org)/dashboard/caps/components/Folders.tsx (1)
apps/web/app/(org)/dashboard/Contexts.tsx (1)
  • useTheme (40-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (2)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (1)

71-73: Verify this component is marked as a Client Component.
You’re using useRiveFile/useRouter/useState. If this file lacks "use client" (or isn’t wrapped by a client parent), it will crash at runtime.

Add if missing at the very top:

"use client";
apps/web/app/(org)/dashboard/caps/components/Folders.tsx (1)

12-15: NormalFolder changes LGTM with theme-aware artboard injection.
The forwardRef typing and useRive riveFile injection look correct.

Also applies to: 18-18

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 (6)
apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (6)

15-15: Use a type-only import for RiveFile to avoid bundling it at runtime.
This trims the bundle and is clearer intent.

-import { RiveFile, useRiveFile } from "@rive-app/react-canvas";
+import type { RiveFile } from "@rive-app/react-canvas";
+import { useRiveFile } from "@rive-app/react-canvas";

41-43: Consistent param name: riveFile (camelCase)
Tiny readability/consistency nit; matches prop name and type.

- component: (rivefile: RiveFile | undefined) => (
-   <NormalFolder riveFile={rivefile} />
+ component: (riveFile: RiveFile | undefined) => (
+   <NormalFolder riveFile={riveFile} />
 ),
- component: (rivefile: RiveFile | undefined) => (
-   <BlueFolder riveFile={rivefile} />
+ component: (riveFile: RiveFile | undefined) => (
+   <BlueFolder riveFile={riveFile} />
 ),
- component: (rivefile: RiveFile | undefined) => (
-   <RedFolder riveFile={rivefile} />
+ component: (riveFile: RiveFile | undefined) => (
+   <RedFolder riveFile={riveFile} />
 ),
- component: (rivefile: RiveFile | undefined) => (
-   <YellowFolder riveFile={rivefile} />
+ component: (riveFile: RiveFile | undefined) => (
+   <YellowFolder riveFile={riveFile} />
 ),

Optional simplification (avoids cloneElement): store components instead of factory fns:

// in options
{ value: "normal", label: "Normal", Component: NormalFolder as React.ForwardRefExoticComponent<any> }
// in render
const Cmp = option.Component;
<Cmp ref={folderRefs.current[option.value]} riveFile={riveFile} />

Also applies to: 48-50, 55-57, 62-64


147-155: Let TS infer the map callback types.
The explicit annotation is noisy and can drift from FolderOptions.

- ( 
-   option: {
-     value: "blue" | "red" | "yellow" | "normal";
-     label: string;
-     component: (riveFile: RiveFile | undefined) => React.JSX.Element;
-   },
-   idx: number,
- ) => {
+(option, idx) => {

164-164: Use a stable key (value) instead of index.
Prevents unnecessary remounts on reordering.

- key={`rive-instance-${idx.toString()}`}
+ key={`rive-${option.value}`}

187-192: Remove unnecessary type assertion and rely on the stricter ref typing.
Avoids masking a potentially undefined riveFile and keeps types honest.

- {React.cloneElement(
-   option.component(riveFile as RiveFile),
-   { ref: folderRefs.current[option.value] },
- )}
+ {React.cloneElement(option.component(riveFile), {
+   ref: folderRefs.current[option.value],
+ })}

91-105: Strongly type folderRefs as React.RefObject<FolderHandle> and initialize them upfront
Remove the useEffect and replace your useRef with:

- const folderRefs = useRef<
-   Record<(typeof FolderOptions)[number]["value"], any>
- >({
-   blue: null,
-   red: null,
-   yellow: null,
-   normal: null,
- });
- useEffect(() => {
-   FolderOptions.forEach((option) => {
-     if (!folderRefs.current[option.value]) {
-       folderRefs.current[option.value] = React.createRef();
-     }
-   });
- }, []);
+ import type { FolderHandle } from "../../../caps/components/Folders";
+
+ const folderRefs = useRef<
+   Record<
+     (typeof FolderOptions)[number]["value"],
+     React.RefObject<FolderHandle>
+   >
+ >({
+   normal: React.createRef<FolderHandle>(),
+   blue: React.createRef<FolderHandle>(),
+   red: React.createRef<FolderHandle>(),
+   yellow: React.createRef<FolderHandle>(),
+ });

Verified that FolderHandle is exported from caps/components/Folders.tsx.

📜 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 bd83186 and a3797a4.

📒 Files selected for processing (1)
  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for client-side server state and data fetching in the web app
Mutations should call Server Actions and perform precise cache updates with setQueryData/setQueriesData, avoiding broad invalidations
Prefer Server Components for initial data and pass initialData to client components for React Query hydration

Files:

  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx
{apps/web,packages/ui}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{apps/web,packages/ui}/**/*.{ts,tsx}: Use Tailwind CSS exclusively for styling in the web app and shared React UI components
Component naming: React components in PascalCase; hooks in camelCase starting with 'use'

Files:

  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; prefer shared types from packages

Files:

  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx
🧬 Code graph analysis (1)
apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (1)
apps/web/app/(org)/dashboard/caps/components/Folders.tsx (4)
  • NormalFolder (12-48)
  • BlueFolder (50-80)
  • RedFolder (82-112)
  • YellowFolder (114-144)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (1)
apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (1)

80-83: Good call using useRiveFile — this achieves the cache objective.
No issues spotted here.

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

♻️ Duplicate comments (2)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (2)

85-96: LGTM – refs eagerly initialized.

This addresses the earlier “refs created after first render” issue.


97-104: Remove redundant ref-seeding effect.

Refs are already eagerly initialized; this effect is dead code.

-useEffect(() => {
-	FolderOptions.forEach((option) => {
-		if (!folderRefs.current[option.value]) {
-			folderRefs.current[option.value] = React.createRef();
-		}
-	});
-}, []);
🧹 Nitpick comments (7)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (7)

13-13: Import RiveFile as a type-only import.

Avoid value import for a type; keeps tree-shaking clean and prevents accidental runtime import.

-import { RiveFile, useRiveFile } from "@rive-app/react-canvas";
+import type { RiveFile } from "@rive-app/react-canvas";
+import { useRiveFile } from "@rive-app/react-canvas";

21-27: Type-only import for FolderHandle.

FolderHandle is used purely as a type here; mark it accordingly.

-import {
-	BlueFolder,
-	FolderHandle,
-	NormalFolder,
-	RedFolder,
-	YellowFolder,
-} from "./Folders";
+import {
+	BlueFolder,
+	NormalFolder,
+	RedFolder,
+	YellowFolder,
+	type FolderHandle,
+} from "./Folders";

35-64: Store component types instead of element-returning functions.

Keeping component constructors in config removes the need for cloneElement, simplifies refs, and tightens types.

-const FolderOptions = [
+const FolderOptions = [
 	{
 		value: "normal",
 		label: "Normal",
-		component: (rivefile: RiveFile | undefined) => (
-			<NormalFolder riveFile={rivefile} />
-		),
+		Component: NormalFolder,
 	},
 	{
 		value: "blue",
 		label: "Blue",
-		component: (rivefile: RiveFile | undefined) => (
-			<BlueFolder riveFile={rivefile} />
-		),
+		Component: BlueFolder,
 	},
 	{
 		value: "red",
 		label: "Red",
-		component: (rivefile: RiveFile | undefined) => (
-			<RedFolder riveFile={rivefile} />
-		),
+		Component: RedFolder,
 	},
 	{
 		value: "yellow",
 		label: "Yellow",
-		component: (rivefile: RiveFile | undefined) => (
-			<YellowFolder riveFile={rivefile} />
-		),
+		Component: YellowFolder,
 	},
 ] as const;

77-80: Confirm cross-mount caching behavior of useRiveFile.

If dialogs unmount between opens, verify the Rive file isn’t re-fetched. If it is, hoist riveFile to a parent (e.g., dashboard layout/provider) and pass via props/context to both dialogs to guarantee reuse.


143-153: Drop explicit annotation; rely on inferred literal types.

The inline type for option duplicates FolderOptions and can drift. Let TS infer from the const tuple.

-{FolderOptions.map(
-	(
-		option: {
-			value: "blue" | "red" | "yellow" | "normal";
-			label: string;
-			component: (
-				rivefile: RiveFile | undefined,
-			) => React.JSX.Element;
-		},
-		idx: number,
-	) => {
+{FolderOptions.map((option) => {

156-163: Use a stable key based on value.

Index-based keys can cause unnecessary remounts; value is unique and stable.

-										key={`rive-instance-${idx.toString()}`}
+										key={option.value}

185-190: Avoid unnecessary type assertion and cloneElement; render component directly.

Pass riveFile as-is and attach ref without cloneElement (pairs with the FolderOptions refactor).

-										{React.cloneElement(
-											option.component(riveFile as RiveFile),
-											{
-												ref: folderRefs.current[option.value],
-											},
-										)}
+										{
+											// after changing FolderOptions to hold Component
+										}
+										<option.Component
+											riveFile={riveFile}
+											ref={folderRefs.current[option.value]}
+										/>

If you keep the current structure, still drop the assertion:

- option.component(riveFile as RiveFile)
+ option.component(riveFile)
📜 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 a3797a4 and bdb18ad.

📒 Files selected for processing (2)
  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (4 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx
🧰 Additional context used
📓 Path-based instructions (3)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for client-side server state and data fetching in the web app
Mutations should call Server Actions and perform precise cache updates with setQueryData/setQueriesData, avoiding broad invalidations
Prefer Server Components for initial data and pass initialData to client components for React Query hydration

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
{apps/web,packages/ui}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{apps/web,packages/ui}/**/*.{ts,tsx}: Use Tailwind CSS exclusively for styling in the web app and shared React UI components
Component naming: React components in PascalCase; hooks in camelCase starting with 'use'

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; prefer shared types from packages

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
🧬 Code graph analysis (1)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (1)
apps/web/app/(org)/dashboard/caps/components/Folders.tsx (5)
  • NormalFolder (12-48)
  • BlueFolder (50-80)
  • RedFolder (82-112)
  • YellowFolder (114-144)
  • FolderHandle (7-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (1)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (1)

170-183: Verify animation names across artboards.

Ensure all folder artboards implement “folder-open” and “folder-close”; otherwise add a safe fallback to “idle”.

  onMouseEnter={() => {
    const folderRef = folderRefs.current[option.value]?.current;
    if (!folderRef) return;
-   folderRef.stop();
-   folderRef.play("folder-open");
+   folderRef.stop();
+   folderRef.play("folder-open"); // TODO: fallback to "idle" if missing
  }}

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 (5)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (5)

39-41: Nit: standardize parameter casing to riveFile.

Minor consistency polish; aligns with prop name and avoids mixed casing.

-    component: (rivefile: RiveFile | undefined) => (
-      <NormalFolder riveFile={rivefile} />
+    component: (riveFile: RiveFile | undefined) => (
+      <NormalFolder riveFile={riveFile} />
     ),
...
-    component: (rivefile: RiveFile | undefined) => (
-      <BlueFolder riveFile={rivefile} />
+    component: (riveFile: RiveFile | undefined) => (
+      <BlueFolder riveFile={riveFile} />
     ),
...
-    component: (rivefile: RiveFile | undefined) => (
-      <RedFolder riveFile={rivefile} />
+    component: (riveFile: RiveFile | undefined) => (
+      <RedFolder riveFile={riveFile} />
     ),
...
-    component: (rivefile: RiveFile | undefined) => (
-      <YellowFolder riveFile={rivefile} />
+    component: (riveFile: RiveFile | undefined) => (
+      <YellowFolder riveFile={riveFile} />
     ),

Also applies to: 46-48, 53-55, 60-62


39-41: Avoid cloneElement for refs; let option.component accept ref.

Passing refs via cloneElement is noisy and can trip TS. Make component accept (riveFile, ref) and render the folder with that ref directly.

-  component: (riveFile: RiveFile | undefined) => (
-    <NormalFolder riveFile={riveFile} />
-  ),
+  component: (riveFile: RiveFile | undefined, ref: React.Ref<FolderHandle>) => (
+    <NormalFolder riveFile={riveFile} ref={ref} />
+  ),
@@
-  component: (riveFile: RiveFile | undefined) => (
-    <BlueFolder riveFile={riveFile} />
-  ),
+  component: (riveFile: RiveFile | undefined, ref: React.Ref<FolderHandle>) => (
+    <BlueFolder riveFile={riveFile} ref={ref} />
+  ),
@@
-  component: (riveFile: RiveFile | undefined) => (
-    <RedFolder riveFile={riveFile} />
-  ),
+  component: (riveFile: RiveFile | undefined, ref: React.Ref<FolderHandle>) => (
+    <RedFolder riveFile={riveFile} ref={ref} />
+  ),
@@
-  component: (riveFile: RiveFile | undefined) => (
-    <YellowFolder riveFile={riveFile} />
-  ),
+  component: (riveFile: RiveFile | undefined, ref: React.Ref<FolderHandle>) => (
+    <YellowFolder riveFile={riveFile} ref={ref} />
+  ),
@@
-  {React.cloneElement(option.component(riveFile as RiveFile), {
-    ref: folderRefs.current[option.value],
-  })}
+  {option.component(riveFile, folderRefs.current[option.value])}

Also applies to: 46-48, 53-55, 60-62, 165-167


77-80: Consider hoisting the Rive file to a shared hook/provider.

If both NewFolderDialog and SubfolderDialog mount independently, a small context (e.g., useDashboardRiveFile) can ensure a single RiveFile instance and zero duplicate loads across screens.


85-95: Auto-generate refs from FolderOptions to prevent drift.

Removes duplication and keeps refs in sync when adding new options.

-const folderRefs = useRef<
-  Record<(typeof FolderOptions)[number]["value"], React.RefObject<FolderHandle>>
->({
-  blue: React.createRef<FolderHandle>(),
-  red: React.createRef<FolderHandle>(),
-  yellow: React.createRef<FolderHandle>(),
-  normal: React.createRef<FolderHandle>(),
-});
+const folderRefs = useRef(
+  FolderOptions.reduce((acc, opt) => {
+    acc[opt.value] = React.createRef<FolderHandle>();
+    return acc;
+  }, {} as Record<(typeof FolderOptions)[number]["value"], React.RefObject<FolderHandle>>),
+);

165-167: Remove unnecessary type assertion on riveFile.

option.component already accepts RiveFile | undefined. The cast is redundant and could mask undefined at compile time.

-{React.cloneElement(option.component(riveFile as RiveFile), {
+{React.cloneElement(option.component(riveFile), {
   ref: folderRefs.current[option.value],
 })}
📜 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 bdb18ad and fc50cef.

📒 Files selected for processing (2)
  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (5 hunks)
  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(org)/dashboard/folder/[id]/components/SubfolderDialog.tsx
🧰 Additional context used
📓 Path-based instructions (3)
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for client-side server state and data fetching in the web app
Mutations should call Server Actions and perform precise cache updates with setQueryData/setQueriesData, avoiding broad invalidations
Prefer Server Components for initial data and pass initialData to client components for React Query hydration

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
{apps/web,packages/ui}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{apps/web,packages/ui}/**/*.{ts,tsx}: Use Tailwind CSS exclusively for styling in the web app and shared React UI components
Component naming: React components in PascalCase; hooks in camelCase starting with 'use'

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use strict TypeScript and avoid any; prefer shared types from packages

Files:

  • apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
🧬 Code graph analysis (1)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (1)
apps/web/app/(org)/dashboard/caps/components/Folders.tsx (5)
  • NormalFolder (12-48)
  • BlueFolder (50-80)
  • RedFolder (82-112)
  • YellowFolder (114-144)
  • FolderHandle (7-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (4)
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx (4)

13-13: Good move: central Rive file hook import.

Importing type RiveFile and useRiveFile unblocks shared-file caching and keeps types tight.


21-27: Nice: correctly typed folder handles and components import.

Brings in FolderHandle and Rive-driven folders with proper types for imperative control.


85-95: Refs initialization is solid; prevents first-render nulls.

Nice fix—eager, typed refs resolve the earlier hover glitch.


144-144: LGTM: stable key per option.

The prefixed key avoids collisions with other lists.

@ameer2468 ameer2468 merged commit 53403b2 into main Sep 2, 2025
15 checks passed
@ameer2468 ameer2468 deleted the cache-folders-rive branch September 2, 2025 09:19
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