From 56de1f70987e351823439a76df6e761f86efa1ab Mon Sep 17 00:00:00 2001 From: rbshh Date: Wed, 29 Apr 2026 23:28:01 +0200 Subject: [PATCH 1/4] fix(web): Safari work items crash from requestIdleCallback without polyfill --- apps/web/app/entry.client.tsx | 1 + .../components/core/render-if-visible-HOC.tsx | 7 ++++--- apps/web/core/lib/polyfills/index.ts | 21 ++++++++++++++++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/web/app/entry.client.tsx b/apps/web/app/entry.client.tsx index 9c665ede072..ddb7bede901 100644 --- a/apps/web/app/entry.client.tsx +++ b/apps/web/app/entry.client.tsx @@ -4,6 +4,7 @@ * See the LICENSE file for details. */ +import "@/lib/polyfills"; import { startTransition, StrictMode } from "react"; import { hydrateRoot } from "react-dom/client"; import { HydratedRouter } from "react-router/dom"; diff --git a/apps/web/core/components/core/render-if-visible-HOC.tsx b/apps/web/core/components/core/render-if-visible-HOC.tsx index bd8a77a04a2..ca43690beb2 100644 --- a/apps/web/core/components/core/render-if-visible-HOC.tsx +++ b/apps/web/core/components/core/render-if-visible-HOC.tsx @@ -7,6 +7,7 @@ import type { ReactNode, MutableRefObject } from "react"; import React, { useState, useRef, useEffect } from "react"; import { cn } from "@plane/utils"; +import { scheduleIdleCallback } from "@/lib/polyfills"; type Props = { defaultHeight?: string; @@ -51,8 +52,8 @@ function RenderIfVisible(props: Props) { const observer = new IntersectionObserver( (entries) => { //DO no remove comments for future - if (typeof window !== undefined && window.requestIdleCallback && useIdletime) { - window.requestIdleCallback(() => setShouldVisible(entries[entries.length - 1].isIntersecting), { + if (typeof window !== "undefined" && useIdletime) { + scheduleIdleCallback(() => setShouldVisible(entries[entries.length - 1].isIntersecting), { timeout: 300, }); } else { @@ -77,7 +78,7 @@ function RenderIfVisible(props: Props) { //Set height after render useEffect(() => { if (intersectionRef.current && isVisible && shouldRecordHeights) { - window.requestIdleCallback(() => { + scheduleIdleCallback(() => { if (intersectionRef.current) placeholderHeight.current = `${intersectionRef.current.offsetHeight}px`; }); } diff --git a/apps/web/core/lib/polyfills/index.ts b/apps/web/core/lib/polyfills/index.ts index 2243dc620c2..297427c53a6 100644 --- a/apps/web/core/lib/polyfills/index.ts +++ b/apps/web/core/lib/polyfills/index.ts @@ -4,8 +4,12 @@ * See the LICENSE file for details. */ -if (typeof window !== "undefined" && window) { - // Add request callback polyfill to browser in case it does not exist +/** Installs `window.requestIdleCallback` / `cancelIdleCallback` when missing (e.g. older Safari). Idempotent. */ +function ensureRequestIdleCallbackPolyfilled(): void { + if (typeof window === "undefined" || !window) { + return; + } + window.requestIdleCallback = window.requestIdleCallback ?? function (cb) { @@ -27,4 +31,15 @@ if (typeof window !== "undefined" && window) { }; } -export {}; +ensureRequestIdleCallbackPolyfilled(); + +export function scheduleIdleCallback( + callback: IdleRequestCallback, + options?: IdleRequestOptions, +): number { + ensureRequestIdleCallbackPolyfilled(); + if (typeof window === "undefined") { + return 0; + } + return window.requestIdleCallback(callback, options); +} From e04805357e14a03d03e41e7b2eed74e3ea4bdb65 Mon Sep 17 00:00:00 2001 From: rbshh Date: Wed, 29 Apr 2026 23:49:03 +0200 Subject: [PATCH 2/4] fix(web): add useIdletime dependency to RenderIfVisible HOC and streamline scheduleIdleCallback export --- apps/web/core/components/core/render-if-visible-HOC.tsx | 7 ++----- apps/web/core/lib/polyfills/index.ts | 5 +---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/apps/web/core/components/core/render-if-visible-HOC.tsx b/apps/web/core/components/core/render-if-visible-HOC.tsx index ca43690beb2..6d7c91f1593 100644 --- a/apps/web/core/components/core/render-if-visible-HOC.tsx +++ b/apps/web/core/components/core/render-if-visible-HOC.tsx @@ -67,13 +67,10 @@ function RenderIfVisible(props: Props) { ); observer.observe(intersectionRef.current); return () => { - if (intersectionRef.current) { - // eslint-disable-next-line react-hooks/exhaustive-deps - observer.unobserve(intersectionRef.current); - } + observer.disconnect(); }; } - }, [intersectionRef, children, root, verticalOffset, horizontalOffset]); + }, [intersectionRef, children, root, verticalOffset, horizontalOffset, useIdletime]); //Set height after render useEffect(() => { diff --git a/apps/web/core/lib/polyfills/index.ts b/apps/web/core/lib/polyfills/index.ts index 297427c53a6..44aae75bf0e 100644 --- a/apps/web/core/lib/polyfills/index.ts +++ b/apps/web/core/lib/polyfills/index.ts @@ -33,10 +33,7 @@ function ensureRequestIdleCallbackPolyfilled(): void { ensureRequestIdleCallbackPolyfilled(); -export function scheduleIdleCallback( - callback: IdleRequestCallback, - options?: IdleRequestOptions, -): number { +export function scheduleIdleCallback(callback: IdleRequestCallback, options?: IdleRequestOptions): number { ensureRequestIdleCallbackPolyfilled(); if (typeof window === "undefined") { return 0; From 1114484a1326241232370ab9ecc4064449905e4e Mon Sep 17 00:00:00 2001 From: rbshh Date: Wed, 29 Apr 2026 23:51:53 +0200 Subject: [PATCH 3/4] refactor(web): enhance documentation for RenderIfVisible HOC and polyfill functions --- .../core/components/core/render-if-visible-HOC.tsx | 4 ++++ apps/web/core/lib/polyfills/index.ts | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/web/core/components/core/render-if-visible-HOC.tsx b/apps/web/core/components/core/render-if-visible-HOC.tsx index 6d7c91f1593..36abcd8e2be 100644 --- a/apps/web/core/components/core/render-if-visible-HOC.tsx +++ b/apps/web/core/components/core/render-if-visible-HOC.tsx @@ -24,6 +24,10 @@ type Props = { forceRender?: boolean; }; +/** + * Renders children only when the element intersects the viewport (or is forced visible), using a placeholder and + * optional height recording to reduce work for long lists. + */ function RenderIfVisible(props: Props) { const { defaultHeight = "300px", diff --git a/apps/web/core/lib/polyfills/index.ts b/apps/web/core/lib/polyfills/index.ts index 44aae75bf0e..641206f7ea4 100644 --- a/apps/web/core/lib/polyfills/index.ts +++ b/apps/web/core/lib/polyfills/index.ts @@ -4,7 +4,11 @@ * See the LICENSE file for details. */ -/** Installs `window.requestIdleCallback` / `cancelIdleCallback` when missing (e.g. older Safari). Idempotent. */ +/** + * Ensures `window.requestIdleCallback` and `window.cancelIdleCallback` exist. + * Installs minimal shims when the browser omits them (e.g. older Safari / WebKit). + * Safe to call repeatedly; only assigns missing APIs once. + */ function ensureRequestIdleCallbackPolyfilled(): void { if (typeof window === "undefined" || !window) { return; @@ -33,6 +37,13 @@ function ensureRequestIdleCallbackPolyfilled(): void { ensureRequestIdleCallbackPolyfilled(); +/** + * Schedules work to run when the browser is idle, or after a short delay when idle scheduling is unavailable. + * + * @param callback - Invoked with an `IdleDeadline`-like object (native or polyfilled). + * @param options - Optional `timeout` forwarded to the native API when present. + * @returns An idle handle for cancellation, or `0` when `window` is undefined (SSR). + */ export function scheduleIdleCallback(callback: IdleRequestCallback, options?: IdleRequestOptions): number { ensureRequestIdleCallbackPolyfilled(); if (typeof window === "undefined") { From 606a9a03fdc85245c61daaabdcc0727fde16720e Mon Sep 17 00:00:00 2001 From: Rishi <48980214+rbshh@users.noreply.github.com> Date: Wed, 29 Apr 2026 23:59:56 +0200 Subject: [PATCH 4/4] Update apps/web/core/components/core/render-if-visible-HOC.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- apps/web/core/components/core/render-if-visible-HOC.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/core/components/core/render-if-visible-HOC.tsx b/apps/web/core/components/core/render-if-visible-HOC.tsx index 36abcd8e2be..889b2a928fd 100644 --- a/apps/web/core/components/core/render-if-visible-HOC.tsx +++ b/apps/web/core/components/core/render-if-visible-HOC.tsx @@ -74,7 +74,7 @@ function RenderIfVisible(props: Props) { observer.disconnect(); }; } - }, [intersectionRef, children, root, verticalOffset, horizontalOffset, useIdletime]); + }, [intersectionRef, root, verticalOffset, horizontalOffset, useIdletime]); //Set height after render useEffect(() => {