Skip to content

YPE-1565: Show error when YouVersionProvider has no appKey#264

Open
cameronapak wants to merge 9 commits into
mainfrom
YPE-1565-missing-app-key-error
Open

YPE-1565: Show error when YouVersionProvider has no appKey#264
cameronapak wants to merge 9 commits into
mainfrom
YPE-1565-missing-app-key-error

Conversation

@cameronapak

@cameronapak cameronapak commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator
image

Empty/missing appKey rendered a blank page. Now: UI provider shows a styled "Missing app key" message; hooks provider throws.

  • Styled MissingAppKey panel + Storybook story
  • i18n (en/fr/es), example cleanup, unit + integration tests, changeset

YPE-1565

🤖 Generated with Claude Code

Greptile Summary

This PR surfaces a clear error when YouVersionProvider receives a missing or empty appKey, replacing the previous silent blank-page behaviour. The UI package renders a styled MissingAppKey panel while the hooks-only package throws a descriptive Error at the outermost wrapper so the hook-bearing inner component is never entered with an invalid key.

  • Hooks package – splits YouVersionProvider into a guard wrapper (throws on invalid key) and YouVersionProviderInner (owns all hooks), correctly sidestepping the Rules of Hooks concern from the previous review thread.
  • UI package – adds a resolveTheme helper and guards appKey before delegating to the base provider; MissingAppKey uses existing i18n keys and a role="alert" panel without the conflicting aria-live attribute flagged earlier.
  • Tests & stories – parameterised Vitest tests cover undefined, empty string, and whitespace-only cases for both packages; a Storybook play story validates the rendered panel end-to-end.

Confidence Score: 5/5

Safe to merge — the change is additive and has no effect on consumers with a valid appKey.

The wrapper + inner split correctly avoids hook-order issues, tests cover all three invalid-key variants in both packages, and the changeset is correctly scoped as minor for both packages.

packages/ui/src/components/YouVersionProvider.tsx — the console.error fires in the render function body rather than in an effect, which causes duplicate log lines under React Strict Mode.

Important Files Changed

Filename Overview
packages/hooks/src/context/YouVersionProvider.tsx Splits the component into a thin guard wrapper (YouVersionProvider) and the hook-bearing inner component (YouVersionProviderInner), correctly resolving the Rules of Hooks concern from the previous review thread.
packages/ui/src/components/YouVersionProvider.tsx Intercepts the missing-appKey case before the base provider, renders MissingAppKey; console.error fires on every render in the error path (including Strict Mode double-invocations), but the overall approach is sound.
packages/ui/src/components/missing-app-key.tsx New styled error panel using existing i18n keys; role="alert" is used cleanly without the conflicting aria-live override that was present in the earlier draft.
packages/hooks/src/context/YouVersionProvider.test.tsx Adds parameterised tests for undefined, empty string, and whitespace-only appKey values; correctly expects the descriptive thrown error.
packages/ui/src/components/YouVersionProvider.test.tsx Adds parameterised tests asserting the alert role is rendered, children are suppressed, and console.error is called with 'appKey'.
packages/ui/src/components/missing-app-key.stories.tsx Storybook stories for light, dark, and integration play scenarios; all reasonable.
examples/vite-react/src/ThemedApp.tsx Removes the empty-string default so an unset env var reaches the SDK guard; passes appKey ?? '' at the call site so the type contract is satisfied.
.changeset/missing-app-key-message.md Minor changeset entry for both packages; matches the scope of the change.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Consumer mounts YouVersionProvider] --> B{appKey valid?}

    subgraph UI Package
        B -- "no (empty/whitespace/undefined)" --> C[console.error to developer]
        C --> D[Render MissingAppKey panel + YvStyles]
        B -- yes --> E[Render BaseYouVersionProvider + YvStyles + children]
    end

    subgraph Hooks Package - BaseYouVersionProvider
        E --> F{appKey valid?}
        F -- no --> G[throw Error - non-empty appKey required]
        F -- yes --> H[YouVersionProviderInner - useResolvedTheme / useMemo / useEffect / YouVersionContext.Provider]
    end

    G --> I[Error boundary or uncaught crash]
    H --> J[Children rendered with full context]
    D --> K[Children suppressed - error panel only]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Consumer mounts YouVersionProvider] --> B{appKey valid?}

    subgraph UI Package
        B -- "no (empty/whitespace/undefined)" --> C[console.error to developer]
        C --> D[Render MissingAppKey panel + YvStyles]
        B -- yes --> E[Render BaseYouVersionProvider + YvStyles + children]
    end

    subgraph Hooks Package - BaseYouVersionProvider
        E --> F{appKey valid?}
        F -- no --> G[throw Error - non-empty appKey required]
        F -- yes --> H[YouVersionProviderInner - useResolvedTheme / useMemo / useEffect / YouVersionContext.Provider]
    end

    G --> I[Error boundary or uncaught crash]
    H --> J[Children rendered with full context]
    D --> K[Children suppressed - error panel only]
Loading

Reviews (8): Last reviewed commit: "refactor(hooks): move appKey guard to wr..." | Re-trigger Greptile

Render a styled "Missing app key" message instead of a blank page; hooks
provider throws for hooks-only consumers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2ddd583

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-hooks Minor
@youversion/platform-react-ui Minor
vite-react Patch
@youversion/platform-core Minor

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

Comment thread packages/ui/src/components/missing-app-key.tsx Outdated
role="alert" already implies aria-live="assertive"; the explicit
polite value downgraded announcement urgency for a config error.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/ui/src/i18n/locales/es.json
cameronapak and others added 2 commits June 24, 2026 12:46
- Remove redundant `missingAppKey*` translation keys
- Reuse generic `errorHeading` and `invalidAppKeyError` strings
- Add a `console.error` in `YouVersionProvider` for developers
The previous commit reused errorHeading/invalidAppKeyError and moved the
actionable developer guidance to console.error. Update the assertions to
match: expect "Error" instead of "Missing app key", and verify the
console.error guidance fires. Also refresh the guard comment to reflect
that the panel is intentionally generic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cameronapak

Copy link
Copy Markdown
Collaborator Author

@Dustin-Kelley can you review this? I'm no longer adding new strings to this and re-using old ones

P.S. I'll learn the new strings process and share that with you soon

Comment thread packages/hooks/src/context/YouVersionProvider.tsx Outdated
camrun91 and others added 5 commits June 26, 2026 08:40
Splits YouVersionProvider so the missing-appKey throw lives in a thin
wrapper and the hook-bearing implementation runs in an inner component.
The inner component is never entered with an invalid key, so a mounted
provider transitioning between valid and empty appKey can't produce an
inconsistent hook count. Addresses Greptile P1 review feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
theme={theme}
apiHost={apiHost}
appKey={appKey}
appKey={appKey ?? ''}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nitpick(non-blocking): I don't know too much about this codebase yet so I could be totally wrong. If it's possible I usually like to guard against the empty appKey before it gets passed to this provider so you can always expect it to be there within the provider. Not sure if that's possible in this case though. Either way it gets the job done!

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