Skip to content

Instantly share code, notes, and snippets.

@azizpunjani
Last active February 27, 2026 20:18
Show Gist options
  • Select an option

  • Save azizpunjani/326cc08b8101e11d552e0c7e308a2129 to your computer and use it in GitHub Desktop.

Select an option

Save azizpunjani/326cc08b8101e11d552e0c7e308a2129 to your computer and use it in GitHub Desktop.
Investigation: Blurry WebLink Thumbnails in Pitch Viewer

Blurry WebLink Thumbnail Investigation

TL;DR

The blurry thumbnails are caused by a server-side issue. WebLink items don't have a "large" thumbnail generated, and the server falls back to "small" instead of using an available larger size.


1. The Feature-Flagged Code in propsForCard.js

View the full diff

The Code Change

// web/client/features/layouts/Cards/helpers/propsForCard.js (lines 86-97)

const thumbnailImg = getUserAvatar({ avatar: item.avatar, thumbnail: item.thumbnail });

// Original behavior - uses item.thumbnail.url directly
let thumbnailUrl = thumbnailImg.srcUrl;

// NEW CODE - gated by feature flag
const shouldOptimizeThumbnails = options?.optimizeThumbnails ?? isFeatureEnabled('uxp_optimize_thumbnail_rendering');
if (item.thumbnails && Object.keys(item.thumbnails).length > 0 && shouldOptimizeThumbnails) {
  const selectedThumbnail = getThumbnailForWidth(item.thumbnails, ThumbnailSize.Medium);
  if (selectedThumbnail?.url) {
    thumbnailUrl = selectedThumbnail.url;
  }
}

Why This Code Is Not Involved

  1. Feature flag is OFF → The new code block never executes
  2. Nullish coalescing (??)isFeatureEnabled('uxp_optimize_thumbnail_rendering') returns false, so shouldOptimizeThumbnails = false
  3. The if block is skipped → Code falls through to use thumbnailImg.srcUrl (the original behavior)
  4. Original behavior → Uses item.thumbnail.url from the server response directly

2. The Client Request

The client requests thumbnail: "large" for high-quality card thumbnails in the pitch viewer.

This was added in PR #64662 in web/client/digitalroombuilder/components/blocks/DigitalRoomContent/DigitalRoomContent.tsx:64:

queryOptions.thumbnail = 'large';

The GraphQL request:

{
  "query": "query GetExternalPitchItemsQuery...",
  "variables": {
    "itemIds": ["68e3b483e9b5d80bfdc27a0c"],
    "domainId": "telusinternational.com",
    "pitchId": "22990636658add3cfecc813f55019887",
    "options": {
      "displayContext": "ViewPitch",
      "itemSettings": {
        "68e3b483e9b5d80bfdc27a0c": {
          "id": "68e3b483e9b5d80bfdc27a0c",
          "openInCloud": false,
          "autoUpdated": false,
          "forceAutoUpdate": false,
          "forceSnapshot": false,
          "preventAutoUpdateChanges": false
        }
      },
      "thumbnail": "large"   // ← CLIENT REQUESTS "large"
    }
  }
}

3. The Server Response

The server ignores the request and returns "small":

{
  "data": {
    "items": [{
      "id": "68e3b483e9b5d80bfdc27a0c",
      "contentType": "WebLink",
      "title": "Black Friday readiness guide for telecommunications providers",
      
      "thumbnail": {
        "url": "https://content-su2.highspot.com/.../small/.../1.gif",
        "width": 245,
        "height": 183,
        "size": "small"   // ← SERVER RETURNS "small" !
      },
      
      "thumbnails": {
        "tiny":  { "width": 32,   "size": "tiny"  },
        "64x64": { "width": 64,   "size": "64x64" },
        "small": { "width": 245,  "size": "small" },
        "490x":  { "width": 490,  "size": "490x"  },
        "980x":  { "width": 980,  "size": "980x"  },
        "1000":  { "width": 1000, "size": "1000"  }
        // ← NO "large" KEY EXISTS!
      }
    }]
  }
}

4. Comparison: Video vs WebLink

Video Item (Works Correctly)

"thumbnails": {
  "tiny":  { "width": 32   },
  "64x64": { "width": 64   },
  "small": { "width": 245  },
  "490x":  { "width": 490  },
  "980x":  { "width": 980  },
  "1000":  { "width": 1000 },
  "large": { "width": 1000 }   // ← HAS "large" key
}

"thumbnail": {
  "size": "large",              // ← Server returns "large"
  "width": 1000
}

WebLink Item (Broken)

"thumbnails": {
  "tiny":  { "width": 32   },
  "64x64": { "width": 64   },
  "small": { "width": 245  },
  "490x":  { "width": 490  },
  "980x":  { "width": 980  },
  "1000":  { "width": 1000 }
  // ← NO "large" key!
}

"thumbnail": {
  "size": "small",              // ← Server falls back to "small"
  "width": 245
}

5. Root Cause

WebLink items don't generate a "large" named thumbnail. This is defined in web/api/helpers/thumbnail_helpers.rb:

IMAGE_DIMENSIONS_WEBLINK = [
  ["tiny",  { ... }],
  ["64x64", { ... }],
  ["small", { ... }],
  ["490x",  { ... }],
  ["980x",  { ... }],
  ["1000",  { ... }]
  # ← NO "large"
].freeze

IMAGE_DIMENSIONS_MEDIA = IMAGE_DIMENSIONS_WEBLINK + [
  ["large", { ... }]   # ← "large" only in MEDIA
]

IMAGE_DIMENSIONS_BY_CONTENT_KIND = {
  Content::Kind::WEBLINK => IMAGE_DIMENSIONS_WEBLINK,  # ← No "large"
  Content::Kind::VIDEO => IMAGE_DIMENSIONS_MEDIA,      # ← Has "large"
  ...
}

When the client requests thumbnail: "large" but it doesn't exist, the server falls back to "small" instead of using an available larger size like "1000" or "980x".


6. Potential Fixes

Option A: Fix Server Fallback Logic

When the requested thumbnail size doesn't exist, fall back to the largest available size instead of "small".

Option B: Add "large" to WebLink Dimensions

Add "large" to IMAGE_DIMENSIONS_WEBLINK so WebLinks generate a "large" thumbnail, matching the behavior of Video/PDF items.


7. Evidence Summary

Aspect Evidence
Client request "thumbnail": "large" in GraphQL variables (PR #64662)
Server response "thumbnail.size": "small" for WebLink
Available sizes 490x, 980x, 1000 exist but aren't used
Missing size No "large" key in WebLink thumbnails
Video comparison Video items DO have "large" and work correctly
Feature flag uxp_optimize_thumbnail_rendering is OFF
Feature-flagged code impact None - code is not executed when FF is off

8. Conclusion

The blurry thumbnails are caused by server-side thumbnail selection logic that:

  1. Doesn't generate a "large" named thumbnail for WebLink items
  2. Falls back to "small" when the requested size doesn't exist, instead of using a larger available size

The client-side code change in propsForCard.js is feature-flagged OFF and is not involved in this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment