Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Fix disappearing widget poput button when changing the widget layout #8754

Merged
merged 1 commit into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions src/components/views/elements/AppTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import { aboveLeftOf, ContextMenuButton } from "../../structures/ContextMenu";
import PersistedElement, { getPersistKey } from "./PersistedElement";
import { WidgetType } from "../../../widgets/WidgetType";
import { StopGapWidget } from "../../../stores/widgets/StopGapWidget";
import { ElementWidget, StopGapWidget } from "../../../stores/widgets/StopGapWidget";
import { ElementWidgetActions } from "../../../stores/widgets/ElementWidgetActions";
import WidgetContextMenu from "../context_menus/WidgetContextMenu";
import WidgetAvatar from "../avatars/WidgetAvatar";
Expand All @@ -50,6 +50,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { ActionPayload } from "../../../dispatcher/payloads";
import { Action } from '../../../dispatcher/actions';
import { ElementWidgetCapabilities } from '../../../stores/widgets/ElementWidgetCapabilities';
import { WidgetMessagingStore } from '../../../stores/widgets/WidgetMessagingStore';

interface IProps {
app: IApp;
Expand Down Expand Up @@ -196,6 +197,24 @@ export default class AppTile extends React.Component<IProps, IState> {
}
};

private determineInitialRequiresClientState(): boolean {
try {
const mockWidget = new ElementWidget(this.props.app);
const widgetApi = WidgetMessagingStore.instance.getMessaging(mockWidget, this.props.room.roomId);
if (widgetApi) {
// Load value from existing API to prevent resetting the requiresClient value on layout changes.
return widgetApi.hasCapability(ElementWidgetCapabilities.RequiresClient);
}
} catch {
// fallback to true
}

// requiresClient is initially set to true. This avoids the broken state of the popout
// button being visible (for an instance) and then disappearing when the widget is loaded.
// requiresClient <-> hide the popout button
return true;
}

/**
* Set initial component state when the App wUrl (widget URL) is being updated.
* Component props *must* be passed (rather than relying on this.props).
Expand All @@ -214,10 +233,7 @@ export default class AppTile extends React.Component<IProps, IState> {
error: null,
menuDisplayed: false,
widgetPageTitle: this.props.widgetPageTitle,
// requiresClient is initially set to true. This avoids the broken state of the popout
// button being visible (for an instance) and then disappearing when the widget is loaded.
// requiresClient <-> hide the popout button
requiresClient: true,
requiresClient: this.determineInitialRequiresClientState(),
};
}

Expand Down
43 changes: 40 additions & 3 deletions test/components/views/elements/AppTile-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import React from "react";
import TestRenderer from "react-test-renderer";
import { jest } from "@jest/globals";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixWidgetType } from "matrix-widget-api";
import { ClientWidgetApi, MatrixWidgetType } from "matrix-widget-api";
import { mount, ReactWrapper } from "enzyme";
import { Optional } from "matrix-events-sdk";

Expand All @@ -39,11 +39,14 @@ import ActiveWidgetStore from "../../../../src/stores/ActiveWidgetStore";
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 { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";

describe("AppTile", () => {
let cli;
let r1;
let r2;
let r1: Room;
let r2: Room;
const resizeNotifier = new ResizeNotifier();
let app1: IApp;
let app2: IApp;
Expand Down Expand Up @@ -328,6 +331,10 @@ describe("AppTile", () => {
moveToContainerSpy = jest.spyOn(WidgetLayoutStore.instance, 'moveToContainer');
});

it("requiresClient should be true", () => {
expect(wrapper.state('requiresClient')).toBe(true);
});

it("clicking 'minimise' should send the widget to the right", () => {
const minimiseButton = wrapper.find('.mx_AppTileMenuBar_iconButton_minimise');
minimiseButton.first().simulate('click');
Expand Down Expand Up @@ -355,5 +362,35 @@ describe("AppTile", () => {
expect(moveToContainerSpy).toHaveBeenCalledWith(r1, app1, Container.Top);
});
});

describe("with an existing widgetApi holding requiresClient = false", () => {
let wrapper: ReactWrapper;

beforeEach(() => {
const api = {
hasCapability: (capability: ElementWidgetCapabilities): boolean => {
return !(capability === ElementWidgetCapabilities.RequiresClient);
},
once: () => {},
} as unknown as ClientWidgetApi;

const mockWidget = new ElementWidget(app1);
WidgetMessagingStore.instance.storeMessaging(mockWidget, r1.roomId, api);

wrapper = mount((
<MatrixClientContext.Provider value={cli}>
<AppTile
key={app1.id}
app={app1}
room={r1}
/>
</MatrixClientContext.Provider>
));
});

it("requiresClient should be false", () => {
expect(wrapper.state('requiresClient')).toBe(false);
});
});
});
});