You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-07-28 15:22:05 +03:00
Apply prettier formatting
This commit is contained in:
@ -64,8 +64,8 @@ the content string, caret nodes need to be ignored, as they would confuse the mo
|
||||
|
||||
As part of the reconciliation, the caret position is also adjusted to any changes
|
||||
the model made to the input. The caret is passed around in two formats.
|
||||
The model receives the caret *offset* within the content string (which includes
|
||||
The model receives the caret _offset_ within the content string (which includes
|
||||
an atNodeEnd flag to make it unambiguous if it is at a part and or the next part start).
|
||||
The model converts this to a caret *position* internally, which has a partIndex
|
||||
The model converts this to a caret _position_ internally, which has a partIndex
|
||||
and an offset within the part text, which is more natural to work with.
|
||||
From there on, the caret *position* is used, also during reconciliation.
|
||||
From there on, the caret _position_ is used, also during reconciliation.
|
||||
|
@ -1,14 +1,17 @@
|
||||
# Cypress in Element Web
|
||||
|
||||
## Scope of this Document
|
||||
|
||||
This doc is about our Cypress tests in Element Web and how we use Cypress to write tests.
|
||||
It aims to cover:
|
||||
* How to run the tests yourself
|
||||
* How the tests work
|
||||
* How to write great Cypress tests
|
||||
* Visual testing
|
||||
|
||||
- How to run the tests yourself
|
||||
- How the tests work
|
||||
- How to write great Cypress tests
|
||||
- Visual testing
|
||||
|
||||
## Running the Tests
|
||||
|
||||
Our Cypress tests run automatically as part of our CI along with our other tests,
|
||||
on every pull request and on every merge to develop & master.
|
||||
|
||||
@ -43,6 +46,7 @@ yarn run test:cypress:open
|
||||
```
|
||||
|
||||
## How the Tests Work
|
||||
|
||||
Everything Cypress-related lives in the `cypress/` subdirectory of react-sdk
|
||||
as is typical for Cypress tests. Likewise, tests live in `cypress/e2e`.
|
||||
|
||||
@ -68,6 +72,7 @@ with each instance in a separate directory named after its ID. These logs are re
|
||||
at the start of each test run.
|
||||
|
||||
## Writing Tests
|
||||
|
||||
Mostly this is the same advice as for writing any other Cypress test: the Cypress
|
||||
docs are well worth a read if you're not already familiar with Cypress testing, eg.
|
||||
https://docs.cypress.io/guides/references/best-practices. To avoid your tests being
|
||||
@ -75,11 +80,12 @@ flaky it is also recommended to give https://docs.cypress.io/guides/core-concept
|
||||
a read.
|
||||
|
||||
### Getting a Synapse
|
||||
The key difference is in starting Synapse instances. Tests use this plugin via
|
||||
|
||||
The key difference is in starting Synapse instances. Tests use this plugin via
|
||||
`cy.startSynapse()` to provide a Synapse instance to log into:
|
||||
|
||||
```javascript
|
||||
cy.startSynapse("consent").then(result => {
|
||||
cy.startSynapse("consent").then((result) => {
|
||||
synapse = result;
|
||||
});
|
||||
```
|
||||
@ -96,32 +102,38 @@ Synapse instance for each test suite, i.e. in `before()`, and then tear it down
|
||||
|
||||
To later destroy your Synapse you should call `stopSynapse`, passing the SynapseInstance
|
||||
object you received when starting it.
|
||||
|
||||
```javascript
|
||||
cy.stopSynapse(synapse);
|
||||
```
|
||||
|
||||
### Synapse Config Templates
|
||||
|
||||
When a Synapse instance is started, it's given a config generated from one of the config
|
||||
templates in `cypress/plugins/synapsedocker/templates`. There are a couple of special files
|
||||
in these templates:
|
||||
* `homeserver.yaml`:
|
||||
Template substitution happens in this file. Template variables are:
|
||||
* `REGISTRATION_SECRET`: The secret used to register users via the REST API.
|
||||
* `MACAROON_SECRET_KEY`: Generated each time for security
|
||||
* `FORM_SECRET`: Generated each time for security
|
||||
* `PUBLIC_BASEURL`: The localhost url + port combination the synapse is accessible at
|
||||
* `localhost.signing.key`: A signing key is auto-generated and saved to this file.
|
||||
Config templates should not contain a signing key and instead assume that one will exist
|
||||
in this file.
|
||||
|
||||
- `homeserver.yaml`:
|
||||
Template substitution happens in this file. Template variables are:
|
||||
- `REGISTRATION_SECRET`: The secret used to register users via the REST API.
|
||||
- `MACAROON_SECRET_KEY`: Generated each time for security
|
||||
- `FORM_SECRET`: Generated each time for security
|
||||
- `PUBLIC_BASEURL`: The localhost url + port combination the synapse is accessible at
|
||||
- `localhost.signing.key`: A signing key is auto-generated and saved to this file.
|
||||
Config templates should not contain a signing key and instead assume that one will exist
|
||||
in this file.
|
||||
|
||||
All other files in the template are copied recursively to `/data/`, so the file `foo.html`
|
||||
in a template can be referenced in the config as `/data/foo.html`.
|
||||
|
||||
### Logging In
|
||||
|
||||
There exists a basic utility to start the app with a random user already logged in:
|
||||
|
||||
```javascript
|
||||
cy.initTestUser(synapse, "Jeff");
|
||||
```
|
||||
|
||||
It takes the SynapseInstance you received from `startSynapse` and a display name for your test user.
|
||||
This custom command will register a random userId using the registrationSecret with a random password
|
||||
and the given display name. The returned Chainable will contain details about the credentials for if
|
||||
@ -132,20 +144,24 @@ The internals of how this custom command run may be swapped out later,
|
||||
but the signature can be maintained for simpler maintenance.
|
||||
|
||||
### Joining a Room
|
||||
|
||||
Many tests will also want to start with the client in a room, ready to send & receive messages. Best
|
||||
way to do this may be to get an access token for the user and use this to create a room with the REST
|
||||
API before logging the user in. You can make use of `cy.getBot(synapse)` and `cy.getClient()` to do this.
|
||||
|
||||
### Convenience APIs
|
||||
|
||||
We should probably end up with convenience APIs that wrap the synapse creation, logging in and room
|
||||
creation that can be called to set up tests.
|
||||
|
||||
### Using matrix-js-sdk
|
||||
|
||||
Due to the way we run the Cypress tests in CI, at this time you can only use the matrix-js-sdk module
|
||||
exposed on `window.matrixcs`. This has the limitation that it is only accessible with the app loaded.
|
||||
This may be revisited in the future.
|
||||
|
||||
## Good Test Hygiene
|
||||
|
||||
This section mostly summarises general good Cypress testing practice, and should not be news to anyone
|
||||
already familiar with Cypress.
|
||||
|
||||
@ -158,11 +174,11 @@ already familiar with Cypress.
|
||||
1. Avoid explicit waits. `cy.get()` will implicitly wait for the specified element to appear and
|
||||
all assertions are retired until they either pass or time out, so you should never need to
|
||||
manually wait for an element.
|
||||
* For example, for asserting about editing an already-edited message, you can't wait for the
|
||||
- For example, for asserting about editing an already-edited message, you can't wait for the
|
||||
'edited' element to appear as there was already one there, but you can assert that the body
|
||||
of the message is what is should be after the second edit and this assertion will pass once
|
||||
it becomes true. You can then assert that the 'edited' element is still in the DOM.
|
||||
* You can also wait for other things like network requests in the
|
||||
- You can also wait for other things like network requests in the
|
||||
browser to complete (https://docs.cypress.io/guides/guides/network-requests#Waiting).
|
||||
Needing to wait for things can also be because of race conditions in the app itself, which ideally
|
||||
shouldn't be there!
|
||||
@ -171,6 +187,7 @@ This is a small selection - the Cypress best practices guide, linked above, has
|
||||
should generally try to adhere to them.
|
||||
|
||||
## Percy Visual Testing
|
||||
|
||||
We also support visual testing via Percy, this extracts the DOM from Cypress and renders it using custom renderers
|
||||
for Safari, Firefox, Chrome & Edge, allowing us to spot visual regressions before they become release regressions.
|
||||
Each `cy.percySnapshot()` call results in 8 screenshots (4 browsers, 2 sizes) this can quickly be exhausted and
|
||||
@ -178,4 +195,3 @@ so we only run Percy testing on `develop` and PRs which are labelled `X-Needs-Pe
|
||||
|
||||
To record a snapshot use `cy.percySnapshot()`, you may have to pass `percyCSS` into the 2nd argument to hide certain
|
||||
elements which contain dynamic/generated data to avoid them cause false positives in the Percy screenshot diffs.
|
||||
|
||||
|
@ -1,37 +1,38 @@
|
||||
# Composer Features
|
||||
|
||||
## Auto Complete
|
||||
|
||||
- Hitting tab tries to auto-complete the word before the caret as a room member
|
||||
- If no matching name is found, a visual bell is shown
|
||||
- @ + a letter opens auto complete for members starting with the given letter
|
||||
- When inserting a user pill at the start in the composer, a colon and space is appended to the pill
|
||||
- When inserting a user pill anywhere else in composer, only a space is appended to the pill
|
||||
- # + a letter opens auto complete for rooms starting with the given letter
|
||||
- : open auto complete for emoji
|
||||
- Pressing arrow-up/arrow-down while the autocomplete is open navigates between auto complete options
|
||||
- Pressing tab while the autocomplete is open goes to the next autocomplete option,
|
||||
- Hitting tab tries to auto-complete the word before the caret as a room member
|
||||
- If no matching name is found, a visual bell is shown
|
||||
- @ + a letter opens auto complete for members starting with the given letter
|
||||
- When inserting a user pill at the start in the composer, a colon and space is appended to the pill
|
||||
- When inserting a user pill anywhere else in composer, only a space is appended to the pill
|
||||
- # + a letter opens auto complete for rooms starting with the given letter
|
||||
- : open auto complete for emoji
|
||||
- Pressing arrow-up/arrow-down while the autocomplete is open navigates between auto complete options
|
||||
- Pressing tab while the autocomplete is open goes to the next autocomplete option,
|
||||
wrapping around at the end after reverting to the typed text first.
|
||||
|
||||
## Formatting
|
||||
|
||||
- When selecting text, a formatting bar appears above the selection.
|
||||
- The formatting bar allows to format the selected test as:
|
||||
- When selecting text, a formatting bar appears above the selection.
|
||||
- The formatting bar allows to format the selected test as:
|
||||
bold, italic, strikethrough, a block quote, and a code block (inline if no linebreak is selected).
|
||||
- Formatting is applied as markdown syntax.
|
||||
- Hitting ctrl/cmd+B also marks the selected text as bold
|
||||
- Hitting ctrl/cmd+I also marks the selected text as italic
|
||||
- Hitting ctrl/cmd+> also marks the selected text as a blockquote
|
||||
- Formatting is applied as markdown syntax.
|
||||
- Hitting ctrl/cmd+B also marks the selected text as bold
|
||||
- Hitting ctrl/cmd+I also marks the selected text as italic
|
||||
- Hitting ctrl/cmd+> also marks the selected text as a blockquote
|
||||
|
||||
## Misc
|
||||
|
||||
- When hitting the arrow-up button while having the caret at the start in the composer,
|
||||
- When hitting the arrow-up button while having the caret at the start in the composer,
|
||||
the last message sent by the syncing user is edited.
|
||||
- Clicking a display name on an event in the timeline inserts a user pill into the composer
|
||||
- Emoticons (like :-), >:-), :-/, ...) are replaced by emojis while typing if the relevant setting is enabled
|
||||
- Typing in the composer sends typing notifications in the room
|
||||
- Pressing ctrl/mod+z and ctrl/mod+y undoes/redoes modifications
|
||||
- Pressing shift+enter inserts a line break
|
||||
- Pressing enter sends the message.
|
||||
- Choosing "Quote" in the context menu of an event inserts a quote of the event body in the composer.
|
||||
- Choosing "Reply" in the context menu of an event shows a preview above the composer to reply to.
|
||||
- Pressing alt+arrow up/arrow down navigates in previously sent messages, putting them in the composer.
|
||||
- Clicking a display name on an event in the timeline inserts a user pill into the composer
|
||||
- Emoticons (like :-), >:-), :-/, ...) are replaced by emojis while typing if the relevant setting is enabled
|
||||
- Typing in the composer sends typing notifications in the room
|
||||
- Pressing ctrl/mod+z and ctrl/mod+y undoes/redoes modifications
|
||||
- Pressing shift+enter inserts a line break
|
||||
- Pressing enter sends the message.
|
||||
- Choosing "Quote" in the context menu of an event inserts a quote of the event body in the composer.
|
||||
- Choosing "Reply" in the context menu of an event shows a preview above the composer to reply to.
|
||||
- Pressing alt+arrow up/arrow down navigates in previously sent messages, putting them in the composer.
|
||||
|
@ -17,7 +17,7 @@ Let's say we want to close a menu when the correct keys were pressed:
|
||||
```ts
|
||||
const onKeyDown = (ev: KeyboardEvent): void => {
|
||||
let handled = true;
|
||||
const action = getKeyBindingManager().getAccessibilityAction(ev)
|
||||
const action = getKeyBindingManager().getAccessibilityAction(ev);
|
||||
switch (action) {
|
||||
case KeyBindingAction.Escape:
|
||||
closeMenu();
|
||||
@ -26,12 +26,12 @@ const onKeyDown = (ev: KeyboardEvent): void => {
|
||||
handled = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (handled) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Managing keyboard shortcuts
|
||||
|
@ -6,6 +6,7 @@ Each .svg exports a `ReactComponent` at the named export `Icon`.
|
||||
Icons have `role="presentation"` and `aria-hidden` automatically applied. These can be overriden by passing props to the icon component.
|
||||
|
||||
eg
|
||||
|
||||
```
|
||||
import { Icon as FavoriteIcon } from 'res/img/element-icons/favorite.svg';
|
||||
|
||||
|
@ -6,23 +6,25 @@ instructions on setting up Jitsi.
|
||||
The react-sdk wraps all Jitsi call widgets in a local wrapper called `jitsi.html`
|
||||
which takes several parameters:
|
||||
|
||||
*Query string*:
|
||||
* `widgetId`: The ID of the widget. This is needed for communication back to the
|
||||
react-sdk.
|
||||
* `parentUrl`: The URL of the parent window. This is also needed for
|
||||
communication back to the react-sdk.
|
||||
_Query string_:
|
||||
|
||||
*Hash/fragment (formatted as a query string)*:
|
||||
* `conferenceDomain`: The domain to connect Jitsi Meet to.
|
||||
* `conferenceId`: The room or conference ID to connect Jitsi Meet to.
|
||||
* `isAudioOnly`: Boolean for whether this is a voice-only conference. May not
|
||||
be present, should default to `false`.
|
||||
* `displayName`: The display name of the user viewing the widget. May not
|
||||
be present or could be null.
|
||||
* `avatarUrl`: The HTTP(S) URL for the avatar of the user viewing the widget. May
|
||||
not be present or could be null.
|
||||
* `userId`: The MXID of the user viewing the widget. May not be present or could
|
||||
be null.
|
||||
- `widgetId`: The ID of the widget. This is needed for communication back to the
|
||||
react-sdk.
|
||||
- `parentUrl`: The URL of the parent window. This is also needed for
|
||||
communication back to the react-sdk.
|
||||
|
||||
_Hash/fragment (formatted as a query string)_:
|
||||
|
||||
- `conferenceDomain`: The domain to connect Jitsi Meet to.
|
||||
- `conferenceId`: The room or conference ID to connect Jitsi Meet to.
|
||||
- `isAudioOnly`: Boolean for whether this is a voice-only conference. May not
|
||||
be present, should default to `false`.
|
||||
- `displayName`: The display name of the user viewing the widget. May not
|
||||
be present or could be null.
|
||||
- `avatarUrl`: The HTTP(S) URL for the avatar of the user viewing the widget. May
|
||||
not be present or could be null.
|
||||
- `userId`: The MXID of the user viewing the widget. May not be present or could
|
||||
be null.
|
||||
|
||||
The react-sdk will assume that `jitsi.html` is at the path of wherever it is currently
|
||||
being served. For example, `https://develop.element.io/jitsi.html` or `vector://webapp/jitsi.html`.
|
||||
|
@ -10,7 +10,7 @@ implementation, such as the `RoomEchoChamber` (which handles echoable details of
|
||||
|
||||
Anything that can be locally echoed will be provided by the `GenericEchoChamber` implementation.
|
||||
The echo chamber will also need to deal with external changes, and has full control over whether
|
||||
or not something has successfully been echoed.
|
||||
or not something has successfully been echoed.
|
||||
|
||||
An `EchoContext` is provided to echo chambers (usually with a matching type: `RoomEchoContext`
|
||||
gets provided to a `RoomEchoChamber` for example) with details about their intended area of
|
||||
@ -21,7 +21,7 @@ The `EchoStore` manages echo chamber instances, builds contexts, and is generall
|
||||
accessible than the `EchoChamber` class. For separation of concerns, and to try and keep things
|
||||
tidy, this is an intentional design decision.
|
||||
|
||||
**Note**: The local echo stack uses a "whenable" pattern, which is similar to thenables and
|
||||
**Note**: The local echo stack uses a "whenable" pattern, which is similar to thenables and
|
||||
`EventEmitter`. Whenables are ways of actioning a changing condition without having to deal
|
||||
with listeners being torn down. Once the reference count of the Whenable causes garbage collection,
|
||||
the Whenable's listeners will also be torn down. This is accelerated by the `IDestroyable` interface
|
||||
@ -36,4 +36,3 @@ mechanisms.
|
||||
|
||||
The `EchoStore` is responsible for ensuring that the appropriate non-urgent toast (lower left)
|
||||
is set up, where the dialog then drives through the contexts and transactions.
|
||||
|
||||
|
@ -5,11 +5,12 @@ It's so complicated it needs its own README.
|
||||

|
||||
|
||||
Legend:
|
||||
* Orange = External event.
|
||||
* Purple = Deterministic flow.
|
||||
* Green = Algorithm definition.
|
||||
* Red = Exit condition/point.
|
||||
* Blue = Process definition.
|
||||
|
||||
- Orange = External event.
|
||||
- Purple = Deterministic flow.
|
||||
- Green = Algorithm definition.
|
||||
- Red = Exit condition/point.
|
||||
- Blue = Process definition.
|
||||
|
||||
## Algorithms involved
|
||||
|
||||
@ -22,7 +23,6 @@ Behaviour of the overall room list (sticky rooms, etc) are determined by the gen
|
||||
class. Here is where much of the coordination from the room list store is done to figure out which list
|
||||
algorithm to call, instead of having all the logic in the room list store itself.
|
||||
|
||||
|
||||
Tag sorting is effectively the comparator supplied to the list algorithm. This gives the list algorithm
|
||||
the power to decide when and how to apply the tag sorting, if at all. For example, the importance algorithm,
|
||||
later described in this document, heavily uses the list ordering behaviour to break the tag into categories.
|
||||
@ -68,14 +68,14 @@ simply get the manual sorting algorithm applied to them with no further involvem
|
||||
algorithm. There are 4 categories: Red, Grey, Bold, and Idle. Each has their own definition based off
|
||||
relative (perceived) importance to the user:
|
||||
|
||||
* **Red**: The room has unread mentions waiting for the user.
|
||||
* **Grey**: The room has unread notifications waiting for the user. Notifications are simply unread
|
||||
messages which cause a push notification or badge count. Typically, this is the default as rooms get
|
||||
set to 'All Messages'.
|
||||
* **Bold**: The room has unread messages waiting for the user. Essentially this is a grey room without
|
||||
a badge/notification count (or 'Mentions Only'/'Muted').
|
||||
* **Idle**: No useful (see definition of useful above) activity has occurred in the room since the user
|
||||
last read it.
|
||||
- **Red**: The room has unread mentions waiting for the user.
|
||||
- **Grey**: The room has unread notifications waiting for the user. Notifications are simply unread
|
||||
messages which cause a push notification or badge count. Typically, this is the default as rooms get
|
||||
set to 'All Messages'.
|
||||
- **Bold**: The room has unread messages waiting for the user. Essentially this is a grey room without
|
||||
a badge/notification count (or 'Mentions Only'/'Muted').
|
||||
- **Idle**: No useful (see definition of useful above) activity has occurred in the room since the user
|
||||
last read it.
|
||||
|
||||
Conveniently, each tag gets ordered by those categories as presented: red rooms appear above grey, grey
|
||||
above bold, etc.
|
||||
|
@ -8,7 +8,6 @@ During an onscroll event, we check whether we're getting close to the top or bot
|
||||
|
||||
ScrollPanel supports a mode to prevent it shrinking. This is used to prevent a jump when at the bottom of the timeline and people start and stop typing. It gets cleared automatically when 200px above the bottom of the timeline.
|
||||
|
||||
|
||||
## BACAT (Bottom-Aligned, Clipped-At-Top) scrolling
|
||||
|
||||
BACAT scrolling implements a different way of restoring the scroll position in the timeline while tiles out of view are changing height or tiles are being added or removed. It was added in https://github.com/matrix-org/matrix-react-sdk/pull/2842.
|
||||
|
@ -5,23 +5,22 @@ different values for a setting at particular levels of interest. For example, a
|
||||
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
|
||||
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 `setting_defaults` key (usually) in `config.json`
|
||||
* `default` - The hardcoded default for the settings
|
||||
|
||||
- `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 `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
|
||||
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
|
||||
@ -29,6 +28,7 @@ Settings are the different options a user may set or experience in the applicati
|
||||
|
||||
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):
|
||||
|
||||
```json5
|
||||
{
|
||||
...
|
||||
@ -56,13 +56,14 @@ target level.
|
||||
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);
|
||||
if (isSupported) {
|
||||
const canSetValue = SettingsStore.canSetValue("mySetting", "!curbf:matrix.org", SettingLevel.ROOM);
|
||||
if (canSetValue) {
|
||||
SettingsStore.setValue("mySetting", "!curbf:matrix.org", SettingLevel.ROOM, newValue);
|
||||
}
|
||||
const canSetValue = SettingsStore.canSetValue("mySetting", "!curbf:matrix.org", SettingLevel.ROOM);
|
||||
if (canSetValue) {
|
||||
SettingsStore.setValue("mySetting", "!curbf:matrix.org", SettingLevel.ROOM, newValue);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -73,19 +74,14 @@ instance, the component which allows changing the setting may be hidden conditio
|
||||
|
||||
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"
|
||||
level={SettingsLevel.ROOM}
|
||||
roomId="!curbf:matrix.org"
|
||||
label={_td("Your label here")} // optional, if falsey then the `SettingsStore` will be used
|
||||
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
|
||||
/>
|
||||
```html
|
||||
<SettingsFlag name="theSettingId" level={SettingsLevel.ROOM} roomId="!curbf:matrix.org" label={_td("Your label here")}
|
||||
// optional, if falsey then the `SettingsStore` will be used 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 />
|
||||
```
|
||||
|
||||
### Getting the display name for a setting
|
||||
@ -93,16 +89,16 @@ Where possible, the `SettingsFlag` component should be used to set simple "flip-
|
||||
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`.
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
Feature flags are just like regular settings with some underlying semantics for how they are meant to be used. Usually
|
||||
a feature flag is used when a portion of the application is under development or not ready for full release yet, such
|
||||
as new functionality or experimental ideas. In these cases, the feature name *should* be named with the `feature_*`
|
||||
as new functionality or experimental ideas. In these cases, the feature name _should_ be named with the `feature_*`
|
||||
convention and must be tagged with `isFeature: true` in the setting definition. By doing so, the feature will automatically
|
||||
appear in the "labs" section of the user's settings.
|
||||
|
||||
Features can be controlled at the config level using the following structure:
|
||||
|
||||
```json
|
||||
"features": {
|
||||
"feature_lazyloading": true
|
||||
@ -144,7 +140,6 @@ additional steps to actually enable notifications.
|
||||
|
||||
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
|
||||
@ -160,7 +155,6 @@ SettingsStore.setValue(...).then(() => {
|
||||
SettingsStore.getValue(...); // this will return the value set in `setValue` above.
|
||||
```
|
||||
|
||||
|
||||
## 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
|
||||
@ -174,12 +168,11 @@ 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.setState({ color: newVal });
|
||||
};
|
||||
this.settingWatcherRef = SettingsStore.watchSetting("roomColor", "!example:matrix.org", callback);
|
||||
}
|
||||
@ -190,7 +183,6 @@ 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
|
||||
|
@ -13,12 +13,12 @@ It exposes a function over a postMessage API, when sent an object with the match
|
||||
|
||||
```json5
|
||||
{
|
||||
"imgSrc": "", // the src of the image to display in the download link
|
||||
"imgStyle": "", // the style to apply to the image
|
||||
"style": "", // the style to apply to the download link
|
||||
"download": "", // download attribute to pass to the <a/> tag
|
||||
"textContent": "", // the text to put inside the download link
|
||||
"blob": "", // the data blob to wrap in an object url and allow the user to download
|
||||
imgSrc: "", // the src of the image to display in the download link
|
||||
imgStyle: "", // the style to apply to the image
|
||||
style: "", // the style to apply to the download link
|
||||
download: "", // download attribute to pass to the <a/> tag
|
||||
textContent: "", // the text to put inside the download link
|
||||
blob: "", // the data blob to wrap in an object url and allow the user to download
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -4,24 +4,25 @@ Rooms can have a default widget layout to auto-pin certain widgets, make the con
|
||||
sizes, etc. These are defined through the `io.element.widgets.layout` state event (empty state key).
|
||||
|
||||
Full example content:
|
||||
|
||||
```json5
|
||||
{
|
||||
"widgets": {
|
||||
"first-widget-id": {
|
||||
"container": "top",
|
||||
"index": 0,
|
||||
"width": 60,
|
||||
"height": 40
|
||||
widgets: {
|
||||
"first-widget-id": {
|
||||
container: "top",
|
||||
index: 0,
|
||||
width: 60,
|
||||
height: 40,
|
||||
},
|
||||
"second-widget-id": {
|
||||
container: "right",
|
||||
},
|
||||
},
|
||||
"second-widget-id": {
|
||||
"container": "right"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As shown, there are two containers possible for widgets. These containers have different behaviour
|
||||
and interpret the other options differently.
|
||||
and interpret the other options differently.
|
||||
|
||||
## `top` container
|
||||
|
||||
@ -32,7 +33,7 @@ therefore fewer messages can be shown).
|
||||
The `index` for a widget determines which order the widgets show up in from left to right. Widgets
|
||||
without an `index` will show up as the rightmost widgets. Tiebreaks (same `index` or multiple defined
|
||||
without an `index`) are resolved by comparing widget IDs. A maximum of 3 widgets can be in the top
|
||||
container - any which exceed this will be ignored (placed into the `right` container). Smaller numbers
|
||||
container - any which exceed this will be ignored (placed into the `right` container). Smaller numbers
|
||||
represent leftmost widgets.
|
||||
|
||||
The `width` is relative width within the container in percentage points. This will be clamped to a
|
||||
@ -43,7 +44,7 @@ attempt to show them at 33% width each.
|
||||
Note that the client may impose minimum widths on the widgets, such as a 10% minimum to avoid pinning
|
||||
hidden widgets. In general, widgets defined in the 30-70% range each will be free of these restrictions.
|
||||
|
||||
The `height` is not in fact applied per-widget but is recorded per-widget for potential future
|
||||
The `height` is not in fact applied per-widget but is recorded per-widget for potential future
|
||||
capabilities in future containers. The top container will take the tallest `height` and use that for
|
||||
the height of the whole container, and thus all widgets in that container. The `height` is relative
|
||||
to the container, like with `width`, meaning that 100% will consume as much space as the client is
|
||||
|
Reference in New Issue
Block a user