Fix widgets getting stuck in loading states (#31314)

* Refer to ClientWidgetApi as "widget API" rather than "messaging"

* Rename StopGapWidgetDriver to ElementWidgetDriver

* Rename StopGapWidget to WidgetMessaging

* Fix WidgetMessaging's lifetime by storing it in WidgetMessagingStore

(Rather than storing just the raw ClientWidgetApi objects.)

* Unfail test

* use an error

* cleanup start

* Add docs

* Prettier

* link to store

* remove a let

* More logging, split up loop

* Add a test demonstrating a regression in Call.start

* Restore Call.start to a single, robust event loop

* Fix test failure by resetting the messaging store

* Expand on the WidgetMessaging doc comment

* Add additional tests to buff up coverage

* Add a test for the sticker picker opening the IM.

* reduce copy paste

---------

Co-authored-by: Half-Shot <will@half-shot.uk>
Co-authored-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Robin
2025-12-05 09:19:06 +00:00
committed by GitHub
co-authored by Half-Shot Timo K
parent f4e74c8dd2
commit 71895a3891
26 changed files with 1242 additions and 806 deletions
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { Room, type MatrixClient } from "matrix-js-sdk/src/matrix";
import { type ClientWidgetApi, type IWidget, MatrixWidgetType } from "matrix-widget-api";
import { type IWidget, MatrixWidgetType } from "matrix-widget-api";
import { act, render, type RenderResult, waitForElementToBeRemoved, waitFor } from "jest-matrix-react";
import userEvent from "@testing-library/user-event";
import {
@@ -34,7 +34,7 @@ import AppTile from "../../../../../src/components/views/elements/AppTile";
import { Container, WidgetLayoutStore } from "../../../../../src/stores/widgets/WidgetLayoutStore";
import AppsDrawer from "../../../../../src/components/views/rooms/AppsDrawer";
import { ElementWidgetCapabilities } from "../../../../../src/stores/widgets/ElementWidgetCapabilities";
import { ElementWidget } from "../../../../../src/stores/widgets/StopGapWidget";
import { ElementWidget, type WidgetMessaging } from "../../../../../src/stores/widgets/WidgetMessaging";
import { WidgetMessagingStore } from "../../../../../src/stores/widgets/WidgetMessagingStore";
import { ModuleRunner } from "../../../../../src/modules/ModuleRunner";
import { RoomPermalinkCreator } from "../../../../../src/utils/permalinks/Permalinks";
@@ -116,9 +116,11 @@ describe("AppTile", () => {
await RightPanelStore.instance.onReady();
});
beforeEach(() => {
beforeEach(async () => {
sdkContext = new SdkContextClass();
jest.spyOn(SettingsStore, "getValue").mockRestore();
// @ts-ignore
await WidgetMessagingStore.instance.onReady();
});
it("destroys non-persisted right panel widget on room change", async () => {
@@ -424,16 +426,20 @@ describe("AppTile", () => {
describe("with an existing widgetApi with requiresClient = false", () => {
beforeEach(() => {
const api = {
hasCapability: (capability: ElementWidgetCapabilities): boolean => {
return !(capability === ElementWidgetCapabilities.RequiresClient);
},
once: () => {},
const messaging = {
on: () => {},
off: () => {},
prepare: async () => {},
stop: () => {},
} as unknown as ClientWidgetApi;
widgetApi: {
hasCapability: (capability: ElementWidgetCapabilities): boolean => {
return !(capability === ElementWidgetCapabilities.RequiresClient);
},
},
} as unknown as WidgetMessaging;
const mockWidget = new ElementWidget(app1);
WidgetMessagingStore.instance.storeMessaging(mockWidget, r1.roomId, api);
WidgetMessagingStore.instance.storeMessaging(mockWidget, r1.roomId, messaging);
renderResult = render(
<MatrixClientContext.Provider value={cli}>