Skip to content

fix(catalog): send browser headers for image requests on all platforms#1896

Draft
Copilot wants to merge 2 commits intomainfrom
copilot/fix-181429480-611845080-b39c409c-f1c8-4c37-8a20-0b5434a547d1
Draft

fix(catalog): send browser headers for image requests on all platforms#1896
Copilot wants to merge 2 commits intomainfrom
copilot/fix-181429480-611845080-b39c409c-f1c8-4c37-8a20-0b5434a547d1

Conversation

Copy link
Contributor

Copilot AI commented Mar 9, 2026

Catalog search results showed placeholder images on iOS because CatalogItemImage was only sending User-Agent and Accept headers on Android. Several image CDNs (Mackage, Black Diamond, etc.) gate image serving on a recognized browser UA — requests without these headers silently fall back to placeholders.

Changes

  • CatalogItemImage.tsx — removed Platform.OS === 'android' guard; headers are now sent unconditionally on all platforms
  • Dropped the now-unused Platform import
- ...(Platform.OS === 'android'
-   ? { headers: { 'User-Agent': '...Chrome...', Accept: 'image/webp,...' } }
-   : {}),
+ headers: {
+   'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ...',
+   Accept: 'image/webp,image/apng,image/*,*/*;q=0.8',
+ },

This explains the per-brand inconsistency: CDNs with stricter UA checks (Mackage, Black Diamond) failed on iOS while more permissive ones (Patagonia) worked fine.

Original prompt

This section details on the original issue you should resolve

<issue_title>iOS – Catalog Search Results Do Not Display Item Images</issue_title>
<issue_description>Description:
On iOS devices, when searching for items in the Catalog screen, the search results display only a placeholder image (e.g., photo icon on grey background) instead of the actual item images.

On Android, the search results display the item images correctly.

Steps to Reproduce:

  1. Open the app on iOS
  2. Navigate to the Catalog screen
  3. Tap the Search bar
  4. Enter a search term for an item
  5. Observe the search results

Screenshots / Screen Recordings:

ScreenRecording_02-25-2026.16-33-21_1.MP4

Expected Result:
Search results should display the actual item images.

Actual Result:

  • Item images are replaced with a placeholder (photo icon on grey background)
  • No images are visible

Environment:

  • Device: iPhone (13 Pro)

Additional Notes:

  • This is iOS-specific; Android displays images correctly
    </issue_description>

Comments on the Issue (you are @copilot in this section)

@JewelTee @mikib0

v2.0.15-alpha.2

Item images load properly in some catalog search categories, but in other categories the images do not display and show placeholders instead. The issue seems to be limited to specific categories.

Image Image Image Image @mikib0 @JewelTee "Jacket", "Shorts" showing images on Android? @JewelTee > [@JewelTee](https://github.com/JewelTee) "Jacket", "Shorts" showing images on Android? @mikib0 It was showing last time, but I checked again just now after clearing data, and images are not showing even on other categories.

Image
Image
Image</comment_new>
<comment_new>@andrew-bierman
@copilot Assigned for POC development. Please investigate and create a proof of concept implementation.</comment_new>


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed inconsistent image loading in catalog by ensuring required headers are consistently included across all platforms.

Co-authored-by: andrew-bierman <94939237+andrew-bierman@users.noreply.github.com>
Copilot AI changed the title [WIP] iOS – Catalog Search Results Do Not Display Item Images fix(catalog): send browser headers for image requests on all platforms Mar 9, 2026
@andrew-bierman
Copy link
Collaborator

@coderabbitai Please review this PR for code quality, best practices, and potential issues.

@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

The PR description images show iOS catalog search results demonstrating the bug and fix:

  • Before fix (iOS): "Shorts" (Black Diamond) and "Jacket" (Mackage) show grey placeholder images with a photo icon; "Gloves" (Patagonia) and "T shirt" (Patagonia) show actual images.
  • After fix (iOS): "Gloves" (Patagonia) now shows the actual product image — and the fix aims to resolve Mackage/Black Diamond CDN failures too.
  • Android screenshots: Also show placeholders for Jacket (Mackage) and Shorts (Black Diamond), confirming the CDN UA-gating issue affects Android too when the PR was developed (interesting — let me look at the actual code).

Let me fetch the file to do a thorough review.


🏁 Script executed:

#!/bin/bash
# Find CatalogItemImage component
fd -t f "CatalogItemImage" --exec cat {}

Length of output: 1619


🏁 Script executed:

#!/bin/bash
# Check getImageExtension implementation
fd -t f "imageUtils" --exec cat {}

# Check ImageCacheManager
fd -t f "ImageCacheManager" --exec cat {}

Length of output: 8508


@andrew-bierman: The PR description images confirm the bug clearly — iOS search for "Jacket" (Mackage) and "Shorts" (Black Diamond) showed placeholder images while Patagonia items worked, because Patagonia's CDN is more permissive. The fix — removing the Platform.OS === 'android' guard — is correct and minimal. Here's the full review:


✅ Core Fix — Correct

Removing the Platform.OS === 'android' guard so headers are sent unconditionally is the right approach. The Platform import cleanup is also good hygiene.


⚠️ Issues Found

1. fetchImageExtension still makes headerless requests — may fail for the same UA-gated CDNs

In imageUtils.ts, both the HEAD request and the fallback range-GET in fetchImageExtension are sent without the User-Agent header:

// HEAD request — no UA header
const response = await fetch(url, {
  method: 'HEAD',
  signal: controller.signal,
});

// Range GET — no UA header
const rangeResponse = await fetch(url, {
  headers: { Range: 'bytes=0-1023' },
  signal: rangeController.signal,
});

For CDNs that block requests without a recognized UA (Mackage, Black Diamond), these requests will fail or return an error, and fetchImageExtension will fall back to defaultExt = 'jpg'. This means cacheCatalogItemImage may cache files with the wrong extension. The fix from this PR should be applied here too:

const BROWSER_HEADERS = {
  'User-Agent':
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
  Accept: 'image/webp,image/apng,image/*,*/*;q=0.8',
};

// HEAD request
const response = await fetch(url, {
  method: 'HEAD',
  headers: BROWSER_HEADERS,
  signal: controller.signal,
});

// Range GET
const rangeResponse = await fetch(url, {
  headers: { ...BROWSER_HEADERS, Range: 'bytes=0-1023' },
  signal: rangeController.signal,
});

2. Duplicate hardcoded UA string — extract to a shared constant

The identical User-Agent + Accept string currently appears in two places:

  • CatalogItemImage.tsx
  • ImageCacheManager.cacheRemoteImage

(And should be added to fetchImageExtension per issue #1 above.)

Centralising it prevents drift if the UA string ever needs updating:

// e.g. packages/expo-app/lib/constants/httpHeaders.ts
export const BROWSER_IMAGE_HEADERS = {
  'User-Agent':
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',
  Accept: 'image/webp,image/apng,image/*,*/*;q=0.8',
} as const;

3. Missing onError fallback — failed image loads render silently

CatalogItemImage only renders the placeholder icon when imageUrl is null/undefined. If the CDN rejects the request at runtime (network error, CDN change, etc.), the <Image> component renders nothing — an empty grey area — without the user-facing placeholder icon. Adding onError would make error states consistent:

import { useState } from 'react';

export function CatalogItemImage({ imageUrl, ...imageProps }: PackItemImageProps) {
  const { colors } = useColorScheme();
  const [hasError, setHasError] = useState(false);

  if (!imageUrl || hasError)
    return (
      <View
        className={`items-center justify-center bg-neutral-300 dark:bg-neutral-600 ${imageProps.className}`}
      >
        <Icon name="image" size={24} color={colors.grey} />
      </View>
    );

  return (
    <Image
      source={{
        uri: imageUrl,
        headers: BROWSER_IMAGE_HEADERS,
      }}
      onError={() => setHasError(true)}
      {...imageProps}
      className={`bg-muted ${imageProps.className}`}
    />
  );
}

Summary

Severity Notes
fetchImageExtension missing UA headers Medium May cause wrong file extension in cache for UA-gated CDNs
Duplicate UA string Low Maintenance risk
No onError fallback Low Silent failure UX

The core fix is solid. Issues #2 and #3 would be good to address in this PR; issue #1 could be a follow-up since it affects a separate code path (cacheCatalogItemImage), not the display path fixed here.

@andrew-bierman
Copy link
Collaborator

@copilot CodeRabbit has reviewed this PR and provided feedback. Please address the review comments and push updates. Once you've made the changes, the monitor will ask CodeRabbit to re-review.

@andrew-bierman
Copy link
Collaborator

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 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: bd10efe4-097d-4466-88b8-16adf2fe931e

📥 Commits

Reviewing files that changed from the base of the PR and between 3f94763 and 08cb9b4.

📒 Files selected for processing (1)
  • apps/expo/features/catalog/components/CatalogItemImage.tsx

📝 Walkthrough

Walkthrough

The change removes platform-specific conditional logic from the CatalogItemImage component's Image source configuration. User-Agent and Accept headers are now always included, replacing the previous Android-only branch to enable image loading on iOS.

Changes

Cohort / File(s) Summary
Image Loading Headers
apps/expo/features/catalog/components/CatalogItemImage.tsx
Removed Platform from imports and eliminated platform-specific conditional headers. Image source now unconditionally includes User-Agent and Accept headers in all cases.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 The images on iOS were hiding away,
Behind conditionals that blocked the day,
Now headers flow freely, no platform in sight,
Every device loads images bright! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and directly summarizes the main change: removing platform-specific conditions to send browser headers for image requests on all platforms instead of only Android.
Linked Issues check ✅ Passed The PR directly addresses the iOS image loading issue [#1848] by removing the Android-only platform guard, enabling header transmission on all platforms to satisfy CDN requirements.
Out of Scope Changes check ✅ Passed All changes are scoped to CatalogItemImage.tsx and directly address the linked issue; no unrelated modifications are present.

✏️ 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 copilot/fix-181429480-611845080-b39c409c-f1c8-4c37-8a20-0b5434a547d1

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

@andrew-bierman
Copy link
Collaborator

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

iOS – Catalog Search Results Do Not Display Item Images

2 participants