Merge pull request #3723 from matrix-org/travis/babel7-reskindex

Implementation of new potential skinning mechanism
This commit is contained in:
Travis Ralston 2019-12-17 08:09:59 -07:00 committed by GitHub
commit d06f476a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1349 additions and 1298 deletions

View File

@ -1,20 +1,26 @@
{ {
"sourceMaps": "inline",
"presets": [ "presets": [
"react", ["@babel/preset-env", {
"es2015", "targets": {
"es2016" "browsers": [
"last 2 versions"
],
"node": 12
},
"modules": "commonjs"
}],
"@babel/preset-typescript",
"@babel/preset-flow",
"@babel/preset-react"
], ],
"plugins": [ "plugins": [
[ ["@babel/plugin-proposal-decorators", { "legacy": true }],
"transform-builtin-extend", "@babel/plugin-proposal-numeric-separator",
{ "@babel/plugin-proposal-class-properties",
"globals": ["Error"] "@babel/plugin-proposal-object-rest-spread",
} "@babel/plugin-transform-flow-comments",
], "@babel/plugin-syntax-dynamic-import",
"transform-class-properties", "@babel/plugin-transform-runtime"
"transform-object-rest-spread",
"transform-runtime",
"add-module-exports",
"syntax-dynamic-import"
] ]
} }

View File

@ -1,85 +1,111 @@
steps: steps:
- label: ":eslint: Lint" - label: ":eslint: JS Lint"
command: command:
- "echo '--- Install js-sdk'" - "echo '--- Install js-sdk'"
- "./scripts/ci/install-deps.sh" - "./scripts/ci/install-deps.sh"
- "yarn lintwithexclusions" - "yarn lint:js"
- "yarn stylelint"
plugins: plugins:
- docker#v3.0.1: - docker#v3.0.1:
image: "node:10" image: "node:12"
- label: ":chains: End-to-End Tests" - label: ":eslint: TS Lint"
agents:
# We use a xlarge sized instance instead of the normal small ones because
# e2e tests otherwise take +-8min
queue: "xlarge"
command: command:
# TODO: Remove hacky chmod for BuildKite
- "echo '--- Setup'"
- "chmod +x ./scripts/ci/*.sh"
- "chmod +x ./scripts/*"
- "echo '--- Install js-sdk'" - "echo '--- Install js-sdk'"
- "./scripts/ci/install-deps.sh" - "./scripts/ci/install-deps.sh"
- "./scripts/ci/end-to-end-tests.sh" - "yarn lint:ts"
plugins: plugins:
- docker#v3.0.1: - docker#v3.0.1:
image: "matrixdotorg/riotweb-ci-e2etests-env:latest" image: "node:12"
propagate-environment: true
- label: ":karma: Tests" - label: ":eslint: Types Lint"
agents:
# We use a medium sized instance instead of the normal small ones because
# webpack loves to gorge itself on resources.
queue: "medium"
command: command:
# Install chrome - "echo '--- Install js-sdk'"
- "echo '--- Installing Chrome'"
- "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -"
- "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
- "apt-get update"
- "apt-get install -y google-chrome-stable"
# Run tests
# TODO: Remove hacky chmod for BuildKite
- "chmod +x ./scripts/ci/*.sh"
- "chmod +x ./scripts/*"
- "echo '--- Installing Dependencies'"
- "./scripts/ci/install-deps.sh" - "./scripts/ci/install-deps.sh"
- "echo '+++ Running Tests'" - "yarn lint:types"
- "./scripts/ci/unit-tests.sh"
env:
CHROME_BIN: "/usr/bin/google-chrome-stable"
plugins: plugins:
- docker#v3.0.1: - docker#v3.0.1:
image: "node:10" image: "node:12"
propagate-environment: true
- label: "🔧 Riot Tests" - label: "🛠 Build"
agents:
# We use a medium sized instance instead of the normal small ones because
# webpack loves to gorge itself on resources.
queue: "medium"
command: command:
# Install chrome - "echo '--- Install js-sdk'"
- "echo '--- Installing Chrome'"
- "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -"
- "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
- "apt-get update"
- "apt-get install -y google-chrome-stable"
# Run tests
# TODO: Remove hacky chmod for BuildKite
- "chmod +x ./scripts/ci/*.sh"
- "chmod +x ./scripts/*"
- "echo '--- Installing Dependencies'"
- "./scripts/ci/install-deps.sh" - "./scripts/ci/install-deps.sh"
- "echo '+++ Running Tests'" - "yarn build"
- "./scripts/ci/riot-unit-tests.sh"
env:
CHROME_BIN: "/usr/bin/google-chrome-stable"
plugins: plugins:
- docker#v3.0.1: - docker#v3.0.1:
image: "node:10" image: "node:12"
propagate-environment: true
# - label: ":chains: End-to-End Tests"
# agents:
# # We use a xlarge sized instance instead of the normal small ones because
# # e2e tests otherwise take +-8min
# queue: "xlarge"
# command:
# # TODO: Remove hacky chmod for BuildKite
# - "echo '--- Setup'"
# - "chmod +x ./scripts/ci/*.sh"
# - "chmod +x ./scripts/*"
# - "echo '--- Install js-sdk'"
# - "./scripts/ci/install-deps.sh"
# - "./scripts/ci/end-to-end-tests.sh"
# plugins:
# - docker#v3.0.1:
# image: "matrixdotorg/riotweb-ci-e2etests-env:latest"
# propagate-environment: true
#
# - label: ":karma: Tests"
# agents:
# # We use a medium sized instance instead of the normal small ones because
# # webpack loves to gorge itself on resources.
# queue: "medium"
# command:
# # Install chrome
# - "echo '--- Installing Chrome'"
# - "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -"
# - "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
# - "apt-get update"
# - "apt-get install -y google-chrome-stable"
# # Run tests
# # TODO: Remove hacky chmod for BuildKite
# - "chmod +x ./scripts/ci/*.sh"
# - "chmod +x ./scripts/*"
# - "echo '--- Installing Dependencies'"
# - "./scripts/ci/install-deps.sh"
# - "echo '+++ Running Tests'"
# - "./scripts/ci/unit-tests.sh"
# env:
# CHROME_BIN: "/usr/bin/google-chrome-stable"
# plugins:
# - docker#v3.0.1:
# image: "node:10"
# propagate-environment: true
#
# - label: "🔧 Riot Tests"
# agents:
# # We use a medium sized instance instead of the normal small ones because
# # webpack loves to gorge itself on resources.
# queue: "medium"
# command:
# # Install chrome
# - "echo '--- Installing Chrome'"
# - "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -"
# - "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
# - "apt-get update"
# - "apt-get install -y google-chrome-stable"
# # Run tests
# # TODO: Remove hacky chmod for BuildKite
# - "chmod +x ./scripts/ci/*.sh"
# - "chmod +x ./scripts/*"
# - "echo '--- Installing Dependencies'"
# - "./scripts/ci/install-deps.sh"
# - "echo '+++ Running Tests'"
# - "./scripts/ci/riot-unit-tests.sh"
# env:
# CHROME_BIN: "/usr/bin/google-chrome-stable"
# plugins:
# - docker#v3.0.1:
# image: "node:10"
# propagate-environment: true
- label: "🌐 i18n" - label: "🌐 i18n"
command: command:

View File

@ -25,6 +25,7 @@ module.exports = {
parserOptions: { parserOptions: {
ecmaFeatures: { ecmaFeatures: {
jsx: true, jsx: true,
legacyDecorators: true,
} }
}, },
rules: { rules: {

View File

@ -67,6 +67,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold:
* After creating a new component you must run `yarn reskindex` to regenerate * After creating a new component you must run `yarn reskindex` to regenerate
the `component-index.js` for the SDK (used in future for skinning) the `component-index.js` for the SDK (used in future for skinning)
<!-- TODO: Remove this once this approach to skinning is replaced -->
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css). * The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
CSS for matrix-react-sdk currently resides in CSS for matrix-react-sdk currently resides in

71
docs/skinning.md Normal file
View File

@ -0,0 +1,71 @@
# Skinning
The react-sdk can be skinned to replace presentation components, CSS, or
other relevant parts of the SDK. Typically consumers will replace entire
components and get the ability for custom CSS as a result.
This doc isn't exhaustive on how skinning works, though it should cover
some of the more complicated parts such as component replacement.
## Loading a skin
1. Generate a `component-index.js` (preferably using the tools that the react-sdk
exposes). This can typically be done with a npm script like `"reskindex -h src/header"`.
2. In your app's entry point, add something like this code:
```javascript
import {loadSkin} from "matrix-react-sdk";
loadSkin(import("component-index").components);
// The rest of your imports go under this.
```
3. Import the remainder of the SDK and bootstrap your app.
It is extremely important that you **do not** import anything else from the
SDK prior to loading your skin as otherwise the skin might not work. Loading
the skin should be one of the first things your app does, if not the very
first thing.
Additionally, **do not** provide `loadSkin` with the react-sdk components
themselves otherwise the app might explode. The SDK is already aware of its
components and doesn't need to be told.
## Replacing components
Components that replace the react-sdk ones MUST have a `replaces` static
key on the component's class to describe which component it overrides. For
example, if your `VectorAuthPage` component is meant to replace the react-sdk
`AuthPage` component then you'd add `static replaces = 'views.auth.AuthPage';`
to the `VectorAuthPage` class.
Other than that, the skin just needs to be loaded normally as mentioned above.
Consumers of the SDK likely will not be interested in the rest of this section.
### SDK developer notes
Components in the react-sdk MUST be decorated with the `@replaceableComponent`
function. For components that can't use the decorator, they must use a
variation that provides similar functionality. The decorator gives consumers
an opportunity to load skinned components by abusing import ordering and
behaviour.
Decorators are executed at import time which is why we can abuse the import
ordering behaviour: importing `loadSkin` doesn't trigger any components to
be imported, allowing the consumer to specify a skin. When the consumer does
import a component (for example, `MatrixChat`), it starts to pull in all the
components via `import` statements. When the components get pulled in the
decorator checks with the skinned components to see if it should be replacing
the component being imported. The decorator then effectively replaces the
components when needed by specifying the skinned component as an override for
the SDK's component, which should in theory override critical functions like
`render()` and lifecycle event handlers.
The decorator also means that older usage of `getComponent()` is no longer
required because components should be replaced by the decorator. Eventually
the react-sdk should only have one usage of `getComponent()`: the decorator.
The decorator assumes that if `getComponent()` returns null that there is
no skinned version of the component and continues on using the SDK's component.
In previous versions of the SDK, the function would throw an error instead
because it also expected the skin to list the SDK's components as well, however
that is no longer possible due to the above.
In short, components should always be `import`ed.

View File

@ -1,39 +0,0 @@
#!/bin/bash
set -e
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm use 10
set -x
scripts/fetchdep.sh matrix-org matrix-js-sdk
pushd matrix-js-sdk
yarn link
yarn install
popd
yarn link matrix-js-sdk
# install the other dependencies
yarn install
# run the mocha tests
yarn test --no-colors
# run eslint
yarn lintall -f checkstyle -o eslint.xml || true
# re-run the linter, excluding any files known to have errors or warnings.
yarn lintwithexclusions
# lint styles
yarn stylelint
# delete the old tarball, if it exists
rm -f matrix-react-sdk-*.tgz
# build our tarball
yarn pack

View File

@ -8,7 +8,7 @@ var fs = require('fs');
* to build everything; however it's the easiest way to load our dependencies * to build everything; however it's the easiest way to load our dependencies
* from node_modules. * from node_modules.
* *
* If you run karma in multi-run mode (with `yarn test-multi`), it will watch * If you run karma in multi-run mode (with `yarn test:multi`), it will watch
* the tests for changes, and webpack will rebuild using a cache. This is much quicker * the tests for changes, and webpack will rebuild using a cache. This is much quicker
* than a clean rebuild. * than a clean rebuild.
*/ */

View File

@ -8,10 +8,7 @@
"url": "https://github.com/matrix-org/matrix-react-sdk" "url": "https://github.com/matrix-org/matrix-react-sdk"
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "lib/index.js",
"files": [ "files": [
".babelrc",
".eslintrc.js",
"CHANGELOG.md", "CHANGELOG.md",
"CONTRIBUTING.rst", "CONTRIBUTING.rst",
"LICENSE", "LICENSE",
@ -19,14 +16,11 @@
"code_style.md", "code_style.md",
"git-revision.txt", "git-revision.txt",
"header", "header",
"jenkins.sh",
"karma.conf.js",
"lib", "lib",
"package.json", "package.json",
"release.sh",
"scripts", "scripts",
"docs",
"src", "src",
"test",
"res" "res"
], ],
"bin": { "bin": {
@ -34,32 +28,33 @@
"matrix-gen-i18n": "scripts/gen-i18n.js", "matrix-gen-i18n": "scripts/gen-i18n.js",
"matrix-prune-i18n": "scripts/prune-i18n.js" "matrix-prune-i18n": "scripts/prune-i18n.js"
}, },
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"scripts": { "scripts": {
"reskindex": "node scripts/reskindex.js -h header",
"reskindex:watch": "node scripts/reskindex.js -h header -w",
"rethemendex": "res/css/rethemendex.sh",
"i18n": "matrix-gen-i18n", "i18n": "matrix-gen-i18n",
"prunei18n": "matrix-prune-i18n", "prunei18n": "matrix-prune-i18n",
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && ./scripts/gen-i18n.js && node scripts/compare-file.js src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json", "diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && ./scripts/gen-i18n.js && node scripts/compare-file.js src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
"build": "yarn reskindex && yarn start:init",
"build:watch": "babel src -w --skip-initial-build -d lib --source-maps --copy-files",
"emoji-data-strip": "node scripts/emoji-data-strip.js", "emoji-data-strip": "node scripts/emoji-data-strip.js",
"start": "yarn start:init && yarn start:all", "reskindex": "node scripts/reskindex.js -h header",
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn build:watch\" \"yarn reskindex:watch\"", "reskindex:watch": "node scripts/reskindex.js -h header -w",
"start:init": "babel src -d lib --source-maps --copy-files", "rethemendex": "res/css/rethemendex.sh",
"lint": "eslint src/",
"lintall": "eslint src/ test/",
"lintwithexclusions": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
"stylelint": "stylelint 'res/css/**/*.scss'",
"clean": "rimraf lib", "clean": "rimraf lib",
"prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt", "build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
"build:compile": "yarn reskindex && babel src -s -d lib --verbose --extensions \".ts,.js\"",
"build:types": "tsc --emitDeclarationOnly",
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:all",
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"",
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
"lint": "yarn lint:types && yarn lint:ts && yarn lint:js && yarn lint:style",
"lint:js": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
"lint:ts": "tslint --project ./tsconfig.json -t stylish",
"lint:types": "tsc --noEmit",
"lint:style": "stylelint 'res/css/**/*.scss'",
"test": "karma start --single-run=true --browsers VectorChromeHeadless", "test": "karma start --single-run=true --browsers VectorChromeHeadless",
"test-multi": "karma start", "test:multi": "karma start",
"e2etests": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080" "test:e2e": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080"
}, },
"dependencies": { "dependencies": {
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-runtime": "^6.26.0",
"blueimp-canvas-to-blob": "^3.5.0", "blueimp-canvas-to-blob": "^3.5.0",
"browser-encrypt-attachment": "^0.3.0", "browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3", "browser-request": "^0.3.3",
@ -115,20 +110,21 @@
"zxcvbn": "^4.4.2" "zxcvbn": "^4.4.2"
}, },
"devDependencies": { "devDependencies": {
"babel-cli": "^6.26.0", "@babel/cli": "^7.7.5",
"babel-core": "^6.26.3", "@babel/core": "^7.7.5",
"babel-eslint": "^10.0.1", "@babel/plugin-proposal-class-properties": "^7.7.4",
"babel-loader": "^7.1.5", "@babel/plugin-proposal-decorators": "^7.7.4",
"babel-plugin-add-module-exports": "^0.2.1", "@babel/plugin-proposal-numeric-separator": "^7.7.4",
"babel-plugin-transform-builtin-extend": "^1.1.2", "@babel/plugin-proposal-object-rest-spread": "^7.7.4",
"babel-plugin-transform-class-properties": "^6.24.1", "@babel/plugin-transform-flow-comments": "^7.7.4",
"babel-plugin-transform-object-rest-spread": "^6.26.0", "@babel/plugin-transform-runtime": "^7.7.6",
"babel-plugin-transform-runtime": "^6.23.0", "@babel/preset-env": "^7.7.6",
"babel-polyfill": "^6.26.0", "@babel/preset-flow": "^7.7.4",
"babel-preset-es2015": "^6.24.1", "@babel/preset-react": "^7.7.4",
"babel-preset-es2016": "^6.24.1", "@babel/preset-typescript": "^7.7.4",
"babel-preset-es2017": "^6.24.1", "@babel/register": "^7.7.4",
"babel-preset-react": "^6.24.1", "@babel/runtime": "^7.7.6",
"babel-eslint": "^10.0.3",
"chokidar": "^2.1.2", "chokidar": "^2.1.2",
"concurrently": "^4.0.1", "concurrently": "^4.0.1",
"eslint": "^5.12.0", "eslint": "^5.12.0",
@ -163,6 +159,8 @@
"stylelint": "^9.10.1", "stylelint": "^9.10.1",
"stylelint-config-standard": "^18.2.0", "stylelint-config-standard": "^18.2.0",
"stylelint-scss": "^3.9.0", "stylelint-scss": "^3.9.0",
"tslint": "^5.20.1",
"typescript": "^3.7.3",
"walk": "^2.3.9", "walk": "^2.3.9",
"webpack": "^4.20.2", "webpack": "^4.20.2",
"webpack-cli": "^3.1.1" "webpack-cli": "^3.1.1"

View File

@ -19,7 +19,6 @@ function reskindex() {
prevFiles = files; prevFiles = files;
var header = args.h || args.header; var header = args.h || args.header;
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
var strm = fs.createWriteStream(componentIndexTmp); var strm = fs.createWriteStream(componentIndexTmp);
@ -34,19 +33,7 @@ function reskindex() {
strm.write(" * so you'd just be trying to swim upstream like a salmon.\n"); strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
strm.write(" * You are not a salmon.\n"); strm.write(" * You are not a salmon.\n");
strm.write(" */\n\n"); strm.write(" */\n\n");
if (packageJson['matrix-react-parent']) {
const parentIndex = packageJson['matrix-react-parent'] +
'/lib/component-index';
strm.write(
`let components = require('${parentIndex}').components;
if (!components) {
throw new Error("'${parentIndex}' didn't export components");
}
`);
} else {
strm.write("let components = {};\n"); strm.write("let components = {};\n");
}
for (var i = 0; i < files.length; ++i) { for (var i = 0; i < files.length; ++i) {
var file = files[i].replace('.js', ''); var file = files[i].replace('.js', '');

View File

@ -48,7 +48,7 @@ interface MatrixClientCreds {
* This module provides a singleton instance of this class so the 'current' * This module provides a singleton instance of this class so the 'current'
* Matrix Client object is available easily. * Matrix Client object is available easily.
*/ */
class MatrixClientPeg { class _MatrixClientPeg {
constructor() { constructor() {
this.matrixClient = null; this.matrixClient = null;
this._justRegisteredUserId = null; this._justRegisteredUserId = null;
@ -245,6 +245,7 @@ class MatrixClientPeg {
} }
if (!global.mxMatrixClientPeg) { if (!global.mxMatrixClientPeg) {
global.mxMatrixClientPeg = new MatrixClientPeg(); global.mxMatrixClientPeg = new _MatrixClientPeg();
} }
export default global.mxMatrixClientPeg; export default global.mxMatrixClientPeg;
export const MatrixClientPeg = global.mxMatrixClientPeg;

View File

@ -20,10 +20,10 @@ import SettingsStore from "./settings/SettingsStore";
import { Service, startTermsFlow, TermsNotSignedError } from './Terms'; import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
const request = require('browser-request'); const request = require('browser-request');
const SdkConfig = require('./SdkConfig');
const MatrixClientPeg = require('./MatrixClientPeg'); const MatrixClientPeg = require('./MatrixClientPeg');
import * as Matrix from 'matrix-js-sdk'; import * as Matrix from 'matrix-js-sdk';
import SdkConfig from "./SdkConfig";
// The version of the integration manager API we're intending to work with // The version of the integration manager API we're intending to work with
const imApiVersion = "1.1"; const imApiVersion = "1.1";

View File

@ -1,5 +1,6 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,7 +15,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
export const DEFAULTS = { export interface ConfigOptions {
[key: string]: any;
}
export const DEFAULTS: ConfigOptions = {
// URL to a page we show in an iframe to configure integrations // URL to a page we show in an iframe to configure integrations
integrations_ui_url: "https://scalar.vector.im/", integrations_ui_url: "https://scalar.vector.im/",
// Base URL to the REST interface of the integrations server // Base URL to the REST interface of the integrations server
@ -23,30 +28,37 @@ export const DEFAULTS = {
bug_report_endpoint_url: null, bug_report_endpoint_url: null,
}; };
class SdkConfig { export default class SdkConfig {
static get() { private static instance: ConfigOptions;
return global.mxReactSdkConfig || {};
private static setInstance(i: ConfigOptions) {
SdkConfig.instance = i;
// For debugging purposes
(<any>window).mxReactSdkConfig = i;
} }
static put(cfg) { static get() {
return SdkConfig.instance || {};
}
static put(cfg: ConfigOptions) {
const defaultKeys = Object.keys(DEFAULTS); const defaultKeys = Object.keys(DEFAULTS);
for (let i = 0; i < defaultKeys.length; ++i) { for (let i = 0; i < defaultKeys.length; ++i) {
if (cfg[defaultKeys[i]] === undefined) { if (cfg[defaultKeys[i]] === undefined) {
cfg[defaultKeys[i]] = DEFAULTS[defaultKeys[i]]; cfg[defaultKeys[i]] = DEFAULTS[defaultKeys[i]];
} }
} }
global.mxReactSdkConfig = cfg; SdkConfig.setInstance(cfg);
} }
static unset() { static unset() {
global.mxReactSdkConfig = undefined; SdkConfig.setInstance({});
} }
static add(cfg) { static add(cfg: ConfigOptions) {
const liveConfig = SdkConfig.get(); const liveConfig = SdkConfig.get();
const newConfig = Object.assign({}, liveConfig, cfg); const newConfig = Object.assign({}, liveConfig, cfg);
SdkConfig.put(newConfig); SdkConfig.put(newConfig);
} }
} }
module.exports = SdkConfig;

View File

@ -28,15 +28,31 @@ class Skinner {
" b) A component has called getComponent at the root level", " b) A component has called getComponent at the root level",
); );
} }
let comp = this.components[name];
const doLookup = (components) => {
if (!components) return null;
let comp = components[name];
// XXX: Temporarily also try 'views.' as we're currently // XXX: Temporarily also try 'views.' as we're currently
// leaving the 'views.' off views. // leaving the 'views.' off views.
if (!comp) { if (!comp) {
comp = this.components['views.'+name]; comp = components['views.' + name];
}
return comp;
};
// Check the skin first
let comp = doLookup(this.components);
// If that failed, check against our own components
if (!comp) {
// Lazily load our own components because they might end up calling .getComponent()
comp = doLookup(require("./component-index").components);
} }
// Just return nothing instead of erroring - the consumer should be smart enough to
// handle this at this point.
if (!comp) { if (!comp) {
throw new Error("No such component: "+name); return null;
} }
// components have to be functions. // components have to be functions.

View File

@ -887,7 +887,7 @@ module.exports = createReactClass({
// rate limited because a power level change will emit an event for every // rate limited because a power level change will emit an event for every
// member in the room. // member in the room.
_updateRoomMembers: new rate_limited_func(function(dueToMember) { _updateRoomMembers: rate_limited_func(function(dueToMember) {
// a member state changed in this room // a member state changed in this room
// refresh the conf call notification state // refresh the conf call notification state
this._updateConfCallNotification(); this._updateConfCallNotification();

View File

@ -26,6 +26,7 @@ import SdkConfig from "../../../SdkConfig";
import PasswordReset from "../../../PasswordReset"; import PasswordReset from "../../../PasswordReset";
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import classNames from 'classnames'; import classNames from 'classnames';
import AuthPage from "../../views/auth/AuthPage";
// Phases // Phases
// Show controls to configure server details // Show controls to configure server details
@ -367,7 +368,6 @@ module.exports = createReactClass({
}, },
render: function() { render: function() {
const AuthPage = sdk.getComponent("auth.AuthPage");
const AuthHeader = sdk.getComponent("auth.AuthHeader"); const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody"); const AuthBody = sdk.getComponent("auth.AuthBody");

View File

@ -26,6 +26,7 @@ import SdkConfig from '../../../SdkConfig';
import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
import classNames from "classnames"; import classNames from "classnames";
import AuthPage from "../../views/auth/AuthPage";
// For validating phone numbers without country codes // For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/; const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
@ -608,7 +609,6 @@ module.exports = createReactClass({
render: function() { render: function() {
const Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
const AuthPage = sdk.getComponent("auth.AuthPage");
const AuthHeader = sdk.getComponent("auth.AuthHeader"); const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody"); const AuthBody = sdk.getComponent("auth.AuthBody");
const loader = this.isBusy() ? <div className="mx_Login_loader"><Loader /></div> : null; const loader = this.isBusy() ? <div className="mx_Login_loader"><Loader /></div> : null;

View File

@ -20,6 +20,7 @@ import PropTypes from 'prop-types';
import sdk from '../../../index'; import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import AuthPage from "../../views/auth/AuthPage";
module.exports = createReactClass({ module.exports = createReactClass({
displayName: 'PostRegistration', displayName: 'PostRegistration',
@ -59,7 +60,6 @@ module.exports = createReactClass({
render: function() { render: function() {
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName'); const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
const AuthPage = sdk.getComponent('auth.AuthPage');
const AuthHeader = sdk.getComponent('auth.AuthHeader'); const AuthHeader = sdk.getComponent('auth.AuthHeader');
const AuthBody = sdk.getComponent("auth.AuthBody"); const AuthBody = sdk.getComponent("auth.AuthBody");
return ( return (

View File

@ -30,6 +30,7 @@ import AutoDiscoveryUtils, {ValidatedServerConfig} from "../../../utils/AutoDisc
import classNames from "classnames"; import classNames from "classnames";
import * as Lifecycle from '../../../Lifecycle'; import * as Lifecycle from '../../../Lifecycle';
import MatrixClientPeg from "../../../MatrixClientPeg"; import MatrixClientPeg from "../../../MatrixClientPeg";
import AuthPage from "../../views/auth/AuthPage";
// Phases // Phases
// Show controls to configure server details // Show controls to configure server details
@ -576,7 +577,6 @@ module.exports = createReactClass({
render: function() { render: function() {
const AuthHeader = sdk.getComponent('auth.AuthHeader'); const AuthHeader = sdk.getComponent('auth.AuthHeader');
const AuthBody = sdk.getComponent("auth.AuthBody"); const AuthBody = sdk.getComponent("auth.AuthBody");
const AuthPage = sdk.getComponent('auth.AuthPage');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let errorText; let errorText;

View File

@ -24,6 +24,7 @@ import Modal from '../../../Modal';
import MatrixClientPeg from "../../../MatrixClientPeg"; import MatrixClientPeg from "../../../MatrixClientPeg";
import {sendLoginRequest} from "../../../Login"; import {sendLoginRequest} from "../../../Login";
import url from 'url'; import url from 'url';
import AuthPage from "../../views/auth/AuthPage";
const LOGIN_VIEW = { const LOGIN_VIEW = {
LOADING: 1, LOADING: 1,
@ -284,7 +285,6 @@ export default class SoftLogout extends React.Component {
} }
render() { render() {
const AuthPage = sdk.getComponent("auth.AuthPage");
const AuthHeader = sdk.getComponent("auth.AuthHeader"); const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody"); const AuthBody = sdk.getComponent("auth.AuthBody");
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');

View File

@ -1,6 +1,7 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 New Vector Ltd Copyright 2019 New Vector Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -16,22 +17,21 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class';
import sdk from '../../../index'; import sdk from '../../../index';
import {replaceableComponent} from "../../../utils/replaceableComponent";
module.exports = createReactClass({ @replaceableComponent("views.auth.AuthPage")
displayName: 'AuthPage', export default class AuthPage extends React.PureComponent {
render() {
render: function() {
const AuthFooter = sdk.getComponent('auth.AuthFooter'); const AuthFooter = sdk.getComponent('auth.AuthFooter');
return ( return (
<div className="mx_AuthPage"> <div className="mx_AuthPage">
<div className="mx_AuthPage_modal"> <div className="mx_AuthPage_modal">
{ this.props.children } {this.props.children}
</div> </div>
<AuthFooter /> <AuthFooter />
</div> </div>
); );
}, }
}); }

View File

@ -17,10 +17,10 @@ limitations under the License.
import React from 'react'; import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import AuthPage from "./AuthPage";
export default class Welcome extends React.PureComponent { export default class Welcome extends React.PureComponent {
render() { render() {
const AuthPage = sdk.getComponent("auth.AuthPage");
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage'); const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
const LanguageSelector = sdk.getComponent('auth.LanguageSelector'); const LanguageSelector = sdk.getComponent('auth.LanguageSelector');

View File

@ -23,9 +23,9 @@ import SdkConfig from '../../../SdkConfig';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import {isValid3pidInvite} from "../../../RoomInvite"; import {isValid3pidInvite} from "../../../RoomInvite";
import rate_limited_func from "../../../ratelimitedfunc";
const MatrixClientPeg = require("../../../MatrixClientPeg"); const MatrixClientPeg = require("../../../MatrixClientPeg");
const sdk = require('../../../index'); const sdk = require('../../../index');
const rate_limited_func = require('../../../ratelimitedfunc');
const CallHandler = require("../../../CallHandler"); const CallHandler = require("../../../CallHandler");
const INITIAL_LOAD_NUM_MEMBERS = 30; const INITIAL_LOAD_NUM_MEMBERS = 30;
@ -187,7 +187,7 @@ module.exports = createReactClass({
} }
}, },
_updateList: new rate_limited_func(function() { _updateList: rate_limited_func(function() {
this._updateListNow(); this._updateListNow();
}, 500), }, 500),

View File

@ -27,7 +27,7 @@ const MatrixClientPeg = require("../../../MatrixClientPeg");
const CallHandler = require('../../../CallHandler'); const CallHandler = require('../../../CallHandler');
const dis = require("../../../dispatcher"); const dis = require("../../../dispatcher");
const sdk = require('../../../index'); const sdk = require('../../../index');
const rate_limited_func = require('../../../ratelimitedfunc'); import rate_limited_func from "../../../ratelimitedfunc";
import * as Rooms from '../../../Rooms'; import * as Rooms from '../../../Rooms';
import DMRoomMap from '../../../utils/DMRoomMap'; import DMRoomMap from '../../../utils/DMRoomMap';
const Receipt = require('../../../utils/Receipt'); const Receipt = require('../../../utils/Receipt');
@ -384,7 +384,7 @@ module.exports = createReactClass({
this._delayedRefreshRoomList(); this._delayedRefreshRoomList();
}, },
_delayedRefreshRoomList: new rate_limited_func(function() { _delayedRefreshRoomList: rate_limited_func(function() {
this.refreshRoomList(); this.refreshRoomList();
}, 500), }, 500),

View File

@ -0,0 +1,40 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import sdk from '../index';
/**
* Replaces a component with a skinned version if a skinned version exists.
* This decorator should only be applied to components which can be skinned. For
* the react-sdk this means all components should be decorated with this.
*
* The decoration works by assuming the skin has been loaded prior to the
* decorator being called. If that's not the case, the developer will find
* out quickly through various amounts of errors and explosions.
*
* For a bit more detail on how this works, see docs/skinning.md
* @param {string} name The dot-path name of the component being replaced.
* @param {React.Component} origComponent The component that can be replaced
* with a skinned version. If no skinned version is available, this component
* will be used.
*/
export function replaceableComponent(name: string, origComponent: React.Component) {
// Decorators return a function to override the class (origComponent). This
// ultimately assumes that `getComponent()` won't throw an error and instead
// return a falsey value like `null` when the skin doesn't have a component.
return () => sdk.getComponent(name) || origComponent;
}

19
tsconfig.json Normal file
View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"moduleResolution": "node",
"target": "es2016",
"noImplicitAny": false,
"sourceMap": false,
"outDir": "./lib",
"declaration": true,
"types": [
"node"
]
},
"include": [
"./src/**/*.ts"
]
}

72
tslint.json Normal file
View File

@ -0,0 +1,72 @@
{
"rules": {
"class-name": false,
"comment-format": [
true
],
"curly": false,
"eofline": false,
"forin": false,
"indent": [
true,
"spaces"
],
"label-position": true,
"max-line-length": false,
"member-access": false,
"member-ordering": [
true,
"static-after-instance",
"variables-before-functions"
],
"no-arg": true,
"no-bitwise": false,
"no-console": false,
"no-construct": true,
"no-debugger": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
"no-inferrable-types": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": false,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"quotemark": false,
"radix": true,
"semicolon": [
"always"
],
"triple-equals": [],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
]
}
}

2030
yarn.lock

File diff suppressed because it is too large Load Diff