From c2777db6561238e5dc2787efd0438d34ed50c356 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Wed, 12 Jun 2024 11:25:36 +0200 Subject: [PATCH] web: Support async onClick callbacks for context menus The current implementation was synchronous only, so that when an async callback was used, the menu was being disposed prematurely. This patch ensures that in case of asynchronous callbacks, the menu will be disposed after they finish. --- web/packages/core/src/ruffle-player.ts | 30 ++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/web/packages/core/src/ruffle-player.ts b/web/packages/core/src/ruffle-player.ts index c4c298a22..97a8eb0e7 100644 --- a/web/packages/core/src/ruffle-player.ts +++ b/web/packages/core/src/ruffle-player.ts @@ -70,7 +70,7 @@ interface ContextMenuItem { * * @param event The mouse event that triggered the click. */ - onClick: (event: MouseEvent) => void; + onClick: (event: MouseEvent) => Promise; /** * Whether this item is clickable. @@ -1380,7 +1380,7 @@ export class RufflePlayer extends HTMLElement { /** * Opens the save manager. */ - private openSaveManager(): void { + private async openSaveManager(): Promise { this.saveManager.classList.remove("hidden"); } @@ -1467,7 +1467,7 @@ export class RufflePlayer extends HTMLElement { // TODO: better checkboxes text: item.caption + (item.checked ? ` (${CHECKMARK})` : ``), - onClick: () => + onClick: async () => this.instance?.run_context_menu_callback(index), enabled: item.enabled, }); @@ -1480,19 +1480,19 @@ export class RufflePlayer extends HTMLElement { if (this.isFullscreen) { items.push({ text: text("context-menu-exit-fullscreen"), - onClick: () => this.setFullscreen(false), + onClick: async () => this.setFullscreen(false), }); } else { items.push({ text: text("context-menu-enter-fullscreen"), - onClick: () => this.setFullscreen(true), + onClick: async () => this.setFullscreen(true), }); } } items.push({ text: text("context-menu-volume-controls"), - onClick: () => { + onClick: async () => { this.openVolumeControls(); }, }); @@ -1533,7 +1533,7 @@ export class RufflePlayer extends HTMLElement { flavor: isExtension ? "extension" : "", version: buildInfo.versionName, }), - onClick() { + async onClick() { window.open(RUFFLE_ORIGIN, "_blank"); }, }); @@ -1543,7 +1543,9 @@ export class RufflePlayer extends HTMLElement { addSeparator(); items.push({ text: text("context-menu-hide"), - onClick: () => (this.contextMenuForceDisabled = true), + onClick: async () => { + this.contextMenuForceDisabled = true; + }, }); } return items; @@ -1663,7 +1665,17 @@ export class RufflePlayer extends HTMLElement { if (enabled !== false) { menuItem.addEventListener( this.contextMenuSupported ? "click" : "pointerup", - onClick, + async (event: MouseEvent) => { + // Prevent the menu from being destroyed. + // It's required when we're dealing with async callbacks, + // as the async callback may still use the menu in the future. + event.stopPropagation(); + + await onClick(event); + + // Then we have to close the context menu manually after the callback finishes. + this.hideContextMenu(); + }, ); } else { menuItem.classList.add("disabled");