72 offline fixes#73
Conversation
|
Warning Rate limit exceeded@InfinityBowman has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 0 minutes and 54 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 (1)
WalkthroughAdds an IndexedDB-backed avatar cache and integrates it into auth and UI; makes Avatar fallback styling configurable; adds image compression utilities; and adds online/offline guards plus reconnection intent management to WebSocket/connection primitives. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI Component
participant Auth as Auth Store
participant Cache as avatarCache
participant DB as IndexedDB
participant Net as Network
UI->>Auth: request user (init / login)
Auth->>Cache: getAvatarWithCache(userId, imageUrl)
Cache->>DB: read cached entry by userId
alt cached & valid
DB-->>Cache: dataURL
Cache-->>Auth: dataURL
Auth-->>UI: user + cached image
else no valid cache
Cache->>Net: fetch image URL
alt fetch success
Net-->>Cache: blob
Cache->>Cache: compress / blob→dataURL
Cache->>DB: put {userId, dataURL, cachedAt}
Cache-->>Auth: dataURL
Auth-->>UI: user + fetched image
else fetch failure
Cache-->>Auth: null
Auth-->>UI: user (no cached image)
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
corates | 9d1d706 | Commit Preview URL | Dec 16 2025, 09:16 PM |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
packages/web/src/components/UserYjsProvider.jsx (1)
51-54: Missing reconnection logic when coming back online.The offline guard prevents connection attempts, but unlike
useNotifications.jsandconnection.js, there's noonlineevent listener to retryconnectToProjectwhen the network returns. Projects skipped during offline will remain disconnected.Consider adding online/offline event handling similar to the pattern used in other files in this PR:
+ // Handle online event to reconnect + function handleOnline() { + if (userProjects().length > 0) { + userProjects().forEach(project => { + if (!connections[project.id]?.connected) { + connectToProject(project.id); + } + }); + } + } + + if (typeof window !== 'undefined') { + window.addEventListener('online', handleOnline); + }And clean up in
onCleanup.packages/web/src/api/better-auth-store.js (1)
143-158: Consider memoizing the user object with cached avatar.The current implementation creates a new object on every
user()call whencachedAvatarUrl()exists. While acceptable for typical usage, you could memoize this withcreateMemoifuser()is called frequently in reactive contexts.+ const userWithAvatar = createMemo(() => { + const currentUser = isOnline() ? sessionUser() : cachedUser(); + if (currentUser && cachedAvatarUrl()) { + return { ...currentUser, image: cachedAvatarUrl() }; + } + return currentUser; + }); + const user = () => { - if (isOnline()) { - const currentUser = sessionUser(); - // When online, prefer cached avatar data URL (works offline, faster loading) - if (currentUser && cachedAvatarUrl()) { - return { ...currentUser, image: cachedAvatarUrl() }; - } - return currentUser; - } - // When offline, return cached user with cached avatar - const cached = cachedUser(); - if (cached && cachedAvatarUrl()) { - return { ...cached, image: cachedAvatarUrl() }; - } - return cached; + return userWithAvatar(); };packages/web/src/primitives/avatarCache.js (2)
39-49: Consider implementing cache expiry using thecachedAtindex.The
cachedAtindex is created but never utilized. Without cache expiry logic, the IndexedDB can grow unbounded over time as avatars are cached for different users. Consider adding a cleanup mechanism that removes entries older than a threshold (e.g., 30 days).Would you like me to generate a cache cleanup function that prunes old avatar entries?
62-69: Consider adding size validation for cached avatars.Large avatar images could result in substantial IndexedDB storage usage. Consider adding a size check before caching (e.g., skip caching if blob.size > 500KB) or compressing/resizing the image before storing.
// Optional: Skip caching very large avatars const MAX_AVATAR_SIZE = 500 * 1024; // 500KB if (blob.size > MAX_AVATAR_SIZE) { console.warn('Avatar too large to cache:', blob.size); return null; }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
packages/ui/src/zag/Avatar.jsx(1 hunks)packages/web/src/api/better-auth-store.js(6 hunks)packages/web/src/components/Navbar.jsx(2 hunks)packages/web/src/components/UserYjsProvider.jsx(2 hunks)packages/web/src/components/profile-ui/ProfilePage.jsx(1 hunks)packages/web/src/components/project-ui/tabs/OverviewTab.jsx(2 hunks)packages/web/src/primitives/avatarCache.js(1 hunks)packages/web/src/primitives/useNotifications.js(4 hunks)packages/web/src/primitives/useProject/connection.js(5 hunks)packages/web/src/primitives/useProject/index.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Use Zod for schema and input validation
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Keep files small, focused, and modular. Extract sub-modules into a folder (e.g.,ComponentName/withindex.jsxand helper components). Move complex logic into separate utility files or primitives. Split large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation
Files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/tabs/OverviewTab.jsxpackages/web/src/components/Navbar.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/primitives/avatarCache.jspackages/ui/src/zag/Avatar.jsxpackages/web/src/primitives/useProject/connection.jspackages/web/src/components/UserYjsProvider.jsxpackages/web/src/api/better-auth-store.jspackages/web/src/primitives/useNotifications.js
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Keep files small, focused, and modular. If a file exceeds a high number of lines, extract sub-modules into a folder with index.jsx and helper components, or move complex logic into separate utility files or primitives
Each file should handle one coherent responsibility
Group related components in subdirectories with an index.js barrel export
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Import stores directly where needed instead of passing values through multiple components
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context (when scoped to a feature)
Do NOT destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function (e.g., () => props.name) to ensure they are always up-to-date
Use createMemo for derived values based on props or state to ensure reactive updates
Use Solid's createStore for complex state or state objects for better performance and reactivity
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering
Components should be lean and focused. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
packages/web/src/**/*.{js,jsx,ts,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 ...
Files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/tabs/OverviewTab.jsxpackages/web/src/components/Navbar.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/primitives/avatarCache.jspackages/web/src/primitives/useProject/connection.jspackages/web/src/components/UserYjsProvider.jsxpackages/web/src/api/better-auth-store.jspackages/web/src/primitives/useNotifications.js
**/*.{js,jsx,ts,tsx,json,yaml,yml,toml}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Prefer using config files rather than hardcoding values
Files:
packages/web/src/primitives/useProject/index.jspackages/web/src/components/project-ui/tabs/OverviewTab.jsxpackages/web/src/components/Navbar.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/primitives/avatarCache.jspackages/ui/src/zag/Avatar.jsxpackages/web/src/primitives/useProject/connection.jspackages/web/src/components/UserYjsProvider.jsxpackages/web/src/api/better-auth-store.jspackages/web/src/primitives/useNotifications.js
**/{index,index.js,index.jsx,index.ts,index.tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Group related components in subdirectories with an
index.jsbarrel export
Files:
packages/web/src/primitives/useProject/index.js
packages/web/src/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/components/**/*.{js,jsx,ts,tsx}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Use responsive design principles for UI components
Use Zag.js for UI components and design system. Zag components exist inpackages/web/src/components/zag/*and should be reused
Files:
packages/web/src/components/project-ui/tabs/OverviewTab.jsxpackages/web/src/components/Navbar.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/components/UserYjsProvider.jsx
🧠 Learnings (1)
📚 Learning: 2025-12-16T18:27:52.770Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-16T18:27:52.770Z
Learning: Applies to packages/workers/**/*.{js,ts} : Use Better-Auth for authentication and user management
Applied to files:
packages/web/src/api/better-auth-store.js
🧬 Code graph analysis (3)
packages/web/src/components/project-ui/tabs/OverviewTab.jsx (1)
packages/ui/src/zag/Avatar.jsx (1)
Avatar(5-38)
packages/web/src/primitives/avatarCache.js (1)
packages/landing/src/components/DefaultSeo.jsx (1)
imageUrl(5-5)
packages/ui/src/zag/Avatar.jsx (3)
packages/ui/src/zag/Clipboard.jsx (2)
api(30-30)api(98-98)packages/ui/src/zag/Dialog.jsx (2)
api(33-33)api(132-132)packages/ui/src/zag/PasswordInput.jsx (1)
api(22-22)
⏰ 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). (1)
- GitHub Check: Workers Builds: corates
🔇 Additional comments (17)
packages/web/src/components/UserYjsProvider.jsx (1)
96-105: LGTM!The error suppression when offline prevents console spam, and the connection state is still properly updated regardless of online status.
packages/web/src/primitives/useNotifications.js (3)
25-36: LGTM!Good implementation of connectivity intent tracking with
shouldConnect. The offline guard prevents unnecessary WebSocket attempts when offline, and the flag ensures proper reconnection behavior when coming back online.
108-129: LGTM!Well-structured online/offline event handling. Resetting
reconnectAttemptsinhandleOnlineprovides good UX by avoiding excessive delays after network recovery. Canceling pending reconnection timeouts inhandleOfflineprevents wasted attempts.
154-159: LGTM!Proper cleanup of event listeners in
onCleanupprevents memory leaks.packages/web/src/primitives/useProject/connection.js (3)
26-48: LGTM!Good implementation of online/offline event handling with proper intent tracking via
shouldBeConnected. The pattern correctly pauses reconnection attempts when offline and resumes when online.
53-59: Good handling of offline state in connect().Setting
shouldBeConnected = trueeven when returning early due to offline status ensures thathandleOnlinewill trigger a connection attempt when network is restored.
133-140: LGTM!The
destroy()function properly cleans up event listeners to prevent memory leaks, which is essential since these listeners are registered at the module level.packages/web/src/primitives/useProject/index.js (1)
118-122: LGTM!Correctly using
destroy()instead ofdisconnect()ensures that the online/offline event listeners registered inconnection.jsare properly cleaned up, preventing memory leaks when the hook unmounts.packages/web/src/components/profile-ui/ProfilePage.jsx (1)
109-110: LGTM!The updated comment provides clearer documentation about the avatar URL priority, aligning with the broader avatar caching strategy introduced in this PR.
packages/ui/src/zag/Avatar.jsx (1)
26-32: LGTM! Clean implementation of configurable fallback styling.The
fallbackClasshelper provides a sensible default while allowing customization. This pattern is consistent with how other props are accessed in the component.packages/web/src/components/project-ui/tabs/OverviewTab.jsx (1)
112-117: LGTM! Good refactor to use the centralized Avatar component.The Avatar component is correctly integrated with appropriate props for the member context. The custom
fallbackClassprovides consistent styling with the project's blue theme.packages/web/src/components/Navbar.jsx (1)
121-131: LGTM! Clean refactor to use Avatar component and FiChevronDown icon.The Avatar component is correctly integrated with reactive accessors (
user()?.image,user()?.name), and the chevron icon replacement simplifies the template. Thearia-hidden="true"is appropriately set for the decorative icon.packages/web/src/api/better-auth-store.js (2)
90-98: LGTM! Avatar cache initialization on module load.The async avatar loading on init correctly handles the case where a cached user exists. The race between this and session fetch is benign since both paths result in a valid cached avatar URL.
360-363: LGTM! Proper avatar cache cleanup on signout.The signout flow correctly clears both the reactive signal and the IndexedDB cache. Not awaiting
clearAvatarCache()is acceptable here since it's cleanup that doesn't affect the signout flow.packages/web/src/primitives/avatarCache.js (3)
1-14: LGTM! Well-structured avatar caching module with IndexedDB.Good use of singleton pattern for database instance, with proper error handling for environments where IndexedDB is unavailable.
157-190: LGTM! Robust avatar fetching with proper CORS handling.The distinction between relative and absolute URLs for credential handling is correct. External avatar providers (like Google) require
no-corsor no credentials, while internal API endpoints need authentication. Error handling gracefully falls back to returningnull.
199-220: LGTM! Good offline-first pattern with cache fallback.The function correctly prioritizes cached data when offline and falls back to cache on fetch failure. This ensures avatar availability in degraded network conditions.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/web/src/components/UserYjsProvider.jsx (1)
98-107: Inconsistent online status check—useisOnline()instead ofnavigator.onLine.Line 100 uses
navigator.onLinewhile the rest of the file (lines 54, 157) uses theisOnline()hook. This creates inconsistency in how online status is determined.Apply this diff to use the same online status source:
ws.onerror = () => { // Suppress error logging when offline to prevent console spam - if (navigator.onLine) { + if (isOnline()) { console.error(`Project ${projectId} WebSocket error`); }
🧹 Nitpick comments (3)
packages/web/src/components/profile-ui/ProfilePage.jsx (1)
11-13: Consider importing default compression settings from the shared utility.The constants
AVATAR_MAX_SIZEandAVATAR_QUALITYduplicate the defaults inimageUtils.js. While this works, you could simplify by omitting the options (to use defaults) or exporting the defaults fromimageUtils.jsfor consistency.packages/web/src/api/better-auth-store.js (1)
147-162: Spreads inuser()create new object references on every call.The spread operator in
user()creates a new object on each invocation whencachedAvatarUrl()is set. In SolidJS, this can trigger unnecessary re-renders in consuming components since object identity changes. Consider memoizing the merged user object withcreateMemoto stabilize references.+ import { createSignal, createRoot, createEffect, createMemo } from 'solid-js'; ... - const user = () => { - if (isOnline()) { - const currentUser = sessionUser(); - // When online, prefer cached avatar data URL (works offline, faster loading) - if (currentUser && cachedAvatarUrl()) { - return { ...currentUser, image: cachedAvatarUrl() }; - } - return currentUser; - } - // When offline, return cached user with cached avatar - const cached = cachedUser(); - if (cached && cachedAvatarUrl()) { - return { ...cached, image: cachedAvatarUrl() }; - } - return cached; - }; + const user = createMemo(() => { + if (isOnline()) { + const currentUser = sessionUser(); + if (currentUser && cachedAvatarUrl()) { + return { ...currentUser, image: cachedAvatarUrl() }; + } + return currentUser; + } + const cached = cachedUser(); + if (cached && cachedAvatarUrl()) { + return { ...cached, image: cachedAvatarUrl() }; + } + return cached; + });packages/web/src/primitives/avatarCache.js (1)
89-113: Transactions aren't explicitly completed before resolving.The IndexedDB transactions complete automatically when all requests succeed and go out of scope, which works for reads. However, for writes in
cacheAvatar, you may want to wait fortx.oncompleteto ensure data persistence before the promise resolves, especially for critical cache operations.export async function cacheAvatar(userId, dataUrl) { if (!userId || !dataUrl) return; try { const db = await getDb(); const tx = db.transaction(AVATAR_STORE_NAME, 'readwrite'); const store = tx.objectStore(AVATAR_STORE_NAME); await new Promise((resolve, reject) => { const request = store.put({ userId, dataUrl, cachedAt: Date.now(), }); - request.onsuccess = () => resolve(); + tx.oncomplete = () => resolve(); request.onerror = () => reject(request.error); + tx.onerror = () => reject(tx.error); }); } catch (err) { console.error('Error caching avatar:', err); } }Also applies to: 120-140
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
packages/web/src/api/better-auth-store.js(6 hunks)packages/web/src/components/UserYjsProvider.jsx(5 hunks)packages/web/src/components/profile-ui/ProfilePage.jsx(3 hunks)packages/web/src/lib/imageUtils.js(1 hunks)packages/web/src/primitives/avatarCache.js(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Use Zod for schema and input validation
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Keep files small, focused, and modular. Extract sub-modules into a folder (e.g.,ComponentName/withindex.jsxand helper components). Move complex logic into separate utility files or primitives. Split large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation
Files:
packages/web/src/lib/imageUtils.jspackages/web/src/components/UserYjsProvider.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/api/better-auth-store.jspackages/web/src/primitives/avatarCache.js
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Keep files small, focused, and modular. If a file exceeds a high number of lines, extract sub-modules into a folder with index.jsx and helper components, or move complex logic into separate utility files or primitives
Each file should handle one coherent responsibility
Group related components in subdirectories with an index.js barrel export
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Import stores directly where needed instead of passing values through multiple components
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context (when scoped to a feature)
Do NOT destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function (e.g., () => props.name) to ensure they are always up-to-date
Use createMemo for derived values based on props or state to ensure reactive updates
Use Solid's createStore for complex state or state objects for better performance and reactivity
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering
Components should be lean and focused. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
packages/web/src/**/*.{js,jsx,ts,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 ...
Files:
packages/web/src/lib/imageUtils.jspackages/web/src/components/UserYjsProvider.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/api/better-auth-store.jspackages/web/src/primitives/avatarCache.js
**/*.{js,jsx,ts,tsx,json,yaml,yml,toml}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Prefer using config files rather than hardcoding values
Files:
packages/web/src/lib/imageUtils.jspackages/web/src/components/UserYjsProvider.jsxpackages/web/src/components/profile-ui/ProfilePage.jsxpackages/web/src/api/better-auth-store.jspackages/web/src/primitives/avatarCache.js
packages/web/src/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/components/**/*.{js,jsx,ts,tsx}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Use responsive design principles for UI components
Use Zag.js for UI components and design system. Zag components exist inpackages/web/src/components/zag/*and should be reused
Files:
packages/web/src/components/UserYjsProvider.jsxpackages/web/src/components/profile-ui/ProfilePage.jsx
🧠 Learnings (3)
📚 Learning: 2025-12-16T18:28:31.657Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-16T18:28:31.657Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : When you have complex state or state objects, use Solid's `createStore` for better performance and reactivity
Applied to files:
packages/web/src/components/UserYjsProvider.jsx
📚 Learning: 2025-12-16T18:27:52.770Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-16T18:27:52.770Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use Solid's createStore for complex state or state objects for better performance and reactivity
Applied to files:
packages/web/src/components/UserYjsProvider.jsx
📚 Learning: 2025-12-16T18:27:52.770Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursorrules:0-0
Timestamp: 2025-12-16T18:27:52.770Z
Learning: Applies to packages/workers/**/*.{js,ts} : Use Better-Auth for authentication and user management
Applied to files:
packages/web/src/api/better-auth-store.js
🧬 Code graph analysis (3)
packages/web/src/components/profile-ui/ProfilePage.jsx (1)
packages/web/src/lib/imageUtils.js (1)
compressImageFile(80-88)
packages/web/src/api/better-auth-store.js (1)
packages/web/src/primitives/avatarCache.js (6)
getCachedAvatar(89-113)dataUrl(267-267)dataUrl(301-301)pruneExpiredAvatars(332-361)fetchAndCacheAvatar(215-277)clearAvatarCache(312-326)
packages/web/src/primitives/avatarCache.js (1)
packages/web/src/lib/imageUtils.js (1)
compressImageBlob(17-69)
⏰ 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). (1)
- GitHub Check: Workers Builds: corates
🔇 Additional comments (8)
packages/web/src/components/UserYjsProvider.jsx (2)
4-4: LGTM: Online status hook integrated correctly.The import and usage of
useOnlineStatusfollows SolidJS conventions correctly—isOnlineis properly used as a signal accessor throughout most of the code.Also applies to: 18-18
53-56: Guard prevents unnecessary connection attempts when offline.The early return when offline is correct and prevents wasted resources. However, note that this does not set any "intent to connect" flag, so if the app goes offline mid-connection attempt, there's no automatic retry mechanism beyond the new reconnection effect added later.
packages/web/src/components/profile-ui/ProfilePage.jsx (1)
7-7: Good refactor to centralized image compression utility.The migration from a local compression function to the shared
compressImageFileutility improves maintainability and follows the DRY principle. The options object correctly passesmaxSizeandqualitymatching the utility's expected signature.Also applies to: 152-155
packages/web/src/api/better-auth-store.js (1)
364-367: LGTM on cache cleanup.The avatar cache cleanup in both
signoutanddeleteAccountis consistent. Fire-and-forget pattern forclearAvatarCache()is acceptable here since the operations don't depend on completion.Also applies to: 654-655
packages/web/src/lib/imageUtils.js (1)
17-69: Well-structured compression utility.The implementation correctly handles aspect ratio preservation, resource cleanup via
URL.revokeObjectURL, and error propagation. The Promise-based API integrates well with async workflows.packages/web/src/primitives/avatarCache.js (3)
35-62: Good singleton pattern for IndexedDB.The database initialization uses a proper singleton pattern with
dbInitPromiseto prevent race conditions when multiple callers request the database simultaneously. The upgrade logic correctly creates the object store and index.
215-277: Solid offline-first avatar fetching implementation.The function handles URL resolution (relative vs absolute), CORS credential handling, rate limiting with exponential backoff, and graceful compression fallback. The separation of concerns between internal API calls and external avatar sources is well thought out.
332-361: LGTM on cache pruning implementation.The cursor-based approach for pruning expired entries is correct and efficient. Using the
cachedAtindex withIDBKeyRange.upperBoundproperly selects entries older than the expiry threshold.
| // Reconnect to projects when coming back online | ||
| createEffect(() => { | ||
| if (isOnline()) { | ||
| const projects = userProjects(); | ||
| for (const project of projects) { | ||
| if (!connections[project.id]?.connected) { | ||
| connectToProject(project.id); | ||
| } | ||
| } | ||
| } | ||
| }); |
There was a problem hiding this comment.
Risk of race conditions and connection thrashing in reconnection effect.
This effect could cause issues:
- Race with initial connection: If
isOnline()changes whilefetchUserProjectsis still running (line 152 effect), both might attempt to connect to the same projects simultaneously. - Connection thrashing: If
isOnline()toggles rapidly (e.g., flaky network), this will repeatedly callconnectToProjectwithout debouncing or backoff. - Redundant attempts: The effect checks
!connections[project.id]?.connected, but if a connection is in progress (not yet connected), it will attempt another connection.
Consider one of these solutions:
- Add a debounce delay (e.g., 1-2 seconds) before attempting reconnection
- Track "connecting" state separately from "connected" to prevent duplicate attempts
- Add backoff logic for repeated failures
- Coordinate with the
fetchUserProjectseffect to avoid concurrent connection attempts
The PR summary mentions "shouldConnect/getShouldReconnect/setShouldReconnect API" in connection.js—consider leveraging that intent management here as well.
🤖 Prompt for AI Agents
In packages/web/src/components/UserYjsProvider.jsx around lines 155-165, the
reconnect effect can race with the initial fetch and thrash connections; modify
it to (1) wait a short debounce (1-2s) before acting, (2) consult and set a
shouldReconnect/shouldConnect flag from connection.js instead of unconditionally
calling connectToProject, (3) treat connections[project.id] with three states
(idle/connecting/connected) and skip projects in the connecting or connected
state, and (4) implement simple exponential backoff for repeated connect
failures (increment retry count on failure and delay next attempt by backoff)
and coordinate with the fetchUserProjects effect by checking a "projectsLoading"
flag to avoid concurrent connection attempts.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
packages/ui/src/zag/Avatar.jsx (1)
31-31: Consider using a memo for the class computation.While the current string concatenation should work, wrapping the class logic in
createMemowould be more defensive against reactivity edge cases and aligns better with SolidJS best practices for computed values.Apply this diff if you want to make the class computation more explicitly reactive:
+ const rootClass = createMemo(() => `overflow-hidden ${props.class || ''}`); + return ( - <div {...api().getRootProps()} class={`overflow-hidden ${props.class || ''}`}> + <div {...api().getRootProps()} class={rootClass()}>packages/web/src/components/project-ui/AddMemberModal.jsx (1)
220-225: LGTM! Consider memoizing the selected user for minor optimization.The Avatar component integration is correct and maintains consistent styling. The slightly larger size (w-10 vs w-8 in search results) provides good visual hierarchy.
For a minor optimization, you could memoize the selected user to avoid repeated signal access:
+ const user = createMemo(() => selectedUser()); + <Show when={selectedUser()}> <div class='bg-blue-50 border border-blue-200 rounded-lg p-3 flex items-center justify-between'> <div class='flex items-center gap-3'> <Avatar - src={selectedUser().image} - name={selectedUser().displayName || selectedUser().name || selectedUser().email} + src={user()?.image} + name={user()?.displayName || user()?.name || user()?.email} class='w-10 h-10 rounded-full' fallbackClass='flex items-center justify-center w-full h-full bg-blue-600 text-white font-medium' /> <div> <p class='text-gray-900 font-medium'> - {selectedUser().displayName || selectedUser().name || 'Unknown'} + {user()?.displayName || user()?.name || 'Unknown'} </p> - <p class='text-gray-500 text-sm'>{selectedUser().email}</p> + <p class='text-gray-500 text-sm'>{user()?.email}</p> </div>
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/ui/src/zag/Avatar.jsx(1 hunks)packages/web/src/components/project-ui/AddMemberModal.jsx(3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Use Zod for schema and input validation
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Keep files small, focused, and modular. Extract sub-modules into a folder (e.g.,ComponentName/withindex.jsxand helper components). Move complex logic into separate utility files or primitives. Split large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation
Files:
packages/ui/src/zag/Avatar.jsxpackages/web/src/components/project-ui/AddMemberModal.jsx
**/*.{js,jsx,ts,tsx,json,yaml,yml,toml}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Prefer using config files rather than hardcoding values
Files:
packages/ui/src/zag/Avatar.jsxpackages/web/src/components/project-ui/AddMemberModal.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Keep files small, focused, and modular. If a file exceeds a high number of lines, extract sub-modules into a folder with index.jsx and helper components, or move complex logic into separate utility files or primitives
Each file should handle one coherent responsibility
Group related components in subdirectories with an index.js barrel export
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Import stores directly where needed instead of passing values through multiple components
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context (when scoped to a feature)
Do NOT destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function (e.g., () => props.name) to ensure they are always up-to-date
Use createMemo for derived values based on props or state to ensure reactive updates
Use Solid's createStore for complex state or state objects for better performance and reactivity
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering
Components should be lean and focused. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
packages/web/src/**/*.{js,jsx,ts,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 ...
Files:
packages/web/src/components/project-ui/AddMemberModal.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/web/src/components/**/*.{js,jsx,ts,tsx}: For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Use responsive design principles for UI components
Use Zag.js for UI components and design system. Zag components exist inpackages/web/src/components/zag/*and should be reused
Files:
packages/web/src/components/project-ui/AddMemberModal.jsx
🧬 Code graph analysis (1)
packages/web/src/components/project-ui/AddMemberModal.jsx (2)
packages/ui/src/zag/Avatar.jsx (1)
Avatar(5-44)packages/web/src/components/profile-ui/ProfilePage.jsx (1)
user(33-33)
⏰ 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). (1)
- GitHub Check: Workers Builds: corates
🔇 Additional comments (3)
packages/ui/src/zag/Avatar.jsx (2)
26-28: LGTM! Good extensibility pattern.The
fallbackClass()function correctly provides a sensible default while allowing customization through props. This aligns well with the usage in AddMemberModal where different fallback styles are needed.
35-41:referrerPolicy='no-referrer'is a necessary security fix for OAuth avatar images.The addition of
referrerPolicy='no-referrer'solves a known issue where Google and other OAuth providers block avatar images with the default referrer policy. This is standard practice across component libraries and required for proper avatar loading from social login providers.packages/web/src/components/project-ui/AddMemberModal.jsx (1)
178-183: LGTM! Good refactoring to use the shared Avatar component.The replacement of manual avatar markup with the
Avatarcomponent improves consistency across the application. The prop values are appropriate, and the blue fallback styling aligns well with the UI context.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
packages/web/src/primitives/useOnlineStatus.js (2)
28-46: Clear timeout infinallyblock to prevent resource leak.If
fetchthrows (e.g., network error), the timeout is never cleared and will fire 3 seconds later attempting to abort an already-finished request. While harmless, this is a minor resource leak.async function verifyConnectivity() { try { // Use a lightweight endpoint - just check if we can reach our API // Falls back to checking if fetch works at all const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 3000); - await fetch('/api/health', { - method: 'HEAD', - signal: controller.signal, - cache: 'no-store', - }); - - clearTimeout(timeoutId); - return true; + try { + await fetch('/api/health', { + method: 'HEAD', + signal: controller.signal, + cache: 'no-store', + }); + return true; + } finally { + clearTimeout(timeoutId); + } } catch { return false; } }
60-67: Subtle race condition with async setTimeout callback.If
handleOnlineis invoked again whileawait verifyConnectivity()is in-flight, a new timer is set. When the original callback resumes and executesonlineDebounceTimer = null(line 66), it overwrites the new timer's reference. If cleanup runs during that window, the new timer won't be cleared.Consider capturing the timer ID and only nullifying if it matches:
onlineDebounceTimer = setTimeout(async () => { + const currentTimer = onlineDebounceTimer; // Verify we're actually online before updating state const actuallyOnline = await verifyConnectivity(); if (actuallyOnline) { setIsOnline(true); } - onlineDebounceTimer = null; + // Only clear if we're still the active timer + if (onlineDebounceTimer === currentTimer) { + onlineDebounceTimer = null; + } }, ONLINE_CONFIRM_DELAY_MS);packages/web/src/lib/imageUtils.js (2)
17-18: Add input validation for compression options.The function should validate that
maxSizeis positive andqualityis within the valid range [0, 1] to prevent unexpected behavior. Per coding guidelines, consider using Zod for schema validation.Add validation at the beginning of the function:
export function compressImageBlob(blob, options = {}) { const { maxSize = DEFAULT_MAX_SIZE, quality = DEFAULT_QUALITY } = options; + + if (maxSize <= 0) { + return Promise.reject(new Error('maxSize must be greater than 0')); + } + + if (quality < 0 || quality > 1) { + return Promise.reject(new Error('quality must be between 0 and 1')); + } return new Promise((resolve, reject) => {
85-86: Consider validating input file type.To provide better error messages and prevent processing non-image files, consider checking that the input file has an image MIME type before attempting compression.
export async function compressImageFile(file, options = {}) { + if (!file.type.startsWith('image/')) { + throw new Error(`Invalid file type: ${file.type}. Expected an image file.`); + } const compressedBlob = await compressImageBlob(file, options);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/web/src/lib/imageUtils.js(1 hunks)packages/web/src/primitives/useOnlineStatus.js(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
For UI icons, use thesolid-iconslibrary or SVGs only. Do not use emojis
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Prefer using config files rather than hardcoding values
Use Zod for schema and input validation
**/*.{js,jsx,ts,tsx}: Do not use emojis in code, comments, or documentation
Follow standard JavaScript/SolidJS/Cloudflare best practices
Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability
Keep files small, focused, and modular. Extract sub-modules into a folder (e.g.,ComponentName/withindex.jsxand helper components). Move complex logic into separate utility files or primitives. Split large forms into section components
Each file should handle one coherent responsibility
Use Zod for schema and input validation
Files:
packages/web/src/primitives/useOnlineStatus.jspackages/web/src/lib/imageUtils.js
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursorrules)
packages/web/src/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Keep files small, focused, and modular. If a file exceeds a high number of lines, extract sub-modules into a folder with index.jsx and helper components, or move complex logic into separate utility files or primitives
Each file should handle one coherent responsibility
Group related components in subdirectories with an index.js barrel export
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Import stores directly where needed instead of passing values through multiple components
Components should receive at most 1–5 props, and only for local configuration, not shared state
If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context (when scoped to a feature)
Do NOT destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function (e.g., () => props.name) to ensure they are always up-to-date
Use createMemo for derived values based on props or state to ensure reactive updates
Use Solid's createStore for complex state or state objects for better performance and reactivity
Create reusable logic in primitives (hooks) that can be shared across components to keep components clean and focused on rendering
Components should be lean and focused. Do not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a 'God component' coordinating multiple large concerns
packages/web/src/**/*.{js,jsx,ts,tsx}: Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 ...
Files:
packages/web/src/primitives/useOnlineStatus.jspackages/web/src/lib/imageUtils.js
**/*.{js,jsx,ts,tsx,json,yaml,yml,toml}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Prefer using config files rather than hardcoding values
Files:
packages/web/src/primitives/useOnlineStatus.jspackages/web/src/lib/imageUtils.js
🧬 Code graph analysis (1)
packages/web/src/lib/imageUtils.js (3)
packages/web/src/primitives/avatarCache.js (1)
blob(256-256)packages/web/src/components/project-ui/ChartSection.jsx (2)
img(48-48)canvas(46-46)packages/workers/src/routes/google-drive.js (1)
maxSize(242-242)
⏰ 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). (1)
- GitHub Check: Workers Builds: corates
🔇 Additional comments (3)
packages/web/src/primitives/useOnlineStatus.js (1)
70-103: Well-structured debouncing and cleanup logic.The offline handling with double-check of
navigator.onLine, proper timer cleanup inonCleanup, and clean event listener management follow SolidJS best practices. The defensive double-check before setting offline state is a good safeguard against false positives.packages/web/src/lib/imageUtils.js (2)
5-7: LGTM!The default compression settings are sensible for avatar images—256px provides adequate resolution while keeping file sizes small, and 0.85 JPEG quality balances visual quality with compression.
25-28: Previous issue resolved.The null check for canvas context has been properly added, addressing the prior review comment.
Summary by CodeRabbit
New Features
Improvements
UI
✏️ Tip: You can customize this high-level summary in your review settings.