diff --git a/packages/tutanota-utils/lib/Utils.ts b/packages/tutanota-utils/lib/Utils.ts index a71302ca4816..3c13a04516fd 100644 --- a/packages/tutanota-utils/lib/Utils.ts +++ b/packages/tutanota-utils/lib/Utils.ts @@ -616,3 +616,21 @@ export function assertValidURL(url: string) { return false } } + +/** + * Excessive resizing of an observed element can result in one or more resize events being deferred to the next render cycle. + * When this happens, the browser sends a `ResizeObserver loop completed with undelivered notifications` error. + * To avoid this, we handle resize events in a `requestAnimationFrame` making sure to cancel any pending requests + */ +export function createResizeObserver(cb: ResizeObserverCallback): ResizeObserver { + let afRequestId: number | null = null + + return new ResizeObserver((entries, observer) => { + if (afRequestId != null) { + cancelAnimationFrame(afRequestId) + } + afRequestId = requestAnimationFrame(() => { + cb(entries, observer) + }) + }) +} diff --git a/src/common/gui/base/List.ts b/src/common/gui/base/List.ts index cf30139a98f8..9e8601e67376 100644 --- a/src/common/gui/base/List.ts +++ b/src/common/gui/base/List.ts @@ -12,6 +12,7 @@ import { theme, ThemeId } from "../theme.js" import { ProgrammingError } from "../../api/common/error/ProgrammingError.js" import { Coordinate2D } from "./SwipeHandler.js" import { styles } from "../styles.js" +import { createResizeObserver } from "@tutao/tutanota-utils/dist/Utils" export type ListState = Readonly<{ items: ReadonlyArray @@ -134,9 +135,7 @@ export class List> implements ClassComponent { - this.updateSize() - }).observe(this.containerDom) + createResizeObserver(() => this.updateSize()).observe(this.containerDom) } else { requestAnimationFrame(() => this.updateSize()) } diff --git a/src/mail-app/mail/view/MailViewer.ts b/src/mail-app/mail/view/MailViewer.ts index 126d0ac57e66..765e7080975a 100644 --- a/src/mail-app/mail/view/MailViewer.ts +++ b/src/mail-app/mail/view/MailViewer.ts @@ -34,6 +34,7 @@ import { responsiveCardHMargin, responsiveCardHPadding } from "../../../common/g import { Dialog } from "../../../common/gui/base/Dialog.js" import { createNewContact } from "../../../common/mailFunctionality/SharedMailUtils.js" import { getExistingRuleForType } from "../model/MailUtils.js" +import { createResizeObserver } from "@tutao/tutanota-utils/dist/Utils" assertMainOrNode() @@ -93,9 +94,6 @@ export class MailViewer implements Component { /** for block quotes in mail bodies, whether to display placeholder or original quote */ private quoteState: "noquotes" | "unset" | "collapsed" | "expanded" = "unset" - /** most recent resize animation frame request ID */ - private resizeRaf: number | undefined - constructor(vnode: Vnode) { this.setViewModel(vnode.attrs.viewModel, vnode.attrs.isPrimary) @@ -298,7 +296,7 @@ export class MailViewer implements Component { this.renderShadowMailBody(sanitizedMailBody, attrs, vnode.dom as HTMLElement) if (client.isMobileDevice()) { this.resizeObserverViewport?.disconnect() - this.resizeObserverViewport = new ResizeObserver((entries) => { + this.resizeObserverViewport = createResizeObserver(() => { if (this.pinchZoomable) { // recreate if the orientation of the device changes -> size of the viewport / mail-body changes this.createPinchZoom(this.pinchZoomable.getZoomable(), vnode.dom as HTMLElement) @@ -423,14 +421,8 @@ export class MailViewer implements Component { if (client.isMobileDevice()) { this.pinchZoomable = null this.resizeObserverZoomable?.disconnect() - this.resizeObserverZoomable = new ResizeObserver((entries) => { - if (this.resizeRaf) { - // did we already schedule a reset for pinch to zoom in the frame - cancelAnimationFrame(this.resizeRaf) - } - this.resizeRaf = requestAnimationFrame(() => { - this.createPinchZoom(wrapNode, parent) // recreate for example if images are loaded slowly - }) + this.resizeObserverZoomable = createResizeObserver(() => { + this.createPinchZoom(wrapNode, parent) // recreate for example if images are loaded slowly }) this.resizeObserverZoomable.observe(wrapNode) } else {