From 95de708f4ee7c30177c40300c602971b876f24a5 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 24 Feb 2022 12:52:08 -0700 Subject: [PATCH] Make customisations (and general file overrides) easier to specify (#21189) Intended for customisation endpoints - see included docs. --- docs/customisations.md | 45 ++++++++++++++++++++++++------------------ webpack.config.js | 22 +++++++++++++++++++++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/docs/customisations.md b/docs/customisations.md index 979c81889d..5af98a01f9 100644 --- a/docs/customisations.md +++ b/docs/customisations.md @@ -16,24 +16,30 @@ the React SDK, you can still override it from the Element Web layer: `element-web/src/customisations/YourNameSecurity.ts` 2. Edit customisations points and make sure export the ones you actually want to activate -3. Tweak the Element build process to use the customised module instead of the - default by adding this to the `additionalPlugins` array in `webpack.config.js`: +3. Create/add an entry to `customisations.json` next to the webpack config: -```js -new webpack.NormalModuleReplacementPlugin( - /src[\/\\]customisations[\/\\]Security\.ts/, - path.resolve(__dirname, 'src/customisations/YourNameSecurity.ts'), -), +```json +{ + "src/customisations/Security.ts": "src/customisations/YourNameSecurity.ts" +} ``` -If we add more customisation modules in the future, we'll likely improve these -steps to remove the need for build changes like the above. - By isolating customisations to their own module, this approach should remove the chance of merge conflicts when updating your fork, and thus simplify ongoing maintenance. +**Note**: The project deliberately does not exclude `customisations.json` from Git. +This is to ensure that in shared projects it's possible to have a common config. By +default, Element Web does *not* ship with this file to prevent conflicts. + +### Custom components + +Instead of implementing skinning from the react-sdk, maintainers can use the above system to override components +if they wish. Maintenance and API surface compatibility are left as a responsibility for the project - the layering +in Element Web (including the react-sdk) do not make guarantees that properties/state machines won't change. + ### Component visibility customisation + UI for some actions can be hidden via the ComponentVisibility customisation: - inviting users to rooms and spaces, - creating rooms, @@ -41,21 +47,22 @@ UI for some actions can be hidden via the ComponentVisibility customisation: To customise visibility create a customisation module from [ComponentVisibility](https://github.com/matrix-org/matrix-react-sdk/blob/master/src/customisations/ComponentVisibility.ts) following the instructions above. -`shouldShowComponent` determines whether or not the active MatrixClient user should be able to use +`shouldShowComponent` determines whether the active MatrixClient user should be able to use the given UI component. When `shouldShowComponent` returns falsy all UI components for that feature will be hidden. If shown, the user might still not be able to use the component depending on their contextual permissions. For example, invite options -might be shown to the user but they won't have permission to invite users to +might be shown to the user, but they won't have permission to invite users to the current room: the button will appear disabled. For example, to only allow users who meet a certain condition to create spaces: -``` +```typescript function shouldShowComponent(component: UIComponent): boolean { - if (component === UIComponent.CreateSpaces) { - const userMeetsCondition = <> - return userMeetsCondition; - } - return true; + if (component === UIComponent.CreateSpaces) { + // customConditionCheck() is a function of your own creation + const userMeetsCondition = customConditionCheck(MatrixClientPeg.get().getUserId()); + return userMeetsCondition; + } + return true; } ``` -In this example, all UI related to creating a space will be hidden unless the users meets a custom condition. \ No newline at end of file +In this example, all UI related to creating a space will be hidden unless the users meets the custom condition. diff --git a/webpack.config.js b/webpack.config.js index 9b6e17bed1..566e7ddfbb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -39,6 +39,26 @@ function getActiveThemes() { return themes; } +// See docs/customisations.md +let fileOverrides = {/* {[file: string]: string} */}; +try { + fileOverrides = require('./customisations.json'); + + // stringify the output so it appears in logs correctly, as large files can sometimes get + // represented as `` which is less than helpful. + console.log("Using customisations.json : " + JSON.stringify(fileOverrides, null, 4)); +} catch (e) { + // ignore - not important +} +const moduleReplacementPlugins = Object.entries(fileOverrides).map(([oldPath, newPath]) => { + return new webpack.NormalModuleReplacementPlugin( + // because the input is effectively defined by the person running the build, we don't + // need to do anything special to protect against regex overrunning, etc. + new RegExp(oldPath.replace(/\//g, '[\\/\\\\]').replace(/\./g, '\\.')), + path.resolve(__dirname, newPath), + ); +}); + module.exports = (env, argv) => { // Establish settings based on the environment and args. // @@ -475,6 +495,8 @@ module.exports = (env, argv) => { }, plugins: [ + ...moduleReplacementPlugins, + // This exports our CSS using the splitChunks and loaders above. new MiniCssExtractPlugin({ filename: useHMR ? "bundles/[name].css" : "bundles/[hash]/[name].css",