From 49f2b22648fd1fceffc0e33a7f664b3dd60ee231 Mon Sep 17 00:00:00 2001 From: Shrija Tewari Date: Fri, 6 Mar 2026 01:59:59 +0530 Subject: [PATCH 1/2] chore: setup fixes for local development - Temporarily disable engines plugin in .yarnrc.yml for Node version compatibility - Add template path to livechat webpack config for proper index.html generation Made-with: Cursor --- .yarnrc.yml | 9 +++++---- packages/livechat/webpack.config.ts | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.yarnrc.yml b/.yarnrc.yml index fd84577753a88..6e7da4264c150 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -8,8 +8,9 @@ enableImmutableInstalls: false nodeLinker: node-modules -plugins: - - path: .yarn/plugins/@yarnpkg/plugin-engines.cjs - spec: "https://raw.githubusercontent.com/devoto13/yarn-plugin-engines/main/bundles/%40yarnpkg/plugin-engines.js" +# Temporarily disabled engines plugin for development +# plugins: +# - path: .yarn/plugins/@yarnpkg/plugin-engines.cjs +# spec: "https://raw.githubusercontent.com/devoto13/yarn-plugin-engines/main/bundles/%40yarnpkg/plugin-engines.js" -yarnPath: .yarn/releases/yarn-4.12.0.cjs +yarnPath: .yarn/releases/yarn-4.12.0.cjs \ No newline at end of file diff --git a/packages/livechat/webpack.config.ts b/packages/livechat/webpack.config.ts index f7a728d816654..ace46266a62b4 100644 --- a/packages/livechat/webpack.config.ts +++ b/packages/livechat/webpack.config.ts @@ -134,6 +134,7 @@ const config = (_env: any, args: webpack.WebpackOptionsNormalized): webpack.Conf 'process.env.NODE_ENV': JSON.stringify(args.mode === 'production' ? 'production' : 'development'), }), new HtmlWebpackPlugin({ + template: _('./src/index.ejs'), title: 'Livechat - Rocket.Chat', chunks: ['polyfills', 'vendor', 'bundle'], chunksSortMode: 'manual', From 0e716902224a1b77edeb46aeb330d6d7d545fa39 Mon Sep 17 00:00:00 2001 From: Shrija Tewari Date: Mon, 9 Mar 2026 23:29:31 +0530 Subject: [PATCH 2/2] fix(fuselage-ui-kit): prevent race condition with cached images in ImageBlock When an image was cached, setting img.src could trigger the load event synchronously before the event listener was properly attached. This caused images in UIKit blocks to never update their loading state, leaving them stuck in skeleton state. The fix checks if the image is already complete before setting src, and only attaches the event listener if the image hasn't loaded yet. This prevents the race condition where: - Cached images fire load event immediately - Event listener might not be attached yet - State never updates, leaving skeleton visible Tested with: - Fresh images (not cached) - Cached images (from browser cache) - Rapid URL changes All scenarios now work correctly. Made-with: Cursor --- .changeset/fix-imageblock-cached-image-race.md | 5 +++++ packages/fuselage-ui-kit/src/blocks/ImageBlock.tsx | 14 ++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 .changeset/fix-imageblock-cached-image-race.md diff --git a/.changeset/fix-imageblock-cached-image-race.md b/.changeset/fix-imageblock-cached-image-race.md new file mode 100644 index 0000000000000..afe5fc30192e5 --- /dev/null +++ b/.changeset/fix-imageblock-cached-image-race.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/fuselage-ui-kit': patch +--- + +Fix race condition in ImageBlock where cached images could trigger load event before event listener was attached, causing images to never render properly in UIKit blocks. \ No newline at end of file diff --git a/packages/fuselage-ui-kit/src/blocks/ImageBlock.tsx b/packages/fuselage-ui-kit/src/blocks/ImageBlock.tsx index 3bd88eecf582f..08bfe022ddd01 100644 --- a/packages/fuselage-ui-kit/src/blocks/ImageBlock.tsx +++ b/packages/fuselage-ui-kit/src/blocks/ImageBlock.tsx @@ -45,18 +45,20 @@ const ImageBlock = ({ className, block, surfaceRenderer }: ImageBlockProps): Rea useEffect(() => { const img = document.createElement('img'); + img.src = block.imageUrl; + + // If image is already cached and loaded, update state immediately + if (img.complete) { + setState(fetchImageState(img)); + return; + } + // Otherwise, wait for the load event const handleLoad = () => { setState(fetchImageState(img)); }; img.addEventListener('load', handleLoad); - img.src = block.imageUrl; - - if (img.complete) { - img.removeEventListener('load', handleLoad); - setState(fetchImageState(img)); - } return () => { img.removeEventListener('load', handleLoad);