Skip to content

Conversation

Oksamies
Copy link
Contributor

@Oksamies Oksamies commented Sep 17, 2025

For some reason the localStorage set's in shouldRevalidate are not
commited before the clientLoader is run. So run the sessionValid
function in both, to ensure currentUser is updated. This does lead to
some extra processing, but acceptable cost for now.

Summary by CodeRabbit

  • Bug Fixes

    • Reduced intermittent sign-outs and data mismatches by validating sessions before loading user and app settings.
    • Pages now re-check data when a session is stale to prevent outdated information.
  • Performance

    • Cuts unnecessary revalidation, improving navigation responsiveness.
    • Enables client-side hydration for more consistent initial renders and restores.
  • Other

    • Removed an older hydration fallback for a simpler, more reliable hydration flow.

Copy link

coderabbitai bot commented Sep 17, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Client loader now validates and persists session state before fetching currentUser and config, shouldRevalidate always invokes session validation and returns session staleness OR the default decision, loader hydration flag is enabled, and the HydrateFallback export was removed.

Changes

Cohort / File(s) Summary of Changes
Root loader and revalidation
apps/cyberstorm-remix/app/root.tsx
- Call sessionValid in clientLoader before obtaining currentUser/config
- Add clientLoader.hydrate = true to enable loader hydration
- Update shouldRevalidate: always call sessionValid, compute sessionIsStale via getSessionStale, return `sessionIsStale

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant R as Router
  participant L as clientLoader
  participant SV as sessionValid
  participant CU as getCurrentUser
  participant CF as getConfig

  rect rgba(230,245,255,0.6)
    note over L: Loader hydration enabled (`hydrate = true`)
  end

  R->>L: invoke loader
  L->>SV: validate & persist session
  SV-->>L: session state ensured
  L->>CU: fetch currentUser
  CU-->>L: user data
  L->>CF: fetch config
  CF-->>L: config
  L-->>R: loader data
Loading
sequenceDiagram
  autonumber
  participant R as Router (nav/update)
  participant SR as shouldRevalidate
  participant SV as sessionValid
  participant GS as getSessionStale
  participant DR as defaultShouldRevalidate

  R->>SR: check revalidation
  SR->>SV: validate session
  SV-->>SR: session ensured
  SR->>GS: compute sessionIsStale
  GS-->>SR: boolean
  SR->>DR: compute defaultShouldRevalidate
  DR-->>SR: boolean
  SR-->>R: return sessionIsStale OR defaultShouldRevalidate
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I twitched my whiskers, checked the den,
Validated tracks and hopped again.
Stale crumbs shuffled, fresh paths found,
Hydrated loader digs the ground.
A rabbit's nibble — session sound. 🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 "Ensure current user is updated" accurately captures the primary intent of this changeset—running sessionValid in the client loader and shouldRevalidate to keep currentUser in sync—so it relates directly to the main change. It is concise, clear, and readable for a teammate scanning PR history. It does not need to list secondary implementation details (like HydrateFallback removal or adding clientLoader.hydrate) to be effective.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 09-17-ensure_current_user_is_updated

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb9c261 and ec751c6.

📒 Files selected for processing (1)
  • apps/cyberstorm-remix/app/root.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/cyberstorm-remix/app/root.tsx
⏰ 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). (6)
  • GitHub Check: chromatic-deployment (apps/cyberstorm-storybook, CHROMATIC_CYBERSTORM_TOKEN)
  • GitHub Check: Test
  • GitHub Check: ESLint
  • GitHub Check: CodeQL
  • GitHub Check: Generate visual diffs
  • GitHub Check: CodeQL

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.

Copy link
Contributor Author

Oksamies commented Sep 17, 2025

@@ -141,6 +141,13 @@ export async function clientLoader() {
publicEnvVariables.VITE_API_URL,
publicEnvVariables.VITE_COOKIE_DOMAIN
);

// We need to run this here too in addition to the, shouldRevalidate function,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: typos

const sessionIsStale = getSessionStale(
new NamespacedStorageManager(SESSION_STORAGE_KEY)
);
if (sessionIsStale || defaultShouldRevalidate) return true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: return sessionIsStale || defaultShouldRevalidate; would be better as it's less to read and would change the functions return type from awkward true | undefined to boolean.

}

clientLoader.hydrate = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like an odd place for this code, hidden between two exported functions. Which leads to question: is there a reason to define shouldRevalidate in root.tsx, an already lengthy file, if it's not even used here? This applies to other exported functions in the same file too. Disclaimer: I've no idea if root.tsx is supposed to be some special file with special responsibilities.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do not understand the question? shouldRevalidate and clientLoader.hydrate do not affect each other.
https://v2.remix.run/docs/route/client-loader#clientloaderhydrate
https://reactrouter.com/start/data/route-object#shouldrevalidate

Both are needed. shouldRevalidate to prevent unnecessary clientLoader calls and clientLoader.hydrate = true to ensure that the currentUser is fetched and set on the initial page load.

As the address the "lengthyness" of the root.tsx file; the file needs to have certain components/functions and loaders in it, as react-router calls the components from the Route files.

We can of course cleanup and organize the 750~ lines to multiple files, if we want that, but let's make a separate task for that.

Base automatically changed from 09-17-fix_session_revalidation_corner_case to master September 18, 2025 15:31
@Oksamies Oksamies force-pushed the 09-17-ensure_current_user_is_updated branch from fce63a7 to b8891d1 Compare September 18, 2025 15:48
Copy link

@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)
apps/cyberstorm-remix/app/root.tsx (1)

183-184: Enabling clientLoader hydration looks good.

This ensures currentUser/config are fetched on initial load. No concerns.

🧹 Nitpick comments (2)
apps/cyberstorm-remix/app/root.tsx (2)

145-150: Tighten the comment; fix typos and wording.

Clarify the rationale and remove typos (“the the”, stray comma). LocalStorage writes are sync; the issue is visibility/ordering relative to clientLoader.

-  // We need to run this here too in addition to the, shouldRevalidate function,
-  // as for some reason the commits to localStorage are not done before the the clientLoader is run
+  // Also run this here (in addition to shouldRevalidate) because localStorage updates
+  // done during navigation may not be observed before clientLoader executes.

172-180: Avoid deep imports; reuse one storage instance; guard missing env.

  • Don’t pass empty strings into sessionValid; short‑circuit to defaultShouldRevalidate if env is missing.
  • Reuse a single storage instance for both calls.
  • Prefer package‑level exports over “/src/*” deep imports to prevent coupling to internals and type mismatches.

Apply within-range changes:

-  sessionValid(
-    new StorageManager(SESSION_STORAGE_KEY),
-    publicEnvVariables.VITE_API_URL || "",
-    publicEnvVariables.VITE_COOKIE_DOMAIN || ""
-  );
-  const sessionIsStale = getSessionStale(
-    new NamespacedStorageManager(SESSION_STORAGE_KEY)
-  );
-  return sessionIsStale || defaultShouldRevalidate;
+  const { VITE_API_URL: apiHost, VITE_COOKIE_DOMAIN: cookieDomain } =
+    publicEnvVariables;
+  if (!apiHost || !cookieDomain) {
+    return defaultShouldRevalidate;
+  }
+  const storage = new NamespacedStorageManager(SESSION_STORAGE_KEY);
+  sessionValid(storage, apiHost, cookieDomain);
+  const sessionIsStale = getSessionStale(storage);
+  return sessionIsStale || defaultShouldRevalidate;

And update imports (outside this hunk) to avoid deep paths:

- import {
-   getSessionContext,
-   getSessionStale,
-   SESSION_STORAGE_KEY,
-   sessionValid,
- } from "@thunderstore/ts-api-react/src/SessionContext";
- import { StorageManager } from "@thunderstore/ts-api-react/src/storage";
+ import {
+   getSessionContext,
+   getSessionStale,
+   SESSION_STORAGE_KEY,
+   sessionValid,
+   StorageManager as NamespacedStorageManager, // or keep the existing alias import
+ } from "@thunderstore/ts-api-react";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a07f2ca and b8891d1.

📒 Files selected for processing (1)
  • apps/cyberstorm-remix/app/root.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/cyberstorm-remix/app/root.tsx (3)
packages/ts-api-react/src/SessionContext.tsx (3)
  • sessionValid (121-158)
  • SESSION_STORAGE_KEY (46-46)
  • getSessionStale (82-84)
packages/ts-api-react/src/index.ts (2)
  • sessionValid (7-7)
  • StorageManager (13-13)
packages/ts-api-react/src/storage.ts (1)
  • StorageManager (1-69)

@Oksamies
Copy link
Contributor Author

ESLint and CodeQL checks are failing because Graphite created a temporary branch when I was fixing and submitting code in each PR in order. (someone has limited the maximum amount of pushes to 2 in a single command and graphite normally tries to do the whole stack at once to prevent needing to create temporary branches)

Comment on lines +172 to +180
sessionValid(
new StorageManager(SESSION_STORAGE_KEY),
publicEnvVariables.VITE_API_URL || "",
publicEnvVariables.VITE_COOKIE_DOMAIN || ""
);
const sessionIsStale = getSessionStale(
new NamespacedStorageManager(SESSION_STORAGE_KEY)
);
return sessionIsStale || defaultShouldRevalidate;
Copy link

Choose a reason for hiding this comment

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

Critical logic bug: The sessionValid() function call is no longer being used to determine revalidation behavior. In the original code, if sessionValid() returned false, the function would return true to trigger revalidation. Now sessionValid() is called but its return value is ignored, and revalidation only depends on sessionIsStale and defaultShouldRevalidate. This means invalid sessions will no longer trigger revalidation, potentially leaving users with stale data when their session becomes invalid. The fix should capture the sessionValid() return value and include it in the revalidation logic: const isSessionInvalid = !sessionValid(...); return sessionIsStale || defaultShouldRevalidate || isSessionInvalid;

Suggested change
sessionValid(
new StorageManager(SESSION_STORAGE_KEY),
publicEnvVariables.VITE_API_URL || "",
publicEnvVariables.VITE_COOKIE_DOMAIN || ""
);
const sessionIsStale = getSessionStale(
new NamespacedStorageManager(SESSION_STORAGE_KEY)
);
return sessionIsStale || defaultShouldRevalidate;
const isSessionValid = sessionValid(
new StorageManager(SESSION_STORAGE_KEY),
publicEnvVariables.VITE_API_URL || "",
publicEnvVariables.VITE_COOKIE_DOMAIN || ""
);
const sessionIsStale = getSessionStale(
new NamespacedStorageManager(SESSION_STORAGE_KEY)
);
return sessionIsStale || defaultShouldRevalidate || !isSessionValid;

Spotted by Diamond

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@Oksamies Oksamies requested a review from anttimaki September 18, 2025 17:35
Copy link

@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

♻️ Duplicate comments (1)
apps/cyberstorm-remix/app/root.tsx (1)

145-150: Fix typos/wording in the comment.

Small cleanup for readability.

-  // We need to run this here too in addition to the, shouldRevalidate function,
-  // as for some reason the commits to localStorage are not done before the the clientLoader is run
+  // We need to run this here too, in addition to shouldRevalidate,
+  // because for some reason localStorage writes may not be committed before the clientLoader runs.
🧹 Nitpick comments (1)
apps/cyberstorm-remix/app/root.tsx (1)

41-47: Consolidate imports to package entrypoint

packages/ts-api-react/src/index.ts re-exports the SessionContext exports and StorageManager (aliased as NamespacedStorageManager). In apps/cyberstorm-remix/app/root.tsx (lines ~41–52) replace the deep import with a single import from the package root:

import {
NamespacedStorageManager,
getSessionContext,
getSessionStale,
SESSION_STORAGE_KEY,
sessionValid,
} from "@thunderstore/ts-api-react";

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8891d1 and bb9c261.

📒 Files selected for processing (1)
  • apps/cyberstorm-remix/app/root.tsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/cyberstorm-remix/app/root.tsx (3)
packages/ts-api-react/src/SessionContext.tsx (3)
  • sessionValid (121-158)
  • SESSION_STORAGE_KEY (46-46)
  • getSessionStale (82-84)
packages/ts-api-react/src/index.ts (2)
  • sessionValid (7-7)
  • StorageManager (13-13)
packages/ts-api-react/src/storage.ts (1)
  • StorageManager (1-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). (6)
  • GitHub Check: Test
  • GitHub Check: ESLint
  • GitHub Check: CodeQL
  • GitHub Check: Test
  • GitHub Check: Generate visual diffs
  • GitHub Check: CodeQL
🔇 Additional comments (1)
apps/cyberstorm-remix/app/root.tsx (1)

183-183: Enabling clientLoader.hydrate looks good.

This ensures currentUser is fetched on initial hydration as intended.

Comment on lines +172 to 181
sessionValid(
new StorageManager(SESSION_STORAGE_KEY),
publicEnvVariables.VITE_API_URL || "",
publicEnvVariables.VITE_COOKIE_DOMAIN || ""
);
const sessionIsStale = getSessionStale(
new NamespacedStorageManager(SESSION_STORAGE_KEY)
);
return sessionIsStale || defaultShouldRevalidate;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against empty envs to avoid clobbering storage; reuse a single storage instance.

Passing "" into sessionValid can overwrite API_HOST_KEY/COOKIE_DOMAIN_KEY in localStorage if envs are missing momentarily. Also, construct one storage instance and reuse it for both calls.

Apply this diff within this block:

-  sessionValid(
-    new StorageManager(SESSION_STORAGE_KEY),
-    publicEnvVariables.VITE_API_URL || "",
-    publicEnvVariables.VITE_COOKIE_DOMAIN || ""
-  );
-  const sessionIsStale = getSessionStale(
-    new NamespacedStorageManager(SESSION_STORAGE_KEY)
-  );
-  return sessionIsStale || defaultShouldRevalidate;
+  const apiHost = publicEnvVariables.VITE_API_URL;
+  const cookieDomain = publicEnvVariables.VITE_COOKIE_DOMAIN;
+  if (!apiHost || !cookieDomain) {
+    // Don't mutate storage with empty values; fall back to router default.
+    return Boolean(defaultShouldRevalidate);
+  }
+  const storage = new NamespacedStorageManager(SESSION_STORAGE_KEY);
+  sessionValid(storage, apiHost, cookieDomain);
+  const sessionIsStale = getSessionStale(storage);
+  return sessionIsStale || defaultShouldRevalidate;

Note: If you apply this, the StorageManager import becomes unused. Remove it (see separate note below).

📝 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
sessionValid(
new StorageManager(SESSION_STORAGE_KEY),
publicEnvVariables.VITE_API_URL || "",
publicEnvVariables.VITE_COOKIE_DOMAIN || ""
);
const sessionIsStale = getSessionStale(
new NamespacedStorageManager(SESSION_STORAGE_KEY)
);
return sessionIsStale || defaultShouldRevalidate;
}
const apiHost = publicEnvVariables.VITE_API_URL;
const cookieDomain = publicEnvVariables.VITE_COOKIE_DOMAIN;
if (!apiHost || !cookieDomain) {
// Don't mutate storage with empty values; fall back to router default.
return Boolean(defaultShouldRevalidate);
}
const storage = new NamespacedStorageManager(SESSION_STORAGE_KEY);
sessionValid(storage, apiHost, cookieDomain);
const sessionIsStale = getSessionStale(storage);
return sessionIsStale || defaultShouldRevalidate;
}
🤖 Prompt for AI Agents
In apps/cyberstorm-remix/app/root.tsx around lines 172 to 181, avoid passing
empty strings into sessionValid (which can clobber
API_HOST_KEY/COOKIE_DOMAIN_KEY) and reuse a single storage instance: create one
NamespacedStorageManager (or StorageManager if appropriate) instance and pass
that same instance into both sessionValid and getSessionStale, and only pass the
env vars to sessionValid when they are non-empty (e.g., conditionally pass
undefined or omit them) so you don't overwrite stored values when envs are
missing; after updating usage, remove the now-unused StorageManager import.

For some reason the localStorage set's in shouldRevalidate are not
commited before the clientLoader is run. So run the sessionValid
function in both, to ensure currentUser is updated. This does lead to
some extra processing, but acceptable cost for now.
Remove HydrateFallback from root to prevent the root loaders from holding back page specific skeleton loaders
@Oksamies Oksamies force-pushed the 09-17-ensure_current_user_is_updated branch from bb9c261 to ec751c6 Compare September 19, 2025 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants