Skip to content

Conversation

@willyogo
Copy link
Member

@willyogo willyogo commented Oct 7, 2025

Summary

  • add guards to ensure a space's cached config matches the page being rendered before using it
  • resolve the active tab from the validated space instead of stale store values and gate tab actions accordingly
  • prevent loading or mutating tabs when the cached space metadata does not match the requested profile space

Testing

  • npm run lint

https://chatgpt.com/codex/tasks/task_e_68e532a097b88325877b26dc7e8f955a

Summary by CodeRabbit

  • Improvements

    • More consistent tab selection/navigation with resolved space and tab identity.
    • Safer tab configuration handling via a new sanitization step and stable in-memory config derivation.
    • Smarter local caching and clearer cache fallbacks when switching spaces.
    • Async flows guarded to avoid state updates after leaving a page.
  • Bug Fixes

    • Prevents actions targeting wrong space/tab and reduces mismatched active states.
    • Fewer errors/warnings during async saves/loads; invalid configs are rejected early.
  • Tests

    • Added tests for tab/space sanitization and preventing saving of invalid configs.
  • Chores

    • New exported sanitizeTabConfig utility; removed one previously exported config accessor.

@vercel
Copy link

vercel bot commented Oct 7, 2025

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

Project Deployment Preview Comments Updated (UTC)
nounspace-ts Ready Ready Preview Comment Oct 8, 2025 4:44pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 7, 2025

Walkthrough

Adds a tab-config sanitizer, mount-safe async guards, and deterministic space/tab resolution; updates tab load/switch/save/commit/reset flows and TabBar wiring; removes a previously exported getCurrentSpaceConfig; includes tests for sanitization and storage behavior.

Changes

Cohort / File(s) Summary of edits
Public space resolution & tab flow
src/app/(spaces)/PublicSpace.tsx
Introduced ServerTabConfig/SanitizableTabConfig types and imports; added isMounted guard for async flows; implemented matchesSpaceData, resolution logic (resolvedTabName, activeSpaceId, fallback/cached config resolution, resolveSpaceIdForActions); integrated sanitizeTabConfig and normalized fidgetInstanceDatums; rewrote tab load/switch/save/commit/reset flows to use resolved identities and guards; removed public getCurrentSpaceConfig exposure; updated TabBar props and handlers.
Space current view mapping
src/common/data/stores/app/currentSpace/index.ts
Replaced inline tab transformation with sanitizeTabConfig per-tab; enforces privacy flags; skips/filters invalid cached tab configs; preserves normalized fidgetInstanceDatums shape for returned config.
Space store save pathway
src/common/data/stores/app/space/spaceStore.ts
Added post-processing sanitization when saving a space tab: calls sanitizeTabConfig and aborts save if sanitizer returns falsy; introduced an early-exit validation gate to avoid persisting invalid shapes.
Sanitizer utility
src/common/utils/sanitizeTabConfig.ts
New exported sanitizeTabConfig and TabConfigSanitizerOptions type; validates top-level and nested fields (fidgetTrayContents, layoutDetails, theme, fidgetInstanceDatums), deep-clones valid candidates, normalizes/coerces isPrivate, and returns undefined on invalid input while optionally logging warnings.
Tests
tests/spaceSanitization.test.ts
New tests for sanitizeTabConfig behavior (reject non-plain objects, deep-clone semantics, isPrivate coercion); mocks localStorage; ensures invalid configs are not saved and corrupted tabs are filtered when reading current space config.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant R as Router
  participant P as PublicSpace
  participant S as SpaceStore
  participant T as TabLoader
  participant B as TabBar
  Note over P: derive identities & sanitize

  U->>R: navigate(spaceId, tab)
  R-->>P: route params
  P->>P: compute currentSpaceId / currentTab
  P->>P: matchesSpaceData -> matchingSpace
  P->>P: compute resolvedTabName & activeSpaceId
  P->>P: sanitizeTabConfig(candidate) -> cached/fallback config
  alt need to load tabs
    P->>T: loadRemainingTabs(activeSpaceId, resolvedTabName)
    T-->>P: tabs loaded
  end
  P-->>B: render (resolvedTabName, activeSpaceId, handlers)
  U->>B: trigger action (switch/save/commit/rename)
  B->>P: invoke handler
  P->>P: isMounted? & matchesSpaceData?
  alt valid
    P->>S: perform mutation via resolveSpaceIdForActions
    S-->>P: store updated
    P-->>U: UI updates
  else invalid
    P-->>U: abort/log
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • j-paterson
  • r4topunk

Poem

"I hop through configs, tidy each nest,
Guard async burrows so saves run their best.
I scrub broken tabs and name the right lane,
Cache honest burrows and skip the cracked grain. 🐇"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the primary bug fix—ensuring correct tab selection for profile spaces when using cached channel configurations—and aligns with the PR objectives without extraneous detail.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/fix-intermittent-app-crash-on-profile-tab

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@willyogo willyogo added bug Something isn't working and removed codex labels Oct 7, 2025
@willyogo willyogo requested a review from j-paterson October 7, 2025 16:53
@willyogo willyogo marked this pull request as ready for review October 7, 2025 16:53
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

Caution

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

⚠️ Outside diff range comments (3)
src/app/(spaces)/PublicSpace.tsx (3)

262-291: Prevent crash: guard all decodeURIComponent() calls

decodeURIComponent(providedTabName) is invoked even when providedTabName is undefined (Lines 275, 283, 289), which throws. Decode once with a guard and reuse.

Apply this diff:

-    let nextSpaceId = spacePageData.spaceId;
-    let nextTabName = providedTabName ? decodeURIComponent(providedTabName) : spacePageData.defaultTab;
+    let nextSpaceId = spacePageData.spaceId;
+    const decodedProvidedTab = providedTabName ? decodeURIComponent(providedTabName) : undefined;
+    let nextTabName = decodedProvidedTab ?? spacePageData.defaultTab;
@@
-      if (existingSpace) {
-        nextSpaceId = existingSpace.id;
-        nextTabName = decodeURIComponent(providedTabName);
-      }
+      if (existingSpace) {
+        nextSpaceId = existingSpace.id;
+        nextTabName = decodedProvidedTab ?? spacePageData.defaultTab;
+      }
@@
-      if (existingSpace) {
-        nextSpaceId = existingSpace.id;
-        nextTabName = decodeURIComponent(providedTabName);
-      }
+      if (existingSpace) {
+        nextSpaceId = existingSpace.id;
+        nextTabName = decodedProvidedTab ?? spacePageData.defaultTab;
+      }
@@
-      if (spacePageData.spaceId) {
-        nextSpaceId = spacePageData.spaceId;
-        nextTabName = decodeURIComponent(providedTabName);
-      }
+      if (spacePageData.spaceId) {
+        nextSpaceId = spacePageData.spaceId;
+        nextTabName = decodedProvidedTab ?? spacePageData.defaultTab;
+      }

227-235: Cache clear condition may wipe the matching local space

Clearing when currentSpaceId !== spacePageData.spaceId is too coarse with the new matching logic; it can purge a correctly matched local space (e.g., token/channel/profile cases).

Apply this diff to gate by matchesSpaceData:

 useEffect(() => {
-  const currentSpaceId = getCurrentSpaceId();
-  if (currentSpaceId !== spacePageData.spaceId) {
+  const currentSpaceId = getCurrentSpaceId();
+  const candidate = currentSpaceId ? localSpaces[currentSpaceId] : undefined;
+  if (currentSpaceId && !matchesSpaceData(candidate)) {
     clearLocalSpaces();
     loadedTabsRef.current = {};
     initialDataLoadRef.current = false;
   }
-}, [clearLocalSpaces, getCurrentSpaceId, spacePageData.spaceId]);
+}, [clearLocalSpaces, getCurrentSpaceId, localSpaces, matchesSpaceData]);

363-364: Wrong fallback tab

Hardcoding "Profile" breaks non-profile spaces. Use the page’s default tab.

Apply this diff:

-    const currentTabName = getCurrentTabName() ?? "Profile";
+    const currentTabName = getCurrentTabName() ?? spacePageData.defaultTab;
🧹 Nitpick comments (2)
src/app/(spaces)/PublicSpace.tsx (2)

715-723: Remove unnecessary await on plain object

config is a plain object; awaiting it is a no-op and misleading.

Apply this diff:

-        const resolvedConfig = await config;
+        const resolvedConfig = config;
         await Promise.all([
           saveLocalSpaceTab(currentSpaceId, currentTabName, resolvedConfig),
           commitSpaceTab(
             currentSpaceId,
             currentTabName,
             isTokenSpace(spacePageData) ? spacePageData.tokenData?.network : undefined
           )
         ]);

205-224: Avoid double-writes: consolidate space synchronization effects

Two effects both adjust currentSpaceId/currentTabName. Merge into one that:

  • derives matchingSpaceId via matchesSpaceData,
  • sets currentSpaceId only when it changes,
  • sets currentTabName using the decoded provided tab or default.

This reduces churn and risk of loops.

Also applies to: 256-303

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b85d719 and 355c293.

📒 Files selected for processing (1)
  • src/app/(spaces)/PublicSpace.tsx (12 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(spaces)/PublicSpace.tsx (2)
src/common/types/spaceData.ts (4)
  • isProfileSpace (81-83)
  • isChannelSpace (93-95)
  • isTokenSpace (85-87)
  • isProposalSpace (89-91)
src/common/data/stores/app/currentSpace/index.ts (2)
  • setCurrentSpaceId (43-47)
  • setCurrentTabName (51-55)
🔇 Additional comments (3)
src/app/(spaces)/PublicSpace.tsx (3)

308-349: Good guardrails in loadRemainingTabs

Early return on non-matching space and using resolvedTabName for the active space avoids loading stale/mismatched tabs.


455-471: resolveSpaceIdForActions is a solid safety net

Validating current space against page context and falling back to matchingSpaceId appropriately gates mutations.


793-798: Using resolvedTabName/current space order in TabBar

Passing currentTab={resolvedTabName} and tabList based on activeSpaceId ensures consistent UI with validated state.

Comment on lines +161 to +163
const requestedTabName =
currentTabNameValue || providedTabName || spacePageData.defaultTab;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use decoded provided tab when resolving requestedTabName

Using the raw providedTabName risks mismatch with config keys and can force an unnecessary default fallback.

Apply this diff:

-  const requestedTabName =
-    currentTabNameValue || providedTabName || spacePageData.defaultTab;
+  const providedTabDecoded = providedTabName
+    ? decodeURIComponent(providedTabName)
+    : undefined;
+  const requestedTabName =
+    currentTabNameValue || providedTabDecoded || spacePageData.defaultTab;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const requestedTabName =
currentTabNameValue || providedTabName || spacePageData.defaultTab;
const providedTabDecoded = providedTabName
? decodeURIComponent(providedTabName)
: undefined;
const requestedTabName =
currentTabNameValue || providedTabDecoded || spacePageData.defaultTab;
🤖 Prompt for AI Agents
In src/app/(spaces)/PublicSpace.tsx around lines 161 to 163, the resolved
requestedTabName uses the raw providedTabName which can be URL-encoded and
mismatch config keys; decode the providedTabName before using it (e.g. safe
decodeURIComponent) and fall back to providedTabName if decoding fails or it's
falsy, then use currentTabNameValue || decodedProvidedTabName ||
spacePageData.defaultTab so encoded names match config keys.

@willyogo willyogo added the LGFTP Looks Good From Testing Perspective label Oct 7, 2025
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 (1)
src/app/(spaces)/PublicSpace.tsx (1)

171-172: Decode providedTabName before using in requestedTabName resolution.

The raw providedTabName can be URL-encoded and won't match config keys, forcing an unnecessary fallback to defaultTab. This issue was flagged in a previous review but remains unaddressed.

Apply this diff:

+  const providedTabDecoded = providedTabName
+    ? decodeURIComponent(providedTabName)
+    : undefined;
   const requestedTabName =
-    currentTabNameValue || providedTabName || spacePageData.defaultTab;
+    currentTabNameValue || providedTabDecoded || spacePageData.defaultTab;
🧹 Nitpick comments (4)
src/app/(spaces)/PublicSpace.tsx (4)

15-25: Consider modular lodash imports to reduce bundle size.

While the current imports work correctly, using modular imports (e.g., import pickBy from 'lodash/pickBy') instead of importing from the main lodash package can reduce bundle size. This is a best practice for lodash usage.

Based on learnings.

Apply this diff if you want to reduce bundle size:

-import {
-  indexOf,
-  isNil,
-  mapValues,
-  noop,
-  debounce,
-  pickBy,
-  isUndefined,
-  isPlainObject,
-  cloneDeep,
-} from "lodash";
+import indexOf from "lodash/indexOf";
+import isNil from "lodash/isNil";
+import mapValues from "lodash/mapValues";
+import noop from "lodash/noop";
+import debounce from "lodash/debounce";
+import pickBy from "lodash/pickBy";
+import isUndefined from "lodash/isUndefined";
+import isPlainObject from "lodash/isPlainObject";
+import cloneDeep from "lodash/cloneDeep";

174-205: Consider extracting the nested transformation logic.

The nested pickBy and mapValues operations (4 levels deep) make this code harder to read and maintain. Consider extracting the tab enrichment logic into a separate helper function.

Example refactor:

const enrichTabConfig = (tabInfo: typeof matchingSpace.tabs[string]) => {
  if (!tabInfo) return undefined;
  
  return {
    ...tabInfo,
    fidgetInstanceDatums: mapValues(
      tabInfo.fidgetInstanceDatums,
      (datum) => ({
        ...datum,
        config: {
          settings: datum.config.settings,
          editable: datum.config.editable,
          data: {},
        },
      }),
    ),
  };
};

const currentConfig = useMemo(() => {
  if (!matchingSpace) return undefined;
  
  const enrichedTabs = pickBy(
    mapValues(matchingSpace.tabs, enrichTabConfig),
    (value) => !isUndefined(value),
  );
  
  return { ...matchingSpace, tabs: enrichedTabs };
}, [matchingSpace]);

382-426: Add logging when matchesSpaceData guard fails.

The early return at lines 384-386 silently skips loading remaining tabs when the cached space doesn't match the page context. This can make debugging difficult.

Apply this diff to add logging:

   async (spaceId: string) => {
     if (!matchesSpaceData(localSpaces[spaceId])) {
+      console.warn(
+        "Skipping loadRemainingTabs because cached space does not match page context",
+        { spaceId, spacePageData }
+      );
       return;
     }

857-949: Approve defensive guards; consider refactoring for complexity.

The addition of matchesSpaceData validation, resolvedTabName usage, and isMountedRef guards significantly improves robustness and prevents stale state issues.

However, this function is now 92 lines long and handles multiple concerns (validation, navigation, saving, loading). Consider extracting the save/commit logic into a separate helper function to improve maintainability and testability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8023a9 and 84e13a1.

📒 Files selected for processing (1)
  • src/app/(spaces)/PublicSpace.tsx (15 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(spaces)/PublicSpace.tsx (4)
src/common/types/spaceData.ts (4)
  • isProfileSpace (81-83)
  • isChannelSpace (93-95)
  • isTokenSpace (85-87)
  • isProposalSpace (89-91)
src/common/data/stores/app/currentSpace/index.ts (2)
  • setCurrentSpaceId (43-47)
  • setCurrentTabName (51-55)
src/app/(spaces)/Space.tsx (1)
  • SpaceConfig (37-49)
src/common/data/stores/app/space/spaceStore.ts (1)
  • UpdatableSpaceConfig (81-83)
🔇 Additional comments (3)
src/app/(spaces)/PublicSpace.tsx (3)

110-116: LGTM!

The isMountedRef pattern is correctly implemented to guard async operations and prevent state updates after unmount. The cleanup function properly sets the ref to false.


118-154: LGTM!

The matchesSpaceData function correctly validates cached spaces against the current page context. The logic properly handles all space types (profile, channel, token, proposal) and uses appropriate type guards to ensure type-safe property access.


540-582: LGTM!

The sanitizeTabConfig helper provides strong defensive validation. It correctly checks for plain objects (preventing prototype pollution), validates required fields, logs warnings for invalid configs, and returns deep clones to prevent shared references.

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 (1)
src/app/(spaces)/PublicSpace.tsx (1)

181-182: Decode providedTabName before resolving requestedTabName.

The raw providedTabName can be URL-encoded (e.g., "My%20Tab"), which will mismatch with config keys that expect "My Tab". This was previously flagged but remains unaddressed.

Apply this diff:

+  const providedTabDecoded = providedTabName
+    ? decodeURIComponent(providedTabName)
+    : undefined;
   const requestedTabName =
-    currentTabNameValue || providedTabName || spacePageData.defaultTab;
+    currentTabNameValue || providedTabDecoded || spacePageData.defaultTab;
🧹 Nitpick comments (2)
src/app/(spaces)/PublicSpace.tsx (2)

283-320: Cache clearing logic is correct but complex.

The effect correctly clears local spaces when switching to a different space identity. The special case (lines 301-308) that skips clearing when there's no explicit spaceId but the activeSpaceId matches current is a subtle optimization to avoid unnecessary cache clears during profile/channel/token space loads.

Note: The logic is correct but quite intricate. Consider adding inline comments explaining each branch condition for future maintainability.


922-1019: Refactored switchTabTo correctly validates space context.

The function now properly:

  • Validates that the current local space matches page data before switching
  • Uses resolvedTabName to identify the current tab being saved
  • Guards state updates and async operations with isMountedRef
  • Converts configs to saveable format using toSavableTabConfig

Minor: Remove unnecessary await on synchronous config value.

Line 946 uses await config, but config (defined at line 661) is a memoized synchronous value, not a promise. The await is harmless but misleading.

Apply this diff:

-        const resolvedConfig = await config;
+        const resolvedConfig = config;

Note: The same pattern appears in renameTab at line 1079 and should be updated there as well.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84e13a1 and aef465d.

📒 Files selected for processing (1)
  • src/app/(spaces)/PublicSpace.tsx (16 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(spaces)/PublicSpace.tsx (4)
src/common/types/spaceData.ts (5)
  • SpacePageData (18-36)
  • isProfileSpace (81-83)
  • isChannelSpace (93-95)
  • isTokenSpace (85-87)
  • isProposalSpace (89-91)
src/common/data/stores/app/space/spaceStore.ts (2)
  • UpdatableSpaceConfig (81-83)
  • UpdatableDatabaseWritableSpaceSaveConfig (76-79)
src/common/data/stores/app/currentSpace/index.ts (2)
  • setCurrentSpaceId (43-47)
  • setCurrentTabName (51-55)
src/app/(spaces)/Space.tsx (1)
  • SpaceConfig (37-49)
🔇 Additional comments (13)
src/app/(spaces)/PublicSpace.tsx (13)

9-48: LGTM!

The new type definitions clearly distinguish between server-side tab configurations and sanitizable configurations, providing good type safety for the config handling logic introduced in this PR.


120-126: LGTM!

Standard pattern for tracking component mount state to prevent state updates after unmount. This ref is properly used throughout the component to guard async operations.


128-164: LGTM!

The matchesSpaceData function provides comprehensive validation across all space types (profile, channel, token, proposal) to ensure cached spaces align with the current page context. The logic correctly handles optional properties and type guards.


217-223: LGTM!

The resolvedTabName logic correctly validates that the requested tab exists in the current config before using it, with a safe fallback to the default tab.


225-243: LGTM!

This effect correctly synchronizes the store's currentSpaceId with the matchingSpaceId, ensuring that the store always reflects a space that matches the current page context or is cleared otherwise.


245-280: LGTM!

The space identity resolution logic provides stable cache keys across space types, preferring explicit IDs when available and falling back to type-specific identifiers (e.g., profile:fid, channel:id).


392-436: LGTM!

The loadRemainingTabs function now correctly validates that the space matches current page data before loading tabs, and uses resolvedTabName to identify the current tab when appropriate.


487-542: LGTM!

Mount guards are correctly placed to prevent state updates after component unmount at all async operation completion points.


550-667: LGTM!

The config helper functions provide robust validation and normalization:

  • sanitizeTabConfig validates structure and clones safely
  • toSavableTabConfig prepares configs for database persistence by removing non-saveable fields
  • Proper memoization prevents unnecessary recomputation

669-684: LGTM!

The resolveSpaceIdForActions helper centralizes the logic for obtaining a validated space ID for tab actions, ensuring consistency across all TabBar action handlers.


884-919: LGTM!

The resetConfig function correctly uses the new sanitization pipeline, falling back to the default config if sanitization fails, and properly converts to a saveable format before persisting.


1036-1121: LGTM!

The TabBar props are correctly updated to use:

  • resolvedTabName for currentTab to ensure the active tab reflects validated state
  • activeSpaceId for tabList to show tabs from the matching space
  • resolveSpaceIdForActions across all action handlers for consistency
  • Mount guards in async handlers to prevent updates after unmount

184-215: No changes needed: clearing config.data in PublicSpace.tsx is intentional to omit fidget data for public views.

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 (1)
src/app/(spaces)/PublicSpace.tsx (1)

181-183: Use decoded providedTabName when computing requestedTabName.

This issue was previously flagged: using the raw providedTabName risks mismatch with config keys if the tab name is URL-encoded. The resolvedTabName computation (lines 217-223) depends on requestedTabName, so this affects tab resolution throughout the component.

Apply this diff:

+ const providedTabDecoded = providedTabName
+   ? decodeURIComponent(providedTabName)
+   : undefined;
  const requestedTabName =
-   currentTabNameValue || providedTabName || spacePageData.defaultTab;
+   currentTabNameValue || providedTabDecoded || spacePageData.defaultTab;
🧹 Nitpick comments (1)
src/app/(spaces)/PublicSpace.tsx (1)

550-612: Well-structured validation and sanitization.

The sanitizeTabConfig function provides thorough validation of tab configs with appropriate warnings. Using cloneDeep ensures immutability, though note that this could impact performance for very large configs (consider profiling if performance issues arise).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aef465d and 2232c3d.

📒 Files selected for processing (1)
  • src/app/(spaces)/PublicSpace.tsx (16 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/(spaces)/PublicSpace.tsx (4)
src/common/types/spaceData.ts (5)
  • SpacePageData (18-36)
  • isProfileSpace (81-83)
  • isChannelSpace (93-95)
  • isTokenSpace (85-87)
  • isProposalSpace (89-91)
src/common/data/stores/app/space/spaceStore.ts (2)
  • UpdatableSpaceConfig (81-83)
  • UpdatableDatabaseWritableSpaceSaveConfig (76-79)
src/common/data/stores/app/currentSpace/index.ts (2)
  • setCurrentSpaceId (43-47)
  • setCurrentTabName (51-55)
src/app/(spaces)/Space.tsx (1)
  • SpaceConfig (37-49)
🔇 Additional comments (1)
src/app/(spaces)/PublicSpace.tsx (1)

1018-1107: Consistent use of resolved space and tab identifiers.

The TabBar props correctly use resolvedTabName for currentTab, activeSpaceId for tabList, and resolveSpaceIdForActions() for all tab actions. This ensures operations target the correct space and tab context.

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 (1)
src/common/utils/sanitizeTabConfig.ts (1)

1-1: Consider per-method lodash imports for smaller bundle size.

While the current destructured import works fine, using per-method imports can reduce bundle size:

-import { cloneDeep, isArray, isPlainObject } from "lodash";
+import cloneDeep from "lodash/cloneDeep";
+import isArray from "lodash/isArray";
+import isPlainObject from "lodash/isPlainObject";

Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb1e2a5 and 3248df0.

📒 Files selected for processing (1)
  • src/common/utils/sanitizeTabConfig.ts (1 hunks)
🔇 Additional comments (1)
src/common/utils/sanitizeTabConfig.ts (1)

1-106: LGTM! Robust validation and sanitization logic.

The implementation correctly validates tab config structure, handles edge cases (null/undefined checks, type guards), and uses deep cloning to prevent mutations. The isPrivate normalization logic properly handles boolean coercion and defaults.

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

Labels

bug Something isn't working LGFTP Looks Good From Testing Perspective

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants