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:
Travis Ralston 2017-10-30 21:09:24 -06:00
parent f070604350
commit 6f8523081b
4 changed files with 128 additions and 16 deletions

108
docs/settings.md Normal file
View 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}

View file

@ -79,7 +79,8 @@ const SIMPLE_SETTINGS = [
{ id: "VideoView.flipVideoHorizontally" },
];
const ANALYTICS_SETTINGS_LABELS = [
// These settings must be defined in SettingsStore
const ANALYTICS_SETTINGS = [
{
id: 'analyticsOptOut',
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' },
];
const CRYPTO_SETTINGS_LABELS = [
// These settings must be defined in SettingsStore
const CRYPTO_SETTINGS = [
{
id: 'blacklistUnverifiedDevices',
fn: function(checked) {
@ -102,9 +105,8 @@ const CRYPTO_SETTINGS_LABELS = [
];
// 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.
// 'value' is the value for the theme setting
//
// 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.
@ -613,7 +615,8 @@ module.exports = React.createClass({
onLanguageChange: function(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({
language: newLang,
});
@ -641,8 +644,8 @@ module.exports = React.createClass({
<div>
<h3>{ _t("User Interface") }</h3>
<div className="mx_UserSettings_section">
{ SIMPLE_SETTINGS.map( this._renderSyncedSetting ) }
{ THEMES.map( this._renderThemeSelector ) }
{ SIMPLE_SETTINGS.map( this._renderAccountSetting ) }
{ THEMES.map( this._renderThemeOption ) }
<table>
<tbody>
<tr>
@ -663,7 +666,7 @@ module.exports = React.createClass({
);
},
_renderSyncedSetting: function(setting) {
_renderAccountSetting: function(setting) {
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
return (
<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 onChange = (v) => dis.dispatch({action: 'set_theme', value: setting.value});
return (
@ -729,7 +732,7 @@ module.exports = React.createClass({
{ importExportButtons }
</div>
<div className="mx_UserSettings_section">
{ CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) }
{ CRYPTO_SETTINGS.map( this._renderDeviceSetting ) }
</div>
</div>
);
@ -755,7 +758,7 @@ module.exports = React.createClass({
} else return (<div />);
},
_renderLocalSetting: function(setting) {
_renderDeviceSetting: function(setting) {
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
return (
<div className="mx_UserSettings_toggle" key={setting.id}>
@ -801,7 +804,7 @@ module.exports = React.createClass({
<h3>{ _t('Analytics') }</h3>
<div className="mx_UserSettings_section">
{ _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>;
},
@ -918,6 +921,8 @@ module.exports = React.createClass({
const settings = this.state.electron_settings;
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>
<h3>{ _t('Desktop specific') }</h3>
<div className="mx_UserSettings_section">
@ -1040,7 +1045,7 @@ module.exports = React.createClass({
return <div>
<h3>{ _t('VoIP') }</h3>
<div className="mx_UserSettings_section">
{ WEBRTC_SETTINGS_LABELS.map(this._renderLocalSetting) }
{ WEBRTC_SETTINGS.map(this._renderDeviceSetting) }
{ this._renderWebRtcDeviceSettings() }
</div>
</div>;

View file

@ -595,6 +595,7 @@ module.exports = React.createClass({
const isGlobalBlacklistUnverified = SettingsStore.getValue("blacklistUnverifiedDevices");
const isRoomBlacklistUnverified = this._isRoomBlacklistUnverified();
// TODO: {Travis} Convert to blacklistUnverifiedDevices with SettingsFlag
const settings =
<label>
<input type="checkbox" ref="blacklistUnverified"

View file

@ -387,8 +387,6 @@ export default class SettingsStore {
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)) {
throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId);
}