|
1 | 1 | # matrix-react-sdk-module-api
|
2 |
| -Proof of concept API surface for writing Modules for the react-sdk |
3 | 2 |
|
4 |
| -## TODO |
| 3 | +API surface for interacting with the [matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk) in a safe |
| 4 | +and predictable way. |
5 | 5 |
|
6 |
| -* [ ] Write a better intro/readme |
7 |
| -* [ ] Proof of concept |
8 |
| -* [ ] If approved, make it a real npm package |
9 |
| -* [ ] If approved, fix access controls |
10 |
| -* [ ] If approved, maintain this |
| 6 | +Modules are simply additional functionality added at compile time for the application and can do things like register |
| 7 | +custom translations, translation overrides, open dialogs, and add/modify UI. |
| 8 | + |
| 9 | +**Note**: This project is still considered alpha/beta quality due to the API surface not being extensive. Please reach |
| 10 | +out in [#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) on Matrix for guidance on how to add to |
| 11 | +this API surface. |
| 12 | + |
| 13 | +In general, new code should target a generalized interface. An example would be the `openDialog()` function: while the |
| 14 | +first module to use it didn't need custom `props`, it is expected that a dialog would at some point, so we expose it. |
| 15 | +On the other hand, we deliberately do not expose the complexity of the react-sdk's dialog stack to this layer until |
| 16 | +we need it. We might choose to open sticky dialogs with a new `openStickyDialog()` function instead of appending more |
| 17 | +arguments to the existing function. |
| 18 | + |
| 19 | +## Using the API |
| 20 | + |
| 21 | +Modules are simply standalone npm packages which get installed/included in the app at compile time. To start, we |
| 22 | +recommend using a simple module as a template, such as [element-web-ilag-module](https://github.com/vector-im/element-web-ilag-module). |
| 23 | + |
| 24 | +The package's `main` entrypoint MUST point to an instance of `RuntimeModule`. That class must be a `default` export |
| 25 | +for the module loader to reference correctly. |
| 26 | + |
| 27 | +The `RuntimeModule` instance MUST have a constructor which accepts a single `ModuleApi` parameter. This is supplied |
| 28 | +to the `super()` constructor. |
| 29 | + |
| 30 | +Otherwise, simply `npm install --save @matrix-org/react-sdk-module-api` and start coding! |
| 31 | + |
| 32 | +### Custom translations / string overrides |
| 33 | + |
| 34 | +Custom translation strings (used within your module) or string overrides can be specified using the `registerTranslations` |
| 35 | +function on a `ModuleApi` instance. For example: |
| 36 | + |
| 37 | +```typescript |
| 38 | +this.moduleApi.registerTranslations({ |
| 39 | + // If you use the translation utilities within your module, register your strings |
| 40 | + "My custom string": { |
| 41 | + "en": "My custom string", |
| 42 | + "fr": "Ma chaîne personnalisée", |
| 43 | + }, |
| 44 | + |
| 45 | + // If you want to override a string already in the app, such as the power level role |
| 46 | + // names, use the base string here and redefine the values for each applicable language. |
| 47 | + "A string that might already exist in the app": { |
| 48 | + "en": "Replacement value for that string", |
| 49 | + "fr": "Valeur de remplacement pour cette chaîne", |
| 50 | + }, |
| 51 | +}); |
| 52 | +``` |
| 53 | + |
| 54 | +If you are within a class provided by the module API then translations are generally accessible with `this.t("my string")`. |
| 55 | +This is a shortcut to `this.moduleApi.translateString()` which in turn calls into the translation engine at runtime to |
| 56 | +determine which appropriately-translated string should be returned. |
| 57 | + |
| 58 | +### Opening dialogs |
| 59 | + |
| 60 | +Dialogs are opened through the `openDialog()` function on a `ModuleApi` instance. They accept a return model, component |
| 61 | +properties definition, and a dialog component type. The dialog component itself must extend `DialogContent<>` from |
| 62 | +the module API in order to open correctly. |
| 63 | + |
| 64 | +The dialog component overrides `trySubmit()` and returns a promise for the return model, which is then passed back through |
| 65 | +to the promise returned by `openDialog()`. |
| 66 | + |
| 67 | +The `DialogContent<>` component is supplied with supporting components at the react-sdk layer to make dialog handling |
| 68 | +generic: all a module needs to do is supply the content that goes into the dialog. |
| 69 | + |
| 70 | +### Using standard UI elements |
| 71 | + |
| 72 | +The react-sdk provides a number of components for building Matrix clients as well as some supporting components to make |
| 73 | +it easier to have standardized styles on things like text inputs. Modules are naturally interested in these components |
| 74 | +so their UI looks nearly indistinguishable from the rest of the app, however the react-sdk's components are not able to |
| 75 | +be accessed directly. |
| 76 | + |
| 77 | +Instead, similar to dialogs and translations, modules use a proxy component which gets replaced by the real thing at |
| 78 | +runtime. For example, there is a `TextInputField` component supplied by the module API which gets translated into a |
| 79 | +decorated field at runtime for the module. |
| 80 | + |
| 81 | +**Note for react-sdk maintainers:** Don't forget to set the `renderFactory` of these components, otherwise the UI will |
| 82 | +be subpar. |
| 83 | + |
| 84 | +### Account management |
| 85 | + |
| 86 | +Modules can register for an account without overriding the logged-in user's auth data with the `registerSimpleAccount()` |
| 87 | +function on a `ModuleApi` instance. If the module would like to use that auth data, or has a different set of |
| 88 | +authentication information in mind, it can call `overwriteAccountAuth()` on a `ModuleApi` instance to overwrite |
| 89 | +(**without warning**) the current user's session. |
| 90 | + |
| 91 | +### View management |
| 92 | + |
| 93 | +From the `RuntimeModule` instance, modules can listen to various events that happen within the client to override |
| 94 | +a small bit of the UI behaviour. For example, listening for `RoomViewLifecycle.PreviewRoomNotLoggedIn` allows the module |
| 95 | +to change the behaviour of the "room preview bar" to enable future cases of `RoomViewLifecycle.JoinFromRoomPreview` |
| 96 | +being raised for additional handling. |
| 97 | + |
| 98 | +The module can also change what room/user/entity the user is looking at, and join it (if it's a room), with |
| 99 | +`navigatePermalink` on a `ModuleApi` instance. |
| 100 | + |
| 101 | +## Contributing / developing |
| 102 | + |
| 103 | +Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for the mechanics of the contribution process. |
| 104 | + |
| 105 | +For development, it is recommended to set up a normal element-web development environment and `yarn link` the |
| 106 | +module API into both the react-sdk and element-web layers. |
| 107 | + |
| 108 | +Visit [#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) for support with getting a development |
| 109 | +environment going. |
| 110 | + |
| 111 | +## Releases |
| 112 | + |
| 113 | +Because this is a scoped package, it needs to be published in a special way: |
| 114 | + |
| 115 | +```bash |
| 116 | +npm publish --access public |
| 117 | +``` |
0 commit comments