Some documentation
Far from complete, and probably needs a bit of work, but it's a start. Signed-off-by: Travis Ralston <travpc@gmail.com>
This commit is contained in:
parent
f070604350
commit
6f8523081b
4 changed files with 128 additions and 16 deletions
108
docs/settings.md
Normal file
108
docs/settings.md
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
# 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 away 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 order of prioirty, 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 `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 that room administrators cannot force account-only settings upon participants.
|
||||||
|
|
||||||
|
|
||||||
|
## `SettingsStore` Usage
|
||||||
|
|
||||||
|
`SettingsStore` has various utility functions exposed to deal with common tasks, such as translations, getting and setting values, and permission to change settings.
|
||||||
|
|
||||||
|
|
||||||
|
### Getting the value of a setting
|
||||||
|
|
||||||
|
For most cases the easiest way to get a setting's value is through `SettingsStore.getValue("the_setting", "!curbf:matrix.org")`. The first argument is the setting name and the second argument is the room ID. The room ID should be included where possible, although it is optional.
|
||||||
|
|
||||||
|
Getting values at particular levels is rare and generally only needed to display information about that level. To get a value at a particular level, use `SettingsStore.getValueAt("room", "the_setting", "!curbf:matrix.org")`. The first argument is the level to read the value at, and the remaining two arguments are just like `getValue()`. This will by default take into consideration any levels that are more generic, if this is undesired (such as when dealing with room-level settings) set the fourth argument (`isExplicit`) to `true`.
|
||||||
|
|
||||||
|
TODO: {Travis} explain `isExplicit` better (when exactly do you use it?)
|
||||||
|
|
||||||
|
|
||||||
|
### Checking to make sure a user can change a setting
|
||||||
|
|
||||||
|
Users often would like to change settings, however they also often need permission to do so. `SettingsStore` exposes simple permission checks to ensure the user is able to change particular settings, allowing the UI to react accordingly. To see if a user can modify a setting, use `SettingsStore.canSetValue("room", "the_setting", "!curbf:matrix.org")`. The first argument is the level to check at, and the other two arguments are just like getting values.
|
||||||
|
|
||||||
|
TODO: {Travis} Also describe how `isLevelSupported` works.
|
||||||
|
|
||||||
|
|
||||||
|
### Setting values for a setting
|
||||||
|
|
||||||
|
Setting values for a setting is as simple as calling `SettingsStore.setValue("the_setting", "!curbf:matrix.org", "room", "the_value")`. The first argument is the setting name and the second is the room ID. Much like getting values, the room ID is optional but should be supplied whenever possible. The third argument is the level to set the value at, and finally the last argument is the value to set. The value may be anything. If the value is set to `null` or `undefined`, the level will become "unset", requiring more generic levels to provide a value for the setting.
|
||||||
|
|
||||||
|
|
||||||
|
### Getting translated names for settings
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
Features (also known as "labs settings") are major components of the SDK which may be undergoing testing before being considered stable and part of the SDK. Features are special cased in Granular Settings to ensure that users do not accidentally get features enabled when they should not be.
|
||||||
|
|
||||||
|
|
||||||
|
### Adding new features
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Checking if a feature is enabled
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Making features enabled
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Forcing features to be enabled or disabled
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
## Adding new settings
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
## `SettingsFlag` Component Usage
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
## Maintainer Documentation
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Handlers
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Level order
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Algorithm
|
||||||
|
|
||||||
|
TODO: {Travis}
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
TODO: {Travis}
|
|
@ -79,7 +79,8 @@ const SIMPLE_SETTINGS = [
|
||||||
{ id: "VideoView.flipVideoHorizontally" },
|
{ id: "VideoView.flipVideoHorizontally" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const ANALYTICS_SETTINGS_LABELS = [
|
// These settings must be defined in SettingsStore
|
||||||
|
const ANALYTICS_SETTINGS = [
|
||||||
{
|
{
|
||||||
id: 'analyticsOptOut',
|
id: 'analyticsOptOut',
|
||||||
fn: function(checked) {
|
fn: function(checked) {
|
||||||
|
@ -88,11 +89,13 @@ const ANALYTICS_SETTINGS_LABELS = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const WEBRTC_SETTINGS_LABELS = [
|
// These settings must be defined in SettingsStore
|
||||||
|
const WEBRTC_SETTINGS = [
|
||||||
{ id: 'webRtcForceTURN' },
|
{ id: 'webRtcForceTURN' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const CRYPTO_SETTINGS_LABELS = [
|
// These settings must be defined in SettingsStore
|
||||||
|
const CRYPTO_SETTINGS = [
|
||||||
{
|
{
|
||||||
id: 'blacklistUnverifiedDevices',
|
id: 'blacklistUnverifiedDevices',
|
||||||
fn: function(checked) {
|
fn: function(checked) {
|
||||||
|
@ -102,9 +105,8 @@ const CRYPTO_SETTINGS_LABELS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
// Enumerate the available themes, with a nice human text label.
|
// Enumerate the available themes, with a nice human text label.
|
||||||
// 'id' gives the key name in the im.vector.web.settings account data event
|
|
||||||
// 'value' is the value for that key in the event
|
|
||||||
// 'label' is how we describe it in the UI.
|
// 'label' is how we describe it in the UI.
|
||||||
|
// 'value' is the value for the theme setting
|
||||||
//
|
//
|
||||||
// XXX: Ideally we would have a theme manifest or something and they'd be nicely
|
// XXX: Ideally we would have a theme manifest or something and they'd be nicely
|
||||||
// packaged up in a single directory, and/or located at the application layer.
|
// packaged up in a single directory, and/or located at the application layer.
|
||||||
|
@ -613,7 +615,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
onLanguageChange: function(newLang) {
|
onLanguageChange: function(newLang) {
|
||||||
if(this.state.language !== newLang) {
|
if(this.state.language !== newLang) {
|
||||||
SettingsStore.setValue("language", null, "device", newLang);
|
// We intentionally promote this to the account level at this point
|
||||||
|
SettingsStore.setValue("language", null, "account", newLang);
|
||||||
this.setState({
|
this.setState({
|
||||||
language: newLang,
|
language: newLang,
|
||||||
});
|
});
|
||||||
|
@ -641,8 +644,8 @@ module.exports = React.createClass({
|
||||||
<div>
|
<div>
|
||||||
<h3>{ _t("User Interface") }</h3>
|
<h3>{ _t("User Interface") }</h3>
|
||||||
<div className="mx_UserSettings_section">
|
<div className="mx_UserSettings_section">
|
||||||
{ SIMPLE_SETTINGS.map( this._renderSyncedSetting ) }
|
{ SIMPLE_SETTINGS.map( this._renderAccountSetting ) }
|
||||||
{ THEMES.map( this._renderThemeSelector ) }
|
{ THEMES.map( this._renderThemeOption ) }
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -663,7 +666,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderSyncedSetting: function(setting) {
|
_renderAccountSetting: function(setting) {
|
||||||
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||||
return (
|
return (
|
||||||
<div className="mx_UserSettings_toggle" key={setting.id}>
|
<div className="mx_UserSettings_toggle" key={setting.id}>
|
||||||
|
@ -675,7 +678,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderThemeSelector: function(setting) {
|
_renderThemeOption: function(setting) {
|
||||||
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||||
const onChange = (v) => dis.dispatch({action: 'set_theme', value: setting.value});
|
const onChange = (v) => dis.dispatch({action: 'set_theme', value: setting.value});
|
||||||
return (
|
return (
|
||||||
|
@ -729,7 +732,7 @@ module.exports = React.createClass({
|
||||||
{ importExportButtons }
|
{ importExportButtons }
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_UserSettings_section">
|
<div className="mx_UserSettings_section">
|
||||||
{ CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) }
|
{ CRYPTO_SETTINGS.map( this._renderDeviceSetting ) }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -755,7 +758,7 @@ module.exports = React.createClass({
|
||||||
} else return (<div />);
|
} else return (<div />);
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderLocalSetting: function(setting) {
|
_renderDeviceSetting: function(setting) {
|
||||||
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||||
return (
|
return (
|
||||||
<div className="mx_UserSettings_toggle" key={setting.id}>
|
<div className="mx_UserSettings_toggle" key={setting.id}>
|
||||||
|
@ -801,7 +804,7 @@ module.exports = React.createClass({
|
||||||
<h3>{ _t('Analytics') }</h3>
|
<h3>{ _t('Analytics') }</h3>
|
||||||
<div className="mx_UserSettings_section">
|
<div className="mx_UserSettings_section">
|
||||||
{ _t('Riot collects anonymous analytics to allow us to improve the application.') }
|
{ _t('Riot collects anonymous analytics to allow us to improve the application.') }
|
||||||
{ ANALYTICS_SETTINGS_LABELS.map( this._renderLocalSetting ) }
|
{ ANALYTICS_SETTINGS.map( this._renderDeviceSetting ) }
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
|
@ -918,6 +921,8 @@ module.exports = React.createClass({
|
||||||
const settings = this.state.electron_settings;
|
const settings = this.state.electron_settings;
|
||||||
if (!settings) return;
|
if (!settings) return;
|
||||||
|
|
||||||
|
// TODO: This should probably be a granular setting, but it only applies to electron
|
||||||
|
// and ends up being get/set outside of matrix anyways (local system setting).
|
||||||
return <div>
|
return <div>
|
||||||
<h3>{ _t('Desktop specific') }</h3>
|
<h3>{ _t('Desktop specific') }</h3>
|
||||||
<div className="mx_UserSettings_section">
|
<div className="mx_UserSettings_section">
|
||||||
|
@ -1040,7 +1045,7 @@ module.exports = React.createClass({
|
||||||
return <div>
|
return <div>
|
||||||
<h3>{ _t('VoIP') }</h3>
|
<h3>{ _t('VoIP') }</h3>
|
||||||
<div className="mx_UserSettings_section">
|
<div className="mx_UserSettings_section">
|
||||||
{ WEBRTC_SETTINGS_LABELS.map(this._renderLocalSetting) }
|
{ WEBRTC_SETTINGS.map(this._renderDeviceSetting) }
|
||||||
{ this._renderWebRtcDeviceSettings() }
|
{ this._renderWebRtcDeviceSettings() }
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -595,6 +595,7 @@ module.exports = React.createClass({
|
||||||
const isGlobalBlacklistUnverified = SettingsStore.getValue("blacklistUnverifiedDevices");
|
const isGlobalBlacklistUnverified = SettingsStore.getValue("blacklistUnverifiedDevices");
|
||||||
const isRoomBlacklistUnverified = this._isRoomBlacklistUnverified();
|
const isRoomBlacklistUnverified = this._isRoomBlacklistUnverified();
|
||||||
|
|
||||||
|
// TODO: {Travis} Convert to blacklistUnverifiedDevices with SettingsFlag
|
||||||
const settings =
|
const settings =
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" ref="blacklistUnverified"
|
<input type="checkbox" ref="blacklistUnverified"
|
||||||
|
|
|
@ -387,8 +387,6 @@ export default class SettingsStore {
|
||||||
throw new Error("Setting " + settingName + " does not have a handler for " + level);
|
throw new Error("Setting " + settingName + " does not have a handler for " + level);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Setting " + settingName +" in " + roomId +" at " + level +" to " + value);
|
|
||||||
|
|
||||||
if (!handler.canSetValue(settingName, roomId)) {
|
if (!handler.canSetValue(settingName, roomId)) {
|
||||||
throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId);
|
throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue