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

Commit

Permalink
Allow adding extra icons to the room header
Browse files Browse the repository at this point in the history
Signed-off-by: Charly Nguyen <charly.nguyen@nordeck.net>
  • Loading branch information
Charly Nguyen committed Oct 31, 2023
1 parent 860764c commit 5ee157b
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 7 deletions.
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
16 changes: 16 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,19 @@ export default class RoomHeader extends React.Component<IProps, IState> {

return (
<>
{this.props.additionalButtons?.map(({ icon, id, label, onClick }) => (
<Tooltip label={label()} key={id}>
<IconButton
onClick={() => {
onClick();
this.forceUpdate();
}}
title={label()}
>
{icon}
</IconButton>
</Tooltip>
))}
{startButtons}
<RoomHeaderButtons
room={this.props.room}
Expand Down
22 changes: 21 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,19 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element {
)}
</Box>
<Flex as="nav" align="center" gap="var(--cpd-space-2x)">
{additionalButtons?.map(({ icon, id, label, onClick }) => (
<Tooltip label={label()} key={id}>
<IconButton
aria-label={label()}
onClick={(event) => {
event.stopPropagation();
onClick();
}}
>
{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 });
});
});
14 changes: 14 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,19 @@ 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();
});
});

interface IRoomCreationInfo {
Expand Down
17 changes: 17 additions & 0 deletions test/components/views/rooms/RoomHeader-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,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 +517,22 @@ 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();
});
});

/**
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
33 changes: 33 additions & 0 deletions test/stores/RoomViewStore-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
import { mocked } from "jest-mock";
import { MatrixError, Room } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
import { RoomViewLifecycle, ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";

import { RoomViewStore } from "../../src/stores/RoomViewStore";
import { Action } from "../../src/dispatcher/actions";
Expand All @@ -43,6 +44,7 @@ import ErrorDialog from "../../src/components/views/dialogs/ErrorDialog";
import { CancelAskToJoinPayload } from "../../src/dispatcher/payloads/CancelAskToJoinPayload";
import { JoinRoomErrorPayload } from "../../src/dispatcher/payloads/JoinRoomErrorPayload";
import { SubmitAskToJoinPayload } from "../../src/dispatcher/payloads/SubmitAskToJoinPayload";
import { ModuleRunner } from "../../src/modules/ModuleRunner";

jest.mock("../../src/Modal");

Expand Down Expand Up @@ -132,6 +134,11 @@ describe("RoomViewStore", function () {
await untilDispatch(Action.CancelAskToJoin, dis);
};

const dispatchRoomLoaded = async () => {
dis.dispatch({ action: Action.RoomLoaded });
await untilDispatch(Action.RoomLoaded, dis);
};

let roomViewStore: RoomViewStore;
let slidingSyncManager: SlidingSyncManager;
let dis: MatrixDispatcher;
Expand Down Expand Up @@ -569,4 +576,30 @@ describe("RoomViewStore", function () {
});
});
});

describe("getViewRoomOpts", () => {
it("returns viewRoomOpts", () => {
expect(roomViewStore.getViewRoomOpts()).toEqual({ buttons: [] });
});
});

describe("Action.RoomLoaded", () => {
it("updates viewRoomOpts", async () => {
const buttons: ViewRoomOpts["buttons"] = [
{
icon: "test-icon",
id: "test-id",
label: () => "test-label",
onClick: () => {},
},
];
jest.spyOn(ModuleRunner.instance, "invoke").mockImplementation((lifecycleEvent, opts) => {
if (lifecycleEvent === RoomViewLifecycle.ViewRoom) {
opts.buttons = buttons;
}
});
await dispatchRoomLoaded();
expect(roomViewStore.getViewRoomOpts()).toEqual({ buttons });
});
});
});
1 change: 1 addition & 0 deletions test/test-utils/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export function getRoomContext(room: Room, override: Partial<IRoomState>): IRoom
msc3946ProcessDynamicPredecessor: false,
canAskToJoin: false,
promptAskToJoin: false,
viewRoomOpts: { buttons: [] },

...override,
};
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1828,10 +1828,10 @@
resolved "https://registry.yarnpkg.com/@matrix-org/olm/-/olm-3.2.15.tgz#55f3c1b70a21bbee3f9195cecd6846b1083451ec"
integrity sha512-S7lOrndAK9/8qOtaTq/WhttJC/o4GAzdfK0MUPpo8ApzsJEC0QjtwrkC3KBXdFP1cD1MXi/mlKR7aaoVMKgs6Q==

"@matrix-org/react-sdk-module-api@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-2.1.1.tgz#54e8617c15185010d608c0325ecaec8d1574d12b"
integrity sha512-dYPY3aXtNwPrg2aEmFeWddMdohus/Ha17XES2QH+WMCawt+hH+uq28jH1EmW1RUOOzxVcdY36lRGOwqRtAJbhA==
"@matrix-org/react-sdk-module-api@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@matrix-org/react-sdk-module-api/-/react-sdk-module-api-2.2.0.tgz#cb284601a82448dc23fac31949c466eb34ec64b4"
integrity sha512-HSicxLdagZRbQp35d3t2SeDFTiT4GmEQDQGih8dWSKRHXK4krVQjb6Kf1NkwweiFDAeU0qgbz2pP4RZqbv0XIg==
dependencies:
"@babel/runtime" "^7.17.9"

Expand Down

0 comments on commit 5ee157b

Please sign in to comment.