Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide extension point to add and remove toolbar buttons #7

Open
Fox32 opened this issue Nov 1, 2022 · 5 comments
Open

Provide extension point to add and remove toolbar buttons #7

Fox32 opened this issue Nov 1, 2022 · 5 comments

Comments

@Fox32
Copy link

Fox32 commented Nov 1, 2022

Is your feature request related to a problem? Please describe.

We currently forked Element to provide the functionality described in element-hq/element-web#15737
We replace the widget toggle button from the toolbar with custom buttons for each widget. We heavily rely on widgets in our setup and want to make them more easily accessible.

Describe the solution you'd like

A general extension point that allows to remove certain toolbar buttons (e.g. at least the toggle widget button) and to add custom buttons (with title and icon) to it. We need to be able to dynamically change the buttons we add (not just once).

Describe alternatives you've considered

Alternative is to land an improvement into Element Web directly (see element-hq/element-web#15737). The issue is already open for some time, so I assume that there is currently no interest in landing such a change in Element itself. But we would also be glad to contribute it directly.

Additional context
I can provide additional details, e.g. the exact changes in out fork privately if you are interested.

@Fox32
Copy link
Author

Fox32 commented Nov 1, 2022

Probably a separate issue, but this would also need access to the WidgetLayoutStore (or parts of it) to perform the operation itself.

@dhenneke
Copy link
Contributor

I want to propose the following API surface to solve this issue. This is an example module using the described APIs:

An Element room header where the generic Apps icon is replaced with a button for each installed widget

Problem 1: Change the buttons in the room header

Create a new RoomHeaderLifecycle.Create lifecycle that can manipulate the startButtons and endButtons lists before rendering them.

We can use it in the matrix-react-sdk as follows:

// ...

export default class RoomHeader extends React.Component<IProps, IState> {
  // ...

  private renderButtons(isVideoRoom: boolean): React.ReactNode {
    const startButtons: JSX.Element[] = [];
    const endButtons: JSX.Element[] = [];

    // existing code that creates all the buttons for the toolbar

    // NEW: pass the configured buttons to the modules and let them edit the lists
    const opts: RoomHeaderCreateOpts = { startButtons, endButtons };
    ModuleRunner.instance.invoke(RoomHeaderLifecycle.Create, opts, this.props.room.roomId);

    // NEW: use `opts.startButtons`/`opts.endButtons` instead of `startButtons`/`endButtons`
    return (
      <>
        {opts.startButtons}
        <RoomHeaderButtons
            room={this.props.room}
            excludedRightPanelPhaseButtons={this.props.excludedRightPanelPhaseButtons}
        />
        {opts.endButtons}
      </>
    );
  }

  // ...
}

(based on https://github.com/matrix-org/matrix-react-sdk/blob/0ce3664434a885ca470fcaa9918f0fec69847d8f/src/components/views/rooms/RoomHeader.tsx#L654-L659)

A module can implement the lifecycle as follows:

export default class ExampleModule extends RuntimeModule {
  public constructor(moduleApi: ModuleApi) {
    super(moduleApi);

    this.on(RoomHeaderLifecycle.Create, this.onRoomHeaderCreate);
  }

  protected onRoomHeaderCreate: RoomHeaderCreateListener = (opts, roomId) => {
    // is called whenever the RoomHeader component is rendered

    opts.startButtons = opts.startButtons
      // Remove a button based on it's React key
      .filter((buttons) => buttons.key !== "apps")
      // add a new button to the list
      .concat(<MyNewButton key="new-button" />);
  };
}

Potential problems

Some issues we can discuss and either accept or change somehow:

  • Hooking into an implementation detail (-> startButtons, endButtons) adds friction when changes in the original <RoomHeader /> are needed in the future because the module api needs to be changed too.
  • The React key property is not guaranteed to be stable so a module could break after an update.

Additions

  • The Module API should provide a TooltipButton component so modules can add new buttons that fit into the design.

Problem 2: Access and manipulate the widget layout in the rooms

Problem 1 lets us add new buttons to the header, but we want to show active widgets on it and let users control which widgets are displayed to the users. So we still lack the interfaces to do that. We add it with two changes:

  1. Extend the ModuleApi with two endpoints to receive a list of all widgets and to change them in the layout:

    /**
     * Gets a list of widgets that are installed into the provided room as well as the container the
     * widgets are hosted.
     *
     * @param roomId The ID of the room to query.
     * @returns The widgets in the room.
     */
    getRoomWidgets(roomId: string): { allWidgets: IWidget[]; top: string[]; center: string[]; };
    
    /**
     * Move the room widget into another display container.
     *
     * @param roomId The ID of the room to query.
     * @param widgetId The ID of the widget to move to a different display container.
     * @param toContainer The container the widget should be moved to.
     */
    moveWidgetToContainer(roomId: string, widgetId: string, toContainer: WidgetContainer): void;
  2. Have a WidgetLifecycle.WidgetsChanged lifecycle that notifies the module that there were changes in the widgets and getRoomWidgets should be called again.

  3. Provide a WidgetAvatar component so modules can render the avatar of a widget.


Would this work or are there concerns with this approach?

@americanrefugee
Copy link

americanrefugee commented Jun 15, 2023

@Fox32 @dhenneke I was discussing this with @nadonomy. We need to identify areas of the app (e.g. Timeline, right side of room header bar) that can be customized without breaking everything else. Then clients can build whatever solution(s) they want or need, without having to worry about their forked version messing with our regular updates.

We shall discuss internally and get back to you with more detail :)

@dhenneke
Copy link
Contributor

dhenneke commented Aug 1, 2023

@americanrefugee @germain-gg did you had any chance to discuss it internally, yet? We would be happy to create a PR once we are aligned how we want to proceed here.

@americanrefugee
Copy link

americanrefugee commented Aug 1, 2023

@americanrefugee @germain-gg did you had any chance to discuss it internally, yet? We would be happy to create a PR once we are aligned how we want to proceed here.

We are still discussing internally what else can / should be customizable beyond the room header. @germain-gg or I shall update you with any more info as soon as we can!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants