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

Allow adding extra icons to the room header #11799

Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@matrix-org/analytics-events": "^0.8.0",
"@matrix-org/emojibase-bindings": "^1.1.2",
"@matrix-org/matrix-wysiwyg": "2.4.1",
"@matrix-org/react-sdk-module-api": "^2.1.1",
"@matrix-org/react-sdk-module-api": "^2.2.0",
"@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^7.0.0",
"@sentry/tracing": "^7.0.0",
Expand Down
13 changes: 12 additions & 1 deletion src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { CallState, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { throttle } from "lodash";
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import shouldHideEvent from "../../shouldHideEvent";
import { _t } from "../../languageHandler";
Expand Down Expand Up @@ -246,6 +247,8 @@ export interface IRoomState {

canAskToJoin: boolean;
promptAskToJoin: boolean;

viewRoomOpts: ViewRoomOpts;
}

interface LocalRoomViewProps {
Expand Down Expand Up @@ -458,6 +461,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
msc3946ProcessDynamicPredecessor: SettingsStore.getValue("feature_dynamic_room_predecessors"),
canAskToJoin: this.askToJoinEnabled,
promptAskToJoin: false,
viewRoomOpts: { buttons: [] },
};

this.dispatcherRef = dis.register(this.onAction);
Expand Down Expand Up @@ -663,6 +667,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
: false,
activeCall: roomId ? CallStore.instance.getActiveCall(roomId) : null,
promptAskToJoin: this.context.roomViewStore.promptAskToJoin(),
viewRoomOpts: this.context.roomViewStore.getViewRoomOpts(),
};

if (
Expand Down Expand Up @@ -1407,6 +1412,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
tombstone: this.getRoomTombstone(room),
liveTimeline: room.getLiveTimeline(),
});

dis.dispatch<ActionPayload>({ action: Action.RoomLoaded });
};

private onRoomTimelineReset = (room?: Room): void => {
Expand Down Expand Up @@ -2601,7 +2608,10 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
data-layout={this.state.layout}
>
{SettingsStore.getValue("feature_new_room_decoration_ui") ? (
<RoomHeader room={this.state.room} />
<RoomHeader
room={this.state.room}
additionalButtons={this.state.viewRoomOpts.buttons}
/>
) : (
<LegacyRoomHeader
room={this.state.room}
Expand All @@ -2619,6 +2629,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
enableRoomOptionsMenu={!this.viewsLocalRoom}
viewingCall={viewingCall}
activeCall={this.state.activeCall}
additionalButtons={this.state.viewRoomOpts.buttons}
/>
)}
{mainSplitBody}
Expand Down
20 changes: 20 additions & 0 deletions src/components/views/rooms/LegacyRoomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import classNames from "classnames";
import { throttle } from "lodash";
import { RoomStateEvent, ISearchResults } from "matrix-js-sdk/src/matrix";
import { CallType } from "matrix-js-sdk/src/webrtc/call";
import { IconButton, Tooltip } from "@vector-im/compound-web";
import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import type { MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
import { _t } from "../../../languageHandler";
Expand Down Expand Up @@ -476,6 +478,7 @@ export interface IProps {
enableRoomOptionsMenu?: boolean;
viewingCall: boolean;
activeCall: Call | null;
additionalButtons?: ViewRoomOpts["buttons"];
}

interface IState {
Expand Down Expand Up @@ -669,6 +672,23 @@ export default class RoomHeader extends React.Component<IProps, IState> {

return (
<>
{this.props.additionalButtons?.map((props) => {
const label = props.label();

return (
<Tooltip label={label} key={props.id}>
<IconButton
onClick={() => {
props.onClick();
this.forceUpdate();
}}
title={label}
>
{props.icon}
</IconButton>
</Tooltip>
);
})}
{startButtons}
<RoomHeaderButtons
room={this.props.room}
Expand Down
26 changes: 25 additions & 1 deletion src/components/views/rooms/RoomHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { Icon as VerifiedIcon } from "@vector-im/compound-design-tokens/icons/ve
import { Icon as ErrorIcon } from "@vector-im/compound-design-tokens/icons/error.svg";
import { Icon as PublicIcon } from "@vector-im/compound-design-tokens/icons/public.svg";
import { EventType, JoinRule, type Room } from "matrix-js-sdk/src/matrix";
import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import { useRoomName } from "../../../hooks/useRoomName";
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
Expand Down Expand Up @@ -63,7 +64,13 @@ function notificationColorToIndicator(color: NotificationColor): React.Component
}
}

export default function RoomHeader({ room }: { room: Room }): JSX.Element {
export default function RoomHeader({
room,
additionalButtons,
}: {
room: Room;
additionalButtons?: ViewRoomOpts["buttons"];
}): JSX.Element {
const client = useMatrixClientContext();

const roomName = useRoomName(room);
Expand Down Expand Up @@ -169,6 +176,23 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element {
)}
</Box>
<Flex as="nav" align="center" gap="var(--cpd-space-2x)">
{additionalButtons?.map((props) => {
const label = props.label();

return (
<Tooltip label={label} key={props.id}>
<IconButton
aria-label={label}
onClick={(event) => {
event.stopPropagation();
props.onClick();
}}
>
{props.icon}
</IconButton>
</Tooltip>
);
})}
{!useElementCallExclusively && (
<Tooltip label={!voiceCallDisabledReason ? _t("voip|voice_call") : voiceCallDisabledReason!}>
<IconButton
Expand Down
1 change: 1 addition & 0 deletions src/contexts/RoomContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const RoomContext = createContext<
msc3946ProcessDynamicPredecessor: false,
canAskToJoin: false,
promptAskToJoin: false,
viewRoomOpts: { buttons: [] },
});
RoomContext.displayName = "RoomContext";
export default RoomContext;
Expand Down
5 changes: 5 additions & 0 deletions src/dispatcher/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,4 +371,9 @@ export enum Action {
* Fired when we want to open spotlight search. Use with a OpenSpotlightPayload.
*/
OpenSpotlight = "open_spotlight",

/**
* Fired when the room loaded.
*/
RoomLoaded = "room_loaded",
}
29 changes: 29 additions & 0 deletions src/stores/RoomViewStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ViewRoom as ViewRoomEvent } from "@matrix-org/analytics-events/types/ty
import { JoinedRoom as JoinedRoomEvent } from "@matrix-org/analytics-events/types/typescript/JoinedRoom";
import { Optional } from "matrix-events-sdk";
import EventEmitter from "events";
import { RoomViewLifecycle, ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import { MatrixDispatcher } from "../dispatcher/dispatcher";
import { MatrixClientPeg } from "../MatrixClientPeg";
Expand Down Expand Up @@ -60,6 +61,7 @@ import { pauseNonLiveBroadcastFromOtherRoom } from "../voice-broadcast/utils/pau
import { ActionPayload } from "../dispatcher/payloads";
import { CancelAskToJoinPayload } from "../dispatcher/payloads/CancelAskToJoinPayload";
import { SubmitAskToJoinPayload } from "../dispatcher/payloads/SubmitAskToJoinPayload";
import { ModuleRunner } from "../modules/ModuleRunner";

const NUM_JOIN_RETRY = 5;

Expand Down Expand Up @@ -119,6 +121,8 @@ interface State {
viewingCall: boolean;

promptAskToJoin: boolean;

viewRoomOpts: ViewRoomOpts;
}

const INITIAL_STATE: State = {
Expand All @@ -140,6 +144,7 @@ const INITIAL_STATE: State = {
wasContextSwitch: false,
viewingCall: false,
promptAskToJoin: false,
viewRoomOpts: { buttons: [] },
};

type Listener = (isActive: boolean) => void;
Expand Down Expand Up @@ -370,6 +375,10 @@ export class RoomViewStore extends EventEmitter {
this.cancelAskToJoin(payload as CancelAskToJoinPayload);
break;
}
case Action.RoomLoaded: {
this.setViewRoomOpts();
break;
}
}
}

Expand Down Expand Up @@ -805,4 +814,24 @@ export class RoomViewStore extends EventEmitter {
}),
);
}

/**
* Gets the current state of the 'viewRoomOpts' property.
*
* @returns {ViewRoomOpts} The value of the 'viewRoomOpts' property.
*/
public getViewRoomOpts(): ViewRoomOpts {
return this.state.viewRoomOpts;
}

/**
* Invokes the view room lifecycle to set the view room options.
*
* @returns {void}
*/
private setViewRoomOpts(): void {
const viewRoomOpts: ViewRoomOpts = { buttons: [] };
ModuleRunner.instance.invoke(RoomViewLifecycle.ViewRoom, viewRoomOpts, this.getRoomId());
this.setState({ viewRoomOpts });
}
}
6 changes: 6 additions & 0 deletions test/components/structures/RoomView-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -585,4 +585,10 @@ describe("RoomView", () => {
expect(dis.dispatch).toHaveBeenCalledWith({ action: "cancel_ask_to_join", roomId: room.roomId });
});
});

it("fires Action.RoomLoaded", async () => {
jest.spyOn(dis, "dispatch");
await mountRoomView();
expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.RoomLoaded });
});
});
29 changes: 29 additions & 0 deletions test/components/views/rooms/LegacyRoomHeader-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { CallType } from "matrix-js-sdk/src/webrtc/call";
import { ClientWidgetApi, Widget } from "matrix-widget-api";
import EventEmitter from "events";
import { setupJestCanvasMock } from "jest-canvas-mock";
import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import type { MatrixClient, MatrixEvent, RoomMember } from "matrix-js-sdk/src/matrix";
import type { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
Expand Down Expand Up @@ -738,6 +739,34 @@ describe("LegacyRoomHeader", () => {
expect(wrapper.container.querySelector(".mx_LegacyRoomHeader_name.mx_AccessibleButton")).toBeFalsy();
},
);

it("renders additionalButtons", async () => {
const additionalButtons: ViewRoomOpts["buttons"] = [
{
icon: <>test-icon</>,
id: "test-id",
label: () => "test-label",
onClick: () => {},
},
];
renderHeader({ additionalButtons });
expect(screen.getByRole("button", { name: "test-icon" })).toBeInTheDocument();
});

it("calls onClick-callback on additionalButtons", () => {
const callback = jest.fn();
const additionalButtons: ViewRoomOpts["buttons"] = [
{
icon: <>test-icon</>,
id: "test-id",
label: () => "test-label",
onClick: callback,
},
];
renderHeader({ additionalButtons });
fireEvent.click(screen.getByRole("button", { name: "test-icon" }));
expect(callback).toHaveBeenCalled();
});
});

interface IRoomCreationInfo {
Expand Down
43 changes: 43 additions & 0 deletions test/components/views/rooms/RoomHeader-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import React from "react";
import { CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { EventType, JoinRule, MatrixClient, MatrixEvent, PendingEventOrdering, Room } from "matrix-js-sdk/src/matrix";
import {
createEvent,
fireEvent,
getAllByLabelText,
getByLabelText,
Expand All @@ -27,6 +28,7 @@ import {
screen,
waitFor,
} from "@testing-library/react";
import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import { filterConsole, mkEvent, stubClient, withClientContextRenderOptions } from "../../../test-utils";
import RoomHeader from "../../../../src/components/views/rooms/RoomHeader";
Expand Down Expand Up @@ -516,6 +518,47 @@ describe("RoomHeader", () => {
await waitFor(() => expect(getByLabelText(container, expectedLabel)).toBeInTheDocument());
});
});

it("renders additionalButtons", async () => {
const additionalButtons: ViewRoomOpts["buttons"] = [
{
icon: <>test-icon</>,
id: "test-id",
label: () => "test-label",
onClick: () => {},
},
];
render(
<RoomHeader room={room} additionalButtons={additionalButtons} />,
withClientContextRenderOptions(MatrixClientPeg.get()!),
);
expect(screen.getByRole("button", { name: "test-label" })).toBeInTheDocument();
});

it("calls onClick-callback on additionalButtons", () => {
const callback = jest.fn();
const additionalButtons: ViewRoomOpts["buttons"] = [
{
icon: <>test-icon</>,
id: "test-id",
label: () => "test-label",
onClick: callback,
},
];

render(
<RoomHeader room={room} additionalButtons={additionalButtons} />,
withClientContextRenderOptions(MatrixClientPeg.get()!),
);

const button = screen.getByRole("button", { name: "test-label" });
const event = createEvent.click(button);
event.stopPropagation = jest.fn();
fireEvent(button, event);

expect(callback).toHaveBeenCalled();
expect(event.stopPropagation).toHaveBeenCalled();
});
});

/**
Expand Down
1 change: 1 addition & 0 deletions test/components/views/rooms/SendMessageComposer-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ describe("<SendMessageComposer/>", () => {
msc3946ProcessDynamicPredecessor: false,
canAskToJoin: false,
promptAskToJoin: false,
viewRoomOpts: { buttons: [] },
};
describe("createMessageContent", () => {
const permalinkCreator = jest.fn() as any;
Expand Down
Loading