Skip to content

Conversation

@bmanquen
Copy link
Collaborator

@bmanquen bmanquen commented Feb 3, 2026

No description provided.

@chatgpt-codex-connector
Copy link

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@changeset-bot
Copy link

changeset-bot bot commented Feb 3, 2026

🦋 Changeset detected

Latest commit: 5c8dda8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-react-ui Patch
@youversion/platform-core Patch
@youversion/platform-react-hooks Patch
nextjs Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 3, 2026

Greptile Overview

Greptile Summary

Refactored bible reader font preference loading to use useLayoutEffect instead of initializing from localStorage in useState, preventing flash of default values when component mounts.

Key Changes:

  • Moved localStorage reads from useState initializer to useLayoutEffect hook
  • Separated validation logic from state initialization
  • Added comprehensive integration test verifying saved preferences load correctly
  • Maintains existing save behavior via separate useEffect hooks

Issues Found:

  • Missing SSR safety check in useLayoutEffect - localStorage accessed without typeof window guard

Confidence Score: 4/5

  • Safe to merge after addressing the SSR safety check
  • The refactoring correctly solves the FOUC issue by using useLayoutEffect, and includes proper testing. However, the missing typeof window check could cause SSR errors in Next.js or other SSR environments, even with 'use client' directive.
  • packages/ui/src/components/bible-reader.tsx needs SSR safety guard added

Important Files Changed

Filename Overview
packages/ui/src/components/bible-reader.tsx Refactored font preferences to load from localStorage using useLayoutEffect to prevent flash of default values

Sequence Diagram

sequenceDiagram
    participant User
    participant BibleReaderRoot
    participant LocalStorage
    participant State
    participant DOM

    User->>BibleReaderRoot: Mount component with props
    BibleReaderRoot->>State: Initialize with validated fontSize & fontFamily from props
    BibleReaderRoot->>DOM: First render with prop values
    Note over DOM: User briefly sees default values
    BibleReaderRoot->>LocalStorage: useLayoutEffect reads saved preferences
    LocalStorage-->>BibleReaderRoot: Return saved fontSize (18) & fontFamily (Source Serif)
    BibleReaderRoot->>State: setCurrentFontSize(18) & setCurrentFontFamily(Source Serif)
    BibleReaderRoot->>DOM: Re-render with localStorage values
    Note over DOM: User sees saved preferences (minimal flash)
    
    User->>BibleReaderRoot: Change font size or family
    BibleReaderRoot->>State: Update state
    State->>LocalStorage: useEffect saves new value
    BibleReaderRoot->>DOM: Render with new values
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +114 to +127
useLayoutEffect(() => {
const savedFontSize = localStorage.getItem('youversion-platform:reader:font-size');
if (savedFontSize) {
const parsed = parseInt(savedFontSize);
if (parsed >= MIN_FONT_SIZE && parsed <= MAX_FONT_SIZE) {
setCurrentFontSize(parsed);
}
}

const savedFontFamily = localStorage.getItem('youversion-platform:reader:font-family');
if (savedFontFamily) {
setCurrentFontFamily(savedFontFamily);
}
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing SSR safety check - localStorage accessed without window check

Suggested change
useLayoutEffect(() => {
const savedFontSize = localStorage.getItem('youversion-platform:reader:font-size');
if (savedFontSize) {
const parsed = parseInt(savedFontSize);
if (parsed >= MIN_FONT_SIZE && parsed <= MAX_FONT_SIZE) {
setCurrentFontSize(parsed);
}
}
const savedFontFamily = localStorage.getItem('youversion-platform:reader:font-family');
if (savedFontFamily) {
setCurrentFontFamily(savedFontFamily);
}
}, []);
// Load saved preferences from localStorage before paint (avoids flash of default values)
useLayoutEffect(() => {
if (typeof window === 'undefined') return;
const savedFontSize = localStorage.getItem('youversion-platform:reader:font-size');
if (savedFontSize) {
const parsed = parseInt(savedFontSize);
if (parsed >= MIN_FONT_SIZE && parsed <= MAX_FONT_SIZE) {
setCurrentFontSize(parsed);
}
}
const savedFontFamily = localStorage.getItem('youversion-platform:reader:font-family');
if (savedFontFamily) {
setCurrentFontFamily(savedFontFamily);
}
}, []);
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/components/bible-reader.tsx
Line: 114:127

Comment:
Missing SSR safety check - `localStorage` accessed without window check

```suggestion
  // Load saved preferences from localStorage before paint (avoids flash of default values)
  useLayoutEffect(() => {
    if (typeof window === 'undefined') return;
    
    const savedFontSize = localStorage.getItem('youversion-platform:reader:font-size');
    if (savedFontSize) {
      const parsed = parseInt(savedFontSize);
      if (parsed >= MIN_FONT_SIZE && parsed <= MAX_FONT_SIZE) {
        setCurrentFontSize(parsed);
      }
    }

    const savedFontFamily = localStorage.getItem('youversion-platform:reader:font-family');
    if (savedFontFamily) {
      setCurrentFontFamily(savedFontFamily);
    }
  }, []);
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All of the localStorage calls are within hooks which run on the client. This is why we don't need to check if window exists.

Copy link
Collaborator

@cameronapak cameronapak left a comment

Choose a reason for hiding this comment

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

meh... I'll rewrite this comment soon

Copy link
Collaborator

@cameronapak cameronapak left a comment

Choose a reason for hiding this comment

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

LGTM!

note: Sorry for the comment earlier that may have been confusing, but it has since been removed.

nitpick: I was a little weary removing the check for window, but since we are using a use client directive at the top, then it seems fine

@bmanquen
Copy link
Collaborator Author

bmanquen commented Feb 3, 2026

LGTM!

note: Sorry for the comment earlier that may have been confusing, but it has since been removed.

nitpick: I was a little weary removing the check for window, but since we are using a use client directive at the top, then it seems fine

All of the localStorage calls are within hooks which can only run on the client.

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.

3 participants