Use & enforce snake_case naming convention on config.json settings (#8062)
* Document and support the established naming convention for config opts This change: * Rename `ConfigOptions` to `IConfigOptions` to match code convention/style, plus move it to a dedicated file * Update comments and surrounding documentation * Define every single documented option (from element-web's config.md) * Enable a linter to enforce the convention * Invent a translation layer for a different change to use * No attempt to fix build errors from doing this (at this stage) * Add demo of lint rule in action * Fix all obvious instances of SdkConfig case conflicts * Fix tests to use SdkConfig directly * Add docs to make unset() calling safer * Appease the linter * Update documentation to match snake_case_config * Fix more instances of square brackets off SdkConfig
This commit is contained in:
parent
09c57b228e
commit
d8a939df5d
56 changed files with 605 additions and 259 deletions
128
docs/settings.md
128
docs/settings.md
|
@ -1,38 +1,38 @@
|
|||
# Settings Reference
|
||||
|
||||
This document serves as developer documentation for using "Granular Settings". Granular Settings allow users to specify
|
||||
different values for a setting at particular levels of interest. For example, a user may say that in a particular room
|
||||
they want URL previews off, but in all other rooms they want them enabled. The `SettingsStore` helps mask the complexity
|
||||
This document serves as developer documentation for using "Granular Settings". Granular Settings allow users to specify
|
||||
different values for a setting at particular levels of interest. For example, a user may say that in a particular room
|
||||
they want URL previews off, but in all other rooms they want them enabled. The `SettingsStore` helps mask the complexity
|
||||
of dealing with the different levels and exposes easy to use getters and setters.
|
||||
|
||||
|
||||
## Levels
|
||||
|
||||
Granular Settings rely on a series of known levels in order to use the correct value for the scenario. These levels, in
|
||||
Granular Settings rely on a series of known levels in order to use the correct value for the scenario. These levels, in
|
||||
order of priority, are:
|
||||
* `device` - The current user's device
|
||||
* `room-device` - The current user's device, but only when in a specific room
|
||||
* `room-account` - The current user's account, but only when in a specific room
|
||||
* `account` - The current user's account
|
||||
* `room` - A specific room (setting for all members of the room)
|
||||
* `config` - Values are defined by the `settingDefaults` key (usually) in `config.json`
|
||||
* `config` - Values are defined by the `setting_defaults` key (usually) in `config.json`
|
||||
* `default` - The hardcoded default for the settings
|
||||
|
||||
Individual settings may control which levels are appropriate for them as part of the defaults. This is often to ensure
|
||||
Individual settings may control which levels are appropriate for them as part of the defaults. This is often to ensure
|
||||
that room administrators cannot force account-only settings upon participants.
|
||||
|
||||
|
||||
## Settings
|
||||
|
||||
Settings are the different options a user may set or experience in the application. These are pre-defined in
|
||||
Settings are the different options a user may set or experience in the application. These are pre-defined in
|
||||
`src/settings/Settings.tsx` under the `SETTINGS` constant, and match the `ISetting` interface as defined there.
|
||||
|
||||
Settings that support the config level can be set in the config file under the `settingDefaults` key (note that some
|
||||
Settings that support the config level can be set in the config file under the `setting_defaults` key (note that some
|
||||
settings, like the "theme" setting, are special cased in the config file):
|
||||
```json
|
||||
```json5
|
||||
{
|
||||
...
|
||||
"settingDefaults": {
|
||||
"setting_defaults": {
|
||||
"settingName": true
|
||||
},
|
||||
...
|
||||
|
@ -41,20 +41,20 @@ settings, like the "theme" setting, are special cased in the config file):
|
|||
|
||||
### Getting values for a setting
|
||||
|
||||
After importing `SettingsStore`, simply make a call to `SettingsStore.getValue`. The `roomId` parameter should always
|
||||
be supplied where possible, even if the setting does not have a per-room level value. This is to ensure that the value
|
||||
After importing `SettingsStore`, simply make a call to `SettingsStore.getValue`. The `roomId` parameter should always
|
||||
be supplied where possible, even if the setting does not have a per-room level value. This is to ensure that the value
|
||||
returned is best represented in the room, particularly if the setting ever gets a per-room level in the future.
|
||||
|
||||
In settings pages it is often desired to have the value at a particular level instead of getting the calculated value.
|
||||
Call `SettingsStore.getValueAt` to get the value of a setting at a particular level, and optionally make it explicitly
|
||||
at that level. By default `getValueAt` will traverse the tree starting at the provided level; making it explicit means
|
||||
it will not go beyond the provided level. When using `getValueAt`, please be sure to use `SettingLevel` to represent the
|
||||
In settings pages it is often desired to have the value at a particular level instead of getting the calculated value.
|
||||
Call `SettingsStore.getValueAt` to get the value of a setting at a particular level, and optionally make it explicitly
|
||||
at that level. By default `getValueAt` will traverse the tree starting at the provided level; making it explicit means
|
||||
it will not go beyond the provided level. When using `getValueAt`, please be sure to use `SettingLevel` to represent the
|
||||
target level.
|
||||
|
||||
### Setting values for a setting
|
||||
|
||||
Values are defined at particular levels and should be done in a safe manner. There are two checks to perform to ensure a
|
||||
clean save: is the level supported and can the user actually set the value. In most cases, neither should be an issue
|
||||
Values are defined at particular levels and should be done in a safe manner. There are two checks to perform to ensure a
|
||||
clean save: is the level supported and can the user actually set the value. In most cases, neither should be an issue
|
||||
although there are circumstances where this changes. An example of a safe call is:
|
||||
```javascript
|
||||
const isSupported = SettingsStore.isLevelSupported(SettingLevel.ROOM);
|
||||
|
@ -66,12 +66,12 @@ if (isSupported) {
|
|||
}
|
||||
```
|
||||
|
||||
These checks may also be performed in different areas of the application to avoid the verbose example above. For
|
||||
These checks may also be performed in different areas of the application to avoid the verbose example above. For
|
||||
instance, the component which allows changing the setting may be hidden conditionally on the above conditions.
|
||||
|
||||
##### `SettingsFlag` component
|
||||
|
||||
Where possible, the `SettingsFlag` component should be used to set simple "flip-a-bit" (true/false) settings. The
|
||||
Where possible, the `SettingsFlag` component should be used to set simple "flip-a-bit" (true/false) settings. The
|
||||
`SettingsFlag` also supports simple radio button options, such as the theme the user would like to use.
|
||||
```html
|
||||
<SettingsFlag name="theSettingId"
|
||||
|
@ -81,7 +81,7 @@ Where possible, the `SettingsFlag` component should be used to set simple "flip-
|
|||
onChange={function(newValue) { }} // optional, called after saving
|
||||
isExplicit={false} // this is passed along to `SettingsStore.getValueAt`, defaulting to false
|
||||
manualSave={false} // if true, saving is delayed. You will need to call .save() on this component
|
||||
|
||||
|
||||
// Options for radio buttons
|
||||
group="your-radio-group" // this enables radio button support
|
||||
value="yourValueHere" // the value for this particular option
|
||||
|
@ -90,7 +90,7 @@ Where possible, the `SettingsFlag` component should be used to set simple "flip-
|
|||
|
||||
### Getting the display name for a setting
|
||||
|
||||
Simply call `SettingsStore.getDisplayName`. The appropriate display name will be returned and automatically translated
|
||||
Simply call `SettingsStore.getDisplayName`. The appropriate display name will be returned and automatically translated
|
||||
for you. If a display name cannot be found, it will return `null`.
|
||||
|
||||
|
||||
|
@ -135,11 +135,11 @@ UI feature is disabled.
|
|||
|
||||
## Setting controllers
|
||||
|
||||
Settings may have environmental factors that affect their value or need additional code to be called when they are
|
||||
modified. A setting controller is able to override the calculated value for a setting and react to changes in that
|
||||
setting. Controllers are not a replacement for the level handlers and should only be used to ensure the environment is
|
||||
kept up to date with the setting where it is otherwise not possible. An example of this is the notification settings:
|
||||
they can only be considered enabled if the platform supports notifications, and enabling notifications requires
|
||||
Settings may have environmental factors that affect their value or need additional code to be called when they are
|
||||
modified. A setting controller is able to override the calculated value for a setting and react to changes in that
|
||||
setting. Controllers are not a replacement for the level handlers and should only be used to ensure the environment is
|
||||
kept up to date with the setting where it is otherwise not possible. An example of this is the notification settings:
|
||||
they can only be considered enabled if the platform supports notifications, and enabling notifications requires
|
||||
additional steps to actually enable notifications.
|
||||
|
||||
For more information, see `src/settings/controllers/SettingController.ts`.
|
||||
|
@ -147,11 +147,11 @@ For more information, see `src/settings/controllers/SettingController.ts`.
|
|||
|
||||
## Local echo
|
||||
|
||||
`SettingsStore` will perform local echo on all settings to ensure that immediately getting values does not cause a
|
||||
split-brain scenario. As mentioned in the "Setting values for a setting" section, the appropriate checks should be done
|
||||
to ensure that the user is allowed to set the value. The local echo system assumes that the user has permission and that
|
||||
the request will go through successfully. The local echo only takes effect until the request to save a setting has
|
||||
completed (either successfully or otherwise).
|
||||
`SettingsStore` will perform local echo on all settings to ensure that immediately getting values does not cause a
|
||||
split-brain scenario. As mentioned in the "Setting values for a setting" section, the appropriate checks should be done
|
||||
to ensure that the user is allowed to set the value. The local echo system assumes that the user has permission and that
|
||||
the request will go through successfully. The local echo only takes effect until the request to save a setting has
|
||||
completed (either successfully or otherwise).
|
||||
|
||||
```javascript
|
||||
SettingsStore.setValue(...).then(() => {
|
||||
|
@ -163,27 +163,27 @@ SettingsStore.getValue(...); // this will return the value set in `setValue` abo
|
|||
|
||||
## Watching for changes
|
||||
|
||||
Most use cases do not need to set up a watcher because they are able to react to changes as they are made, or the
|
||||
changes which are made are not significant enough for it to matter. Watchers are intended to be used in scenarios where
|
||||
it is important to react to changes made by other logged in devices. Typically, this would be done within the component
|
||||
itself, however the component should not be aware of the intricacies of setting inversion or remapping to particular
|
||||
data structures. Instead, a generic watcher interface is provided on `SettingsStore` to watch (and subsequently unwatch)
|
||||
Most use cases do not need to set up a watcher because they are able to react to changes as they are made, or the
|
||||
changes which are made are not significant enough for it to matter. Watchers are intended to be used in scenarios where
|
||||
it is important to react to changes made by other logged in devices. Typically, this would be done within the component
|
||||
itself, however the component should not be aware of the intricacies of setting inversion or remapping to particular
|
||||
data structures. Instead, a generic watcher interface is provided on `SettingsStore` to watch (and subsequently unwatch)
|
||||
for changes in a setting.
|
||||
|
||||
An example of a watcher in action would be:
|
||||
|
||||
```javascript
|
||||
class MyComponent extends React.Component {
|
||||
|
||||
|
||||
settingWatcherRef = null;
|
||||
|
||||
|
||||
componentWillMount() {
|
||||
const callback = (settingName, roomId, level, newValAtLevel, newVal) => {
|
||||
this.setState({color: newVal});
|
||||
};
|
||||
this.settingWatcherRef = SettingsStore.watchSetting("roomColor", "!example:matrix.org", callback);
|
||||
}
|
||||
|
||||
|
||||
componentWillUnmount() {
|
||||
SettingsStore.unwatchSetting(this.settingWatcherRef);
|
||||
}
|
||||
|
@ -193,29 +193,29 @@ class MyComponent extends React.Component {
|
|||
|
||||
# Maintainers Reference
|
||||
|
||||
The granular settings system has a few complex parts to power it. This section is to document how the `SettingsStore` is
|
||||
The granular settings system has a few complex parts to power it. This section is to document how the `SettingsStore` is
|
||||
supposed to work.
|
||||
|
||||
### General information
|
||||
|
||||
The `SettingsStore` uses the hardcoded `LEVEL_ORDER` constant to ensure that it is using the correct override procedure.
|
||||
The array is checked from left to right, simulating the behaviour of overriding values from the higher levels. Each
|
||||
The `SettingsStore` uses the hardcoded `LEVEL_ORDER` constant to ensure that it is using the correct override procedure.
|
||||
The array is checked from left to right, simulating the behaviour of overriding values from the higher levels. Each
|
||||
level should be defined in this array, including `default`.
|
||||
|
||||
Handlers (`src/settings/handlers/SettingsHandler.ts`) represent a single level and are responsible for getting and
|
||||
setting values at that level. Handlers also provide additional information to the `SettingsStore` such as if the level
|
||||
is supported or if the current user may set values at the level. The `SettingsStore` will use the handler to enforce
|
||||
checks and manipulate settings. Handlers are also responsible for dealing with migration patterns or legacy settings for
|
||||
their level (for example, a setting being renamed or using a different key from other settings in the underlying store).
|
||||
Handlers are provided to the `SettingsStore` via the `LEVEL_HANDLERS` constant. `SettingsStore` will optimize lookups by
|
||||
Handlers (`src/settings/handlers/SettingsHandler.ts`) represent a single level and are responsible for getting and
|
||||
setting values at that level. Handlers also provide additional information to the `SettingsStore` such as if the level
|
||||
is supported or if the current user may set values at the level. The `SettingsStore` will use the handler to enforce
|
||||
checks and manipulate settings. Handlers are also responsible for dealing with migration patterns or legacy settings for
|
||||
their level (for example, a setting being renamed or using a different key from other settings in the underlying store).
|
||||
Handlers are provided to the `SettingsStore` via the `LEVEL_HANDLERS` constant. `SettingsStore` will optimize lookups by
|
||||
only considering handlers that are supported on the platform.
|
||||
|
||||
Local echo is achieved through `src/settings/handlers/LocalEchoWrapper.ts` which acts as a wrapper around a given
|
||||
handler. This is automatically applied to all defined `LEVEL_HANDLERS` and proxies the calls to the wrapped handler
|
||||
where possible. The echo is achieved by a simple object cache stored within the class itself. The cache is invalidated
|
||||
Local echo is achieved through `src/settings/handlers/LocalEchoWrapper.ts` which acts as a wrapper around a given
|
||||
handler. This is automatically applied to all defined `LEVEL_HANDLERS` and proxies the calls to the wrapped handler
|
||||
where possible. The echo is achieved by a simple object cache stored within the class itself. The cache is invalidated
|
||||
immediately upon the proxied save call succeeding or failing.
|
||||
|
||||
Controllers are notified of changes by the `SettingsStore`, and are given the opportunity to override values after the
|
||||
Controllers are notified of changes by the `SettingsStore`, and are given the opportunity to override values after the
|
||||
`SettingsStore` has deemed the value calculated. Controllers are invoked as the last possible step in the code.
|
||||
|
||||
### Features
|
||||
|
@ -224,17 +224,17 @@ See above for feature reference.
|
|||
|
||||
### Watchers
|
||||
|
||||
Watchers can appear complicated under the hood: there is a central `WatchManager` which handles the actual invocation
|
||||
of callbacks, and callbacks are managed by the SettingsStore by redirecting the caller's callback to a dedicated
|
||||
callback. This is done so that the caller can reuse the same function as their callback without worrying about whether
|
||||
or not it'll unsubscribe all watchers.
|
||||
Watchers can appear complicated under the hood: there is a central `WatchManager` which handles the actual invocation
|
||||
of callbacks, and callbacks are managed by the SettingsStore by redirecting the caller's callback to a dedicated
|
||||
callback. This is done so that the caller can reuse the same function as their callback without worrying about whether
|
||||
or not it'll unsubscribe all watchers.
|
||||
|
||||
Setting changes are emitted into the default `WatchManager`, which calculates the new value for the setting. Ideally,
|
||||
we'd also try and suppress updates which don't have a consequence on this value, however there's not an easy way to do
|
||||
this. Instead, we just dispatch an update for all changes and leave it up to the consumer to deduplicate.
|
||||
Setting changes are emitted into the default `WatchManager`, which calculates the new value for the setting. Ideally,
|
||||
we'd also try and suppress updates which don't have a consequence on this value, however there's not an easy way to do
|
||||
this. Instead, we just dispatch an update for all changes and leave it up to the consumer to deduplicate.
|
||||
|
||||
In practice, handlers which rely on remote changes (account data, room events, etc) will always attach a listener to the
|
||||
`MatrixClient`. They then watch for changes to events they care about and send off appropriate updates to the
|
||||
generalized `WatchManager` - a class specifically designed to deduplicate the logic of managing watchers. The handlers
|
||||
which are localized to the local client (device) generally just trigger the `WatchManager` when they manipulate the
|
||||
In practice, handlers which rely on remote changes (account data, room events, etc) will always attach a listener to the
|
||||
`MatrixClient`. They then watch for changes to events they care about and send off appropriate updates to the
|
||||
generalized `WatchManager` - a class specifically designed to deduplicate the logic of managing watchers. The handlers
|
||||
which are localized to the local client (device) generally just trigger the `WatchManager` when they manipulate the
|
||||
setting themselves as there's nothing to really 'watch'.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue