-
Notifications
You must be signed in to change notification settings - Fork 1.2k
improvement: cache rive folders art #982
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
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 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. 📒 Files selected for processing (2)
WalkthroughReplaces static Rive asset usage with an injectable Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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.
📒 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.tsxapps/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.tsxapps/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.tsxapps/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
apps/web/app/(org)/dashboard/caps/components/NewFolderDialog.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this 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 fromFolderOptions.- ( - 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 undefinedriveFileand 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 typefolderRefsasReact.RefObject<FolderHandle>and initialize them upfront
Remove theuseEffectand replace youruseRefwith:- 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
FolderHandleis exported fromcaps/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.
📒 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.
There was a problem hiding this 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.
📒 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 }}
There was a problem hiding this 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
cloneElementis noisy and can trip TS. Makecomponentaccept(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
RiveFileinstance 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.componentalready acceptsRiveFile | 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.
📒 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 RiveFileanduseRiveFileunblocks shared-file caching and keeps types tight.
21-27: Nice: correctly typed folder handles and components import.Brings in
FolderHandleand 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.
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
Refactor