Create Action Buttons

In the previous sections, we have learned about creating contextual bars and the list of building blocks available in the UIKit. Let's add to this knowledge and see how to create interactive buttons and handle the interactions.

Action buttons are UIKit elements that, when registered, can be displayed and used in different contexts within the Rocket.Chat UI to trigger or initiate a set of actions. The action button context consists of the following:

  • MESSAGE_ACTION: The action button is displayed in the message menu. Click the kebab menu against a message to access the button.

  • MESSAGE_BOX_ACTION: The button is displayed in the message text menu. Click the plus icon in the message box, and the button will appear under Apps.

  • ROOM_ACTION: The button is displayed in the room menu. Click the kebab menu of a room to access the button.

  • USER_DROPDOWN_ACTION: The button is displayed under the marketplace menu under Apps on the user panel.

To demonstrate this, we will create an action button on the MESSAGE_ACTION context that will display a modal with text to let us know that the interaction was received.

Step 1: Register a button

Action buttons are registered during the extendConfiguration lifecycle method. The code snippet is as follows:

protected async extendConfiguration(configuration: IConfigurationExtend, environmentRead: IEnvironmentRead): Promise<void> {
    configuration.ui.registerButton({
        actionId: 'my-action-id', // this identifies your button in the interaction event
        labelI18n: 'my-action-name', // key of the i18n string containing the name of the button
        context: UIActionButtonContext.MESSAGE_ACTION, // the context in which the action button will be displayed on the UI. You can also try using another context to see where the button will be displayed.
    });
}

Add the following import statement to your file for UIActionButtonContext:

import {UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui';

Registering a button requires some permissions. Add the following to your app manifest file (app.json):

{
    "id": "some-app-id",
    "name": "App Name",
    // ... other definitions
    "permissions": [
        { "name": "ui.registerButtons" },
        { "name" : "ui.interact" }
    ]
}

For more information, see App Permission System.

Deploy your app to test and you can see that the button gets added to the list of options against the context specified, in this case, a message.

Click on the options icon across any message, and you will see the action we just created, as seen below:

UI Kit new Action button

Step 2: Handle an interaction

After registering the button, we can see the button but cannot take any actions with it. Now whenever the user clicks the action button, we want the app to receive an interaction event.

Here is an example of how to handle it:

export class ActionbuttonApp extends App implements IUIKitInteractionHandler {
    protected async extendConfiguration(configuration: IConfigurationExtend, _environmentRead: IEnvironmentRead): Promise<void> {
        configuration.ui.registerButton({
            actionId: 'my-action-id', // this identifies your button in the interaction event
            labelI18n: 'my-action-name', // key of the i18n string containing the name of the button
            context: UIActionButtonContext.MESSAGE_ACTION
});
}
    public async executeActionButtonHandler(
        context: UIKitActionButtonInteractionContext,
        _read: IRead,
        _http: IHttp,
        _persistence: IPersistence,
        modify: IModify,
    ): Promise<IUIKitResponse> {
        const {
            buttonContext,
            actionId,
            triggerId,
            user,
            room,
            message,
        } = context.getInteractionData();

        // If you have multiple action buttons, use `actionId` to determine 
        // which one the user interacted with
        if (actionId === 'my-action-id') {
            modify.getUiController().openSurfaceView( // the method to define the modal
                {
                    type: UIKitSurfaceType.MODAL,
                    title: {
                        text: 'Interaction received', // title of the modal
                        type: 'plain_text'
                    },
                    blocks: [
                        {
                            type: 'section',
                            blockId: 'block_setion_1',
                            text: {
                                type: 'plain_text',
                                text: 'We received your interaction, thanks! :)',
                                emoji: true
                            }
                        }
                    ]
                },
                { triggerId: context.getInteractionData().triggerId },
                context.getInteractionData().user
            )
        }
        return context.getInteractionResponder().successResponse();
}}

Deprecated method of defining a modal

The following code snippet contains a deprecated method to define the modal:

export class MyApp extends App implements IUIKitInteractionHandler {
 protected async extendConfiguration(configuration: IConfigurationExtend, environmentRead: IEnvironmentRead): Promise<void> {
    configuration.ui.registerButton({
        actionId: 'my-action-id', // this identifies your button in the interaction event
        labelI18n: 'my-action-name', // key of the i18n string containing the name of the button
        context: UIActionButtonContext.MESSAGE_ACTION, // the context in which the action button will be displayed on the UI
    });
    public async executeActionButtonHandler(
        context: UIKitActionButtonInteractionContext,
        read: IRead,
        http: IHttp,
        persistence: IPersistence,
        modify: IModify
    ): Promise<IUIKitResponse> {
        const { 
            buttonContext, 
            actionId, 
            triggerId, 
            user, 
            room, 
            message,
        } = context.getInteractionData();

        // If you have multiple action buttons, use `actionId` to determine 
        // which one the user interacted with
        if (actionId === 'my-action-id') {
            const blockBuilder = modify.getCreator().getBlockBuilder();
            
            return context.getInteractionResponder().openModalViewResponse({ // this is deprecated, use the "openSurfaceVew" method instead
                title: blockBuilder.newPlainTextObject('Interaction received'),
                blocks: blockBuilder.addSectionBlock({
                    text: blockBuilder.newPlainTextObject('We received your interaction, thanks!')
                }).getBlocks(),
            });
        }

        return context.getInteractionResponder().successResponse();
    }
}

Now deploy and test the app again. You will see that when you click the action button that we had registered, a modal named Interaction received opens with the message We received your interaction, thanks!. You can also view the Logs tab of your app for details.

Step 3: Choose when your button is displayed

Most of the time, you will have an action button that does something specific and should not be displayed everywhere the context is available. For that, you can use the when prop while registering the button as follows:

protected async extendConfiguration(configuration: IConfigurationExtend, environmentRead: IEnvironmentRead): Promise<void> {
    await configuration.ui.registerButton({
        actionId: 'my-action-id',
        labelI18n: 'my-action-name',
        context: UIActionButtonContext.MESSAGE_ACTION,
        // If you want to choose `when` the button should be displayed
        when: {
            roomTypes: [
                RoomTypeFilter.PUBLIC_CHANNEL, 
                RoomTypeFilter.PRIVATE_CHANNEL, 
                RoomTypeFilter.DIRECT,
            ],
            hasOnePermission: ['create-d'],
            hasAllRoles: ['admin', 'moderator'],
        }
    });
}

Add the following import statement to your file for UIActionButtonContext and RoomTypeFilter:

import { RoomTypeFilter, UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui';

Now the button can only be seen in public and private channels and direct messages, by users that have the create-d permission and the admin role. The user must also be a moderator of the channel to see the action button for messages in that channel.

Add localization

Localization for your app is important to better suit different users. To add localization to your app, create an i18n folder in the project's root directory and add .json files for the various languages.

For this example, let us add an en.json file with the content as follows:

{
    "my-action-name": "Test UI Action"
}

This code will create a reference for the English language against the labelI18n value we specified when we registered the button in the previous section.

With these changes, deploy the app again and this time you will see something like the screenshot below when the button is clicked.

UI Kit Action Button triggered

With this example, we know how to handle interactions using action buttons and specify when the button should be displayed. We've also taken a look at defining localization files here.

To create such interfaces with Apps-Engine for the Rocket.Chat UI, you need to follow some guidelines. In the next topic, you will find detailed guidelines for menus, messages, chatbots, and so on.