Skip to content

[bug]: v1.3.1 web app unusable on iOS/WebKit: unguarded requestIdleCallback crashes workspace pages; expanded sidebar overlays the board and swallows all taps #9356

Description

@Jacobabergam

Environment

Self-hosted CE v1.3.1 (docker compose deploy). Reproduced on real iPhone Safari (iOS 26) and with Playwright WebKit (iPhone 13 device profile). Desktop browsers are unaffected (both bugs are WebKit/viewport-specific).

Two independent bugs make the v1.3.1 web app effectively unusable on iOS. Root-cause analysis for both is included below (file/line refs are against the v1.3.1 tag).


Bug 1 — unguarded requestIdleCallback crashes every workspace page on WebKit

Symptom: any workspace page (kanban, list, …) immediately shows the "🚧 Looks like something went wrong!" error boundary.

Console:

TypeError: window.requestIdleCallback is not a function.
(In 'window.requestIdleCallback(()=>{h.current&&(S.current=`${h.current.offsetHeight}px`)})')
React Router caught the following error during render TypeError: window.requestIdleCallback is not a function.

Root cause: Safari/WebKit still does not ship requestIdleCallback (it remains behind an experimental flag), and a layout component rendered by workspace pages calls window.requestIdleCallback(...) unguarded, throwing during render.

Suggested fix: feature-guard the call (window.requestIdleCallback ?? setTimeout fallback) or add a polyfill to the app entry.

Workaround we're running: a setTimeout-based polyfill injected into index.html ahead of the bundle — with it, the pages render fine, which confirms this is the only fatal error on the path.

(While debugging we also consistently see React hydration errors #418/#423 on the prerendered shell in WebKit — non-fatal since React recovers, but noisy and possibly related to Bug 2's state confusion.)


Bug 2 — on <768px viewports the expanded sidebar overlays the entire board and swallows every tap

Symptom: tapping any kanban card does nothing — users report "opening/modifying a card never loads". The peek overlay itself is healthy: programmatically force-clicking a card through the overlay opens it normally.

Evidence: Playwright cannot deliver a tap to any card:

<div id="main-sidebar" ... class="z-20 ... translate-x-0 opacity-100 absolute"> subtree intercepts pointer events

Root cause chain (v1.3.1):

  1. apps/web/core/components/sidebar/resizable-sidebar.tsx (~line 185): when not collapsed the sidebar renders translate-x-0 opacity-100, plus absolute when isMobile — i.e. a full-height z-20 overlay on phones.
  2. The mobile auto-collapse lives in apps/web/core/components/sidebar/sidebar-wrapper.tsx (~line 46): if (windowSize[0] < 768 && !sidebarCollapsed) toggleSidebar(). On project/workspace issue pages this component's effect does not end up collapsing the sidebar (and a synthetic resize event doesn't either), so the overlay stays up.
  3. That effect also calls toggleSidebar() (toggle) instead of toggleSidebar(true) (set): if it runs more than once (two wrapper instances / re-mounts), the state flips collapsed → expanded again. We observed app_sidebar_collapsed being written "true" then "false" during a single boot on a fresh profile.
  4. store-wrapper.tsx (~line 48) only applies localStorage.app_sidebar_collapsed when it exists, and there is no width-aware default — so a fresh mobile visitor always starts expanded/overlaying.

Suggested fix: default sidebarCollapsed to true when the viewport is <768px, and make the auto-collapse effect use toggleSidebar(true) so repeated runs are idempotent.

Workaround we're running: setting localStorage.app_sidebar_collapsed = "true" for narrow screens in a script before the bundle boots. (Gotcha for anyone reproducing: that script runs before the viewport meta is parsed, so window.innerWidth still reports iOS's legacy 980px layout viewport — screen.width must be used.)


Steps to reproduce

  1. Self-host CE v1.3.1, log in on an iPhone (or Playwright WebKit with an iPhone descriptor).
  2. Open any project's work-items page → error boundary (Bug 1).
  3. Apply a requestIdleCallback polyfill so the page renders → tap any kanban card → nothing happens (Bug 2).

Repro harness (Playwright):

import { webkit, devices } from 'playwright';
const browser = await webkit.launch();
const ctx = await browser.newContext({ ...devices['iPhone 13'] });
// + authenticated session cookie
const page = await ctx.newPage();
page.on('pageerror', e => console.log('PAGEERROR:', e.message));
await page.goto('https://<instance>/<workspace>/projects/<id>/issues/');
await page.waitForTimeout(10000);
// Bug 1: pageerror = requestIdleCallback TypeError
// Bug 2 (after polyfill): tap times out with "subtree intercepts pointer events" from #main-sidebar
await (await page.$('[id^="issue-"]')).tap({ timeout: 10000 });

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions