Skip to content

Native report button#8

Merged
jameshaworthcs merged 17 commits intomainfrom
native-report-button
Mar 16, 2026
Merged

Native report button#8
jameshaworthcs merged 17 commits intomainfrom
native-report-button

Conversation

@jameshaworthcs
Copy link
Owner

@jameshaworthcs jameshaworthcs commented Mar 15, 2026

Summary by CodeRabbit

  • New Features

    • Report incorrect codes from checkout via a bottom-sheet flow with optional message and email for guests
    • Report action added to session items and options for per-code reporting
  • Improvements

    • Authentication readiness exposed to improve startup/state handling
    • Modal/overlay handling refined for more reliable refresh behavior
    • iOS scrolling behavior adjusted for native environments
  • Chores

    • App version bumped to 1.2.7

- Updated tab layout to conditionally render NativeTabs or standard Tabs based on app ownership.
- Replaced VersionAwareTabBar with direct tab configurations and integrated Ionicons for tab icons.
- Added tab re-press listeners in Checkout and Settings screens to trigger refresh actions.
- Removed unused components and optimized imports for better performance and clarity.
- Enhanced bottom tab handling with safe area insets for improved layout consistency across devices.
- Refactored TabLayoutContent to conditionally render TopBar and VersionUpdateBanner based on app ownership.
- Updated CheckoutScreen and HistoryScreen to utilize safe area insets for better layout on various devices.
- Adjusted loading indicators and error handling in CheckoutScreen for improved user experience.
- Enhanced TabScreenScrollView to manage padding dynamically based on device type.
- Added absolute positioning for TopBar and VersionUpdateBanner to ensure consistent visibility across screens.
- Added useThemeColor hook to dynamically set the background color based on the current theme.
- Updated ScrollView style to incorporate the dynamic background color, improving visual consistency across different themes.
- Updated TabLayoutContent to include disableTransparentOnScrollEdge for improved tab visibility.
- Refactored tab re-press listeners in CheckoutScreen and SettingsScreen to prevent unnecessary refresh actions when the tab is already focused.
- Enhanced SettingsScreen to fetch account info upon refresh parameter change, improving data retrieval efficiency.
- Modified ParallaxScrollView to utilize context for dynamic bottom tab overflow handling, ensuring better layout across devices.
- Replaced Ionicons with custom icon components (CheckOutIcon, AutocheckinIcon, HistoryIcon, SettingsIcon) for each tab in TabLayoutContent.
- Enhanced code readability and maintainability by utilizing dedicated icon components.
- Introduced useNativeInset to conditionally manage content insets and offsets based on device type, improving layout consistency on iOS.
- Updated scroll behavior to account for dynamic top inset, ensuring a smoother user experience when scrolling to the top.
- Added VersionCheckProvider to manage version state and context across the application.
- Updated RootLayout to include VersionCheckProvider, ensuring version checks are accessible throughout the app.
- Refactored TabLayoutContent and TabScreenScrollView to conditionally render VersionUpdateBanner based on version state.
- Enhanced VersionUpdateBanner to utilize context for version information and improve layout handling with safe area insets.
- Renamed `useNativeInset` to `isNativeIOS` for clarity in determining iOS-specific behavior.
- Updated scroll behavior to simplify scrolling logic, removing unnecessary top inset adjustments.
- Adjusted content inset behavior to use "scrollableAxes" for iOS, enhancing layout consistency across devices.
- Updated the loading indicator's style prop for better formatting and clarity.
- Enhanced code readability by restructuring the style array in HistoryScreen component.
- Introduced ReportSheet component to allow users to report issues with codes.
- Updated CheckoutScreen to manage report sheet visibility and state.
- Enhanced OptionsSheet and SessionCard components to trigger report functionality.
- Implemented form handling and submission logic within ReportSheet for user feedback on reports.
- Updated CheckoutScreen to allow reporting of specific codes by passing the selected code to the report function.
- Enhanced OptionsSheet and SessionCard to support reporting with the selected code.
- Modified ReportSheet to handle multiple codes and allow users to select which code to report.
- Improved state management for selected code in ReportSheet, ensuring accurate reporting based on user selection.
@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3f4e1d66-d957-48c0-a124-b6ce4f51adc2

📥 Commits

Reviewing files that changed from the base of the PR and between 4d880b7 and a3bcacc.

📒 Files selected for processing (2)
  • components/checkout/ReportSheet.tsx
  • hooks/useAuth.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/checkout/ReportSheet.tsx

📝 Walkthrough

Walkthrough

Adds a reporting flow: new ReportSheet component, CheckoutScreen wiring (state + open/close handlers), SessionCard and OptionsSheet onReportCode callbacks, expanded modal blocker types, and useAuth now exposes isAuthReady.

Changes

Cohort / File(s) Summary
Checkout screen wiring
app/(tabs)/checkout.tsx
Adds reportSession/reportCode state, openReport/closeReport handlers, passes onReportCode to child components, and conditionally renders ReportSheet.
Report UI component
components/checkout/ReportSheet.tsx
New bottom-sheet component for submitting code reports (props: visible, onClose, session, codes, initialCode); validation, submit POST to /api/support/submit, handles 200/429/errors, email for unauthenticated users.
Session & Options controls
components/checkout/SessionCard.tsx, components/checkout/OptionsSheet.tsx
Introduce optional onReportCode?: (code: Code) => void props; replace in-component report logic with callbacks; select primary code by highest rejectScore; add "Report" UI actions invoking the callback.
Scroll behavior
components/TabScreenScrollView.tsx
Add isNativeIOS detection and set contentInsetAdjustmentBehavior to 'scrollableAxes' on native iOS, otherwise 'never'.
Modal blocker types
types/liveClasses.ts
Expand RefreshBlocker union to include undo-modal, options-modal, share-modal, verification-modal, and report-modal.
Auth hook
hooks/useAuth.ts
Expose isAuthReady; load/parse accountInfo from storage with parse-failure handling and robust initialization error handling.
App metadata
app.json
Bump Expo app version 1.2.6 → 1.2.7.

Sequence Diagram

sequenceDiagram
    participant User
    participant CheckoutScreen
    participant ReportSheet
    participant SupportAPI

    User->>CheckoutScreen: Tap "Report" on a code
    CheckoutScreen->>CheckoutScreen: openReport(session, code) sets reportSession/reportCode
    CheckoutScreen->>ReportSheet: Render visible with session,codes,initialCode
    User->>ReportSheet: Select code, enter message/email, Submit
    ReportSheet->>ReportSheet: Validate inputs (email if unauthenticated)
    ReportSheet->>SupportAPI: POST /api/support/submit (payload: issueType, codeId, chc, message, email?)
    SupportAPI-->>ReportSheet: 200 / 429 / error
    alt 200 Success
        ReportSheet->>CheckoutScreen: onClose()
        CheckoutScreen->>CheckoutScreen: clear reportSession/reportCode
    else 429 Rate limit
        ReportSheet->>User: show rate-limit error
    else Other error
        ReportSheet->>User: show generic error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I hopped into checkout, a sheet in tow,

Codes whispered secrets that needed to go.
I nudged a callback, a little report,
Sent it off to servers — a tidy sort.
Hop-hop! a toast, the rabbit’s work is done.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'Native report button' accurately reflects the main feature added: integrating a native reporting flow with report buttons in SessionCard, OptionsSheet, and the new ReportSheet component.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch native-report-button
📝 Coding Plan
  • Generate coding plan for human review comments

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

@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: 3

Caution

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

⚠️ Outside diff range comments (3)
components/checkout/OptionsSheet.tsx (1)

79-88: ⚠️ Potential issue | 🟡 Minor

Hide the report action when onReportCode is not provided.

At Line 86, reporting is optional-called, but the row is still visible and closes the sheet at Line 87. This creates a no-op action path. Gate rendering with onReportCode so users only see actionable options.

Proposed fix
-          {primaryCode && (
+          {primaryCode && onReportCode && (
             <TouchableOpacity
               style={[
                 styles.option,
                 { backgroundColor: isDark ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.05)' },
               ]}
               onPress={() => {
-                onReportCode?.(primaryCode);
+                onReportCode(primaryCode);
                 onClose();
               }}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/checkout/OptionsSheet.tsx` around lines 79 - 88, The touchable
"report code" row is rendered whenever primaryCode exists but should only be
shown when the handler exists; update the render condition to require both
primaryCode and onReportCode so the TouchableOpacity (the element using
styles.option and backgroundColor) is not displayed as a no-op, and inside the
onPress keep calling onReportCode(primaryCode) and onClose(); ensure you change
the conditional that currently checks only primaryCode to check both primaryCode
&& onReportCode.
components/TabScreenScrollView.tsx (1)

1-99: ⚠️ Potential issue | 🟡 Minor

Prettier is failing for this file in CI.

GitHub Actions reports a formatting failure for components/TabScreenScrollView.tsx. Please run prettier --write to unblock the pipeline.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/TabScreenScrollView.tsx` around lines 1 - 99, The file containing
the exported TabScreenScrollView component (see symbol TabScreenScrollView and
related constants like TOP_BAR_HEIGHT, CONNECTION_BANNER_HEIGHT) is
misformatted; run your project's Prettier formatter (e.g., prettier --write) on
that file to apply the repo formatting rules, review the diffs, and commit the
updated file so CI no longer fails.
app/(tabs)/checkout.tsx (1)

373-377: ⚠️ Potential issue | 🟠 Major

Options→Report handoff unblocks refresh due to shared 'modal' key.

The Report sheet (openReport line 400) and Options sheet (openOptions line 347) both use the same 'modal' blocker key, but leverage a Set-based block/unblock mechanism. When the user selects "Report incorrect code" (OptionsSheet lines 85–87), the sequence is:

  1. onReportCode(code) fires → openReport() calls blockRefresh('modal') (line 400)
  2. onClose() immediately fires → closeOptions() calls unblockRefresh('modal') (line 376)

Since blockersRef is a Set<RefreshBlocker> with no reference counting, the second call to unblockRefresh('modal') removes the key entirely, even though the Report sheet is still opening. This allows pending refresh data to apply while ReportSheet is visible.

Use distinct keys per sheet ('options-modal', 'report-modal') or ensure Options closes before Report opens.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(tabs)/checkout.tsx around lines 373 - 377, closeOptions is unblocking
the shared 'modal' blocker key while openReport also blocks it, causing
premature unblock via blockersRef Set; update the blocking keys to be unique per
sheet (e.g., change blockRefresh('modal')/unblockRefresh('modal') usages in
openOptions/openReport/closeOptions to 'options-modal' and 'report-modal') so
each sheet manages its own blocker, or alternatively change the sequence so
closeOptions completes before openReport calls blockRefresh; locate references
to openOptions, openReport, closeOptions, blockRefresh, unblockRefresh and
replace the shared 'modal' key with distinct keys to fix the race.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/checkout/ReportSheet.tsx`:
- Around line 23-49: The guest-email validation in handleSubmit uses isLoggedIn
from useAuth which can be false during async auth hydration; update ReportSheet
to rely on an authenticated/hydration-ready signal instead of raw
isLoggedIn—either read and use a hydration-ready flag from useAuth (e.g.,
isHydrated or isAuthReady) or accept an auth-hydrated prop from the parent, and
only enforce EMAIL_REGEX validation when hydration is complete (i.e., when
isAuthReady is true); adjust the conditional in handleSubmit (and any early
returns) to skip guest-only email checks until the auth-hydration flag indicates
a finalized auth state.
- Around line 54-58: The current body construction uses
selectedCode.codeIDs?.[0] || '' which can send an empty codeId; change the
submit flow to validate presence of selectedCode.codeIDs?.[0] (or another
canonical id) before building the body or calling the API: if no id is present,
set a local/form error state (e.g., via the component's error setter) and return
early instead of calling /api/support/submit; only construct body = { issueType:
'report-code', codeId: selectedCode.codeIDs[0], chc: selectedCode.checkinCode }
and proceed when the id exists.

In `@hooks/useAuth.ts`:
- Around line 35-39: When JSON.parse(storedAccountInfo) in the hook
initialization (where storedAccountInfo is read) throws, do not leave the
malformed data in storage; catch the error, remove/clear the persisted
ACCOUNT_INFO_KEY (and call setAccountInfo(null) or a safe default) so the hook
won't repeatedly fail on init; update the catch block around
setAccountInfo(JSON.parse(storedAccountInfo)) to remove the key from storage
(and reset local state) on parse failure to recover cleanly.

---

Outside diff comments:
In `@app/`(tabs)/checkout.tsx:
- Around line 373-377: closeOptions is unblocking the shared 'modal' blocker key
while openReport also blocks it, causing premature unblock via blockersRef Set;
update the blocking keys to be unique per sheet (e.g., change
blockRefresh('modal')/unblockRefresh('modal') usages in
openOptions/openReport/closeOptions to 'options-modal' and 'report-modal') so
each sheet manages its own blocker, or alternatively change the sequence so
closeOptions completes before openReport calls blockRefresh; locate references
to openOptions, openReport, closeOptions, blockRefresh, unblockRefresh and
replace the shared 'modal' key with distinct keys to fix the race.

In `@components/checkout/OptionsSheet.tsx`:
- Around line 79-88: The touchable "report code" row is rendered whenever
primaryCode exists but should only be shown when the handler exists; update the
render condition to require both primaryCode and onReportCode so the
TouchableOpacity (the element using styles.option and backgroundColor) is not
displayed as a no-op, and inside the onPress keep calling
onReportCode(primaryCode) and onClose(); ensure you change the conditional that
currently checks only primaryCode to check both primaryCode && onReportCode.

In `@components/TabScreenScrollView.tsx`:
- Around line 1-99: The file containing the exported TabScreenScrollView
component (see symbol TabScreenScrollView and related constants like
TOP_BAR_HEIGHT, CONNECTION_BANNER_HEIGHT) is misformatted; run your project's
Prettier formatter (e.g., prettier --write) on that file to apply the repo
formatting rules, review the diffs, and commit the updated file so CI no longer
fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: daf85173-9e83-43cf-b9aa-0fdfa7d824c3

📥 Commits

Reviewing files that changed from the base of the PR and between 9fdf88f and 17cb0b1.

📒 Files selected for processing (7)
  • app/(tabs)/checkout.tsx
  • components/TabScreenScrollView.tsx
  • components/VersionUpdateBanner.tsx
  • components/checkout/OptionsSheet.tsx
  • components/checkout/ReportSheet.tsx
  • components/checkout/SessionCard.tsx
  • hooks/useAuth.ts

- Updated CheckoutScreen to use specific modal identifiers for blocking and unblocking refresh actions, improving clarity and maintainability.
- Enhanced OptionsSheet and ReportSheet to conditionally render reporting options based on the presence of a reporting function.
- Introduced isAuthReady state in useAuth hook to ensure authentication readiness before checking login status, enhancing user experience during report submissions.
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

🧹 Nitpick comments (2)
app/(tabs)/checkout.tsx (1)

392-406: Optional: extract shared code-ranking helper.

rejectScore sorting is repeated in multiple places; centralizing it would reduce drift and keep report targeting consistent.

Refactor sketch
+const sortCodesByRejectScore = (codes: Code[]) =>
+  [...codes].sort((a, b) => b.rejectScore - a.rejectScore);

   const openReport = useCallback(
     (session: Session, code?: Code) => {
-      const sortedCodes = [...(session.codes || [])].sort((a, b) => b.rejectScore - a.rejectScore);
+      const sortedCodes = sortCodesByRejectScore(session.codes || []);
       const targetCode = code || sortedCodes[0];
       if (!targetCode) return;
       ...
     },
     [blockRefresh]
   );
...
-          codes={[...(reportSession.codes || [])].sort((a, b) => b.rejectScore - a.rejectScore)}
+          codes={sortCodesByRejectScore(reportSession.codes || [])}

Also applies to: 705-706

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(tabs)/checkout.tsx around lines 392 - 406, Extract the repeated sorting
logic into a small helper (e.g., getTopCodeByRejectScore or
sortCodesByRejectScore) and replace the inline [...(session.codes ||
[])].sort((a, b) => b.rejectScore - a.rejectScore) in openReport and the other
occurrence (around the existing calls to target code selection) with a call to
that helper; ensure the helper accepts an array of Code | undefined, returns a
sorted array or the top Code, and update openReport to use the helper to
determine targetCode before calling setReportSession/setReportCode and
blockRefresh('report-modal').
components/checkout/ReportSheet.tsx (1)

206-219: Consider disabling submit until auth hydration completes.

A quick tap before auth readiness resolves can trigger a request path with incomplete guest/auth assumptions. Gating submit on isAuthReady makes this flow deterministic.

Proposed tweak
          <TouchableOpacity
             style={[styles.button, { backgroundColor: theme.ctaBackground }]}
             onPress={handleSubmit}
-            disabled={isSubmitting}
+            disabled={isSubmitting || !isAuthReady}
             activeOpacity={0.7}>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/checkout/ReportSheet.tsx` around lines 206 - 219, The submit path
can run before auth hydration completes; update the ReportSheet component so the
Submit TouchableOpacity (the one using handleSubmit) is disabled until
isAuthReady is true by changing its disabled prop to combine isSubmitting and
!isAuthReady (e.g., disabled={isSubmitting || !isAuthReady}), and also guard the
handleSubmit entry point itself by returning early if !isAuthReady to prevent
accidental invocation; reference the TouchableOpacity that calls handleSubmit
and the isAuthReady and isSubmitting flags when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hooks/useAuth.ts`:
- Around line 27-45: The storage hydration block in useAuth (where
storage.getItem is called and setToken/setIsLoggedIn/setAccountInfo are applied)
can throw and prevent setIsAuthReady(true) from running; wrap the async
initialization logic (the Promise.all call, the storedAccountInfo JSON.parse and
potential storage.deleteItem) in a try/finally so that setIsAuthReady(true) is
always invoked in the finally block; keep existing setToken, setIsLoggedIn,
setAccountInfo, and storage.deleteItem logic inside the try and only move
setIsAuthReady(true) to the finally.

---

Nitpick comments:
In `@app/`(tabs)/checkout.tsx:
- Around line 392-406: Extract the repeated sorting logic into a small helper
(e.g., getTopCodeByRejectScore or sortCodesByRejectScore) and replace the inline
[...(session.codes || [])].sort((a, b) => b.rejectScore - a.rejectScore) in
openReport and the other occurrence (around the existing calls to target code
selection) with a call to that helper; ensure the helper accepts an array of
Code | undefined, returns a sorted array or the top Code, and update openReport
to use the helper to determine targetCode before calling
setReportSession/setReportCode and blockRefresh('report-modal').

In `@components/checkout/ReportSheet.tsx`:
- Around line 206-219: The submit path can run before auth hydration completes;
update the ReportSheet component so the Submit TouchableOpacity (the one using
handleSubmit) is disabled until isAuthReady is true by changing its disabled
prop to combine isSubmitting and !isAuthReady (e.g., disabled={isSubmitting ||
!isAuthReady}), and also guard the handleSubmit entry point itself by returning
early if !isAuthReady to prevent accidental invocation; reference the
TouchableOpacity that calls handleSubmit and the isAuthReady and isSubmitting
flags when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a5728706-3fa3-4688-a726-23c227298c4c

📥 Commits

Reviewing files that changed from the base of the PR and between 17cb0b1 and 91dfa77.

📒 Files selected for processing (7)
  • app.json
  • app/(tabs)/checkout.tsx
  • components/TabScreenScrollView.tsx
  • components/checkout/OptionsSheet.tsx
  • components/checkout/ReportSheet.tsx
  • hooks/useAuth.ts
  • types/liveClasses.ts
✅ Files skipped from review due to trivial changes (1)
  • app.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/TabScreenScrollView.tsx

- Updated ReportSheet to disable the submit button until authentication is ready, improving user experience during report submissions.
- Refactored useAuth hook to ensure isAuthReady is set after attempting to retrieve authentication data, enhancing the flow of authentication state management.
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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/checkout/ReportSheet.tsx`:
- Around line 33-41: The effect currently resets selectedCode only when the
sheet is closed, so if initialCode changes while the sheet is reopened
selectedCode can remain stale; update the useEffect tied to visible and
initialCode (or add a separate effect) to setSelectedCode(initialCode) whenever
the sheet becomes visible (i.e., when visible is true) or when initialCode
changes while visible, while keeping the existing cleanup resets (setMessage,
setEmail, setError, setIsSubmitting) when closing; refer to the useEffect,
selectedCode and setSelectedCode identifiers to locate and update the logic.
- Around line 65-70: The guest-report path can still send auth headers because
checkoutApi auto-attaches x-checkout-key; update checkoutApi (utils/api.ts) to
accept an option like skipAuth?: boolean that, when true, prevents attaching the
auth header, and then call checkoutApi from ReportSheet.tsx with skipAuth: true
in the guest branch (the code around isAuthReady / isLoggedIn / email and the
call to checkoutApi) so guest submissions only send the email/body and never the
token.

In `@hooks/useAuth.ts`:
- Around line 27-48: Wrap the outer Promise.all storage access in a
try/catch/finally so storage errors are caught and handled instead of bubbling
as unhandled rejections: add a catch block alongside the existing finally that
catches errors from Promise.all([storage.getItem(API_TOKEN_KEY),
storage.getItem(IS_LOGGED_IN_KEY), storage.getItem(ACCOUNT_INFO_KEY)]) and log
or handle the error (e.g., clear token/state via setToken(null),
setIsLoggedIn(false), setAccountInfo(null) as appropriate), then ensure
setIsAuthReady(true) remains in the finally; reference the existing symbols
storage.getItem, API_TOKEN_KEY, IS_LOGGED_IN_KEY, ACCOUNT_INFO_KEY, setToken,
setIsLoggedIn, setAccountInfo, storage.deleteItem, and setIsAuthReady to locate
and update the code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5a11fbb-44a5-4cf8-8d7c-fb68f3f1b853

📥 Commits

Reviewing files that changed from the base of the PR and between 91dfa77 and 4d880b7.

📒 Files selected for processing (2)
  • components/checkout/ReportSheet.tsx
  • hooks/useAuth.ts

Comment on lines +33 to +41
useEffect(() => {
if (!visible) {
setMessage('');
setEmail('');
setError(null);
setIsSubmitting(false);
setSelectedCode(initialCode);
}
}, [visible, initialCode]);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

selectedCode can stay stale across reopen and report the wrong code.

Line 34 only syncs state on close. If initialCode changes when opening, selectedCode may still point to the prior code and submit the wrong codeId.

Suggested fix
-  // Reset state when sheet closes
-  useEffect(() => {
-    if (!visible) {
-      setMessage('');
-      setEmail('');
-      setError(null);
-      setIsSubmitting(false);
-      setSelectedCode(initialCode);
-    }
-  }, [visible, initialCode]);
+  // Reset transient fields when sheet closes
+  useEffect(() => {
+    if (!visible) {
+      setMessage('');
+      setEmail('');
+      setError(null);
+      setIsSubmitting(false);
+    }
+  }, [visible]);
+
+  // Always sync selected code to the opening context
+  useEffect(() => {
+    if (visible) {
+      setSelectedCode(initialCode);
+    }
+  }, [visible, initialCode]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/checkout/ReportSheet.tsx` around lines 33 - 41, The effect
currently resets selectedCode only when the sheet is closed, so if initialCode
changes while the sheet is reopened selectedCode can remain stale; update the
useEffect tied to visible and initialCode (or add a separate effect) to
setSelectedCode(initialCode) whenever the sheet becomes visible (i.e., when
visible is true) or when initialCode changes while visible, while keeping the
existing cleanup resets (setMessage, setEmail, setError, setIsSubmitting) when
closing; refer to the useEffect, selectedCode and setSelectedCode identifiers to
locate and update the logic.

Comment on lines +65 to +70
if (isAuthReady && !isLoggedIn && email) body.email = email;

const result = await checkoutApi('/api/support/submit', {
method: 'POST',
body,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guest report requests can be sent with an auth header unintentionally.

Line 67 uses checkoutApi, which (per utils/api.ts:21-47) auto-attaches x-checkout-key when a token exists. In the guest path (Line 65), that can send both email and auth token together if storage/auth flags drift, causing ambiguous server behavior.

Suggested direction
-      const result = await checkoutApi('/api/support/submit', {
+      const result = await checkoutApi('/api/support/submit', {
         method: 'POST',
         body,
+        // add support in checkoutApi to suppress token attachment for guest submissions
+        // e.g. skipAuth: isAuthReady && !isLoggedIn,
       });

And in utils/api.ts, add an option like skipAuth?: boolean so this flow can explicitly avoid auth headers for guest reports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/checkout/ReportSheet.tsx` around lines 65 - 70, The guest-report
path can still send auth headers because checkoutApi auto-attaches
x-checkout-key; update checkoutApi (utils/api.ts) to accept an option like
skipAuth?: boolean that, when true, prevents attaching the auth header, and then
call checkoutApi from ReportSheet.tsx with skipAuth: true in the guest branch
(the code around isAuthReady / isLoggedIn / email and the call to checkoutApi)
so guest submissions only send the email/body and never the token.

- Updated ReportSheet to reset the selected code when the sheet opens, improving user experience during code reporting.
- Enhanced useAuth hook to log errors during authentication hydration, ensuring better debugging and state management.
@jameshaworthcs jameshaworthcs merged commit 3f3b220 into main Mar 16, 2026
4 checks passed
@jameshaworthcs jameshaworthcs deleted the native-report-button branch March 16, 2026 18:40
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.

1 participant