docs: Discourage usage of overrides and add hook to example plugin

This commit is contained in:
JC Brand 2022-07-19 10:35:45 +02:00
parent eb29d962f5
commit 990aefc6cb
2 changed files with 73 additions and 90 deletions

View File

@ -23,7 +23,7 @@ to fix a bug or to add new functionality.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
setup_dev_environment setup_dev_environment
plugin_development plugin_development
api/index api/index
testing testing

View File

@ -16,7 +16,7 @@ and is itself composed out of plugins.
There are only a few files that are included in the default build of Converse There are only a few files that are included in the default build of Converse
which aren't plugins. which aren't plugins.
An important one is `converse-core.js <https://github.com/conversejs/converse.js/blob/master/src/headless/converse-core.js>`_, An important one is `core.js <https://github.com/conversejs/.js/blob/master/src/headless/core.js>`_,
which is responsible for bootstrapping the plugin architecture, which is responsible for bootstrapping the plugin architecture,
setting up and maintaining the connection to the XMPP setting up and maintaining the connection to the XMPP
server and declaring the public (`window.converse </docs/html/api/converse.html>`_) and protected (`_converse.api </docs/html/api/-_converse.api.html>`_) APIs. server and declaring the public (`window.converse </docs/html/api/converse.html>`_) and protected (`_converse.api </docs/html/api/-_converse.api.html>`_) APIs.
@ -180,11 +180,59 @@ The code for it could look something like this:
These dependencies are closured so that they don't pollute the global These dependencies are closured so that they don't pollute the global
namespace, that's why you need to access them in such a way inside the module. namespace, that's why you need to access them in such a way inside the module.
Overrides Overriding templates
--------- --------------------
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_
templates and templates are imported as separate files.
It's possible to configure your module bundler (e.g. Webpack) in such as way that a
different file is loaded when a template is imported.
This allows you to create your own templates that are used instead of the ones
that would have originally been imported.
With Webpack (which Converse uses internally), you can specify an
``alias`` for the template you want to override. This alias then points to your
own custom template.
For example, in your webpack config file, you could add the following to the
``config`` object that gets exported:
.. code-block:: javascript
resolve: {
extensions: ['.js'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'node_modules/converse.js/src')
],
alias: {
'plugins/profile/templates/profile.js$': path.resolve(__dirname, 'templates/custom-profile.js')
}
}
This will override the template that gets imported at the path ``plugins/profile/templates/profile.js``
with your own template at the path ``templates/custom-profile.js`` (relative to your webpack config file).
Object and class Overrides
--------------------------
.. note:: Using the `overrides` feature from pluggable.js is discouraged. It's
much better to use events, promises and `hooks`_ to modify the behaviour of
Converse.
The pluggable.js `overrides` will only work on objects and classes that are
set as attributes on the `_converse` object, which doesn't apply to many
newer classes and objects, such as the web components. For these clasess,
overrides won't work at all.
This section is left here for completeness, because in some special cases
overrides are still used.
Plugins can override core code or code from other plugins. You can specify Plugins can override core code or code from other plugins. You can specify
overrides in the object passed to ``converse.plugins.add``. overrides in the object passed to ``converse.plugins.add``.
In an override you can still call the overridden function, by calling In an override you can still call the overridden function, by calling
``this.__super__.methodName.apply(this, arguments);`` where ``methodName`` is ``this.__super__.methodName.apply(this, arguments);`` where ``methodName`` is
@ -242,7 +290,7 @@ monkey patching which pollutes the call stack and can make your code fragile
and prone to bugs when Converse gets updated. Too much use of ``overrides`` and prone to bugs when Converse gets updated. Too much use of ``overrides``
is therefore a "code smell" which should ideally be avoided. is therefore a "code smell" which should ideally be avoided.
A better approach is to listen to the events emitted by Converse, and to add A better approach is to use the events and `hooks`_ emitted by Converse, and to add
your code in event handlers. This is however not always possible, in which case your code in event handlers. This is however not always possible, in which case
the overrides are a powerful tool. the overrides are a powerful tool.
@ -260,40 +308,6 @@ For example:
Object.assign(_converse.ChatBoxView.prototype, { doSomething }); Object.assign(_converse.ChatBoxView.prototype, { doSomething });
Overriding a template
~~~~~~~~~~~~~~~~~~~~~
Converse uses `lit-html <https://lit-html.polymer-project.org/guide>`_
templates.
It's not possible to override a template with the plugin's ``overrides``
feature, instead you should configure a new path to your own template via your
module bundler.
For example, with Webpack (which Converse uses internall), you can specify an
``alias`` for the template you want to override. This alias then points to your
own custom template.
For example, in your webpack config file, you could add the following to the
``config`` object that gets exported:
.. code-block:: javascript
resolve: {
extensions: ['.js'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'node_modules/converse.js/src')
],
alias: {
'plugins/profile/templates/profile.js$': path.resolve(__dirname, 'templates/custom-profile.js')
}
}
This will override the template that gets imported at the path ``plugins/profile/templates/profile.js``
with your own template at the path ``templates/custom-profile.js`` (relative to
your webpack config file).
.. _`dependencies`: .. _`dependencies`:
@ -515,8 +529,8 @@ The `Actions <https://github.com/conversejs/community-plugins/tree/master/packag
``like`` or ``dislike`` to chat messages. ``like`` or ``dislike`` to chat messages.
A full example plugin An example plugin
--------------------- -----------------
Below follows a documented example of a plugin. This is the same code that gets Below follows a documented example of a plugin. This is the same code that gets
generated by `generator-conversejs <https://github.com/jcbrand/generator-conversejs>`_. generated by `generator-conversejs <https://github.com/jcbrand/generator-conversejs>`_.
@ -590,8 +604,8 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
* _converse.api.promises.add('myPromise'); * _converse.api.promises.add('myPromise');
* *
* Your plugin should then, when appropriate, resolve the * Your plugin should then, when appropriate, resolve the
* promise by calling `_converse.api.emit`, which will also * promise by calling `_converse.api.trigger`, which will also
* emit an event with the same name as the promise. * trigger an event with the same name as the promise.
* For example: * For example:
* *
* _converse.api.trigger('operationCompleted'); * _converse.api.trigger('operationCompleted');
@ -605,53 +619,22 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
* *
* _converse.api.waitUntil('operationCompleted', function { ... }); * _converse.api.waitUntil('operationCompleted', function { ... });
*/ */
},
/* If you want to override some function or a model or
* view defined elsewhere in Converse, then you do that under /* In your plugin, you can also listen for hooks.
* the "overrides" namespace. * Hooks allow you to add or modify data and properties used by
*/ * Converse.
overrides: { *
/* For example, the private *_converse* object has a * For example, the getToolbarButtons hook allows you to add new buttons to the chat toolbar.
* method "onConnected". You can override that method as follows: * https://conversejs.org/docs/html/api/-_converse.html#event:getToolbarButtons
*/ */
onConnected: function () { api.listen.on('getToolbarButtons', (toolbar_el, buttons) => {
// Overrides the onConnected method in Converse buttons.push(html`
<button class="my-button" @click=${alert('hello world!')}>
// Top-level functions in "overrides" are bound to the <converse-icon class="fa fa-eye" size="1em" color="blue"></converse-icon>
// inner "_converse" object. </button>
const _converse = this; `);
return buttons;
// Your custom code can come here ... });
// You can access the original function being overridden
// via the __super__ attribute.
// Make sure to pass on the arguments supplied to this
// function and also to apply the proper "this" object.
_converse.__super__.onConnected.apply(this, arguments);
// Your custom code can come here ...
},
/* Override Converse's XMPPStatus model so that we can override the
* function that sends out the presence stanza.
*/
XMPPStatus: {
sendPresence: function (type, status_message, jid) {
// The "_converse" object is available via the __super__
// attribute.
const _converse = this.__super__._converse;
// Custom code can come here ...
// You can call the original overridden method, by
// accessing it via the __super__ attribute.
// When calling it, you need to apply the proper
// context as reference by the "this" variable.
this.__super__.sendPresence.apply(this, arguments);
// Custom code can come here ...
}
}
} }
}); });