Mattermost plugins: The web app
The web app side lets your plugins change the layout and user experience of Mattermost
(Originally published at controlaltdieliet.be)
This is the fourth installment in a series of articles on Mattermost plugins. First, we talked about how to set up your developer environment. We then examined the structure of server-side and web app plugins before walking through how to build a server-side plugin in Mattermost. In this piece, we’ll explore how to create web app plugins.
The web app is written in JavaScript. It uses Redux, and you can write your plugin in Typescript as well. In this article, we’ll use JavaScript. By the end of this article, you’ll add an item to the menu and an icon to the channel header.
I made a git-repository from the Mattermost Starter Template Plugin because it is minimalistic and is a good place to start. My repository contains a branch for each additional step. The Mattermost demo plugin gives a very nice overview. It can be overwhelming for the first time, but it is a good reference. I also recommend taking a look at the Mattermost Developer Plugins Reference or the Todo bot as a starting point.
Install some dependencies
Assuming you’ve already followed the steps from my previous articles, you’ve already have a server app. Now, we’re going to add the web app part (even though you can write plugins that have just a server part or just a web app part).
Run git checkout origin/webapp_step1
to see the code. You’ll see a new folder called webapp plugin
.
Because we removed the web app in a previous version, there is no build in the Makefile. So, you need to generate a new Makefile. In the code you downloaded, this is already done for you. But you also have to install some dependencies:
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli
npm install --save react
A closer look into the webapp folder
Let’s take a look at the webapp
folder. The i18n
folder will contain the translated strings for your app. We will not use this in this tutorial. And we can ignore the tests
folder as well.
The node_modules
folder contains the necessary nodejs modules for building your app. You’ll write your code in the src
folder, but we will get back to that later.
The webpack.config.js
is important. Around line 37 you’ll find module.exports = {entry: [ 'src/index.js']...
. This line defines where your code for your web app starts. If you start your web app plugin in index.txs
or start.js
, you’ll have to modify this line or your web app plugin will be empty.
The index.js file
We start with a minimal index.js
file.
In the repository, this is branch webapp_step2
. Use git checkout origin/webapp_step2
to load the code.
First, we load React. Then we define a class plugin. The code in the function will run when the plugin gets enabled. You have to register your plugin with the unique ID that you also defined in the plugin.json
file as we described here.
import React from 'react';
class HelloWorldPlugin {
initialize(registry, store) {
console.log("Hello there in the console");
}
}
window.registerPlugin('be.controlaltdieliet.demoortom.first_mattermost_plugin', new HelloWorldPlugin());
When you enable the plugin, you will see this in your console:
Add a button to the channel header
From now on, you can start implementing all the cool stuff that you find in the web app reference. Let’s add a button to the top of every channel that says hello when you click it.
In the repository, this is branch webapp_step3
. Run git checkout origin/webapp_step3
to load the code:
import React from 'react';
const Icon = () =>
<i className='icon fa fa-plug'/>;
class HelloWorldPlugin {
initialize(registry, store) {
registry.registerChannelHeaderButtonAction(
// icon - JSX element to use as the button's icon
,
// action - a function called when the button is clicked, passed the channel and channel member as arguments
// null,
() => {
alert("Hi there");
},
// dropdown_text - string or JSX element shown for the dropdown button description
"Says hello",
);
}
}
window.registerPlugin('be.controlaltdieliet.demoortom.first_mattermost_plugin', new HelloWorldPlugin());
Add an item to the Main Menu
You can add an item to the Main Menu in just a few lines.
In the repository, this is branch webapp_step4
. Run git checkout origin/webapp_step4
to load the code:
registry.registerMainMenuAction(
"Saying hi",
() => alert("Hi again"),
);
Create a React component
While showing alerts is fun, we can do more. Let’s create a component that you will add to the left channel panel. For this, we need to create a React component.
To do that, create a new folder called components
in your src
folder. In this new folder, create another folder called my_first_component
. In this folder, create a file called index.js
.
Next, import React and create a class called MyFirstComponent
and render some HTML code into it. Run git checkout origin/webapp_step4
to retrieve the code in your folder:
import React from 'react'
export default class MyFirstComponent extends React.PureComponent {
render() {
const iconStyle = {
display: 'inline-block',
margin: '0 7px 0 1px',
};
const style = {
margin: '.5em 0 .5em',
padding: '0 12px 0 15px',
backgroundColor: 'rgba(255,255,255,0.6)',
};
const url= "https://developers.mattermost.com/extend/plugins/webapp/reference"
return (
<i
className='icon fa fa-plug'
style={iconStyle}
/>
<a href={url}>More info on plugins</a<
);
}
}
Display the component in the channel sidebar
Now, in the index.js
that’s located in the webapp/src
folder, import your first component and add it to the left sidebar. You can see this in the branch webapp_step5
. Use git checkout origin/webapp_step5
to see the code:
import MyFirstComponent from './components/my_first_component'
const Icon = () => <i className='icon fa fa-plug'/>;
class HelloWorldPlugin {
initialize(registry, store) {
const {leftsidebarheader}=registry.registerLeftSidebarHeaderComponent(MyFirstComponent)
Toggle the right sidebar
We created a new component RightSideFolder
in the components folder. This component will be shown in the right sidebar.
First, import the component the same as you did with your MyFirstComponent
. Next, register it with registry.registerRightHandSidebarComponent
. This will return a variable that tells you if the sidebar is shown or not.
After that, bind it to the channel header button with store.dispatch(toggleRHSPlugin)
. When you click the channel header button, you’ll receive a well-deserved applause!
Use git checkout origin/webapp_step6
to load the code:
import React from 'react';
import RightSideBar from './components/right_hand_sidebar'
import MyFirstComponent from './components/my_first_component'
const Icon = () => <i className='icon fa fa-plug'/>;
class HelloWorldPlugin {
initialize(registry, store) {
registry.registerMainMenuAction(
"Saying hi",
() => allert("Hi again"),
);
registry.registerChannelHeaderButtonAction(
,
() => store.dispatch(toggleRHSPlugin),
"Says hello",
);
const {toggleRHSPlugin} = registry.registerRightHandSidebarComponent(
RightSideBar,"Applause!");
const {leftsidebarheader}=registry.registerLeftSidebarHeaderComponent(MyFirstComponent)
}
}
window.registerPlugin('be.controlaltdieliet.demoortom.first_mattermost_plugin', new HelloWorldPlugin());
Start building your own plugins!
Congratulations!
You made it to the end of this series and should be able now to start building your own plugins.
Since you’ve made it the far, perhaps you’re interested in my previous series on Mattermost integrations: