Compare commits
231 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd4de4832c | |||
| 9e74c934a1 | |||
| a056d4916a | |||
| 31630859a2 | |||
| 8cb41f6797 | |||
| c3a8aeca42 | |||
| eaa95fb1e5 | |||
| 8e6bca34d7 | |||
| 1cdffa2c81 | |||
| 8441589ce6 | |||
| b52ba89cec | |||
| b99e1205c4 | |||
| 8d502743a5 | |||
| 732a764ec6 | |||
| 9975786bac | |||
| 89ef4aa6e7 | |||
| 7e82ac3620 | |||
| c3440c506c | |||
| f16ef93cc3 | |||
| b6f3fc5466 | |||
| 6690f59410 | |||
| 65e08b9633 | |||
| 2e916e63f5 | |||
| 94addb6315 | |||
| f81d6b6157 | |||
| 5d544c773d | |||
| 526fbfa8f1 | |||
| b7e33b237b | |||
| 7e3a146240 | |||
| 2395d2856b | |||
| 34ae473e6f | |||
| 656c54ead9 | |||
| c6e21c9c5c | |||
| e71e32122c | |||
| e3b008e19a | |||
| 85cf2a3692 | |||
| c9b700ef6a | |||
| 34fde7d16d | |||
| 5911c4d2db | |||
| dfae72e9af | |||
| 085493d580 | |||
| 5245c7f2ab | |||
| 4ccd649358 | |||
| 32f42d59b1 | |||
| ca618f2bf2 | |||
| a0ae0b3922 | |||
| a09329949a | |||
| b67e360302 | |||
| 3d30ad843f | |||
| d37935dd78 | |||
| 512d5882c9 | |||
| 247deacbb7 | |||
| e79926db6c | |||
| 34a0bd4c38 | |||
| fb820fa9a7 | |||
| 423175f539 | |||
| 007848c42e | |||
| 49e6fd3c60 | |||
| 80129e7483 | |||
| dc74a2326f | |||
| fc0117869d | |||
| 7bca05af64 | |||
| 9b354ba99e | |||
| 194fad7445 | |||
| 18001dc539 | |||
| 78031f2c04 | |||
| 07261fa5d9 | |||
| aa4ffc7bda | |||
| cee7f7a280 | |||
| c7f173bbb2 | |||
| 118bd75a68 | |||
| 9a593f147f | |||
| dfcbdeb002 | |||
| df20365a6d | |||
| 056ed3b3c4 | |||
| 317898d41c | |||
| c8b26eeac4 | |||
| 766d8f0ba4 | |||
| 0fa9f7c609 | |||
| c24b580165 | |||
| cc39ede920 | |||
| 7a4ef7b257 | |||
| 8c52870e07 | |||
| 922dd6cf9c | |||
| 9633532dd4 | |||
| 5abf6b9f20 | |||
| 478550ec93 | |||
| 5f28bc4468 | |||
| 7ed65407e6 | |||
| 97e421306b | |||
| 1ce9e7c6bb | |||
| df4ae597a5 | |||
| 8c512bce9e | |||
| 4775010452 | |||
| 03f4b15c61 | |||
| d8c23c0dcb | |||
| 4ab261b89f | |||
| e057956ede | |||
| 543b9cf0ce | |||
| 591b56d794 | |||
| 7f8375d864 | |||
| 31af4bbeb5 | |||
| 0a11404be2 | |||
| ff723980ac | |||
| 0dfd60ad5e | |||
| 18f57a2100 | |||
| 9b5cb3a631 | |||
| 09e4e4709f | |||
| 00895f00e6 | |||
| c57be7b966 | |||
| 51d94e63f4 | |||
| 6daeec838f | |||
| 53f23939c1 | |||
| 0bbec9e182 | |||
| 101970dcd9 | |||
| 6644151d19 | |||
| 548ffdced1 | |||
| cba4b24b23 | |||
| 3e922c2d41 | |||
| ae6a409cc2 | |||
| 94d79edbd0 | |||
| 4dc331d629 | |||
| 18131735d7 | |||
| 2a51e7a665 | |||
| df7ac77113 | |||
| 1b222249c4 | |||
| 126967cb90 | |||
| 2afa381cae | |||
| 05cbc217a0 | |||
| 54eec40d20 | |||
| 3ab34f911b | |||
| d6e4d0a417 | |||
| fac40f5183 | |||
| ce684a6628 | |||
| 14fac241f7 | |||
| 335579e250 | |||
| c8565be3a5 | |||
| 76e76269cf | |||
| 3c43e2718d | |||
| f2676772c8 | |||
| c9bf4270fc | |||
| 41ddb7660b | |||
| b3e93ffadf | |||
| 582576b1c3 | |||
| 456135a6ec | |||
| c7357952ec | |||
| b796246d9d | |||
| 598e48e39b | |||
| 2e3c349c5e | |||
| 7f4ff352e8 | |||
| b11bff5a5b | |||
| 223bd459f6 | |||
| ed1673c66c | |||
| 0fda43b603 | |||
| d3b63c592e | |||
| a32af7d77c | |||
| 742d942baa | |||
| 73e86bfc5d | |||
| 0a4c41c958 | |||
| 79a699f0be | |||
| 4a2e6a826b | |||
| 95f56f95ec | |||
| 67e75fb7af | |||
| ebda89d1ae | |||
| 5ce5299651 | |||
| 41bd518182 | |||
| 739e94302d | |||
| e54541aecf | |||
| 338c707579 | |||
| 301ab01911 | |||
| 99089c0f5f | |||
| ec124847d7 | |||
| 89ced19874 | |||
| f997b4a1d5 | |||
| def99728e7 | |||
| fab234dccb | |||
| 0c0572948e | |||
| a4e281265f | |||
| ce71de0d78 | |||
| c5afcaeaf7 | |||
| 889bfce65d | |||
| 1f33d76e87 | |||
| d8de23228f | |||
| 579218aafc | |||
| aefdacc566 | |||
| d619495136 | |||
| 036d1da013 | |||
| 4ba8e7e072 | |||
| f6830992ea | |||
| 769a0cb76f | |||
| 766e837775 | |||
| 749f53a22b | |||
| 851b33aac2 | |||
| fc958a3922 | |||
| 2c31b72c52 | |||
| 8decb02027 | |||
| a0fd87c032 | |||
| c0d862c9f0 | |||
| 8143abc9e7 | |||
| af95dcaef6 | |||
| 5b4aedd4be | |||
| d8c0b16d7e | |||
| 909b56d48e | |||
| a5d857945a | |||
| 1a03e534bd | |||
| e623b539c4 | |||
| 2ff6f5f958 | |||
| 1532188d95 | |||
| 04093692c9 | |||
| a96389a3e4 | |||
| 00e7c84a93 | |||
| e0c924870d | |||
| 1a27ad22a7 | |||
| 7029083266 | |||
| a5f0ec7c7d | |||
| e7dcc06855 | |||
| 867ac49b50 | |||
| bfffbea4a0 | |||
| f8b1c124df | |||
| bc9e290c11 | |||
| 777ef83378 | |||
| 24283dcbd5 | |||
| 2113c83679 | |||
| 77508f38bb | |||
| 6c3eb19b74 | |||
| e173d822e8 | |||
| e2d3ace476 | |||
| e27bf04ced | |||
| 65f1b3c976 | |||
| 4529578cd6 | |||
| 6769c96942 |
@@ -0,0 +1,67 @@
|
||||
module.exports = {
|
||||
parser: "babel-eslint",
|
||||
parserOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
}
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
},
|
||||
extends: ["eslint:recommended", "google"],
|
||||
rules: {
|
||||
// rules we've always adhered to or now do
|
||||
"max-len": ["error", {
|
||||
code: 90,
|
||||
ignoreComments: true,
|
||||
}],
|
||||
curly: ["error", "multi-line"],
|
||||
"prefer-const": ["error"],
|
||||
"comma-dangle": ["error", {
|
||||
arrays: "always-multiline",
|
||||
objects: "always-multiline",
|
||||
imports: "always-multiline",
|
||||
exports: "always-multiline",
|
||||
functions: "always-multiline",
|
||||
}],
|
||||
|
||||
// loosen jsdoc requirements a little
|
||||
"require-jsdoc": ["error", {
|
||||
require: {
|
||||
FunctionDeclaration: false,
|
||||
}
|
||||
}],
|
||||
"valid-jsdoc": ["error", {
|
||||
requireParamDescription: false,
|
||||
requireReturn: false,
|
||||
requireReturnDescription: false,
|
||||
}],
|
||||
|
||||
// rules we do not want from eslint-recommended
|
||||
"no-console": ["off"],
|
||||
"no-constant-condition": ["off"],
|
||||
"no-empty": ["error", { "allowEmptyCatch": true }],
|
||||
|
||||
// rules we do not want from the google styleguide
|
||||
"object-curly-spacing": ["off"],
|
||||
"spaced-comment": ["off"],
|
||||
|
||||
// in principle we prefer single quotes, but life is too short
|
||||
quotes: ["off"],
|
||||
|
||||
// rules we'd ideally like to adhere to, but the current
|
||||
// code does not (in most cases because it's still ES5)
|
||||
// we set these to warnings, and assert that the number
|
||||
// of warnings doesn't exceed a given threshold
|
||||
"no-var": ["warn"],
|
||||
"brace-style": ["warn", "1tbs", {"allowSingleLine": true}],
|
||||
"prefer-rest-params": ["warn"],
|
||||
"prefer-spread": ["warn"],
|
||||
"one-var": ["warn"],
|
||||
"padded-blocks": ["warn"],
|
||||
"no-extend-native": ["warn"],
|
||||
"camelcase": ["warn"],
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ lib-cov
|
||||
out
|
||||
reports
|
||||
/dist
|
||||
/lib
|
||||
/specbuild
|
||||
|
||||
# version file and tarball created by 'npm pack'
|
||||
/git-revision.txt
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"node": true,
|
||||
"jasmine": true,
|
||||
|
||||
"nonew": true,
|
||||
"curly": true,
|
||||
"forin": true,
|
||||
"freeze": false,
|
||||
"undef": true,
|
||||
"unused": "vars"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- node # Latest stable version of nodejs.
|
||||
script:
|
||||
- npm run lint
|
||||
- npm run test
|
||||
+172
@@ -1,3 +1,175 @@
|
||||
Changes in [0.7.5-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.5-rc.2) (2017-02-03)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.5-rc.1...v0.7.5-rc.2)
|
||||
|
||||
* Use the device change notifications interface
|
||||
[\#348](https://github.com/matrix-org/matrix-js-sdk/pull/348)
|
||||
* Rewrite the device key query logic
|
||||
[\#347](https://github.com/matrix-org/matrix-js-sdk/pull/347)
|
||||
|
||||
Changes in [0.7.5-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.5-rc.2) (2017-02-03)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.5-rc.1...v0.7.5-rc.2)
|
||||
|
||||
* Use the device change notifications interface
|
||||
[\#348](https://github.com/matrix-org/matrix-js-sdk/pull/348)
|
||||
* Rewrite the device key query logic
|
||||
[\#347](https://github.com/matrix-org/matrix-js-sdk/pull/347)
|
||||
|
||||
Changes in [0.7.5-rc.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.5-rc.2) (2017-02-03)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.5-rc.1...v0.7.5-rc.2)
|
||||
|
||||
* Use the device change notifications interface
|
||||
[\#348](https://github.com/matrix-org/matrix-js-sdk/pull/348)
|
||||
* Rewrite the device key query logic
|
||||
[\#347](https://github.com/matrix-org/matrix-js-sdk/pull/347)
|
||||
|
||||
Changes in [0.7.5-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.5-rc.1) (2017-02-03)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.4...v0.7.5-rc.1)
|
||||
|
||||
* Support for blacklisting unverified devices, both per-room and globally
|
||||
[\#336](https://github.com/matrix-org/matrix-js-sdk/pull/336)
|
||||
* track errors when events can't be sent
|
||||
[\#349](https://github.com/matrix-org/matrix-js-sdk/pull/349)
|
||||
* Factor out device list management
|
||||
[\#346](https://github.com/matrix-org/matrix-js-sdk/pull/346)
|
||||
* Support for warning users when unknown devices show up
|
||||
[\#335](https://github.com/matrix-org/matrix-js-sdk/pull/335)
|
||||
* Enable sourcemaps in browserified distro
|
||||
[\#345](https://github.com/matrix-org/matrix-js-sdk/pull/345)
|
||||
* Record all e2e room settings in localstorage
|
||||
[\#344](https://github.com/matrix-org/matrix-js-sdk/pull/344)
|
||||
* Make Olm work with browserified js-sdk
|
||||
[\#340](https://github.com/matrix-org/matrix-js-sdk/pull/340)
|
||||
* Make browserify a dev dependency
|
||||
[\#339](https://github.com/matrix-org/matrix-js-sdk/pull/339)
|
||||
* Allow single line brace-style
|
||||
[\#338](https://github.com/matrix-org/matrix-js-sdk/pull/338)
|
||||
* Turn on comma-dangle for function calls
|
||||
[\#333](https://github.com/matrix-org/matrix-js-sdk/pull/333)
|
||||
* Add prefer-const
|
||||
[\#331](https://github.com/matrix-org/matrix-js-sdk/pull/331)
|
||||
* Support for importing and exporting megolm sessions
|
||||
[\#326](https://github.com/matrix-org/matrix-js-sdk/pull/326)
|
||||
* Fix linting on all tests
|
||||
[\#329](https://github.com/matrix-org/matrix-js-sdk/pull/329)
|
||||
* Fix ESLint warnings and errors
|
||||
[\#325](https://github.com/matrix-org/matrix-js-sdk/pull/325)
|
||||
* BREAKING CHANGE: Remove WebStorageStore
|
||||
[\#324](https://github.com/matrix-org/matrix-js-sdk/pull/324)
|
||||
|
||||
Changes in [0.7.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.4) (2017-01-16)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.4-rc.1...v0.7.4)
|
||||
|
||||
* Fix non-conference calling
|
||||
|
||||
Changes in [0.7.4-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.4-rc.1) (2017-01-13)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.3...v0.7.4-rc.1)
|
||||
|
||||
* Remove babel-polyfill
|
||||
[\#321](https://github.com/matrix-org/matrix-js-sdk/pull/321)
|
||||
* Update build process for ES6
|
||||
[\#320](https://github.com/matrix-org/matrix-js-sdk/pull/320)
|
||||
* 'babel' is not a babel package anymore
|
||||
[\#319](https://github.com/matrix-org/matrix-js-sdk/pull/319)
|
||||
* Add Babel for ES6 support
|
||||
[\#318](https://github.com/matrix-org/matrix-js-sdk/pull/318)
|
||||
* Move screen sharing check/error
|
||||
[\#317](https://github.com/matrix-org/matrix-js-sdk/pull/317)
|
||||
* release.sh: Bail early if there are uncommitted changes
|
||||
[\#316](https://github.com/matrix-org/matrix-js-sdk/pull/316)
|
||||
|
||||
Changes in [0.7.3](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.3) (2017-01-04)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.2...v0.7.3)
|
||||
|
||||
* User presence list feature
|
||||
[\#310](https://github.com/matrix-org/matrix-js-sdk/pull/310)
|
||||
* Allow clients the ability to set a default local timeout
|
||||
[\#313](https://github.com/matrix-org/matrix-js-sdk/pull/313)
|
||||
* Add API to delete threepid
|
||||
[\#312](https://github.com/matrix-org/matrix-js-sdk/pull/312)
|
||||
|
||||
Changes in [0.7.2](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.2) (2016-12-15)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.1...v0.7.2)
|
||||
|
||||
* Bump to Olm 2.0
|
||||
[\#309](https://github.com/matrix-org/matrix-js-sdk/pull/309)
|
||||
* Sanity check payload length before encrypting
|
||||
[\#307](https://github.com/matrix-org/matrix-js-sdk/pull/307)
|
||||
* Remove dead _sendPingToDevice function
|
||||
[\#308](https://github.com/matrix-org/matrix-js-sdk/pull/308)
|
||||
* Add setRoomDirectoryVisibilityAppService
|
||||
[\#306](https://github.com/matrix-org/matrix-js-sdk/pull/306)
|
||||
* Update release script to do signed releases
|
||||
[\#305](https://github.com/matrix-org/matrix-js-sdk/pull/305)
|
||||
* e2e: Wait for pending device lists
|
||||
[\#304](https://github.com/matrix-org/matrix-js-sdk/pull/304)
|
||||
* Start a new megolm session when devices are blacklisted
|
||||
[\#303](https://github.com/matrix-org/matrix-js-sdk/pull/303)
|
||||
* E2E: Download our own devicelist on startup
|
||||
[\#302](https://github.com/matrix-org/matrix-js-sdk/pull/302)
|
||||
|
||||
Changes in [0.7.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.1) (2016-12-09)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.1-rc.1...v0.7.1)
|
||||
|
||||
No changes
|
||||
|
||||
|
||||
Changes in [0.7.1-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.1-rc.1) (2016-12-05)
|
||||
==========================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.7.0...v0.7.1-rc.1)
|
||||
|
||||
* Avoid NPE when no sessionStore is given
|
||||
[\#300](https://github.com/matrix-org/matrix-js-sdk/pull/300)
|
||||
* Improve decryption error messages
|
||||
[\#299](https://github.com/matrix-org/matrix-js-sdk/pull/299)
|
||||
* Revert "Use native Array.isArray when available."
|
||||
[\#283](https://github.com/matrix-org/matrix-js-sdk/pull/283)
|
||||
* Use native Array.isArray when available.
|
||||
[\#282](https://github.com/matrix-org/matrix-js-sdk/pull/282)
|
||||
|
||||
Changes in [0.7.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.7.0) (2016-11-18)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.6.4...v0.7.0)
|
||||
|
||||
* Avoid a packetstorm of device queries on startup
|
||||
[\#297](https://github.com/matrix-org/matrix-js-sdk/pull/297)
|
||||
* E2E: Check devices to share keys with on each send
|
||||
[\#295](https://github.com/matrix-org/matrix-js-sdk/pull/295)
|
||||
* Apply unknown-keyshare mitigations
|
||||
[\#296](https://github.com/matrix-org/matrix-js-sdk/pull/296)
|
||||
* distinguish unknown users from deviceless users
|
||||
[\#294](https://github.com/matrix-org/matrix-js-sdk/pull/294)
|
||||
* Allow starting client with initialSyncLimit = 0
|
||||
[\#293](https://github.com/matrix-org/matrix-js-sdk/pull/293)
|
||||
* Make timeline-window _unpaginate public and rename to unpaginate
|
||||
[\#289](https://github.com/matrix-org/matrix-js-sdk/pull/289)
|
||||
* Send a STOPPED sync updated after call to stopClient
|
||||
[\#286](https://github.com/matrix-org/matrix-js-sdk/pull/286)
|
||||
* Fix bug in verifying megolm event senders
|
||||
[\#292](https://github.com/matrix-org/matrix-js-sdk/pull/292)
|
||||
* Handle decryption of events after they arrive
|
||||
[\#288](https://github.com/matrix-org/matrix-js-sdk/pull/288)
|
||||
* Fix examples.
|
||||
[\#287](https://github.com/matrix-org/matrix-js-sdk/pull/287)
|
||||
* Add a travis.yml
|
||||
[\#278](https://github.com/matrix-org/matrix-js-sdk/pull/278)
|
||||
* Encrypt all events, including 'm.call.*'
|
||||
[\#277](https://github.com/matrix-org/matrix-js-sdk/pull/277)
|
||||
* Ignore reshares of known megolm sessions
|
||||
[\#276](https://github.com/matrix-org/matrix-js-sdk/pull/276)
|
||||
* Log to the console on unknown session
|
||||
[\#274](https://github.com/matrix-org/matrix-js-sdk/pull/274)
|
||||
* Make it easier for SDK users to wrap prevailing the 'request' function
|
||||
[\#273](https://github.com/matrix-org/matrix-js-sdk/pull/273)
|
||||
|
||||
Changes in [0.6.4](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v0.6.4) (2016-11-04)
|
||||
================================================================================================
|
||||
[Full Changelog](https://github.com/matrix-org/matrix-js-sdk/compare/v0.6.4-rc.2...v0.6.4)
|
||||
|
||||
@@ -65,6 +65,7 @@ Later versions of the SDK will:
|
||||
Usage
|
||||
=====
|
||||
|
||||
|
||||
Conventions
|
||||
-----------
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
There is a script `release.sh` which does the following, but if you need to do
|
||||
a release manually, here are the steps:
|
||||
|
||||
- `git checkout -b release-v0.x.x`
|
||||
- Update `CHANGELOG.md`
|
||||
- `npm version 0.x.x`
|
||||
- Merge `release-v0.x.x` onto `master`.
|
||||
- Push `master`.
|
||||
- Push the tag: `git push --tags`
|
||||
- `npm publish`
|
||||
- Generate documentation: `npm run gendoc` (this outputs HTML to `.jsdoc`)
|
||||
- Copy the documentation from `.jsdoc` to the `gh-pages` branch and update `index.html`
|
||||
- Merge `master` onto `develop`.
|
||||
- Push `develop`.
|
||||
@@ -0,0 +1,31 @@
|
||||
Random notes from Matthew on the two possible approaches for warning users about unexpected
|
||||
unverified devices popping up in their rooms....
|
||||
|
||||
Original idea...
|
||||
================
|
||||
|
||||
Warn when an existing user adds an unknown device to a room.
|
||||
|
||||
Warn when a user joins the room with unverified or unknown devices.
|
||||
|
||||
Warn when you initial sync if the room has any unverified devices in it.
|
||||
^ this is good enough if we're doing local storage.
|
||||
OR, better:
|
||||
Warn when you initial sync if the room has any new undefined devices since you were last there.
|
||||
=> This means persisting the rooms that devices are in, across initial syncs.
|
||||
|
||||
|
||||
Updated idea...
|
||||
===============
|
||||
|
||||
Warn when the user tries to send a message:
|
||||
- If the room has unverified devices which the user has not yet been told about in the context of this room
|
||||
...or in the context of this user? currently all verification is per-user, not per-room.
|
||||
...this should be good enough.
|
||||
|
||||
- so track whether we have warned the user or not about unverified devices - blocked, unverified, verified, unverified_warned.
|
||||
throw an error when trying to encrypt if there are pure unverified devices there
|
||||
app will have to search for the devices which are pure unverified to warn about them - have to do this from MembersList anyway?
|
||||
- or megolm could warn which devices are causing the problems.
|
||||
|
||||
Why do we wait to establish outbound sessions? It just makes a horrible pause when we first try to send a message... but could otherwise unnecessarily consume resources?
|
||||
@@ -1 +1 @@
|
||||
../../../dist/browser-matrix-dev.js
|
||||
../../../dist/browser-matrix.js
|
||||
@@ -135,11 +135,15 @@ rl.on('line', function(line) {
|
||||
// ==== END User input
|
||||
|
||||
// show the room list after syncing.
|
||||
matrixClient.on("syncComplete", function() {
|
||||
setRoomList();
|
||||
printRoomList();
|
||||
printHelp();
|
||||
rl.prompt();
|
||||
matrixClient.on("sync", function(state, prevState, data) {
|
||||
switch (state) {
|
||||
case "PREPARED":
|
||||
setRoomList();
|
||||
printRoomList();
|
||||
printHelp();
|
||||
rl.prompt();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
matrixClient.on("Room", function() {
|
||||
|
||||
@@ -44,7 +44,15 @@ window.onload = function() {
|
||||
disableButtons(true, true, true);
|
||||
};
|
||||
|
||||
client.on("syncComplete", function () {
|
||||
matrixClient.on("sync", function(state, prevState, data) {
|
||||
switch (state) {
|
||||
case "PREPARED":
|
||||
syncComplete();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function syncComplete() {
|
||||
document.getElementById("result").innerHTML = "<p>Ready for calls.</p>";
|
||||
disableButtons(false, true, true);
|
||||
|
||||
@@ -85,5 +93,5 @@ client.on("syncComplete", function () {
|
||||
call = c;
|
||||
addListeners(call);
|
||||
});
|
||||
});
|
||||
}
|
||||
client.startClient();
|
||||
|
||||
@@ -1 +1 @@
|
||||
../../../dist/browser-matrix-dev.js
|
||||
../../../dist/browser-matrix.js
|
||||
+7
-9
@@ -1,9 +1,12 @@
|
||||
#!/bin/bash -l
|
||||
|
||||
set -x
|
||||
|
||||
export NVM_DIR="/home/jenkins/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
||||
nvm use 0.10
|
||||
npm install
|
||||
|
||||
nvm use 6 || exit $?
|
||||
npm install || exit $?
|
||||
|
||||
RC=0
|
||||
|
||||
@@ -14,13 +17,8 @@ function fail {
|
||||
|
||||
npm test || fail "npm test finished with return code $?"
|
||||
|
||||
jshint --reporter=checkstyle -c .jshint lib spec > jshint.xml ||
|
||||
fail "jshint finished with return code $?"
|
||||
|
||||
gjslint --unix_mode --disable 0131,0211,0200,0222,0212 \
|
||||
--max_line_length 90 \
|
||||
-r lib/ -r spec/ > gjslint.log ||
|
||||
fail "gjslint finished with return code $?"
|
||||
npm run -s lint -- -f checkstyle > eslint.xml ||
|
||||
fail "eslint finished with return code $?"
|
||||
|
||||
# delete the old tarball, if it exists
|
||||
rm -f matrix-js-sdk-*.tgz
|
||||
|
||||
@@ -1,527 +0,0 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Defines m.olm encryption/decryption
|
||||
*
|
||||
* @module crypto/algorithms/megolm
|
||||
*/
|
||||
|
||||
var q = require("q");
|
||||
|
||||
var utils = require("../../utils");
|
||||
var olmlib = require("../olmlib");
|
||||
var base = require("./base");
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @constructor
|
||||
*
|
||||
* @param {string} sessionId
|
||||
*
|
||||
* @property {string} sessionId
|
||||
* @property {Number} useCount number of times this session has been used
|
||||
* @property {Number} creationTime when the session was created (ms since the epoch)
|
||||
* @property {module:client.Promise?} sharePromise If a share operation is in progress,
|
||||
* a promise which resolves when it is complete.
|
||||
*/
|
||||
function OutboundSessionInfo(sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
this.useCount = 0;
|
||||
this.creationTime = new Date().getTime();
|
||||
this.sharePromise = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if it's time to rotate the session
|
||||
*
|
||||
* @param {Number} rotationPeriodMsgs
|
||||
* @param {Number} rotationPeriodMs
|
||||
* @return {Boolean}
|
||||
*/
|
||||
OutboundSessionInfo.prototype.needsRotation = function(
|
||||
rotationPeriodMsgs, rotationPeriodMs
|
||||
) {
|
||||
var sessionLifetime = new Date().getTime() - this.creationTime;
|
||||
|
||||
if (this.useCount >= rotationPeriodMsgs ||
|
||||
sessionLifetime >= rotationPeriodMs
|
||||
) {
|
||||
console.log(
|
||||
"Rotating megolm session after " + this.useCount +
|
||||
" messages, " + sessionLifetime + "ms"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Megolm encryption implementation
|
||||
*
|
||||
* @constructor
|
||||
* @extends {module:crypto/algorithms/base.EncryptionAlgorithm}
|
||||
*
|
||||
* @param {object} params parameters, as per
|
||||
* {@link module:crypto/algorithms/base.EncryptionAlgorithm}
|
||||
*/
|
||||
function MegolmEncryption(params) {
|
||||
base.EncryptionAlgorithm.call(this, params);
|
||||
|
||||
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
|
||||
// that even if this is non-null, it may not be ready for use (in which
|
||||
// case _outboundSession.sharePromise will be non-null.)
|
||||
this._outboundSession = null;
|
||||
|
||||
// devices which have joined since we last sent a message.
|
||||
// userId -> {deviceId -> true}, or
|
||||
// userId -> true
|
||||
this._devicesPendingKeyShare = {};
|
||||
|
||||
// default rotation periods
|
||||
this._sessionRotationPeriodMsgs = 100;
|
||||
this._sessionRotationPeriodMs = 7 * 24 * 3600 * 1000;
|
||||
|
||||
if (params.config.rotation_period_ms !== undefined) {
|
||||
this._sessionRotationPeriodMs = params.config.rotation_period_ms;
|
||||
}
|
||||
|
||||
if (params.config.rotation_period_msgs !== undefined) {
|
||||
this._sessionRotationPeriodMsgs = params.config.rotation_period_msgs;
|
||||
}
|
||||
}
|
||||
utils.inherits(MegolmEncryption, base.EncryptionAlgorithm);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {module:models/room} room
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves to the
|
||||
* OutboundSessionInfo when setup is complete.
|
||||
*/
|
||||
MegolmEncryption.prototype._ensureOutboundSession = function(room) {
|
||||
var self = this;
|
||||
|
||||
var session = this._outboundSession;
|
||||
|
||||
// need to make a brand new session?
|
||||
if (!session || session.needsRotation(self._sessionRotationPeriodMsgs,
|
||||
self._sessionRotationPeriodMs)
|
||||
) {
|
||||
this._outboundSession = session = this._prepareNewSession(room);
|
||||
}
|
||||
|
||||
if (session.sharePromise) {
|
||||
// key share already in progress
|
||||
return session.sharePromise;
|
||||
}
|
||||
|
||||
// no share in progress: check for new devices
|
||||
var shareMap = this._devicesPendingKeyShare;
|
||||
this._devicesPendingKeyShare = {};
|
||||
|
||||
// check each user is (still) a member of the room
|
||||
for (var userId in shareMap) {
|
||||
if (!shareMap.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// XXX what about rooms where invitees can see the content?
|
||||
var member = room.getMember(userId);
|
||||
if (member.membership !== "join") {
|
||||
delete shareMap[userId];
|
||||
}
|
||||
}
|
||||
|
||||
session.sharePromise = this._shareKeyWithDevices(
|
||||
session.sessionId, shareMap
|
||||
).finally(function() {
|
||||
session.sharePromise = null;
|
||||
}).then(function() {
|
||||
return session;
|
||||
});
|
||||
|
||||
return session.sharePromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {module:models/room} room
|
||||
*
|
||||
* @return {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
||||
*/
|
||||
MegolmEncryption.prototype._prepareNewSession = function(room) {
|
||||
var session_id = this._olmDevice.createOutboundGroupSession();
|
||||
var key = this._olmDevice.getOutboundGroupSessionKey(session_id);
|
||||
|
||||
this._olmDevice.addInboundGroupSession(
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, session_id,
|
||||
key.key, {ed25519: this._olmDevice.deviceEd25519Key}
|
||||
);
|
||||
|
||||
// we're going to share the key with all current members of the room,
|
||||
// so we can reset this.
|
||||
this._devicesPendingKeyShare = {};
|
||||
|
||||
var session = new OutboundSessionInfo(session_id);
|
||||
|
||||
var roomMembers = utils.map(room.getJoinedMembers(), function(u) {
|
||||
return u.userId;
|
||||
});
|
||||
|
||||
var shareMap = {};
|
||||
for (var i = 0; i < roomMembers.length; i++) {
|
||||
var userId = roomMembers[i];
|
||||
shareMap[userId] = true;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
// TODO: we need to give the user a chance to block any devices or users
|
||||
// before we send them the keys; it's too late to download them here.
|
||||
session.sharePromise = this._crypto.downloadKeys(
|
||||
roomMembers, false
|
||||
).then(function(res) {
|
||||
return self._shareKeyWithDevices(session_id, shareMap);
|
||||
}).then(function() {
|
||||
return session;
|
||||
}).finally(function() {
|
||||
session.sharePromise = null;
|
||||
});
|
||||
|
||||
return session;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {string} session_id
|
||||
*
|
||||
* @param {Object<string, Object<string, boolean>|boolean>} shareMap
|
||||
* Map from userid to either: true (meaning this is a new user in the room,
|
||||
* so all of his devices need the keys); or a map from deviceid to true
|
||||
* (meaning this user has one or more new devices, which need the keys).
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves once the key sharing
|
||||
* message has been sent.
|
||||
*/
|
||||
MegolmEncryption.prototype._shareKeyWithDevices = function(session_id, shareMap) {
|
||||
var self = this;
|
||||
|
||||
var key = this._olmDevice.getOutboundGroupSessionKey(session_id);
|
||||
var payload = {
|
||||
type: "m.room_key",
|
||||
content: {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
room_id: this._roomId,
|
||||
session_id: session_id,
|
||||
session_key: key.key,
|
||||
chain_index: key.chain_index,
|
||||
}
|
||||
};
|
||||
|
||||
// we downloaded the user's device list when they joined the room, or when
|
||||
// the new device announced itself, so there is no need to do so now.
|
||||
|
||||
return self._crypto.ensureOlmSessionsForUsers(
|
||||
utils.keys(shareMap)
|
||||
).then(function(devicemap) {
|
||||
var contentMap = {};
|
||||
var haveTargets = false;
|
||||
|
||||
for (var userId in devicemap) {
|
||||
if (!devicemap.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var devicesToShareWith = shareMap[userId];
|
||||
var sessionResults = devicemap[userId];
|
||||
|
||||
for (var deviceId in sessionResults) {
|
||||
if (!sessionResults.hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (devicesToShareWith === true) {
|
||||
// all devices
|
||||
} else if (!devicesToShareWith[deviceId]) {
|
||||
// not a new device
|
||||
continue;
|
||||
}
|
||||
|
||||
var sessionResult = sessionResults[deviceId];
|
||||
if (!sessionResult.sessionId) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
//
|
||||
// we could send them a to_device message anyway, as a
|
||||
// signal that they have missed out on the key sharing
|
||||
// message because of the lack of keys, but there's not
|
||||
// much point in that really; it will mostly serve to clog
|
||||
// up to_device inboxes.
|
||||
//
|
||||
// ensureOlmSessionsForUsers has already done the logging,
|
||||
// so just skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"sharing keys with device " + userId + ":" + deviceId
|
||||
);
|
||||
|
||||
var deviceInfo = sessionResult.device;
|
||||
|
||||
var encryptedContent = {
|
||||
algorithm: olmlib.OLM_ALGORITHM,
|
||||
sender_key: self._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: {},
|
||||
};
|
||||
|
||||
olmlib.encryptMessageForDevice(
|
||||
encryptedContent.ciphertext,
|
||||
self._userId,
|
||||
self._deviceId,
|
||||
self._olmDevice,
|
||||
userId,
|
||||
deviceInfo,
|
||||
payload
|
||||
);
|
||||
|
||||
if (!contentMap[userId]) {
|
||||
contentMap[userId] = {};
|
||||
}
|
||||
|
||||
contentMap[userId][deviceId] = encryptedContent;
|
||||
haveTargets = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveTargets) {
|
||||
return q();
|
||||
}
|
||||
|
||||
// TODO: retries
|
||||
return self._baseApis.sendToDevice("m.room.encrypted", contentMap);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {module:models/room} room
|
||||
* @param {string} eventType
|
||||
* @param {object} plaintext event content
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||
*/
|
||||
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
var self = this;
|
||||
return this._ensureOutboundSession(room).then(function(session) {
|
||||
var payloadJson = {
|
||||
room_id: self._roomId,
|
||||
type: eventType,
|
||||
content: content
|
||||
};
|
||||
|
||||
var ciphertext = self._olmDevice.encryptGroupMessage(
|
||||
session.sessionId, JSON.stringify(payloadJson)
|
||||
);
|
||||
|
||||
var encryptedContent = {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
sender_key: self._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: ciphertext,
|
||||
session_id: session.sessionId,
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
device_id: self._deviceId,
|
||||
};
|
||||
|
||||
session.useCount++;
|
||||
return encryptedContent;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {module:models/event.MatrixEvent} event event causing the change
|
||||
* @param {module:models/room-member} member user whose membership changed
|
||||
* @param {string=} oldMembership previous membership
|
||||
*/
|
||||
MegolmEncryption.prototype.onRoomMembership = function(event, member, oldMembership) {
|
||||
// if we haven't yet made a session, there's nothing to do here.
|
||||
if (!this._outboundSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newMembership = member.membership;
|
||||
|
||||
if (newMembership === 'join') {
|
||||
this._onNewRoomMember(member.userId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newMembership === 'invite' && oldMembership !== 'join') {
|
||||
// we don't (yet) share keys with invited members, so nothing to do yet
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise we assume the user is leaving, and start a new outbound session.
|
||||
console.log("Discarding outbound megolm session due to change in " +
|
||||
"membership of " + member.userId + " (" + oldMembership +
|
||||
"->" + newMembership + ")");
|
||||
|
||||
// this ensures that we will start a new session on the next message.
|
||||
this._outboundSession = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* handle a new user joining a room
|
||||
*
|
||||
* @param {string} userId new member
|
||||
*/
|
||||
MegolmEncryption.prototype._onNewRoomMember = function(userId) {
|
||||
// make sure we have a list of this user's devices. We are happy to use a
|
||||
// cached version here: we assume that if we already have a list of the
|
||||
// user's devices, then we already share an e2e room with them, which means
|
||||
// that they will have announced any new devices via an m.new_device.
|
||||
this._crypto.downloadKeys([userId], false).done();
|
||||
|
||||
// also flag this user up for needing a keyshare.
|
||||
this._devicesPendingKeyShare[userId] = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {string} userId owner of the device
|
||||
* @param {string} deviceId deviceId of the device
|
||||
*/
|
||||
MegolmEncryption.prototype.onNewDevice = function(userId, deviceId) {
|
||||
var d = this._devicesPendingKeyShare[userId];
|
||||
|
||||
if (d === true) {
|
||||
// we already want to share keys with all devices for this user
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d) {
|
||||
this._devicesPendingKeyShare[userId] = d = {};
|
||||
}
|
||||
|
||||
d[deviceId] = true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Megolm decryption implementation
|
||||
*
|
||||
* @constructor
|
||||
* @extends {module:crypto/algorithms/base.DecryptionAlgorithm}
|
||||
*
|
||||
* @param {object} params parameters, as per
|
||||
* {@link module:crypto/algorithms/base.DecryptionAlgorithm}
|
||||
*/
|
||||
function MegolmDecryption(params) {
|
||||
base.DecryptionAlgorithm.call(this, params);
|
||||
}
|
||||
utils.inherits(MegolmDecryption, base.DecryptionAlgorithm);
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {object} event raw event
|
||||
*
|
||||
* @return {null} The event referred to an unknown megolm session
|
||||
* @return {module:crypto.DecryptionResult} decryption result
|
||||
*
|
||||
* @throws {module:crypto/algorithms/base.DecryptionError} if there is a
|
||||
* problem decrypting the event
|
||||
*/
|
||||
MegolmDecryption.prototype.decryptEvent = function(event) {
|
||||
var content = event.content;
|
||||
|
||||
if (!content.sender_key || !content.session_id ||
|
||||
!content.ciphertext
|
||||
) {
|
||||
throw new base.DecryptionError("Missing fields in input");
|
||||
}
|
||||
|
||||
var res;
|
||||
try {
|
||||
res = this._olmDevice.decryptGroupMessage(
|
||||
event.room_id, content.sender_key, content.session_id, content.ciphertext
|
||||
);
|
||||
} catch (e) {
|
||||
throw new base.DecryptionError(e);
|
||||
}
|
||||
|
||||
if (res === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var payload = JSON.parse(res.result);
|
||||
|
||||
// belt-and-braces check that the room id matches that indicated by the HS
|
||||
// (this is somewhat redundant, since the megolm session is scoped to the
|
||||
// room, so neither the sender nor a MITM can lie about the room_id).
|
||||
if (payload.room_id !== event.room_id) {
|
||||
throw new base.DecryptionError(
|
||||
"Message intended for room " + payload.room_id
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
payload: payload,
|
||||
keysClaimed: res.keysClaimed,
|
||||
keysProved: res.keysProved,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {module:models/event.MatrixEvent} event key event
|
||||
*/
|
||||
MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
||||
console.log("Adding key from ", event);
|
||||
var content = event.getContent();
|
||||
|
||||
if (!content.room_id ||
|
||||
!content.session_id ||
|
||||
!content.session_key
|
||||
) {
|
||||
console.error("key event is missing fields");
|
||||
return;
|
||||
}
|
||||
|
||||
this._olmDevice.addInboundGroupSession(
|
||||
content.room_id, event.getSenderKey(), content.session_id,
|
||||
content.session_key, event.getKeysClaimed()
|
||||
);
|
||||
};
|
||||
|
||||
base.registerAlgorithm(
|
||||
olmlib.MEGOLM_ALGORITHM, MegolmEncryption, MegolmDecryption
|
||||
);
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module olmlib
|
||||
*
|
||||
* Utilities common to olm encryption algorithms
|
||||
*/
|
||||
|
||||
var utils = require("../utils");
|
||||
|
||||
/**
|
||||
* matrix algorithm tag for olm
|
||||
*/
|
||||
module.exports.OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
||||
|
||||
/**
|
||||
* matrix algorithm tag for megolm
|
||||
*/
|
||||
module.exports.MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt an event payload for an Olm device
|
||||
*
|
||||
* @param {Object<string, string>} resultsObject The `ciphertext` property
|
||||
* of the m.room.encrypted event to which to add our result
|
||||
*
|
||||
* @param {string} ourUserId
|
||||
* @param {string} ourDeviceId
|
||||
* @param {module:crypto/OlmDevice} olmDevice olm.js wrapper
|
||||
* @param {string} recipientUserId
|
||||
* @param {module:crypto/deviceinfo} recipientDevice
|
||||
* @param {object} payloadFields fields to include in the encrypted payload
|
||||
*/
|
||||
module.exports.encryptMessageForDevice = function(
|
||||
resultsObject,
|
||||
ourUserId, ourDeviceId, olmDevice, recipientUserId, recipientDevice,
|
||||
payloadFields
|
||||
) {
|
||||
var deviceKey = recipientDevice.getIdentityKey();
|
||||
var sessionId = olmDevice.getSessionIdForDevice(deviceKey);
|
||||
if (sessionId === null) {
|
||||
// If we don't have a session for a device then
|
||||
// we can't encrypt a message for it.
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"Using sessionid " + sessionId + " for device " +
|
||||
recipientUserId + ":" + recipientDevice.deviceId
|
||||
);
|
||||
|
||||
var payload = {
|
||||
sender: ourUserId,
|
||||
sender_device: ourDeviceId,
|
||||
|
||||
// Include the Ed25519 key so that the recipient knows what
|
||||
// device this message came from.
|
||||
// We don't need to include the curve25519 key since the
|
||||
// recipient will already know this from the olm headers.
|
||||
// When combined with the device keys retrieved from the
|
||||
// homeserver signed by the ed25519 key this proves that
|
||||
// the curve25519 key and the ed25519 key are owned by
|
||||
// the same device.
|
||||
keys: {
|
||||
"ed25519": olmDevice.deviceEd25519Key,
|
||||
},
|
||||
|
||||
// include the recipient device details in the payload,
|
||||
// to avoid unknown key attacks, per
|
||||
// https://github.com/vector-im/vector-web/issues/2483
|
||||
recipient: recipientUserId,
|
||||
recipient_keys: {
|
||||
"ed25519": recipientDevice.getFingerprint(),
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: technically, a bunch of that stuff only needs to be included for
|
||||
// pre-key messages: after that, both sides know exactly which devices are
|
||||
// involved in the session. If we're looking to reduce data transfer in the
|
||||
// future, we could elide them for subsequent messages.
|
||||
|
||||
utils.extend(payload, payloadFields);
|
||||
|
||||
resultsObject[deviceKey] = olmDevice.encryptMessage(
|
||||
deviceKey, sessionId, JSON.stringify(payload)
|
||||
);
|
||||
};
|
||||
@@ -1,686 +0,0 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
"use strict";
|
||||
/**
|
||||
* This is an internal module. Implementation details:
|
||||
* <pre>
|
||||
* Room data is stored as follows:
|
||||
* room_$ROOMID_timeline_$INDEX : [ Event, Event, Event ]
|
||||
* room_$ROOMID_state : {
|
||||
* pagination_token: <oldState.paginationToken>,
|
||||
* events: {
|
||||
* <event_type>: { <state_key> : {JSON} }
|
||||
* }
|
||||
* }
|
||||
* User data is stored as follows:
|
||||
* user_$USERID : User
|
||||
* Sync token:
|
||||
* sync_token : $TOKEN
|
||||
*
|
||||
* Room Retrieval
|
||||
* --------------
|
||||
* Retrieving a room requires the $ROOMID which then pulls out the current state
|
||||
* from room_$ROOMID_state. A defined starting batch of timeline events are then
|
||||
* extracted from the highest numbered $INDEX for room_$ROOMID_timeline_$INDEX
|
||||
* (more indices as required). The $INDEX may be negative. These are
|
||||
* added to the timeline in the same way as /initialSync (old state will diverge).
|
||||
* If there exists a room_$ROOMID_timeline_live key, then a timeline sync should
|
||||
* be performed before retrieving.
|
||||
*
|
||||
* Retrieval of earlier messages
|
||||
* -----------------------------
|
||||
* The earliest event the Room instance knows about is E. Retrieving earlier
|
||||
* messages requires a Room which has a storageToken defined.
|
||||
* This token maps to the index I where the Room is at. Events are then retrieved from
|
||||
* room_$ROOMID_timeline_{I} and elements before E are extracted. If the limit
|
||||
* demands more events, I-1 is retrieved, up until I=min $INDEX where it gives
|
||||
* less than the limit. Index may go negative if you have paginated in the past.
|
||||
*
|
||||
* Full Insertion
|
||||
* --------------
|
||||
* Storing a room requires the timeline and state keys for $ROOMID to
|
||||
* be blown away and completely replaced, which is computationally expensive.
|
||||
* Room.timeline is batched according to the given batch size B. These batches
|
||||
* are then inserted into storage as room_$ROOMID_timeline_$INDEX. Finally,
|
||||
* the current room state is persisted to room_$ROOMID_state.
|
||||
*
|
||||
* Incremental Insertion
|
||||
* ---------------------
|
||||
* As events arrive, the store can quickly persist these new events. This
|
||||
* involves pushing the events to room_$ROOMID_timeline_live. If the
|
||||
* current room state has been modified by the new event, then
|
||||
* room_$ROOMID_state should be updated in addition to the timeline.
|
||||
*
|
||||
* Timeline sync
|
||||
* -------------
|
||||
* Retrieval of events from the timeline depends on the proper batching of
|
||||
* events. This is computationally expensive to perform on every new event, so
|
||||
* is deferred by inserting live events to room_$ROOMID_timeline_live. A
|
||||
* timeline sync reconciles timeline_live and timeline_$INDEX. This involves
|
||||
* retrieving _live and the highest numbered $INDEX batch. If the batch is < B,
|
||||
* the earliest entries from _live are inserted into the $INDEX until the
|
||||
* batch == B. Then, the remaining entries in _live are batched to $INDEX+1,
|
||||
* $INDEX+2, and so on. The easiest way to visualise this is that the timeline
|
||||
* goes from old to new, left to right:
|
||||
* -2 -1 0 1
|
||||
* <--OLD---------------------------------------NEW-->
|
||||
* [a,b,c] [d,e,f] [g,h,i] [j,k,l]
|
||||
*
|
||||
* Purging
|
||||
* -------
|
||||
* Events from the timeline can be purged by removing the lowest
|
||||
* timeline_$INDEX in the store.
|
||||
*
|
||||
* Example
|
||||
* -------
|
||||
* A room with room_id !foo:bar has 9 messages (M1->9 where 9=newest) with a
|
||||
* batch size of 4. The very first time, there is no entry for !foo:bar until
|
||||
* storeRoom() is called, which results in the keys: [Full Insert]
|
||||
* room_!foo:bar_timeline_0 : [M1, M2, M3, M4]
|
||||
* room_!foo:bar_timeline_1 : [M5, M6, M7, M8]
|
||||
* room_!foo:bar_timeline_2 : [M9]
|
||||
* room_!foo:bar_state: { ... }
|
||||
*
|
||||
* 5 new messages (N1-5, 5=newest) arrive and are then added: [Incremental Insert]
|
||||
* room_!foo:bar_timeline_live: [N1]
|
||||
* room_!foo:bar_timeline_live: [N1, N2]
|
||||
* room_!foo:bar_timeline_live: [N1, N2, N3]
|
||||
* room_!foo:bar_timeline_live: [N1, N2, N3, N4]
|
||||
* room_!foo:bar_timeline_live: [N1, N2, N3, N4, N5]
|
||||
*
|
||||
* App is shutdown. Restarts. The timeline is synced [Timeline Sync]
|
||||
* room_!foo:bar_timeline_2 : [M9, N1, N2, N3]
|
||||
* room_!foo:bar_timeline_3 : [N4, N5]
|
||||
* room_!foo:bar_timeline_live: []
|
||||
*
|
||||
* And the room is retrieved with 8 messages: [Room Retrieval]
|
||||
* Room.timeline: [M7, M8, M9, N1, N2, N3, N4, N5]
|
||||
* Room.storageToken: => early_index = 1 because that's where M7 is.
|
||||
*
|
||||
* 3 earlier messages are requested: [Earlier retrieval]
|
||||
* Use storageToken to find batch index 1. Scan batch for earliest event ID.
|
||||
* earliest event = M7
|
||||
* events = room_!foo:bar_timeline_1 where event < M7 = [M5, M6]
|
||||
* Too few events, use next index (0) and get 1 more:
|
||||
* events = room_!foo:bar_timeline_0 = [M1, M2, M3, M4] => [M4]
|
||||
* Return concatentation:
|
||||
* [M4, M5, M6]
|
||||
*
|
||||
* Purge oldest events: [Purge]
|
||||
* del room_!foo:bar_timeline_0
|
||||
* </pre>
|
||||
* @module store/webstorage
|
||||
*/
|
||||
var DEBUG = false; // set true to enable console logging.
|
||||
var utils = require("../utils");
|
||||
var Room = require("../models/room");
|
||||
var User = require("../models/user");
|
||||
var MatrixEvent = require("../models/event").MatrixEvent;
|
||||
|
||||
/**
|
||||
* Construct a web storage store, capable of storing rooms and users.
|
||||
* @constructor
|
||||
* @param {WebStorage} webStore A web storage implementation, e.g.
|
||||
* 'window.localStorage' or 'window.sessionStorage' or a custom implementation.
|
||||
* @param {integer} batchSize The number of events to store per key/value (room
|
||||
* scoped). Use -1 to store all events for a room under one key/value.
|
||||
* @throws if the supplied 'store' does not meet the Storage interface of the
|
||||
* WebStorage API.
|
||||
*/
|
||||
function WebStorageStore(webStore, batchSize) {
|
||||
this.store = webStore;
|
||||
this.batchSize = batchSize;
|
||||
if (!utils.isFunction(webStore.getItem) || !utils.isFunction(webStore.setItem) ||
|
||||
!utils.isFunction(webStore.removeItem) || !utils.isFunction(webStore.key)) {
|
||||
throw new Error(
|
||||
"Supplied webStore does not meet the WebStorage API interface"
|
||||
);
|
||||
}
|
||||
if (!parseInt(webStore.length) && webStore.length !== 0) {
|
||||
throw new Error(
|
||||
"Supplied webStore does not meet the WebStorage API interface (length)"
|
||||
);
|
||||
}
|
||||
// cached list of room_ids this is storing.
|
||||
this._roomIds = [];
|
||||
this._syncedWithStore = false;
|
||||
// tokens used to remember which index the room instance is at.
|
||||
this._tokens = [
|
||||
// { earliestIndex: -4 }
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the token to stream from.
|
||||
* @return {string} The token or null.
|
||||
*/
|
||||
WebStorageStore.prototype.getSyncToken = function() {
|
||||
return this.store.getItem("sync_token");
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the token to stream from.
|
||||
* @param {string} token The token to stream from.
|
||||
*/
|
||||
WebStorageStore.prototype.setSyncToken = function(token) {
|
||||
this.store.setItem("sync_token", token);
|
||||
};
|
||||
|
||||
/**
|
||||
* Store a room in web storage.
|
||||
* @param {Room} room
|
||||
*/
|
||||
WebStorageStore.prototype.storeRoom = function(room) {
|
||||
var serRoom = SerialisedRoom.fromRoom(room, this.batchSize);
|
||||
persist(this.store, serRoom);
|
||||
if (this._roomIds.indexOf(room.roomId) === -1) {
|
||||
this._roomIds.push(room.roomId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve a room from web storage.
|
||||
* @param {string} roomId
|
||||
* @return {?Room}
|
||||
*/
|
||||
WebStorageStore.prototype.getRoom = function(roomId) {
|
||||
// probe if room exists; break early if not. Every room should have state.
|
||||
if (!getItem(this.store, keyName(roomId, "state"))) {
|
||||
debuglog("getRoom: No room with id %s found.", roomId);
|
||||
return null;
|
||||
}
|
||||
var timelineKeys = getTimelineIndices(this.store, roomId);
|
||||
if (timelineKeys.indexOf("live") !== -1) {
|
||||
debuglog("getRoom: Live events found. Syncing timeline for %s", roomId);
|
||||
this._syncTimeline(roomId, timelineKeys);
|
||||
}
|
||||
return loadRoom(this.store, roomId, this.batchSize, this._tokens);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of all rooms from web storage.
|
||||
* @return {Array} An empty array.
|
||||
*/
|
||||
WebStorageStore.prototype.getRooms = function() {
|
||||
var rooms = [];
|
||||
var i;
|
||||
if (!this._syncedWithStore) {
|
||||
// sync with the store to set this._roomIds correctly. We know there is
|
||||
// exactly one 'state' key for each room, so we grab them.
|
||||
this._roomIds = [];
|
||||
for (i = 0; i < this.store.length; i++) {
|
||||
if (this.store.key(i).indexOf("room_") === 0 &&
|
||||
this.store.key(i).indexOf("_state") !== -1) {
|
||||
// grab the middle bit which is the room ID
|
||||
var k = this.store.key(i);
|
||||
this._roomIds.push(
|
||||
k.substring("room_".length, k.length - "_state".length)
|
||||
);
|
||||
}
|
||||
}
|
||||
this._syncedWithStore = true;
|
||||
}
|
||||
// call getRoom on each room_id
|
||||
for (i = 0; i < this._roomIds.length; i++) {
|
||||
var rm = this.getRoom(this._roomIds[i]);
|
||||
if (rm) {
|
||||
rooms.push(rm);
|
||||
}
|
||||
}
|
||||
return rooms;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list of summaries from web storage.
|
||||
* @return {Array} An empty array.
|
||||
*/
|
||||
WebStorageStore.prototype.getRoomSummaries = function() {
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Store a user in web storage.
|
||||
* @param {User} user
|
||||
*/
|
||||
WebStorageStore.prototype.storeUser = function(user) {
|
||||
// persist the events used to make the user, we can reconstruct on demand.
|
||||
setItem(this.store, "user_" + user.userId, {
|
||||
presence: user.events.presence ? user.events.presence.event : null
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a user from web storage.
|
||||
* @param {string} userId
|
||||
* @return {User}
|
||||
*/
|
||||
WebStorageStore.prototype.getUser = function(userId) {
|
||||
var userData = getItem(this.store, "user_" + userId);
|
||||
if (!userData) {
|
||||
return null;
|
||||
}
|
||||
var user = new User(userId);
|
||||
if (userData.presence) {
|
||||
user.setPresenceEvent(new MatrixEvent(userData.presence));
|
||||
}
|
||||
return user;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve scrollback for this room. Automatically adds events to the timeline.
|
||||
* @param {Room} room The matrix room to add the events to the start of the timeline.
|
||||
* @param {integer} limit The max number of old events to retrieve.
|
||||
* @return {Array<Object>} An array of objects which will be at most 'limit'
|
||||
* length and at least 0. The objects are the raw event JSON. The last element
|
||||
* is the 'oldest' (for parity with homeserver scrollback APIs).
|
||||
*/
|
||||
WebStorageStore.prototype.scrollback = function(room, limit) {
|
||||
if (room.storageToken === undefined || room.storageToken >= this._tokens.length) {
|
||||
return [];
|
||||
}
|
||||
// find the index of the earliest event in this room's timeline
|
||||
var storeData = this._tokens[room.storageToken] || {};
|
||||
var i;
|
||||
var earliestIndex = storeData.earliestIndex;
|
||||
var earliestEventId = room.timeline[0] ? room.timeline[0].getId() : null;
|
||||
debuglog(
|
||||
"scrollback in %s (timeline=%s msgs) i=%s, timeline[0].id=%s - req %s events",
|
||||
room.roomId, room.timeline.length, earliestIndex, earliestEventId, limit
|
||||
);
|
||||
var batch = getItem(
|
||||
this.store, keyName(room.roomId, "timeline", earliestIndex)
|
||||
);
|
||||
if (!batch) {
|
||||
// bad room or already at start, either way we have nothing to give.
|
||||
debuglog("No batch with index %s found.", earliestIndex);
|
||||
return [];
|
||||
}
|
||||
// populate from this batch first
|
||||
var scrollback = [];
|
||||
var foundEventId = false;
|
||||
for (i = batch.length - 1; i >= 0; i--) {
|
||||
// go back and find the earliest event ID, THEN start adding entries.
|
||||
// Make a MatrixEvent so we don't assume .event_id exists
|
||||
// (e.g v2/v3 JSON may be different)
|
||||
var matrixEvent = new MatrixEvent(batch[i]);
|
||||
if (matrixEvent.getId() === earliestEventId) {
|
||||
foundEventId = true;
|
||||
debuglog(
|
||||
"Found timeline[0] event at position %s in batch %s",
|
||||
i, earliestIndex
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (!foundEventId) {
|
||||
continue;
|
||||
}
|
||||
// add entry
|
||||
debuglog("Add event at position %s in batch %s", i, earliestIndex);
|
||||
scrollback.push(batch[i]);
|
||||
if (scrollback.length === limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (scrollback.length === limit) {
|
||||
debuglog("Batch has enough events to satisfy request.");
|
||||
return scrollback;
|
||||
}
|
||||
if (!foundEventId) {
|
||||
// the earliest index batch didn't contain the event. In other words,
|
||||
// this timeline is at a state we don't know, so bail.
|
||||
debuglog(
|
||||
"Failed to find event ID %s in batch %s", earliestEventId, earliestIndex
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
// get the requested earlier events from earlier batches
|
||||
while (scrollback.length < limit) {
|
||||
earliestIndex--;
|
||||
batch = getItem(
|
||||
this.store, keyName(room.roomId, "timeline", earliestIndex)
|
||||
);
|
||||
if (!batch) {
|
||||
// no more events
|
||||
debuglog("No batch found at index %s", earliestIndex);
|
||||
break;
|
||||
}
|
||||
for (i = batch.length - 1; i >= 0; i--) {
|
||||
debuglog("Add event at position %s in batch %s", i, earliestIndex);
|
||||
scrollback.push(batch[i]);
|
||||
if (scrollback.length === limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
debuglog(
|
||||
"Out of %s requested events, returning %s. New index=%s",
|
||||
limit, scrollback.length, earliestIndex
|
||||
);
|
||||
room.addEventsToTimeline(utils.map(scrollback, function(e) {
|
||||
return new MatrixEvent(e);
|
||||
}), true, room.getLiveTimeline());
|
||||
|
||||
this._tokens[room.storageToken] = {
|
||||
earliestIndex: earliestIndex
|
||||
};
|
||||
return scrollback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store events for a room. The events have already been added to the timeline.
|
||||
* @param {Room} room The room to store events for.
|
||||
* @param {Array<MatrixEvent>} events The events to store.
|
||||
* @param {string} token The token associated with these events.
|
||||
* @param {boolean} toStart True if these are paginated results. The last element
|
||||
* is the 'oldest' (for parity with homeserver scrollback APIs).
|
||||
*/
|
||||
WebStorageStore.prototype.storeEvents = function(room, events, token, toStart) {
|
||||
if (toStart) {
|
||||
// add paginated events to lowest batch indexes (can go -ve)
|
||||
var lowIndex = getIndexExtremity(
|
||||
getTimelineIndices(this.store, room.roomId), true
|
||||
);
|
||||
var i, key, batch;
|
||||
for (i = 0; i < events.length; i++) { // loop events to be stored
|
||||
key = keyName(room.roomId, "timeline", lowIndex);
|
||||
batch = getItem(this.store, key) || [];
|
||||
while (batch.length < this.batchSize && i < events.length) {
|
||||
batch.unshift(events[i].event);
|
||||
i++; // increment to insert next event into this batch
|
||||
}
|
||||
i--; // decrement to avoid skipping one (for loop ++s)
|
||||
setItem(this.store, key, batch);
|
||||
lowIndex--; // decrement index to get a new batch.
|
||||
}
|
||||
}
|
||||
else {
|
||||
// dump as live events
|
||||
var liveEvents = getItem(
|
||||
this.store, keyName(room.roomId, "timeline", "live")
|
||||
) || [];
|
||||
debuglog(
|
||||
"Adding %s events to %s live list (which has %s already)",
|
||||
events.length, room.roomId, liveEvents.length
|
||||
);
|
||||
var updateState = false;
|
||||
liveEvents = liveEvents.concat(utils.map(events, function(me) {
|
||||
// cheeky check to avoid looping twice
|
||||
if (me.isState()) {
|
||||
updateState = true;
|
||||
}
|
||||
return me.event;
|
||||
}));
|
||||
setItem(
|
||||
this.store, keyName(room.roomId, "timeline", "live"), liveEvents
|
||||
);
|
||||
if (updateState) {
|
||||
debuglog("Storing state for %s as new events updated state", room.roomId);
|
||||
// use 0 batch size; we don't care about batching right now.
|
||||
var serRoom = SerialisedRoom.fromRoom(room, 0);
|
||||
setItem(this.store, keyName(serRoom.roomId, "state"), serRoom.state);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync the 'live' timeline, batching live events according to 'batchSize'.
|
||||
* @param {string} roomId The room to sync the timeline.
|
||||
* @param {Array<String>} timelineIndices Optional. The indices in the timeline
|
||||
* if known already.
|
||||
*/
|
||||
WebStorageStore.prototype._syncTimeline = function(roomId, timelineIndices) {
|
||||
timelineIndices = timelineIndices || getTimelineIndices(this.store, roomId);
|
||||
var liveEvents = getItem(this.store, keyName(roomId, "timeline", "live")) || [];
|
||||
|
||||
// get the highest numbered $INDEX batch
|
||||
var highestIndex = getIndexExtremity(timelineIndices);
|
||||
var hiKey = keyName(roomId, "timeline", highestIndex);
|
||||
var hiBatch = getItem(this.store, hiKey) || [];
|
||||
// fill up the existing batch first.
|
||||
while (hiBatch.length < this.batchSize && liveEvents.length > 0) {
|
||||
hiBatch.push(liveEvents.shift());
|
||||
}
|
||||
setItem(this.store, hiKey, hiBatch);
|
||||
|
||||
// start adding new batches as required
|
||||
var batch = [];
|
||||
while (liveEvents.length > 0) {
|
||||
batch.push(liveEvents.shift());
|
||||
if (batch.length === this.batchSize || liveEvents.length === 0) {
|
||||
// persist the full batch and make another
|
||||
highestIndex++;
|
||||
hiKey = keyName(roomId, "timeline", highestIndex);
|
||||
setItem(this.store, hiKey, batch);
|
||||
batch = [];
|
||||
}
|
||||
}
|
||||
// reset live array
|
||||
setItem(this.store, keyName(roomId, "timeline", "live"), []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Store a filter.
|
||||
* @param {Filter} filter
|
||||
*/
|
||||
WebStorageStore.prototype.storeFilter = function(filter) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve a filter.
|
||||
* @param {string} userId
|
||||
* @param {string} filterId
|
||||
* @return {?Filter} A filter or null.
|
||||
*/
|
||||
WebStorageStore.prototype.getFilter = function(userId, filterId) {
|
||||
return null;
|
||||
};
|
||||
|
||||
function SerialisedRoom(roomId) {
|
||||
this.state = {
|
||||
events: {}
|
||||
};
|
||||
this.timeline = {
|
||||
// $INDEX: []
|
||||
};
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Room instance into a SerialisedRoom instance which can be stored
|
||||
* in the key value store.
|
||||
* @param {Room} room The matrix room to convert
|
||||
* @param {integer} batchSize The number of events per timeline batch
|
||||
* @return {SerialisedRoom} A serialised room representation of 'room'.
|
||||
*/
|
||||
SerialisedRoom.fromRoom = function(room, batchSize) {
|
||||
var self = new SerialisedRoom(room.roomId);
|
||||
var index;
|
||||
self.state.pagination_token = room.oldState.paginationToken;
|
||||
// [room_$ROOMID_state] downcast to POJO from MatrixEvent
|
||||
utils.forEach(utils.keys(room.currentState.events), function(eventType) {
|
||||
utils.forEach(utils.keys(room.currentState.events[eventType]), function(skey) {
|
||||
if (!self.state.events[eventType]) {
|
||||
self.state.events[eventType] = {};
|
||||
}
|
||||
self.state.events[eventType][skey] = (
|
||||
room.currentState.events[eventType][skey].event
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// [room_$ROOMID_timeline_$INDEX]
|
||||
if (batchSize > 0) {
|
||||
index = 0;
|
||||
while (index * batchSize < room.timeline.length) {
|
||||
self.timeline[index] = room.timeline.slice(
|
||||
index * batchSize, (index + 1) * batchSize
|
||||
);
|
||||
self.timeline[index] = utils.map(self.timeline[index], function(me) {
|
||||
// use POJO not MatrixEvent
|
||||
return me.event;
|
||||
});
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else { // don't batch
|
||||
self.timeline[0] = utils.map(room.timeline, function(matrixEvent) {
|
||||
return matrixEvent.event;
|
||||
});
|
||||
}
|
||||
return self;
|
||||
};
|
||||
|
||||
function loadRoom(store, roomId, numEvents, tokenArray) {
|
||||
var room = new Room(roomId, {
|
||||
storageToken: tokenArray.length
|
||||
});
|
||||
|
||||
// populate state (flatten nested struct to event array)
|
||||
var currentStateMap = getItem(store, keyName(roomId, "state"));
|
||||
var stateEvents = [];
|
||||
utils.forEach(utils.keys(currentStateMap.events), function(eventType) {
|
||||
utils.forEach(utils.keys(currentStateMap.events[eventType]), function(skey) {
|
||||
stateEvents.push(currentStateMap.events[eventType][skey]);
|
||||
});
|
||||
});
|
||||
// TODO: Fix logic dupe with MatrixClient._processRoomEvents
|
||||
var oldStateEvents = utils.map(
|
||||
utils.deepCopy(stateEvents), function(e) {
|
||||
return new MatrixEvent(e);
|
||||
}
|
||||
);
|
||||
var currentStateEvents = utils.map(stateEvents, function(e) {
|
||||
return new MatrixEvent(e);
|
||||
}
|
||||
);
|
||||
room.oldState.setStateEvents(oldStateEvents);
|
||||
room.currentState.setStateEvents(currentStateEvents);
|
||||
|
||||
// add most recent numEvents
|
||||
var recentEvents = [];
|
||||
var index = getIndexExtremity(getTimelineIndices(store, roomId));
|
||||
var eventIndex = index;
|
||||
var i, key, batch;
|
||||
while (recentEvents.length < numEvents) {
|
||||
key = keyName(roomId, "timeline", index);
|
||||
batch = getItem(store, key) || [];
|
||||
if (batch.length === 0) {
|
||||
// nothing left in the store.
|
||||
break;
|
||||
}
|
||||
for (i = batch.length - 1; i >= 0; i--) {
|
||||
recentEvents.unshift(new MatrixEvent(batch[i]));
|
||||
if (recentEvents.length === numEvents) {
|
||||
eventIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
index--;
|
||||
}
|
||||
// add events backwards to diverge old state correctly.
|
||||
room.addEventsToTimeline(recentEvents.reverse(), true, room.getLiveTimeline());
|
||||
room.oldState.paginationToken = currentStateMap.pagination_token;
|
||||
// set the token data to let us know which index this room instance is at
|
||||
// for scrollback.
|
||||
tokenArray.push({
|
||||
earliestIndex: eventIndex
|
||||
});
|
||||
return room;
|
||||
}
|
||||
|
||||
function persist(store, serRoom) {
|
||||
setItem(store, keyName(serRoom.roomId, "state"), serRoom.state);
|
||||
utils.forEach(utils.keys(serRoom.timeline), function(index) {
|
||||
setItem(store,
|
||||
keyName(serRoom.roomId, "timeline", index),
|
||||
serRoom.timeline[index]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getTimelineIndices(store, roomId) {
|
||||
var keys = [];
|
||||
for (var i = 0; i < store.length; i++) {
|
||||
if (store.key(i).indexOf(keyName(roomId, "timeline_")) !== -1) {
|
||||
// e.g. room_$ROOMID_timeline_0 => 0
|
||||
keys.push(
|
||||
store.key(i).replace(keyName(roomId, "timeline_"), "")
|
||||
);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
function getIndexExtremity(timelineIndices, getLowest) {
|
||||
var extremity, index;
|
||||
for (var i = 0; i < timelineIndices.length; i++) {
|
||||
index = parseInt(timelineIndices[i]);
|
||||
if (!isNaN(index) && (
|
||||
extremity === undefined ||
|
||||
!getLowest && index > extremity ||
|
||||
getLowest && index < extremity)) {
|
||||
extremity = index;
|
||||
}
|
||||
}
|
||||
return extremity;
|
||||
}
|
||||
|
||||
function keyName(roomId, key, index) {
|
||||
return "room_" + roomId + "_" + key + (
|
||||
index === undefined ? "" : ("_" + index)
|
||||
);
|
||||
}
|
||||
|
||||
function getItem(store, key) {
|
||||
try {
|
||||
return JSON.parse(store.getItem(key));
|
||||
}
|
||||
catch (e) {
|
||||
debuglog("Failed to get key %s: %s", key, e);
|
||||
debuglog(e.stack);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function setItem(store, key, val) {
|
||||
store.setItem(key, JSON.stringify(val));
|
||||
}
|
||||
|
||||
function debuglog() {
|
||||
if (DEBUG) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function delRoomStruct(store, roomId) {
|
||||
var prefix = "room_" + roomId;
|
||||
var keysToRemove = [];
|
||||
for (var i = 0; i < store.length; i++) {
|
||||
if (store.key(i).indexOf(prefix) !== -1) {
|
||||
keysToRemove.push(store.key(i));
|
||||
}
|
||||
}
|
||||
utils.forEach(keysToRemove, function(key) {
|
||||
store.removeItem(key);
|
||||
});
|
||||
} */
|
||||
|
||||
/** Web Storage Store class. */
|
||||
module.exports = WebStorageStore;
|
||||
+33
-11
@@ -1,17 +1,19 @@
|
||||
{
|
||||
"name": "matrix-js-sdk",
|
||||
"version": "0.6.4",
|
||||
"version": "0.7.5-rc.2",
|
||||
"description": "Matrix Client-Server SDK for Javascript",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "istanbul cover --report cobertura --config .istanbul.yml -i \"lib/**/*.js\" jasmine-node -- spec --verbose --junitreport --captureExceptions",
|
||||
"check": "jasmine-node spec --verbose --junitreport --captureExceptions",
|
||||
"buildtest": "babel -s -d specbuild spec",
|
||||
"test": "npm run buildtest && istanbul cover --report cobertura --config .istanbul.yml -i \"lib/**/*.js\" jasmine-node -- specbuild --verbose --junitreport --captureExceptions",
|
||||
"check": "npm run buildtest && jasmine-node specbuild --verbose --junitreport --captureExceptions",
|
||||
"gendoc": "jsdoc -r lib -P package.json -R README.md -d .jsdoc",
|
||||
"build": "jshint -c .jshint lib/ && rimraf dist && mkdir dist && browserify --exclude olm browser-index.js -o dist/browser-matrix.js --ignore-missing && uglifyjs -c -m -o dist/browser-matrix.min.js dist/browser-matrix.js",
|
||||
"start": "babel -s -w -d lib src",
|
||||
"build": "babel -s -d lib src && rimraf dist && mkdir dist && browserify -d browser-index.js | exorcist dist/browser-matrix.js.map > dist/browser-matrix.js && uglifyjs -c -m -o dist/browser-matrix.min.js --source-map dist/browser-matrix.min.js.map --in-source-map dist/browser-matrix.js.map dist/browser-matrix.js",
|
||||
"dist": "npm run build",
|
||||
"watch": "watchify --exclude olm browser-index.js -o dist/browser-matrix-dev.js -v",
|
||||
"lint": "jshint -c .jshint lib spec && gjslint --unix_mode --disable 0131,0211,0200,0222,0212 --max_line_length 90 -r spec/ -r lib/",
|
||||
"prepublish": "git rev-parse HEAD > git-revision.txt"
|
||||
"watch": "watchify -d browser-index.js -o 'exorcist dist/browser-matrix.js.map > dist/browser-matrix.js' -v",
|
||||
"lint": "eslint --max-warnings 121 src spec",
|
||||
"prepublish": "npm run build && git rev-parse HEAD > git-revision.txt"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://github.com/matrix-org/matrix-js-sdk"
|
||||
@@ -23,6 +25,9 @@
|
||||
"author": "matrix.org",
|
||||
"license": "Apache-2.0",
|
||||
"files": [
|
||||
".babelrc",
|
||||
".eslintrc.js",
|
||||
"spec/.eslintrc.js",
|
||||
"CHANGELOG.md",
|
||||
"CONTRIBUTING.rst",
|
||||
"LICENSE",
|
||||
@@ -37,25 +42,42 @@
|
||||
"lib",
|
||||
"package.json",
|
||||
"release.sh",
|
||||
"spec"
|
||||
"spec",
|
||||
"src"
|
||||
],
|
||||
"dependencies": {
|
||||
"another-json": "^0.2.0",
|
||||
"browser-request": "^0.3.3",
|
||||
"browserify": "^10.2.3",
|
||||
"q": "^1.4.1",
|
||||
"request": "^2.53.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"browserify": "^14.0.0",
|
||||
"browserify-shim": "^3.8.13",
|
||||
"eslint": "^3.13.1",
|
||||
"eslint-config-google": "^0.7.1",
|
||||
"exorcist": "^0.4.0",
|
||||
"istanbul": "^0.3.13",
|
||||
"jasmine-node": "^1.14.5",
|
||||
"jsdoc": "^3.4.0",
|
||||
"jshint": "^2.8.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"sourceify": "^0.1.0",
|
||||
"uglifyjs": "^2.4.10",
|
||||
"watchify": "^3.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"olm": "https://matrix.org/packages/npm/olm/olm-1.3.0.tgz"
|
||||
"olm": "https://matrix.org/packages/npm/olm/olm-2.2.1.tgz"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
"sourceify",
|
||||
"browserify-shim"
|
||||
]
|
||||
},
|
||||
"browserify-shim": {
|
||||
"olm": "global:Olm"
|
||||
}
|
||||
}
|
||||
|
||||
+57
-12
@@ -1,17 +1,17 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Script to perform a release of matrix-js-sdk. Performs the steps documented
|
||||
# in RELEASING.md
|
||||
# Script to perform a release of matrix-js-sdk.
|
||||
#
|
||||
# Requires:
|
||||
# github-changelog-generator; to install, do
|
||||
# github-changelog-generator; install via:
|
||||
# pip install git+https://github.com/matrix-org/github-changelog-generator.git
|
||||
# jq; install from your distibution's package manager (https://stedolan.github.io/jq/)
|
||||
# jq; install from your distribution's package manager (https://stedolan.github.io/jq/)
|
||||
# hub; install via brew (OSX) or source/pre-compiled binaries (debian) (https://github.com/github/hub) - Tested on v2.2.9
|
||||
|
||||
set -e
|
||||
|
||||
jq --version > /dev/null || (echo "jq is required: please install it"; exit)
|
||||
hub --version > /dev/null || (echo "hub is required: please install it"; exit)
|
||||
jq --version > /dev/null || (echo "jq is required: please install it"; kill $$)
|
||||
hub --version > /dev/null || (echo "hub is required: please install it"; kill $$)
|
||||
|
||||
USAGE="$0 [-xz] [-c changelog_file] vX.Y.Z"
|
||||
|
||||
@@ -32,6 +32,16 @@ if [ "$ret" -eq 0 ]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
if ! git diff-index --quiet --cached HEAD; then
|
||||
echo "this git checkout has staged (uncommitted) changes. Refusing to release."
|
||||
exit
|
||||
fi
|
||||
|
||||
if ! git diff-files --quiet; then
|
||||
echo "this git checkout has uncommitted changes. Refusing to release."
|
||||
exit
|
||||
fi
|
||||
|
||||
skip_changelog=
|
||||
skip_jsdoc=
|
||||
changelog_file="CHANGELOG.md"
|
||||
@@ -111,19 +121,29 @@ if [ -z "$skip_changelog" ]; then
|
||||
git commit "$changelog_file" -m "Prepare changelog for $tag"
|
||||
fi
|
||||
fi
|
||||
latest_changes=`mktemp`
|
||||
cat "${changelog_file}" | `dirname $0`/scripts/changelog_head.py > "${latest_changes}"
|
||||
|
||||
set -x
|
||||
|
||||
# Bump package.json, build the dist, and tag
|
||||
# Bump package.json and build the dist
|
||||
echo "npm version"
|
||||
# npm version will automatically commit its modification
|
||||
# and make a release tag. We don't want it to create the tag
|
||||
# because github will do that, but we can only turn off both
|
||||
# of these behaviours, so we have to manually commit the
|
||||
# result.
|
||||
# because it can only sign with the default key, but we can
|
||||
# only turn off both of these behaviours, so we have to
|
||||
# manually commit the result.
|
||||
npm version --no-git-tag-version "$release"
|
||||
git commit package.json -m "$tag"
|
||||
|
||||
|
||||
# figure out if we should be signing this release
|
||||
signing_id=
|
||||
if [ -f release_config.yaml ]; then
|
||||
signing_id=`cat release_config.yaml | python -c "import yaml; import sys; print yaml.load(sys.stdin)['signing_id']"`
|
||||
fi
|
||||
|
||||
|
||||
# If there is a 'dist' script in the package.json,
|
||||
# run it in a separate checkout of the project, then
|
||||
# upload any files in the 'dist' directory as release
|
||||
@@ -140,14 +160,21 @@ if [ $dodist -eq 0 ]; then
|
||||
echo "Building distribution copy in $builddir"
|
||||
pushd "$builddir"
|
||||
git clone "$projdir" .
|
||||
git co "$rel_branch"
|
||||
git checkout "$rel_branch"
|
||||
npm install
|
||||
# We haven't tagged yet, so tell the dist script what version
|
||||
# it's building
|
||||
DIST_VERSION="$tag" npm run dist
|
||||
|
||||
popd
|
||||
|
||||
for i in "$builddir"/dist/*; do
|
||||
assets="$assets -a $i"
|
||||
if [ -n "$signing_id" ]
|
||||
then
|
||||
gpg -u "$signing_id" --armor --output "$i".asc --detach-sig "$i"
|
||||
assets="$assets -a $i.asc"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -155,15 +182,33 @@ fi
|
||||
# a branch it doesn't have)
|
||||
git push origin "$rel_branch"
|
||||
|
||||
if [ -n "$signing_id" ]; then
|
||||
# make a signed tag
|
||||
# gnupg seems to fail to get the right tty device unless we set it here
|
||||
GIT_COMMITTER_EMAIL="$signing_id" GPG_TTY=`tty` git tag -u "$signing_id" -F "${latest_changes}" "$tag"
|
||||
else
|
||||
git tag -a -F "${latest_changes}" "$tag"
|
||||
fi
|
||||
|
||||
# push the tag
|
||||
git push origin "$tag"
|
||||
|
||||
hubflags=''
|
||||
if [ $prerelease -eq 1 ]; then
|
||||
hubflags='-p'
|
||||
fi
|
||||
hub release create $hubflags $assets -m "$tag" "$tag"
|
||||
|
||||
release_text=`mktemp`
|
||||
echo "$tag" > "${release_text}"
|
||||
echo >> "${release_text}"
|
||||
cat "${latest_changes}" >> "${release_text}"
|
||||
hub release create $hubflags $assets -f "${release_text}" "$tag"
|
||||
|
||||
if [ $dodist -eq 0 ]; then
|
||||
rm -rf "$builddir"
|
||||
fi
|
||||
rm "${release_text}"
|
||||
rm "${latest_changes}"
|
||||
|
||||
if [ -z "$skip_jsdoc" ]; then
|
||||
echo "generating jsdocs"
|
||||
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Outputs the body of the first entry of changelog file on stdin
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
found_first_header = False
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
if re.match(r"^Changes in \[.*\]", line):
|
||||
if found_first_header:
|
||||
break
|
||||
found_first_header = True
|
||||
elif not re.match(r"^=+$", line) and len(line) > 0:
|
||||
print line
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
jasmine: true,
|
||||
},
|
||||
}
|
||||
@@ -40,14 +40,16 @@ MockStorageApi.prototype = {
|
||||
return this.keys[index];
|
||||
},
|
||||
_recalc: function() {
|
||||
var keys = [];
|
||||
for (var k in this.data) {
|
||||
if (!this.data.hasOwnProperty(k)) { continue; }
|
||||
const keys = [];
|
||||
for (const k in this.data) {
|
||||
if (!this.data.hasOwnProperty(k)) {
|
||||
continue;
|
||||
}
|
||||
keys.push(k);
|
||||
}
|
||||
this.keys = keys;
|
||||
this.length = keys.length;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/** */
|
||||
|
||||
@@ -1,46 +1,30 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var q = require("q");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../../lib/utils");
|
||||
var test_utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const q = require("q");
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../../lib/utils");
|
||||
const testUtils = require("../test-utils");
|
||||
|
||||
function MockStorageApi() {
|
||||
this.data = {};
|
||||
}
|
||||
MockStorageApi.prototype = {
|
||||
setItem: function(k, v) {
|
||||
this.data[k] = v;
|
||||
},
|
||||
getItem: function(k) {
|
||||
return this.data[k] || null;
|
||||
},
|
||||
removeItem: function(k) {
|
||||
delete this.data[k];
|
||||
}
|
||||
};
|
||||
|
||||
var aliHttpBackend;
|
||||
var bobHttpBackend;
|
||||
var aliClient;
|
||||
var roomId = "!room:localhost";
|
||||
var aliUserId = "@ali:localhost";
|
||||
var aliDeviceId = "zxcvb";
|
||||
var aliAccessToken = "aseukfgwef";
|
||||
var bobClient;
|
||||
var bobUserId = "@bob:localhost";
|
||||
var bobDeviceId = "bvcxz";
|
||||
var bobAccessToken = "fewgfkuesa";
|
||||
var bobOneTimeKeys;
|
||||
var aliDeviceKeys;
|
||||
var bobDeviceKeys;
|
||||
var bobDeviceCurve25519Key;
|
||||
var bobDeviceEd25519Key;
|
||||
var aliLocalStore;
|
||||
var aliStorage;
|
||||
var bobStorage;
|
||||
var aliMessages;
|
||||
var bobMessages;
|
||||
let aliHttpBackend;
|
||||
let bobHttpBackend;
|
||||
let aliClient;
|
||||
const roomId = "!room:localhost";
|
||||
const aliUserId = "@ali:localhost";
|
||||
const aliDeviceId = "zxcvb";
|
||||
const aliAccessToken = "aseukfgwef";
|
||||
let bobClient;
|
||||
const bobUserId = "@bob:localhost";
|
||||
const bobDeviceId = "bvcxz";
|
||||
const bobAccessToken = "fewgfkuesa";
|
||||
let bobOneTimeKeys;
|
||||
let aliDeviceKeys;
|
||||
let bobDeviceKeys;
|
||||
let bobDeviceCurve25519Key;
|
||||
let bobDeviceEd25519Key;
|
||||
let aliStorage;
|
||||
let bobStorage;
|
||||
let aliMessages;
|
||||
let bobMessages;
|
||||
|
||||
|
||||
/**
|
||||
@@ -54,8 +38,8 @@ var bobMessages;
|
||||
* {one_time_keys: {}, device_keys: {}}
|
||||
*/
|
||||
function expectKeyUpload(deviceId, httpBackend) {
|
||||
var uploadPath = "/keys/upload/" + deviceId;
|
||||
var keys = {};
|
||||
const uploadPath = "/keys/upload/" + deviceId;
|
||||
const keys = {};
|
||||
|
||||
httpBackend.when("POST", uploadPath).respond(200, function(path, content) {
|
||||
expect(content.one_time_keys).not.toBeDefined();
|
||||
@@ -68,8 +52,8 @@ function expectKeyUpload(deviceId, httpBackend) {
|
||||
expect(content.device_keys).not.toBeDefined();
|
||||
expect(content.one_time_keys).toBeDefined();
|
||||
expect(content.one_time_keys).not.toEqual({});
|
||||
var count = 0;
|
||||
for (var key in content.one_time_keys) {
|
||||
let count = 0;
|
||||
for (const key in content.one_time_keys) {
|
||||
if (content.one_time_keys.hasOwnProperty(key)) {
|
||||
count++;
|
||||
}
|
||||
@@ -121,7 +105,7 @@ function expectBobKeyUpload() {
|
||||
}
|
||||
|
||||
function bobUploadsKeys() {
|
||||
bobClient.uploadKeys(5).catch(test_utils.failTest);
|
||||
bobClient.uploadKeys(5).catch(testUtils.failTest);
|
||||
return expectBobKeyUpload();
|
||||
}
|
||||
|
||||
@@ -135,11 +119,11 @@ function expectAliQueryKeys() {
|
||||
// can't query keys before bob has uploaded them
|
||||
expect(bobDeviceKeys).toBeDefined();
|
||||
|
||||
var bobKeys = {};
|
||||
const bobKeys = {};
|
||||
bobKeys[bobDeviceId] = bobDeviceKeys;
|
||||
aliHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
|
||||
expect(content.device_keys[bobUserId]).toEqual({});
|
||||
var result = {};
|
||||
const result = {};
|
||||
result[bobUserId] = bobKeys;
|
||||
return {device_keys: result};
|
||||
});
|
||||
@@ -155,11 +139,11 @@ function expectBobQueryKeys() {
|
||||
// can't query keys before ali has uploaded them
|
||||
expect(aliDeviceKeys).toBeDefined();
|
||||
|
||||
var aliKeys = {};
|
||||
const aliKeys = {};
|
||||
aliKeys[aliDeviceId] = aliDeviceKeys;
|
||||
bobHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
|
||||
expect(content.device_keys[aliUserId]).toEqual({});
|
||||
var result = {};
|
||||
const result = {};
|
||||
result[aliUserId] = aliKeys;
|
||||
return {device_keys: result};
|
||||
});
|
||||
@@ -176,16 +160,17 @@ function expectAliClaimKeys() {
|
||||
expect(bobOneTimeKeys).toBeDefined();
|
||||
|
||||
aliHttpBackend.when("POST", "/keys/claim").respond(200, function(path, content) {
|
||||
var claimType = content.one_time_keys[bobUserId][bobDeviceId];
|
||||
const claimType = content.one_time_keys[bobUserId][bobDeviceId];
|
||||
expect(claimType).toEqual("signed_curve25519");
|
||||
for (var keyId in bobOneTimeKeys) {
|
||||
let keyId = null;
|
||||
for (keyId in bobOneTimeKeys) {
|
||||
if (bobOneTimeKeys.hasOwnProperty(keyId)) {
|
||||
if (keyId.indexOf(claimType + ":") === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var result = {};
|
||||
const result = {};
|
||||
result[bobUserId] = {};
|
||||
result[bobUserId][bobDeviceId] = {};
|
||||
result[bobUserId][bobDeviceId][keyId] = bobOneTimeKeys[keyId];
|
||||
@@ -200,7 +185,7 @@ function aliDownloadsKeys() {
|
||||
// can't query keys before bob has uploaded them
|
||||
expect(bobDeviceEd25519Key).toBeDefined();
|
||||
|
||||
var p1 = aliClient.downloadKeys([bobUserId]).then(function() {
|
||||
const p1 = aliClient.downloadKeys([bobUserId]).then(function() {
|
||||
expect(aliClient.listDeviceKeys(bobUserId)).toEqual([{
|
||||
id: "bvcxz",
|
||||
key: bobDeviceEd25519Key,
|
||||
@@ -209,12 +194,12 @@ function aliDownloadsKeys() {
|
||||
display_name: null,
|
||||
}]);
|
||||
});
|
||||
var p2 = expectAliQueryKeys();
|
||||
const p2 = expectAliQueryKeys();
|
||||
|
||||
// check that the localStorage is updated as we expect (not sure this is
|
||||
// an integration test, but meh)
|
||||
return q.all([p1, p2]).then(function() {
|
||||
var devices = aliStorage.getEndToEndDevicesForUser(bobUserId);
|
||||
const devices = aliStorage.getEndToEndDevicesForUser(bobUserId);
|
||||
expect(devices[bobDeviceId].keys).toEqual(bobDeviceKeys.keys);
|
||||
expect(devices[bobDeviceId].verified).
|
||||
toBe(0); // DeviceVerification.UNVERIFIED
|
||||
@@ -248,7 +233,7 @@ function aliSendsFirstMessage() {
|
||||
sendMessage(aliClient),
|
||||
expectAliQueryKeys()
|
||||
.then(expectAliClaimKeys)
|
||||
.then(expectAliSendMessageRequest)
|
||||
.then(expectAliSendMessageRequest),
|
||||
]).spread(function(_, ciphertext) {
|
||||
return ciphertext;
|
||||
});
|
||||
@@ -263,7 +248,7 @@ function aliSendsFirstMessage() {
|
||||
function aliSendsMessage() {
|
||||
return q.all([
|
||||
sendMessage(aliClient),
|
||||
expectAliSendMessageRequest()
|
||||
expectAliSendMessageRequest(),
|
||||
]).spread(function(_, ciphertext) {
|
||||
return ciphertext;
|
||||
});
|
||||
@@ -279,7 +264,7 @@ function bobSendsReplyMessage() {
|
||||
return q.all([
|
||||
sendMessage(bobClient),
|
||||
expectBobQueryKeys()
|
||||
.then(expectBobSendMessageRequest)
|
||||
.then(expectBobSendMessageRequest),
|
||||
]).spread(function(_, ciphertext) {
|
||||
return ciphertext;
|
||||
});
|
||||
@@ -294,7 +279,7 @@ function expectAliSendMessageRequest() {
|
||||
return expectSendMessageRequest(aliHttpBackend).then(function(content) {
|
||||
aliMessages.push(content);
|
||||
expect(utils.keys(content.ciphertext)).toEqual([bobDeviceCurve25519Key]);
|
||||
var ciphertext = content.ciphertext[bobDeviceCurve25519Key];
|
||||
const ciphertext = content.ciphertext[bobDeviceCurve25519Key];
|
||||
expect(ciphertext).toBeDefined();
|
||||
return ciphertext;
|
||||
});
|
||||
@@ -308,10 +293,10 @@ function expectAliSendMessageRequest() {
|
||||
function expectBobSendMessageRequest() {
|
||||
return expectSendMessageRequest(bobHttpBackend).then(function(content) {
|
||||
bobMessages.push(content);
|
||||
var aliKeyId = "curve25519:" + aliDeviceId;
|
||||
var aliDeviceCurve25519Key = aliDeviceKeys.keys[aliKeyId];
|
||||
const aliKeyId = "curve25519:" + aliDeviceId;
|
||||
const aliDeviceCurve25519Key = aliDeviceKeys.keys[aliKeyId];
|
||||
expect(utils.keys(content.ciphertext)).toEqual([aliDeviceCurve25519Key]);
|
||||
var ciphertext = content.ciphertext[aliDeviceCurve25519Key];
|
||||
const ciphertext = content.ciphertext[aliDeviceCurve25519Key];
|
||||
expect(ciphertext).toBeDefined();
|
||||
return ciphertext;
|
||||
});
|
||||
@@ -319,13 +304,13 @@ function expectBobSendMessageRequest() {
|
||||
|
||||
function sendMessage(client) {
|
||||
return client.sendMessage(
|
||||
roomId, {msgtype: "m.text", body: "Hello, World"}
|
||||
roomId, {msgtype: "m.text", body: "Hello, World"},
|
||||
);
|
||||
}
|
||||
|
||||
function expectSendMessageRequest(httpBackend) {
|
||||
var path = "/send/m.room.encrypted/";
|
||||
var sent;
|
||||
const path = "/send/m.room.encrypted/";
|
||||
let sent;
|
||||
httpBackend.when("PUT", path).respond(200, function(path, content) {
|
||||
sent = content;
|
||||
return {
|
||||
@@ -338,39 +323,39 @@ function expectSendMessageRequest(httpBackend) {
|
||||
}
|
||||
|
||||
function aliRecvMessage() {
|
||||
var message = bobMessages.shift();
|
||||
const message = bobMessages.shift();
|
||||
return recvMessage(aliHttpBackend, aliClient, bobUserId, message);
|
||||
}
|
||||
|
||||
function bobRecvMessage() {
|
||||
var message = aliMessages.shift();
|
||||
const message = aliMessages.shift();
|
||||
return recvMessage(bobHttpBackend, bobClient, aliUserId, message);
|
||||
}
|
||||
|
||||
function recvMessage(httpBackend, client, sender, message) {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "x",
|
||||
rooms: {
|
||||
join: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
syncData.rooms.join[roomId] = {
|
||||
timeline: {
|
||||
events: [
|
||||
test_utils.mkEvent({
|
||||
testUtils.mkEvent({
|
||||
type: "m.room.encrypted",
|
||||
room: roomId,
|
||||
content: message,
|
||||
sender: sender,
|
||||
})
|
||||
]
|
||||
}
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
var deferred = q.defer();
|
||||
var onEvent = function(event) {
|
||||
const deferred = q.defer();
|
||||
const onEvent = function(event) {
|
||||
console.log(client.credentials.userId + " received event",
|
||||
event);
|
||||
|
||||
@@ -382,7 +367,7 @@ function recvMessage(httpBackend, client, sender, message) {
|
||||
expect(event.getType()).toEqual("m.room.message");
|
||||
expect(event.getContent()).toEqual({
|
||||
msgtype: "m.text",
|
||||
body: "Hello, World"
|
||||
body: "Hello, World",
|
||||
});
|
||||
expect(event.isEncrypted()).toBeTruthy();
|
||||
|
||||
@@ -398,7 +383,16 @@ function recvMessage(httpBackend, client, sender, message) {
|
||||
|
||||
|
||||
function aliStartClient() {
|
||||
expectAliKeyUpload().catch(test_utils.failTest);
|
||||
expectAliKeyUpload().catch(testUtils.failTest);
|
||||
|
||||
// ali will try to query her own keys on start
|
||||
aliHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
|
||||
expect(content.device_keys[aliUserId]).toEqual({});
|
||||
const result = {};
|
||||
result[aliUserId] = {};
|
||||
return {device_keys: result};
|
||||
});
|
||||
|
||||
startClient(aliHttpBackend, aliClient);
|
||||
return aliHttpBackend.flush().then(function() {
|
||||
console.log("Ali client started");
|
||||
@@ -406,7 +400,16 @@ function aliStartClient() {
|
||||
}
|
||||
|
||||
function bobStartClient() {
|
||||
expectBobKeyUpload().catch(test_utils.failTest);
|
||||
expectBobKeyUpload().catch(testUtils.failTest);
|
||||
|
||||
// bob will try to query his own keys on start
|
||||
bobHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
|
||||
expect(content.device_keys[bobUserId]).toEqual({});
|
||||
const result = {};
|
||||
result[bobUserId] = {};
|
||||
return {device_keys: result};
|
||||
});
|
||||
|
||||
startClient(bobHttpBackend, bobClient);
|
||||
return bobHttpBackend.flush().then(function() {
|
||||
console.log("Bob client started");
|
||||
@@ -426,28 +429,28 @@ function startClient(httpBackend, client) {
|
||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "fid" });
|
||||
|
||||
// send a sync response including our test room.
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "x",
|
||||
rooms: {
|
||||
join: { }
|
||||
}
|
||||
join: { },
|
||||
},
|
||||
};
|
||||
syncData.rooms.join[roomId] = {
|
||||
state: {
|
||||
events: [
|
||||
test_utils.mkMembership({
|
||||
testUtils.mkMembership({
|
||||
mship: "join",
|
||||
user: aliUserId,
|
||||
}),
|
||||
test_utils.mkMembership({
|
||||
testUtils.mkMembership({
|
||||
mship: "join",
|
||||
user: bobUserId,
|
||||
}),
|
||||
]
|
||||
],
|
||||
},
|
||||
timeline: {
|
||||
events: []
|
||||
}
|
||||
events: [],
|
||||
},
|
||||
};
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
@@ -461,11 +464,9 @@ describe("MatrixClient crypto", function() {
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
aliLocalStore = new MockStorageApi();
|
||||
aliStorage = new sdk.WebStorageSessionStore(aliLocalStore);
|
||||
bobStorage = new sdk.WebStorageSessionStore(new MockStorageApi());
|
||||
test_utils.beforeEach(this);
|
||||
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
|
||||
aliStorage = new sdk.WebStorageSessionStore(new testUtils.MockStorageApi());
|
||||
aliHttpBackend = new HttpBackend();
|
||||
aliClient = sdk.createClient({
|
||||
baseUrl: "http://alis.server",
|
||||
@@ -476,6 +477,7 @@ describe("MatrixClient crypto", function() {
|
||||
request: aliHttpBackend.requestFn,
|
||||
});
|
||||
|
||||
bobStorage = new sdk.WebStorageSessionStore(new testUtils.MockStorageApi());
|
||||
bobHttpBackend = new HttpBackend();
|
||||
bobClient = sdk.createClient({
|
||||
baseUrl: "http://bobs.server",
|
||||
@@ -500,17 +502,38 @@ describe("MatrixClient crypto", function() {
|
||||
bobClient.stopClient();
|
||||
});
|
||||
|
||||
it('Ali knows the difference between a new user and one with no devices',
|
||||
function(done) {
|
||||
aliHttpBackend.when('POST', '/keys/query').respond(200, {
|
||||
device_keys: {
|
||||
'@bob:id': {},
|
||||
},
|
||||
});
|
||||
|
||||
const p1 = aliClient.downloadKeys(['@bob:id']);
|
||||
const p2 = aliHttpBackend.flush('/keys/query', 1);
|
||||
|
||||
q.all([p1, p2]).then(function() {
|
||||
const devices = aliStorage.getEndToEndDevicesForUser('@bob:id');
|
||||
expect(utils.keys(devices).length).toEqual(0);
|
||||
|
||||
// request again: should be no more requests
|
||||
return aliClient.downloadKeys(['@bob:id']);
|
||||
}).nodeify(done);
|
||||
},
|
||||
);
|
||||
|
||||
it("Bob uploads without one-time keys and with one-time keys", function(done) {
|
||||
q()
|
||||
.then(bobUploadsKeys)
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali downloads Bobs keys", function(done) {
|
||||
q()
|
||||
.then(bobUploadsKeys)
|
||||
.then(aliDownloadsKeys)
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali gets keys with an invalid signature", function(done) {
|
||||
@@ -528,13 +551,13 @@ describe("MatrixClient crypto", function() {
|
||||
// should get an empty list
|
||||
expect(aliClient.listDeviceKeys(bobUserId)).toEqual([]);
|
||||
})
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali gets keys with an incorrect userId", function(done) {
|
||||
var eveUserId = "@eve:localhost";
|
||||
const eveUserId = "@eve:localhost";
|
||||
|
||||
var bobDeviceKeys = {
|
||||
const bobDeviceKeys = {
|
||||
algorithms: ['m.olm.v1.curve25519-aes-sha2', 'm.megolm.v1.aes-sha2'],
|
||||
device_id: 'bvcxz',
|
||||
keys: {
|
||||
@@ -550,26 +573,26 @@ describe("MatrixClient crypto", function() {
|
||||
},
|
||||
};
|
||||
|
||||
var bobKeys = {};
|
||||
const bobKeys = {};
|
||||
bobKeys[bobDeviceId] = bobDeviceKeys;
|
||||
aliHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
|
||||
var result = {};
|
||||
const result = {};
|
||||
result[bobUserId] = bobKeys;
|
||||
return {device_keys: result};
|
||||
});
|
||||
|
||||
q.all(
|
||||
aliClient.downloadKeys([bobUserId, eveUserId]),
|
||||
aliHttpBackend.flush("/keys/query", 1)
|
||||
aliHttpBackend.flush("/keys/query", 1),
|
||||
).then(function() {
|
||||
// should get an empty list
|
||||
expect(aliClient.listDeviceKeys(bobUserId)).toEqual([]);
|
||||
expect(aliClient.listDeviceKeys(eveUserId)).toEqual([]);
|
||||
}).catch(test_utils.failTest).done(done);
|
||||
}).catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali gets keys with an incorrect deviceId", function(done) {
|
||||
var bobDeviceKeys = {
|
||||
const bobDeviceKeys = {
|
||||
algorithms: ['m.olm.v1.curve25519-aes-sha2', 'm.megolm.v1.aes-sha2'],
|
||||
device_id: 'bad_device',
|
||||
keys: {
|
||||
@@ -585,21 +608,21 @@ describe("MatrixClient crypto", function() {
|
||||
},
|
||||
};
|
||||
|
||||
var bobKeys = {};
|
||||
const bobKeys = {};
|
||||
bobKeys[bobDeviceId] = bobDeviceKeys;
|
||||
aliHttpBackend.when("POST", "/keys/query").respond(200, function(path, content) {
|
||||
var result = {};
|
||||
const result = {};
|
||||
result[bobUserId] = bobKeys;
|
||||
return {device_keys: result};
|
||||
});
|
||||
|
||||
q.all(
|
||||
aliClient.downloadKeys([bobUserId]),
|
||||
aliHttpBackend.flush("/keys/query", 1)
|
||||
aliHttpBackend.flush("/keys/query", 1),
|
||||
).then(function() {
|
||||
// should get an empty list
|
||||
expect(aliClient.listDeviceKeys(bobUserId)).toEqual([]);
|
||||
}).catch(test_utils.failTest).done(done);
|
||||
}).catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali enables encryption", function(done) {
|
||||
@@ -607,7 +630,7 @@ describe("MatrixClient crypto", function() {
|
||||
.then(bobUploadsKeys)
|
||||
.then(aliStartClient)
|
||||
.then(aliEnablesEncryption)
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali sends a message", function(done) {
|
||||
@@ -616,7 +639,7 @@ describe("MatrixClient crypto", function() {
|
||||
.then(aliStartClient)
|
||||
.then(aliEnablesEncryption)
|
||||
.then(aliSendsFirstMessage)
|
||||
.catch(test_utils.failTest).nodeify(done);
|
||||
.catch(testUtils.failTest).nodeify(done);
|
||||
});
|
||||
|
||||
it("Bob receives a message", function(done) {
|
||||
@@ -627,7 +650,7 @@ describe("MatrixClient crypto", function() {
|
||||
.then(aliSendsFirstMessage)
|
||||
.then(bobStartClient)
|
||||
.then(bobRecvMessage)
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Bob receives a message with a bogus sender", function(done) {
|
||||
@@ -638,31 +661,31 @@ describe("MatrixClient crypto", function() {
|
||||
.then(aliSendsFirstMessage)
|
||||
.then(bobStartClient)
|
||||
.then(function() {
|
||||
var message = aliMessages.shift();
|
||||
var syncData = {
|
||||
const message = aliMessages.shift();
|
||||
const syncData = {
|
||||
next_batch: "x",
|
||||
rooms: {
|
||||
join: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
syncData.rooms.join[roomId] = {
|
||||
timeline: {
|
||||
events: [
|
||||
test_utils.mkEvent({
|
||||
testUtils.mkEvent({
|
||||
type: "m.room.encrypted",
|
||||
room: roomId,
|
||||
content: message,
|
||||
sender: "@bogus:sender",
|
||||
})
|
||||
]
|
||||
}
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
bobHttpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
var deferred = q.defer();
|
||||
var onEvent = function(event) {
|
||||
const deferred = q.defer();
|
||||
const onEvent = function(event) {
|
||||
console.log(bobClient.credentials.userId + " received event",
|
||||
event);
|
||||
|
||||
@@ -684,7 +707,7 @@ describe("MatrixClient crypto", function() {
|
||||
bobHttpBackend.flush();
|
||||
return deferred.promise;
|
||||
})
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Ali blocks Bob's device", function(done) {
|
||||
@@ -695,8 +718,8 @@ describe("MatrixClient crypto", function() {
|
||||
.then(aliDownloadsKeys)
|
||||
.then(function() {
|
||||
aliClient.setDeviceBlocked(bobUserId, bobDeviceId, true);
|
||||
var p1 = sendMessage(aliClient);
|
||||
var p2 = expectAliQueryKeys()
|
||||
const p1 = sendMessage(aliClient);
|
||||
const p2 = expectAliQueryKeys()
|
||||
.then(expectAliClaimKeys)
|
||||
.then(function() {
|
||||
return expectSendMessageRequest(aliHttpBackend);
|
||||
@@ -705,7 +728,7 @@ describe("MatrixClient crypto", function() {
|
||||
expect(sentContent.ciphertext).toEqual({});
|
||||
});
|
||||
return q.all([p1, p2]);
|
||||
}).catch(test_utils.failTest).nodeify(done);
|
||||
}).catch(testUtils.failTest).nodeify(done);
|
||||
});
|
||||
|
||||
it("Bob receives two pre-key messages", function(done) {
|
||||
@@ -718,7 +741,7 @@ describe("MatrixClient crypto", function() {
|
||||
.then(bobRecvMessage)
|
||||
.then(aliSendsMessage)
|
||||
.then(bobRecvMessage)
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("Bob replies to the message", function(done) {
|
||||
@@ -733,6 +756,33 @@ describe("MatrixClient crypto", function() {
|
||||
.then(bobSendsReplyMessage).then(function(ciphertext) {
|
||||
expect(ciphertext.type).toEqual(1);
|
||||
}).then(aliRecvMessage)
|
||||
.catch(test_utils.failTest).done(done);
|
||||
.catch(testUtils.failTest).done(done);
|
||||
});
|
||||
|
||||
|
||||
it("Ali does a key query when she gets a new_device event", function(done) {
|
||||
q()
|
||||
.then(bobUploadsKeys)
|
||||
.then(aliStartClient)
|
||||
.then(function() {
|
||||
const syncData = {
|
||||
next_batch: '2',
|
||||
to_device: {
|
||||
events: [
|
||||
testUtils.mkEvent({
|
||||
content: {
|
||||
device_id: 'TEST_DEVICE',
|
||||
rooms: [],
|
||||
},
|
||||
sender: bobUserId,
|
||||
type: 'm.new_device',
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
aliHttpBackend.when('GET', '/sync').respond(200, syncData);
|
||||
return aliHttpBackend.flush('/sync', 1);
|
||||
}).then(expectAliQueryKeys)
|
||||
.nodeify(done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("MatrixClient events", function() {
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var client, httpBackend;
|
||||
var selfUserId = "@alice:localhost";
|
||||
var selfAccessToken = "aseukfgwef";
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
let client;
|
||||
let httpBackend;
|
||||
const selfUserId = "@alice:localhost";
|
||||
const selfAccessToken = "aseukfgwef";
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
sdk.request(httpBackend.requestFn);
|
||||
client = sdk.createClient({
|
||||
baseUrl: baseUrl,
|
||||
userId: selfUserId,
|
||||
accessToken: selfAccessToken
|
||||
accessToken: selfAccessToken,
|
||||
});
|
||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
||||
@@ -28,14 +29,14 @@ describe("MatrixClient events", function() {
|
||||
});
|
||||
|
||||
describe("emissions", function() {
|
||||
var SYNC_DATA = {
|
||||
const SYNC_DATA = {
|
||||
next_batch: "s_5_3",
|
||||
presence: {
|
||||
events: [
|
||||
utils.mkPresence({
|
||||
user: "@foo:bar", name: "Foo Bar", presence: "online"
|
||||
})
|
||||
]
|
||||
user: "@foo:bar", name: "Foo Bar", presence: "online",
|
||||
}),
|
||||
],
|
||||
},
|
||||
rooms: {
|
||||
join: {
|
||||
@@ -43,30 +44,30 @@ describe("MatrixClient events", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: "!erufh:bar", user: "@foo:bar", msg: "hmmm"
|
||||
})
|
||||
room: "!erufh:bar", user: "@foo:bar", msg: "hmmm",
|
||||
}),
|
||||
],
|
||||
prev_batch: "s"
|
||||
prev_batch: "s",
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
utils.mkMembership({
|
||||
room: "!erufh:bar", mship: "join", user: "@foo:bar"
|
||||
room: "!erufh:bar", mship: "join", user: "@foo:bar",
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: "!erufh:bar",
|
||||
user: "@foo:bar",
|
||||
content: {
|
||||
creator: "@foo:bar"
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
creator: "@foo:bar",
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
var NEXT_SYNC_DATA = {
|
||||
const NEXT_SYNC_DATA = {
|
||||
next_batch: "e_6_7",
|
||||
rooms: {
|
||||
join: {
|
||||
@@ -74,25 +75,26 @@ describe("MatrixClient events", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: "!erufh:bar", user: "@foo:bar", msg: "ello ello"
|
||||
room: "!erufh:bar", user: "@foo:bar",
|
||||
msg: "ello ello",
|
||||
}),
|
||||
utils.mkMessage({
|
||||
room: "!erufh:bar", user: "@foo:bar", msg: ":D"
|
||||
room: "!erufh:bar", user: "@foo:bar", msg: ":D",
|
||||
}),
|
||||
]
|
||||
],
|
||||
},
|
||||
ephemeral: {
|
||||
events: [
|
||||
utils.mkEvent({
|
||||
type: "m.typing", room: "!erufh:bar", content: {
|
||||
user_ids: ["@foo:bar"]
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
user_ids: ["@foo:bar"],
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it("should emit events from both the first and subsequent /sync calls",
|
||||
@@ -100,18 +102,18 @@ describe("MatrixClient events", function() {
|
||||
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA);
|
||||
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
|
||||
|
||||
var expectedEvents = [];
|
||||
let expectedEvents = [];
|
||||
expectedEvents = expectedEvents.concat(
|
||||
SYNC_DATA.presence.events,
|
||||
SYNC_DATA.rooms.join["!erufh:bar"].timeline.events,
|
||||
SYNC_DATA.rooms.join["!erufh:bar"].state.events,
|
||||
NEXT_SYNC_DATA.rooms.join["!erufh:bar"].timeline.events,
|
||||
NEXT_SYNC_DATA.rooms.join["!erufh:bar"].ephemeral.events
|
||||
NEXT_SYNC_DATA.rooms.join["!erufh:bar"].ephemeral.events,
|
||||
);
|
||||
|
||||
client.on("event", function(event) {
|
||||
var found = false;
|
||||
for (var i = 0; i < expectedEvents.length; i++) {
|
||||
let found = false;
|
||||
for (let i = 0; i < expectedEvents.length; i++) {
|
||||
if (expectedEvents[i].event_id === event.getId()) {
|
||||
expectedEvents.splice(i, 1);
|
||||
found = true;
|
||||
@@ -119,7 +121,7 @@ describe("MatrixClient events", function() {
|
||||
}
|
||||
}
|
||||
expect(found).toBe(
|
||||
true, "Unexpected 'event' emitted: " + event.getType()
|
||||
true, "Unexpected 'event' emitted: " + event.getType(),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -127,7 +129,7 @@ describe("MatrixClient events", function() {
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
expect(expectedEvents.length).toEqual(
|
||||
0, "Failed to see all events from /sync calls"
|
||||
0, "Failed to see all events from /sync calls",
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -136,16 +138,18 @@ describe("MatrixClient events", function() {
|
||||
it("should emit User events", function(done) {
|
||||
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA);
|
||||
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
|
||||
var fired = false;
|
||||
let fired = false;
|
||||
client.on("User.presence", function(event, user) {
|
||||
fired = true;
|
||||
expect(user).toBeDefined();
|
||||
expect(event).toBeDefined();
|
||||
if (!user || !event) { return; }
|
||||
if (!user || !event) {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(event.event).toEqual(SYNC_DATA.presence.events[0]);
|
||||
expect(user.presence).toEqual(
|
||||
SYNC_DATA.presence.events[0].content.presence
|
||||
SYNC_DATA.presence.events[0].content.presence,
|
||||
);
|
||||
});
|
||||
client.startClient();
|
||||
@@ -159,9 +163,9 @@ describe("MatrixClient events", function() {
|
||||
it("should emit Room events", function(done) {
|
||||
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA);
|
||||
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
|
||||
var roomInvokeCount = 0;
|
||||
var roomNameInvokeCount = 0;
|
||||
var timelineFireCount = 0;
|
||||
let roomInvokeCount = 0;
|
||||
let roomNameInvokeCount = 0;
|
||||
let timelineFireCount = 0;
|
||||
client.on("Room", function(room) {
|
||||
roomInvokeCount++;
|
||||
expect(room.roomId).toEqual("!erufh:bar");
|
||||
@@ -178,13 +182,13 @@ describe("MatrixClient events", function() {
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
expect(roomInvokeCount).toEqual(
|
||||
1, "Room fired wrong number of times."
|
||||
1, "Room fired wrong number of times.",
|
||||
);
|
||||
expect(roomNameInvokeCount).toEqual(
|
||||
1, "Room.name fired wrong number of times."
|
||||
1, "Room.name fired wrong number of times.",
|
||||
);
|
||||
expect(timelineFireCount).toEqual(
|
||||
3, "Room.timeline fired the wrong number of times"
|
||||
3, "Room.timeline fired the wrong number of times",
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -194,17 +198,17 @@ describe("MatrixClient events", function() {
|
||||
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA);
|
||||
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
|
||||
|
||||
var roomStateEventTypes = [
|
||||
"m.room.member", "m.room.create"
|
||||
const roomStateEventTypes = [
|
||||
"m.room.member", "m.room.create",
|
||||
];
|
||||
var eventsInvokeCount = 0;
|
||||
var membersInvokeCount = 0;
|
||||
var newMemberInvokeCount = 0;
|
||||
let eventsInvokeCount = 0;
|
||||
let membersInvokeCount = 0;
|
||||
let newMemberInvokeCount = 0;
|
||||
client.on("RoomState.events", function(event, state) {
|
||||
eventsInvokeCount++;
|
||||
var index = roomStateEventTypes.indexOf(event.getType());
|
||||
const index = roomStateEventTypes.indexOf(event.getType());
|
||||
expect(index).not.toEqual(
|
||||
-1, "Unexpected room state event type: " + event.getType()
|
||||
-1, "Unexpected room state event type: " + event.getType(),
|
||||
);
|
||||
if (index >= 0) {
|
||||
roomStateEventTypes.splice(index, 1);
|
||||
@@ -227,13 +231,13 @@ describe("MatrixClient events", function() {
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
expect(membersInvokeCount).toEqual(
|
||||
1, "RoomState.members fired wrong number of times"
|
||||
1, "RoomState.members fired wrong number of times",
|
||||
);
|
||||
expect(newMemberInvokeCount).toEqual(
|
||||
1, "RoomState.newMember fired wrong number of times"
|
||||
1, "RoomState.newMember fired wrong number of times",
|
||||
);
|
||||
expect(eventsInvokeCount).toEqual(
|
||||
2, "RoomState.events fired wrong number of times"
|
||||
2, "RoomState.events fired wrong number of times",
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -243,10 +247,10 @@ describe("MatrixClient events", function() {
|
||||
httpBackend.when("GET", "/sync").respond(200, SYNC_DATA);
|
||||
httpBackend.when("GET", "/sync").respond(200, NEXT_SYNC_DATA);
|
||||
|
||||
var typingInvokeCount = 0;
|
||||
var powerLevelInvokeCount = 0;
|
||||
var nameInvokeCount = 0;
|
||||
var membershipInvokeCount = 0;
|
||||
let typingInvokeCount = 0;
|
||||
let powerLevelInvokeCount = 0;
|
||||
let nameInvokeCount = 0;
|
||||
let membershipInvokeCount = 0;
|
||||
client.on("RoomMember.name", function(event, member) {
|
||||
nameInvokeCount++;
|
||||
});
|
||||
@@ -266,16 +270,16 @@ describe("MatrixClient events", function() {
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
expect(typingInvokeCount).toEqual(
|
||||
1, "RoomMember.typing fired wrong number of times"
|
||||
1, "RoomMember.typing fired wrong number of times",
|
||||
);
|
||||
expect(powerLevelInvokeCount).toEqual(
|
||||
0, "RoomMember.powerLevel fired wrong number of times"
|
||||
0, "RoomMember.powerLevel fired wrong number of times",
|
||||
);
|
||||
expect(nameInvokeCount).toEqual(
|
||||
0, "RoomMember.name fired wrong number of times"
|
||||
0, "RoomMember.name fired wrong number of times",
|
||||
);
|
||||
expect(membershipInvokeCount).toEqual(
|
||||
1, "RoomMember.membership fired wrong number of times"
|
||||
1, "RoomMember.membership fired wrong number of times",
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -284,7 +288,7 @@ describe("MatrixClient events", function() {
|
||||
it("should emit Session.logged_out on M_UNKNOWN_TOKEN", function(done) {
|
||||
httpBackend.when("GET", "/sync").respond(401, { errcode: 'M_UNKNOWN_TOKEN' });
|
||||
|
||||
var sessionLoggedOutCount = 0;
|
||||
let sessionLoggedOutCount = 0;
|
||||
client.on("Session.logged_out", function(event, member) {
|
||||
sessionLoggedOutCount++;
|
||||
});
|
||||
@@ -293,11 +297,10 @@ describe("MatrixClient events", function() {
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
expect(sessionLoggedOutCount).toEqual(
|
||||
1, "Session.logged_out fired wrong number of times"
|
||||
1, "Session.logged_out fired wrong number of times",
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
"use strict";
|
||||
var q = require("q");
|
||||
var sdk = require("../..");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
var EventTimeline = sdk.EventTimeline;
|
||||
const q = require("q");
|
||||
const sdk = require("../..");
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../test-utils");
|
||||
const EventTimeline = sdk.EventTimeline;
|
||||
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var userId = "@alice:localhost";
|
||||
var userName = "Alice";
|
||||
var accessToken = "aseukfgwef";
|
||||
var roomId = "!foo:bar";
|
||||
var otherUserId = "@bob:localhost";
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
const userId = "@alice:localhost";
|
||||
const userName = "Alice";
|
||||
const accessToken = "aseukfgwef";
|
||||
const roomId = "!foo:bar";
|
||||
const otherUserId = "@bob:localhost";
|
||||
|
||||
var USER_MEMBERSHIP_EVENT = utils.mkMembership({
|
||||
room: roomId, mship: "join", user: userId, name: userName
|
||||
const USER_MEMBERSHIP_EVENT = utils.mkMembership({
|
||||
room: roomId, mship: "join", user: userId, name: userName,
|
||||
});
|
||||
|
||||
var ROOM_NAME_EVENT = utils.mkEvent({
|
||||
const ROOM_NAME_EVENT = utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: otherUserId,
|
||||
content: {
|
||||
name: "Old room name"
|
||||
}
|
||||
name: "Old room name",
|
||||
},
|
||||
});
|
||||
|
||||
var INITIAL_SYNC_DATA = {
|
||||
const INITIAL_SYNC_DATA = {
|
||||
next_batch: "s_5_3",
|
||||
rooms: {
|
||||
join: {
|
||||
@@ -31,33 +31,33 @@ var INITIAL_SYNC_DATA = {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: otherUserId, msg: "hello"
|
||||
})
|
||||
room: roomId, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
],
|
||||
prev_batch: "f_1_1"
|
||||
prev_batch: "f_1_1",
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
ROOM_NAME_EVENT,
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "join",
|
||||
user: otherUserId, name: "Bob"
|
||||
user: otherUserId, name: "Bob",
|
||||
}),
|
||||
USER_MEMBERSHIP_EVENT,
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomId, user: userId,
|
||||
content: {
|
||||
creator: userId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
creator: userId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var EVENTS = [
|
||||
const EVENTS = [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: userId, msg: "we",
|
||||
}),
|
||||
@@ -81,7 +81,7 @@ function startClient(httpBackend, client) {
|
||||
client.startClient();
|
||||
|
||||
// set up a promise which will resolve once the client is initialised
|
||||
var deferred = q.defer();
|
||||
const deferred = q.defer();
|
||||
client.on("sync", function(state) {
|
||||
console.log("sync", state);
|
||||
if (state != "SYNCING") {
|
||||
@@ -96,11 +96,11 @@ function startClient(httpBackend, client) {
|
||||
|
||||
|
||||
describe("getEventTimeline support", function() {
|
||||
var httpBackend;
|
||||
var client;
|
||||
let httpBackend;
|
||||
let client;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
sdk.request(httpBackend.requestFn);
|
||||
});
|
||||
@@ -118,12 +118,13 @@ describe("getEventTimeline support", function() {
|
||||
accessToken: accessToken,
|
||||
});
|
||||
|
||||
startClient(httpBackend, client
|
||||
startClient(httpBackend, client,
|
||||
).then(function() {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
expect(function() { client.getEventTimeline(timelineSet, "event"); })
|
||||
.toThrow();
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
expect(function() {
|
||||
client.getEventTimeline(timelineSet, "event");
|
||||
}).toThrow();
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
|
||||
@@ -135,12 +136,13 @@ describe("getEventTimeline support", function() {
|
||||
timelineSupport: true,
|
||||
});
|
||||
|
||||
startClient(httpBackend, client
|
||||
startClient(httpBackend, client,
|
||||
).then(function() {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
expect(function() { client.getEventTimeline(timelineSet, "event"); })
|
||||
.not.toThrow();
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
expect(function() {
|
||||
client.getEventTimeline(timelineSet, "event");
|
||||
}).not.toThrow();
|
||||
}).catch(utils.failTest).done(done);
|
||||
|
||||
httpBackend.flush().catch(utils.failTest);
|
||||
@@ -155,9 +157,9 @@ describe("getEventTimeline support", function() {
|
||||
userId: userId,
|
||||
accessToken: accessToken,
|
||||
});
|
||||
var room;
|
||||
let room;
|
||||
|
||||
startClient(httpBackend, client
|
||||
startClient(httpBackend, client,
|
||||
).then(function() {
|
||||
room = client.getRoom(roomId);
|
||||
|
||||
@@ -218,10 +220,11 @@ describe("getEventTimeline support", function() {
|
||||
});
|
||||
|
||||
describe("MatrixClient event timelines", function() {
|
||||
var client, httpBackend;
|
||||
let client = null;
|
||||
let httpBackend = null;
|
||||
|
||||
beforeEach(function(done) {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
sdk.request(httpBackend.requestFn);
|
||||
|
||||
@@ -243,8 +246,8 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
describe("getEventTimeline", function() {
|
||||
it("should create a new timeline for new events", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
httpBackend.when("GET", "/rooms/!foo%3Abar/context/event1%3Abar")
|
||||
.respond(200, function() {
|
||||
return {
|
||||
@@ -262,7 +265,7 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
client.getEventTimeline(timelineSet, "event1:bar").then(function(tl) {
|
||||
expect(tl.getEvents().length).toEqual(4);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
expect(tl.getEvents()[i].event).toEqual(EVENTS[i]);
|
||||
expect(tl.getEvents()[i].sender.name).toEqual(userName);
|
||||
}
|
||||
@@ -276,8 +279,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should return existing timeline for known events", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
httpBackend.when("GET", "/sync").respond(200, {
|
||||
next_batch: "s_5_4",
|
||||
rooms: {
|
||||
@@ -308,8 +311,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should update timelines where they overlap a previous /sync", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
httpBackend.when("GET", "/sync").respond(200, {
|
||||
next_batch: "s_5_4",
|
||||
rooms: {
|
||||
@@ -340,7 +343,7 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
client.on("sync", function() {
|
||||
client.getEventTimeline(timelineSet, EVENTS[2].event_id
|
||||
client.getEventTimeline(timelineSet, EVENTS[2].event_id,
|
||||
).then(function(tl) {
|
||||
expect(tl.getEvents().length).toEqual(4);
|
||||
expect(tl.getEvents()[0].event).toEqual(EVENTS[1]);
|
||||
@@ -358,8 +361,8 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
it("should join timelines where they overlap a previous /context",
|
||||
function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
|
||||
// we fetch event 0, then 2, then 3, and finally 1. 1 is returned
|
||||
// with context which joins them all up.
|
||||
@@ -415,15 +418,15 @@ describe("MatrixClient event timelines", function() {
|
||||
};
|
||||
});
|
||||
|
||||
var tl0, tl2, tl3;
|
||||
client.getEventTimeline(timelineSet, EVENTS[0].event_id
|
||||
let tl0;
|
||||
let tl3;
|
||||
client.getEventTimeline(timelineSet, EVENTS[0].event_id,
|
||||
).then(function(tl) {
|
||||
expect(tl.getEvents().length).toEqual(1);
|
||||
tl0 = tl;
|
||||
return client.getEventTimeline(timelineSet, EVENTS[2].event_id);
|
||||
}).then(function(tl) {
|
||||
expect(tl.getEvents().length).toEqual(1);
|
||||
tl2 = tl;
|
||||
return client.getEventTimeline(timelineSet, EVENTS[3].event_id);
|
||||
}).then(function(tl) {
|
||||
expect(tl.getEvents().length).toEqual(1);
|
||||
@@ -452,8 +455,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should fail gracefully if there is no event field", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
// we fetch event 0, then 2, then 3, and finally 1. 1 is returned
|
||||
// with context which joins them all up.
|
||||
httpBackend.when("GET", "/rooms/!foo%3Abar/context/event1")
|
||||
@@ -467,7 +470,7 @@ describe("MatrixClient event timelines", function() {
|
||||
};
|
||||
});
|
||||
|
||||
client.getEventTimeline(timelineSet, "event1"
|
||||
client.getEventTimeline(timelineSet, "event1",
|
||||
).then(function(tl) {
|
||||
// could do with a fail()
|
||||
expect(true).toBeFalsy();
|
||||
@@ -481,8 +484,8 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
describe("paginateEventTimeline", function() {
|
||||
it("should allow you to paginate backwards", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
|
||||
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
|
||||
encodeURIComponent(EVENTS[0].event_id))
|
||||
@@ -499,7 +502,7 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
httpBackend.when("GET", "/rooms/!foo%3Abar/messages")
|
||||
.check(function(req) {
|
||||
var params = req.queryParams;
|
||||
const params = req.queryParams;
|
||||
expect(params.dir).toEqual("b");
|
||||
expect(params.from).toEqual("start_token0");
|
||||
expect(params.limit).toEqual(30);
|
||||
@@ -510,8 +513,8 @@ describe("MatrixClient event timelines", function() {
|
||||
};
|
||||
});
|
||||
|
||||
var tl;
|
||||
client.getEventTimeline(timelineSet, EVENTS[0].event_id
|
||||
let tl;
|
||||
client.getEventTimeline(timelineSet, EVENTS[0].event_id,
|
||||
).then(function(tl0) {
|
||||
tl = tl0;
|
||||
return client.paginateEventTimeline(tl, {backwards: true});
|
||||
@@ -532,8 +535,8 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
|
||||
it("should allow you to paginate forwards", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
|
||||
httpBackend.when("GET", "/rooms/!foo%3Abar/context/" +
|
||||
encodeURIComponent(EVENTS[0].event_id))
|
||||
@@ -550,7 +553,7 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
httpBackend.when("GET", "/rooms/!foo%3Abar/messages")
|
||||
.check(function(req) {
|
||||
var params = req.queryParams;
|
||||
const params = req.queryParams;
|
||||
expect(params.dir).toEqual("f");
|
||||
expect(params.from).toEqual("end_token0");
|
||||
expect(params.limit).toEqual(20);
|
||||
@@ -561,8 +564,8 @@ describe("MatrixClient event timelines", function() {
|
||||
};
|
||||
});
|
||||
|
||||
var tl;
|
||||
client.getEventTimeline(timelineSet, EVENTS[0].event_id
|
||||
let tl;
|
||||
client.getEventTimeline(timelineSet, EVENTS[0].event_id,
|
||||
).then(function(tl0) {
|
||||
tl = tl0;
|
||||
return client.paginateEventTimeline(
|
||||
@@ -584,8 +587,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
describe("event timeline for sent events", function() {
|
||||
var TXN_ID = "txn1";
|
||||
var event = utils.mkMessage({
|
||||
const TXN_ID = "txn1";
|
||||
const event = utils.mkMessage({
|
||||
room: roomId, user: userId, msg: "a body",
|
||||
});
|
||||
event.unsigned = {transaction_id: TXN_ID};
|
||||
@@ -604,7 +607,7 @@ describe("MatrixClient event timelines", function() {
|
||||
"!foo:bar": {
|
||||
timeline: {
|
||||
events: [
|
||||
event
|
||||
event,
|
||||
],
|
||||
prev_batch: "f_1_1",
|
||||
},
|
||||
@@ -615,8 +618,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should work when /send returns before /sync", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
|
||||
client.sendTextMessage(roomId, "a body", TXN_ID).then(function(res) {
|
||||
expect(res.event_id).toEqual(event.event_id);
|
||||
@@ -639,8 +642,8 @@ describe("MatrixClient event timelines", function() {
|
||||
});
|
||||
|
||||
it("should work when /send returns after /sync", function(done) {
|
||||
var room = client.getRoom(roomId);
|
||||
var timelineSet = room.getTimelineSets()[0];
|
||||
const room = client.getRoom(roomId);
|
||||
const timelineSet = room.getTimelineSets()[0];
|
||||
|
||||
// initiate the send, and set up checks to be done when it completes
|
||||
// - but note that it won't complete until after the /sync does, below.
|
||||
@@ -672,18 +675,18 @@ describe("MatrixClient event timelines", function() {
|
||||
// https://github.com/vector-im/vector-web/issues/1389
|
||||
|
||||
// a state event, followed by a redaction thereof
|
||||
var event = utils.mkMembership({
|
||||
room: roomId, mship: "join", user: otherUserId
|
||||
const event = utils.mkMembership({
|
||||
room: roomId, mship: "join", user: otherUserId,
|
||||
});
|
||||
var redaction = utils.mkEvent({
|
||||
const redaction = utils.mkEvent({
|
||||
type: "m.room.redaction",
|
||||
room_id: roomId,
|
||||
sender: otherUserId,
|
||||
content: {}
|
||||
content: {},
|
||||
});
|
||||
redaction.redacts = event.event_id;
|
||||
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "batch1",
|
||||
rooms: {
|
||||
join: {},
|
||||
@@ -701,12 +704,12 @@ describe("MatrixClient event timelines", function() {
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
httpBackend.flush().then(function() {
|
||||
var room = client.getRoom(roomId);
|
||||
var tl = room.getLiveTimeline();
|
||||
const room = client.getRoom(roomId);
|
||||
const tl = room.getLiveTimeline();
|
||||
expect(tl.getEvents().length).toEqual(3);
|
||||
expect(tl.getEvents()[1].isRedacted()).toBe(true);
|
||||
|
||||
var sync2 = {
|
||||
const sync2 = {
|
||||
next_batch: "batch2",
|
||||
rooms: {
|
||||
join: {},
|
||||
@@ -716,7 +719,7 @@ describe("MatrixClient event timelines", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: otherUserId, msg: "world"
|
||||
room: roomId, user: otherUserId, msg: "world",
|
||||
}),
|
||||
],
|
||||
limited: true,
|
||||
@@ -727,8 +730,8 @@ describe("MatrixClient event timelines", function() {
|
||||
|
||||
return httpBackend.flush();
|
||||
}).then(function() {
|
||||
var room = client.getRoom(roomId);
|
||||
var tl = room.getLiveTimeline();
|
||||
const room = client.getRoom(roomId);
|
||||
const tl = room.getLiveTimeline();
|
||||
expect(tl.getEvents().length).toEqual(1);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var publicGlobals = require("../../lib/matrix");
|
||||
var Room = publicGlobals.Room;
|
||||
var MatrixInMemoryStore = publicGlobals.MatrixInMemoryStore;
|
||||
var Filter = publicGlobals.Filter;
|
||||
var utils = require("../test-utils");
|
||||
var MockStorageApi = require("../MockStorageApi");
|
||||
const sdk = require("../..");
|
||||
const HttpBackend = require("../mock-request");
|
||||
const publicGlobals = require("../../lib/matrix");
|
||||
const Room = publicGlobals.Room;
|
||||
const MatrixInMemoryStore = publicGlobals.MatrixInMemoryStore;
|
||||
const Filter = publicGlobals.Filter;
|
||||
const utils = require("../test-utils");
|
||||
const MockStorageApi = require("../MockStorageApi");
|
||||
|
||||
describe("MatrixClient", function() {
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var client, httpBackend, store, sessionStore;
|
||||
var userId = "@alice:localhost";
|
||||
var accessToken = "aseukfgwef";
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
let client = null;
|
||||
let httpBackend = null;
|
||||
let store = null;
|
||||
let sessionStore = null;
|
||||
const userId = "@alice:localhost";
|
||||
const accessToken = "aseukfgwef";
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
store = new MatrixInMemoryStore();
|
||||
|
||||
var mockStorage = new MockStorageApi();
|
||||
const mockStorage = new MockStorageApi();
|
||||
sessionStore = new sdk.WebStorageSessionStore(mockStorage);
|
||||
|
||||
sdk.request(httpBackend.requestFn);
|
||||
@@ -38,10 +41,10 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("uploadContent", function() {
|
||||
var buf = new Buffer('hello world');
|
||||
const buf = new Buffer('hello world');
|
||||
it("should upload the file", function(done) {
|
||||
httpBackend.when(
|
||||
"POST", "/_matrix/media/v1/upload"
|
||||
"POST", "/_matrix/media/v1/upload",
|
||||
).check(function(req) {
|
||||
expect(req.data).toEqual(buf);
|
||||
expect(req.queryParams.filename).toEqual("hi.txt");
|
||||
@@ -51,7 +54,7 @@ describe("MatrixClient", function() {
|
||||
expect(req.opts.timeout).toBe(undefined);
|
||||
}).respond(200, "content");
|
||||
|
||||
var prom = client.uploadContent({
|
||||
const prom = client.uploadContent({
|
||||
stream: buf,
|
||||
name: "hi.txt",
|
||||
type: "text/plain",
|
||||
@@ -59,7 +62,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
expect(prom).toBeDefined();
|
||||
|
||||
var uploads = client.getCurrentUploads();
|
||||
const uploads = client.getCurrentUploads();
|
||||
expect(uploads.length).toEqual(1);
|
||||
expect(uploads[0].promise).toBe(prom);
|
||||
expect(uploads[0].loaded).toEqual(0);
|
||||
@@ -68,7 +71,7 @@ describe("MatrixClient", function() {
|
||||
// for backwards compatibility, we return the raw JSON
|
||||
expect(response).toEqual("content");
|
||||
|
||||
var uploads = client.getCurrentUploads();
|
||||
const uploads = client.getCurrentUploads();
|
||||
expect(uploads.length).toEqual(0);
|
||||
}).catch(utils.failTest).done(done);
|
||||
|
||||
@@ -77,7 +80,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
it("should parse the response if rawResponse=false", function(done) {
|
||||
httpBackend.when(
|
||||
"POST", "/_matrix/media/v1/upload"
|
||||
"POST", "/_matrix/media/v1/upload",
|
||||
).check(function(req) {
|
||||
expect(req.opts.json).toBeFalsy();
|
||||
}).respond(200, JSON.stringify({ "content_uri": "uri" }));
|
||||
@@ -98,7 +101,7 @@ describe("MatrixClient", function() {
|
||||
it("should parse errors into a MatrixError", function(done) {
|
||||
// opts.json is false, so request returns unparsed json.
|
||||
httpBackend.when(
|
||||
"POST", "/_matrix/media/v1/upload"
|
||||
"POST", "/_matrix/media/v1/upload",
|
||||
).check(function(req) {
|
||||
expect(req.data).toEqual(buf);
|
||||
expect(req.opts.json).toBeFalsy();
|
||||
@@ -123,13 +126,13 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
it("should return a promise which can be cancelled", function(done) {
|
||||
var prom = client.uploadContent({
|
||||
const prom = client.uploadContent({
|
||||
stream: buf,
|
||||
name: "hi.txt",
|
||||
type: "text/plain",
|
||||
});
|
||||
|
||||
var uploads = client.getCurrentUploads();
|
||||
const uploads = client.getCurrentUploads();
|
||||
expect(uploads.length).toEqual(1);
|
||||
expect(uploads[0].promise).toBe(prom);
|
||||
expect(uploads[0].loaded).toEqual(0);
|
||||
@@ -139,23 +142,23 @@ describe("MatrixClient", function() {
|
||||
}, function(error) {
|
||||
expect(error).toEqual("aborted");
|
||||
|
||||
var uploads = client.getCurrentUploads();
|
||||
const uploads = client.getCurrentUploads();
|
||||
expect(uploads.length).toEqual(0);
|
||||
}).catch(utils.failTest).done(done);
|
||||
|
||||
var r = client.cancelUpload(prom);
|
||||
const r = client.cancelUpload(prom);
|
||||
expect(r).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("joinRoom", function() {
|
||||
it("should no-op if you've already joined a room", function() {
|
||||
var roomId = "!foo:bar";
|
||||
var room = new Room(roomId);
|
||||
const roomId = "!foo:bar";
|
||||
const room = new Room(roomId);
|
||||
room.addLiveEvents([
|
||||
utils.mkMembership({
|
||||
user: userId, room: roomId, mship: "join", event: true
|
||||
})
|
||||
user: userId, room: roomId, mship: "join", event: true,
|
||||
}),
|
||||
]);
|
||||
store.storeRoom(room);
|
||||
client.joinRoom(roomId);
|
||||
@@ -164,11 +167,11 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("getFilter", function() {
|
||||
var filterId = "f1lt3r1d";
|
||||
const filterId = "f1lt3r1d";
|
||||
|
||||
it("should return a filter from the store if allowCached", function(done) {
|
||||
var filter = Filter.fromJson(userId, filterId, {
|
||||
event_format: "client"
|
||||
const filter = Filter.fromJson(userId, filterId, {
|
||||
event_format: "client",
|
||||
});
|
||||
store.storeFilter(filter);
|
||||
client.getFilter(userId, filterId, true).done(function(gotFilter) {
|
||||
@@ -180,16 +183,16 @@ describe("MatrixClient", function() {
|
||||
|
||||
it("should do an HTTP request if !allowCached even if one exists",
|
||||
function(done) {
|
||||
var httpFilterDefinition = {
|
||||
event_format: "federation"
|
||||
const httpFilterDefinition = {
|
||||
event_format: "federation",
|
||||
};
|
||||
|
||||
httpBackend.when(
|
||||
"GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId
|
||||
"GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId,
|
||||
).respond(200, httpFilterDefinition);
|
||||
|
||||
var storeFilter = Filter.fromJson(userId, filterId, {
|
||||
event_format: "client"
|
||||
const storeFilter = Filter.fromJson(userId, filterId, {
|
||||
event_format: "client",
|
||||
});
|
||||
store.storeFilter(storeFilter);
|
||||
client.getFilter(userId, filterId, false).done(function(gotFilter) {
|
||||
@@ -202,13 +205,13 @@ describe("MatrixClient", function() {
|
||||
|
||||
it("should do an HTTP request if nothing is in the cache and then store it",
|
||||
function(done) {
|
||||
var httpFilterDefinition = {
|
||||
event_format: "federation"
|
||||
const httpFilterDefinition = {
|
||||
event_format: "federation",
|
||||
};
|
||||
expect(store.getFilter(userId, filterId)).toBeNull();
|
||||
|
||||
httpBackend.when(
|
||||
"GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId
|
||||
"GET", "/user/" + encodeURIComponent(userId) + "/filter/" + filterId,
|
||||
).respond(200, httpFilterDefinition);
|
||||
client.getFilter(userId, filterId, true).done(function(gotFilter) {
|
||||
expect(gotFilter.getDefinition()).toEqual(httpFilterDefinition);
|
||||
@@ -221,21 +224,21 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("createFilter", function() {
|
||||
var filterId = "f1llllllerid";
|
||||
const filterId = "f1llllllerid";
|
||||
|
||||
it("should do an HTTP request and then store the filter", function(done) {
|
||||
expect(store.getFilter(userId, filterId)).toBeNull();
|
||||
|
||||
var filterDefinition = {
|
||||
event_format: "client"
|
||||
const filterDefinition = {
|
||||
event_format: "client",
|
||||
};
|
||||
|
||||
httpBackend.when(
|
||||
"POST", "/user/" + encodeURIComponent(userId) + "/filter"
|
||||
"POST", "/user/" + encodeURIComponent(userId) + "/filter",
|
||||
).check(function(req) {
|
||||
expect(req.data).toEqual(filterDefinition);
|
||||
}).respond(200, {
|
||||
filter_id: filterId
|
||||
filter_id: filterId,
|
||||
});
|
||||
|
||||
client.createFilter(filterDefinition).done(function(gotFilter) {
|
||||
@@ -249,8 +252,7 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("searching", function() {
|
||||
|
||||
var response = {
|
||||
const response = {
|
||||
search_categories: {
|
||||
room_events: {
|
||||
count: 24,
|
||||
@@ -263,26 +265,26 @@ describe("MatrixClient", function() {
|
||||
room_id: "!feuiwhf:localhost",
|
||||
content: {
|
||||
body: "a result",
|
||||
msgtype: "m.text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
msgtype: "m.text",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it("searchMessageText should perform a /search for room_events", function(done) {
|
||||
client.searchMessageText({
|
||||
query: "monkeys"
|
||||
query: "monkeys",
|
||||
});
|
||||
httpBackend.when("POST", "/search").check(function(req) {
|
||||
expect(req.data).toEqual({
|
||||
search_categories: {
|
||||
room_events: {
|
||||
search_term: "monkeys"
|
||||
}
|
||||
}
|
||||
search_term: "monkeys",
|
||||
},
|
||||
},
|
||||
});
|
||||
}).respond(200, response);
|
||||
|
||||
@@ -295,9 +297,9 @@ describe("MatrixClient", function() {
|
||||
|
||||
describe("downloadKeys", function() {
|
||||
it("should do an HTTP request and then store the keys", function(done) {
|
||||
var ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78";
|
||||
const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78";
|
||||
// ed25519key = client.getDeviceEd25519Key();
|
||||
var borisKeys = {
|
||||
const borisKeys = {
|
||||
dev1: {
|
||||
algorithms: ["1"],
|
||||
device_id: "dev1",
|
||||
@@ -306,14 +308,14 @@ describe("MatrixClient", function() {
|
||||
boris: {
|
||||
"ed25519:dev1":
|
||||
"RAhmbNDq1efK3hCpBzZDsKoGSsrHUxb25NW5/WbEV9R" +
|
||||
"JVwLdP032mg5QsKt/pBDUGtggBcnk43n3nBWlA88WAw"
|
||||
}
|
||||
"JVwLdP032mg5QsKt/pBDUGtggBcnk43n3nBWlA88WAw",
|
||||
},
|
||||
},
|
||||
unsigned: { "abc": "def" },
|
||||
user_id: "boris",
|
||||
}
|
||||
},
|
||||
};
|
||||
var chazKeys = {
|
||||
const chazKeys = {
|
||||
dev2: {
|
||||
algorithms: ["2"],
|
||||
device_id: "dev2",
|
||||
@@ -322,12 +324,12 @@ describe("MatrixClient", function() {
|
||||
chaz: {
|
||||
"ed25519:dev2":
|
||||
"FwslH/Q7EYSb7swDJbNB5PSzcbEO1xRRBF1riuijqvL" +
|
||||
"EkrK9/XVN8jl4h7thGuRITQ01siBQnNmMK9t45QfcCQ"
|
||||
}
|
||||
"EkrK9/XVN8jl4h7thGuRITQ01siBQnNmMK9t45QfcCQ",
|
||||
},
|
||||
},
|
||||
unsigned: { "ghi": "def" },
|
||||
user_id: "chaz",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -345,7 +347,11 @@ describe("MatrixClient", function() {
|
||||
*/
|
||||
|
||||
httpBackend.when("POST", "/keys/query").check(function(req) {
|
||||
expect(req.data).toEqual({device_keys: {boris: {}, chaz: {}}});
|
||||
expect(req.data).toEqual({device_keys: {
|
||||
'@alice:localhost': {},
|
||||
'boris': {},
|
||||
'chaz': {},
|
||||
}});
|
||||
}).respond(200, {
|
||||
device_keys: {
|
||||
boris: borisKeys,
|
||||
@@ -363,7 +369,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
assertObjectContains(res.chaz.dev2, {
|
||||
verified: 0, // DeviceVerification.UNVERIFIED
|
||||
keys: { "ed25519:dev2" : ed25519key },
|
||||
keys: { "ed25519:dev2": ed25519key },
|
||||
algorithms: ["2"],
|
||||
unsigned: { "ghi": "def" },
|
||||
});
|
||||
@@ -371,34 +377,19 @@ describe("MatrixClient", function() {
|
||||
|
||||
httpBackend.flush();
|
||||
});
|
||||
|
||||
it("should return a rejected promise if the request fails", function(done) {
|
||||
httpBackend.when("POST", "/keys/query").respond(400);
|
||||
|
||||
var exceptionThrown;
|
||||
client.downloadKeys(["bottom"]).then(function() {
|
||||
fail("download didn't fail");
|
||||
}, function(err) {
|
||||
exceptionThrown = err;
|
||||
}).then(function() {
|
||||
expect(exceptionThrown).toBeTruthy();
|
||||
}).catch(utils.failTest).done(done);
|
||||
|
||||
httpBackend.flush();
|
||||
});
|
||||
});
|
||||
|
||||
describe("deleteDevice", function() {
|
||||
var auth = {a: 1};
|
||||
const auth = {a: 1};
|
||||
it("should pass through an auth dict", function(done) {
|
||||
httpBackend.when(
|
||||
"DELETE", "/_matrix/client/unstable/devices/my_device"
|
||||
"DELETE", "/_matrix/client/unstable/devices/my_device",
|
||||
).check(function(req) {
|
||||
expect(req.data).toEqual({auth: auth});
|
||||
}).respond(200);
|
||||
|
||||
client.deleteDevice(
|
||||
"my_device", auth
|
||||
"my_device", auth,
|
||||
).catch(utils.failTest).done(done);
|
||||
|
||||
httpBackend.flush();
|
||||
@@ -407,7 +398,7 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
function assertObjectContains(obj, expected) {
|
||||
for (var k in expected) {
|
||||
for (const k in expected) {
|
||||
if (expected.hasOwnProperty(k)) {
|
||||
expect(obj[k]).toEqual(expected[k]);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var MatrixClient = sdk.MatrixClient;
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const MatrixClient = sdk.MatrixClient;
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("MatrixClient opts", function() {
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var client, httpBackend;
|
||||
var userId = "@alice:localhost";
|
||||
var userB = "@bob:localhost";
|
||||
var accessToken = "aseukfgwef";
|
||||
var roomId = "!foo:bar";
|
||||
var syncData = {
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
let client = null;
|
||||
let httpBackend = null;
|
||||
const userId = "@alice:localhost";
|
||||
const userB = "@bob:localhost";
|
||||
const accessToken = "aseukfgwef";
|
||||
const roomId = "!foo:bar";
|
||||
const syncData = {
|
||||
next_batch: "s_5_3",
|
||||
presence: {},
|
||||
rooms: {
|
||||
@@ -20,40 +21,40 @@ describe("MatrixClient opts", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: userB, msg: "hello"
|
||||
})
|
||||
room: roomId, user: userB, msg: "hello",
|
||||
}),
|
||||
],
|
||||
prev_batch: "f_1_1"
|
||||
prev_batch: "f_1_1",
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: userB,
|
||||
content: {
|
||||
name: "Old room name"
|
||||
}
|
||||
name: "Old room name",
|
||||
},
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "join", user: userB, name: "Bob"
|
||||
room: roomId, mship: "join", user: userB, name: "Bob",
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "join", user: userId, name: "Alice"
|
||||
room: roomId, mship: "join", user: userId, name: "Alice",
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomId, user: userId,
|
||||
content: {
|
||||
creator: userId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
creator: userId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
});
|
||||
|
||||
@@ -69,7 +70,7 @@ describe("MatrixClient opts", function() {
|
||||
baseUrl: baseUrl,
|
||||
userId: userId,
|
||||
accessToken: accessToken,
|
||||
scheduler: new sdk.MatrixScheduler()
|
||||
scheduler: new sdk.MatrixScheduler(),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -78,9 +79,9 @@ describe("MatrixClient opts", function() {
|
||||
});
|
||||
|
||||
it("should be able to send messages", function(done) {
|
||||
var eventId = "$flibble:wibble";
|
||||
const eventId = "$flibble:wibble";
|
||||
httpBackend.when("PUT", "/txn1").respond(200, {
|
||||
event_id: eventId
|
||||
event_id: eventId,
|
||||
});
|
||||
client.sendTextMessage("!foo:bar", "a body", "txn1").done(function(res) {
|
||||
expect(res.event_id).toEqual(eventId);
|
||||
@@ -90,16 +91,16 @@ describe("MatrixClient opts", function() {
|
||||
});
|
||||
|
||||
it("should be able to sync / get new events", function(done) {
|
||||
var expectedEventTypes = [ // from /initialSync
|
||||
const expectedEventTypes = [ // from /initialSync
|
||||
"m.room.message", "m.room.name", "m.room.member", "m.room.member",
|
||||
"m.room.create"
|
||||
"m.room.create",
|
||||
];
|
||||
client.on("event", function(event) {
|
||||
expect(expectedEventTypes.indexOf(event.getType())).not.toEqual(
|
||||
-1, "Recv unexpected event type: " + event.getType()
|
||||
-1, "Recv unexpected event type: " + event.getType(),
|
||||
);
|
||||
expectedEventTypes.splice(
|
||||
expectedEventTypes.indexOf(event.getType()), 1
|
||||
expectedEventTypes.indexOf(event.getType()), 1,
|
||||
);
|
||||
});
|
||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||
@@ -112,7 +113,7 @@ describe("MatrixClient opts", function() {
|
||||
return httpBackend.flush("/sync", 1);
|
||||
}).done(function() {
|
||||
expect(expectedEventTypes.length).toEqual(
|
||||
0, "Expected to see event types: " + expectedEventTypes
|
||||
0, "Expected to see event types: " + expectedEventTypes,
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -127,14 +128,14 @@ describe("MatrixClient opts", function() {
|
||||
baseUrl: baseUrl,
|
||||
userId: userId,
|
||||
accessToken: accessToken,
|
||||
scheduler: undefined
|
||||
scheduler: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("shouldn't retry sending events", function(done) {
|
||||
httpBackend.when("PUT", "/txn1").fail(500, {
|
||||
errcode: "M_SOMETHING",
|
||||
error: "Ruh roh"
|
||||
error: "Ruh roh",
|
||||
});
|
||||
client.sendTextMessage("!foo:bar", "a body", "txn1").done(function(res) {
|
||||
expect(false).toBe(true, "sendTextMessage resolved but shouldn't");
|
||||
@@ -147,13 +148,13 @@ describe("MatrixClient opts", function() {
|
||||
|
||||
it("shouldn't queue events", function(done) {
|
||||
httpBackend.when("PUT", "/txn1").respond(200, {
|
||||
event_id: "AAA"
|
||||
event_id: "AAA",
|
||||
});
|
||||
httpBackend.when("PUT", "/txn2").respond(200, {
|
||||
event_id: "BBB"
|
||||
event_id: "BBB",
|
||||
});
|
||||
var sentA = false;
|
||||
var sentB = false;
|
||||
let sentA = false;
|
||||
let sentB = false;
|
||||
client.sendTextMessage("!foo:bar", "a body", "txn1").done(function(res) {
|
||||
sentA = true;
|
||||
expect(sentB).toBe(true);
|
||||
@@ -171,7 +172,7 @@ describe("MatrixClient opts", function() {
|
||||
|
||||
it("should be able to send messages", function(done) {
|
||||
httpBackend.when("PUT", "/txn1").respond(200, {
|
||||
event_id: "foo"
|
||||
event_id: "foo",
|
||||
});
|
||||
client.sendTextMessage("!foo:bar", "a body", "txn1").done(function(res) {
|
||||
expect(res.event_id).toEqual("foo");
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
var EventStatus = sdk.EventStatus;
|
||||
const sdk = require("../..");
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../test-utils");
|
||||
const EventStatus = sdk.EventStatus;
|
||||
|
||||
describe("MatrixClient retrying", function() {
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var client, httpBackend;
|
||||
var scheduler;
|
||||
var userId = "@alice:localhost";
|
||||
var accessToken = "aseukfgwef";
|
||||
var roomId = "!room:here";
|
||||
var room;
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
let client = null;
|
||||
let httpBackend = null;
|
||||
let scheduler;
|
||||
const userId = "@alice:localhost";
|
||||
const accessToken = "aseukfgwef";
|
||||
const roomId = "!room:here";
|
||||
let room;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
sdk.request(httpBackend.requestFn);
|
||||
scheduler = new sdk.MatrixScheduler();
|
||||
@@ -49,9 +50,7 @@ describe("MatrixClient retrying", function() {
|
||||
});
|
||||
|
||||
it("should mark events as EventStatus.CANCELLED when cancelled", function(done) {
|
||||
|
||||
// send a couple of events; the second will be queued
|
||||
var ev1, ev2;
|
||||
client.sendMessage(roomId, "m1").then(function(ev) {
|
||||
expect(ev).toEqual(ev1);
|
||||
});
|
||||
@@ -60,10 +59,10 @@ describe("MatrixClient retrying", function() {
|
||||
});
|
||||
|
||||
// both events should be in the timeline at this point
|
||||
var tl = room.getLiveTimeline().getEvents();
|
||||
const tl = room.getLiveTimeline().getEvents();
|
||||
expect(tl.length).toEqual(2);
|
||||
ev1 = tl[0];
|
||||
ev2 = tl[1];
|
||||
const ev1 = tl[0];
|
||||
const ev2 = tl[1];
|
||||
|
||||
expect(ev1.status).toEqual(EventStatus.SENDING);
|
||||
expect(ev2.status).toEqual(EventStatus.SENDING);
|
||||
@@ -79,8 +78,9 @@ describe("MatrixClient retrying", function() {
|
||||
expect(tl.length).toEqual(1);
|
||||
|
||||
// shouldn't be able to cancel the first message yet
|
||||
expect(function() { client.cancelPendingEvent(ev1); })
|
||||
.toThrow();
|
||||
expect(function() {
|
||||
client.cancelPendingEvent(ev1);
|
||||
}).toThrow();
|
||||
}).respond(400); // fail the first message
|
||||
|
||||
httpBackend.flush().then(function() {
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var EventStatus = sdk.EventStatus;
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const EventStatus = sdk.EventStatus;
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("MatrixClient room timelines", function() {
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var client, httpBackend;
|
||||
var userId = "@alice:localhost";
|
||||
var userName = "Alice";
|
||||
var accessToken = "aseukfgwef";
|
||||
var roomId = "!foo:bar";
|
||||
var otherUserId = "@bob:localhost";
|
||||
var USER_MEMBERSHIP_EVENT = utils.mkMembership({
|
||||
room: roomId, mship: "join", user: userId, name: userName
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
let client = null;
|
||||
let httpBackend = null;
|
||||
const userId = "@alice:localhost";
|
||||
const userName = "Alice";
|
||||
const accessToken = "aseukfgwef";
|
||||
const roomId = "!foo:bar";
|
||||
const otherUserId = "@bob:localhost";
|
||||
const USER_MEMBERSHIP_EVENT = utils.mkMembership({
|
||||
room: roomId, mship: "join", user: userId, name: userName,
|
||||
});
|
||||
var ROOM_NAME_EVENT = utils.mkEvent({
|
||||
const ROOM_NAME_EVENT = utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: otherUserId,
|
||||
content: {
|
||||
name: "Old room name"
|
||||
}
|
||||
name: "Old room name",
|
||||
},
|
||||
});
|
||||
var NEXT_SYNC_DATA;
|
||||
var SYNC_DATA = {
|
||||
let NEXT_SYNC_DATA;
|
||||
const SYNC_DATA = {
|
||||
next_batch: "s_5_3",
|
||||
rooms: {
|
||||
join: {
|
||||
@@ -30,30 +31,30 @@ describe("MatrixClient room timelines", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: otherUserId, msg: "hello"
|
||||
})
|
||||
room: roomId, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
],
|
||||
prev_batch: "f_1_1"
|
||||
prev_batch: "f_1_1",
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
ROOM_NAME_EVENT,
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "join",
|
||||
user: otherUserId, name: "Bob"
|
||||
user: otherUserId, name: "Bob",
|
||||
}),
|
||||
USER_MEMBERSHIP_EVENT,
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomId, user: userId,
|
||||
content: {
|
||||
creator: userId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
creator: userId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function setNextSyncData(events) {
|
||||
@@ -67,11 +68,11 @@ describe("MatrixClient room timelines", function() {
|
||||
"!foo:bar": {
|
||||
timeline: { events: [] },
|
||||
state: { events: [] },
|
||||
ephemeral: { events: [] }
|
||||
}
|
||||
ephemeral: { events: [] },
|
||||
},
|
||||
},
|
||||
leave: {}
|
||||
}
|
||||
leave: {},
|
||||
},
|
||||
};
|
||||
events.forEach(function(e) {
|
||||
if (e.room_id !== roomId) {
|
||||
@@ -81,7 +82,7 @@ describe("MatrixClient room timelines", function() {
|
||||
if (e.__prev_event === undefined) {
|
||||
throw new Error(
|
||||
"setNextSyncData needs the prev state set to '__prev_event' " +
|
||||
"for " + e.type
|
||||
"for " + e.type,
|
||||
);
|
||||
}
|
||||
if (e.__prev_event !== null) {
|
||||
@@ -90,18 +91,16 @@ describe("MatrixClient room timelines", function() {
|
||||
}
|
||||
// push the current
|
||||
NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e);
|
||||
}
|
||||
else if (["m.typing", "m.receipt"].indexOf(e.type) !== -1) {
|
||||
} else if (["m.typing", "m.receipt"].indexOf(e.type) !== -1) {
|
||||
NEXT_SYNC_DATA.rooms.join[roomId].ephemeral.events.push(e);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
NEXT_SYNC_DATA.rooms.join[roomId].timeline.events.push(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function(done) {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
sdk.request(httpBackend.requestFn);
|
||||
client = sdk.createClient({
|
||||
@@ -130,12 +129,13 @@ describe("MatrixClient room timelines", function() {
|
||||
});
|
||||
|
||||
describe("local echo events", function() {
|
||||
|
||||
it("should be added immediately after calling MatrixClient.sendEvent " +
|
||||
"with EventStatus.SENDING and the right event.sender", function(done) {
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
|
||||
client.sendTextMessage(roomId, "I am a fish", "txn1");
|
||||
@@ -144,7 +144,7 @@ describe("MatrixClient room timelines", function() {
|
||||
// check status
|
||||
expect(room.timeline[1].status).toEqual(EventStatus.SENDING);
|
||||
// check member
|
||||
var member = room.timeline[1].sender;
|
||||
const member = room.timeline[1].sender;
|
||||
expect(member.userId).toEqual(userId);
|
||||
expect(member.name).toEqual(userName);
|
||||
|
||||
@@ -157,21 +157,23 @@ describe("MatrixClient room timelines", function() {
|
||||
|
||||
it("should be updated correctly when the send request finishes " +
|
||||
"BEFORE the event comes down the event stream", function(done) {
|
||||
var eventId = "$foo:bar";
|
||||
const eventId = "$foo:bar";
|
||||
httpBackend.when("PUT", "/txn1").respond(200, {
|
||||
event_id: eventId
|
||||
event_id: eventId,
|
||||
});
|
||||
|
||||
var ev = utils.mkMessage({
|
||||
body: "I am a fish", user: userId, room: roomId
|
||||
const ev = utils.mkMessage({
|
||||
body: "I am a fish", user: userId, room: roomId,
|
||||
});
|
||||
ev.event_id = eventId;
|
||||
ev.unsigned = {transaction_id: "txn1"};
|
||||
setNextSyncData([ev]);
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
client.sendTextMessage(roomId, "I am a fish", "txn1").done(
|
||||
function() {
|
||||
expect(room.timeline[1].getId()).toEqual(eventId);
|
||||
@@ -187,22 +189,24 @@ describe("MatrixClient room timelines", function() {
|
||||
|
||||
it("should be updated correctly when the send request finishes " +
|
||||
"AFTER the event comes down the event stream", function(done) {
|
||||
var eventId = "$foo:bar";
|
||||
const eventId = "$foo:bar";
|
||||
httpBackend.when("PUT", "/txn1").respond(200, {
|
||||
event_id: eventId
|
||||
event_id: eventId,
|
||||
});
|
||||
|
||||
var ev = utils.mkMessage({
|
||||
body: "I am a fish", user: userId, room: roomId
|
||||
const ev = utils.mkMessage({
|
||||
body: "I am a fish", user: userId, room: roomId,
|
||||
});
|
||||
ev.event_id = eventId;
|
||||
ev.unsigned = {transaction_id: "txn1"};
|
||||
setNextSyncData([ev]);
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
var promise = client.sendTextMessage(roomId, "I am a fish", "txn1");
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
const promise = client.sendTextMessage(roomId, "I am a fish", "txn1");
|
||||
httpBackend.flush("/sync", 1).done(function() {
|
||||
expect(room.timeline.length).toEqual(2);
|
||||
httpBackend.flush("/txn1", 1);
|
||||
@@ -212,15 +216,14 @@ describe("MatrixClient room timelines", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
httpBackend.flush("/sync", 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("paginated events", function() {
|
||||
var sbEvents;
|
||||
var sbEndTok = "pagin_end";
|
||||
let sbEvents;
|
||||
const sbEndTok = "pagin_end";
|
||||
|
||||
beforeEach(function() {
|
||||
sbEvents = [];
|
||||
@@ -228,7 +231,7 @@ describe("MatrixClient room timelines", function() {
|
||||
return {
|
||||
chunk: sbEvents,
|
||||
start: "pagin_start",
|
||||
end: sbEndTok
|
||||
end: sbEndTok,
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -236,8 +239,10 @@ describe("MatrixClient room timelines", function() {
|
||||
it("should set Room.oldState.paginationToken to null at the start" +
|
||||
" of the timeline.", function(done) {
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
|
||||
client.scrollback(room).done(function() {
|
||||
@@ -263,49 +268,51 @@ describe("MatrixClient room timelines", function() {
|
||||
// <Bob> hello
|
||||
|
||||
// make an m.room.member event for alice's join
|
||||
var joinMshipEvent = utils.mkMembership({
|
||||
const joinMshipEvent = utils.mkMembership({
|
||||
mship: "join", user: userId, room: roomId, name: "Old Alice",
|
||||
url: null
|
||||
url: null,
|
||||
});
|
||||
|
||||
// make an m.room.member event with prev_content for alice's nick
|
||||
// change
|
||||
var oldMshipEvent = utils.mkMembership({
|
||||
const oldMshipEvent = utils.mkMembership({
|
||||
mship: "join", user: userId, room: roomId, name: userName,
|
||||
url: "mxc://some/url"
|
||||
url: "mxc://some/url",
|
||||
});
|
||||
oldMshipEvent.prev_content = {
|
||||
displayname: "Old Alice",
|
||||
avatar_url: null,
|
||||
membership: "join"
|
||||
membership: "join",
|
||||
};
|
||||
|
||||
// set the list of events to return on scrollback (/messages)
|
||||
// N.B. synapse returns /messages in reverse chronological order
|
||||
sbEvents = [
|
||||
utils.mkMessage({
|
||||
user: userId, room: roomId, msg: "I'm alice"
|
||||
user: userId, room: roomId, msg: "I'm alice",
|
||||
}),
|
||||
oldMshipEvent,
|
||||
utils.mkMessage({
|
||||
user: userId, room: roomId, msg: "I'm old alice"
|
||||
user: userId, room: roomId, msg: "I'm old alice",
|
||||
}),
|
||||
joinMshipEvent,
|
||||
];
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
// sync response
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
|
||||
client.scrollback(room).done(function() {
|
||||
expect(room.timeline.length).toEqual(5);
|
||||
var joinMsg = room.timeline[0];
|
||||
const joinMsg = room.timeline[0];
|
||||
expect(joinMsg.sender.name).toEqual("Old Alice");
|
||||
var oldMsg = room.timeline[1];
|
||||
const oldMsg = room.timeline[1];
|
||||
expect(oldMsg.sender.name).toEqual("Old Alice");
|
||||
var newMsg = room.timeline[3];
|
||||
const newMsg = room.timeline[3];
|
||||
expect(newMsg.sender.name).toEqual(userName);
|
||||
done();
|
||||
});
|
||||
@@ -320,16 +327,18 @@ describe("MatrixClient room timelines", function() {
|
||||
// set the list of events to return on scrollback
|
||||
sbEvents = [
|
||||
utils.mkMessage({
|
||||
user: userId, room: roomId, msg: "I am new"
|
||||
user: userId, room: roomId, msg: "I am new",
|
||||
}),
|
||||
utils.mkMessage({
|
||||
user: userId, room: roomId, msg: "I am old"
|
||||
})
|
||||
user: userId, room: roomId, msg: "I am old",
|
||||
}),
|
||||
];
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
expect(room.timeline.length).toEqual(1);
|
||||
|
||||
client.scrollback(room).done(function() {
|
||||
@@ -349,13 +358,15 @@ describe("MatrixClient room timelines", function() {
|
||||
// set the list of events to return on scrollback
|
||||
sbEvents = [
|
||||
utils.mkMessage({
|
||||
user: userId, room: roomId, msg: "I am new"
|
||||
})
|
||||
user: userId, room: roomId, msg: "I am new",
|
||||
}),
|
||||
];
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
expect(room.oldState.paginationToken).toBeDefined();
|
||||
|
||||
client.scrollback(room, 1).done(function() {
|
||||
@@ -373,17 +384,19 @@ describe("MatrixClient room timelines", function() {
|
||||
|
||||
describe("new events", function() {
|
||||
it("should be added to the right place in the timeline", function(done) {
|
||||
var eventData = [
|
||||
const eventData = [
|
||||
utils.mkMessage({user: userId, room: roomId}),
|
||||
utils.mkMessage({user: userId, room: roomId}),
|
||||
utils.mkMessage({user: userId, room: roomId})
|
||||
];
|
||||
setNextSyncData(eventData);
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
|
||||
var index = 0;
|
||||
let index = 0;
|
||||
client.on("Room.timeline", function(event, rm, toStart) {
|
||||
expect(toStart).toBe(false);
|
||||
expect(rm).toEqual(room);
|
||||
@@ -396,10 +409,10 @@ describe("MatrixClient room timelines", function() {
|
||||
expect(index).toEqual(2);
|
||||
expect(room.timeline.length).toEqual(3);
|
||||
expect(room.timeline[2].event).toEqual(
|
||||
eventData[1]
|
||||
eventData[1],
|
||||
);
|
||||
expect(room.timeline[1].event).toEqual(
|
||||
eventData[0]
|
||||
eventData[0],
|
||||
);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
@@ -407,22 +420,24 @@ describe("MatrixClient room timelines", function() {
|
||||
});
|
||||
|
||||
it("should set the right event.sender values", function(done) {
|
||||
var eventData = [
|
||||
const eventData = [
|
||||
utils.mkMessage({user: userId, room: roomId}),
|
||||
utils.mkMembership({
|
||||
user: userId, room: roomId, mship: "join", name: "New Name"
|
||||
user: userId, room: roomId, mship: "join", name: "New Name",
|
||||
}),
|
||||
utils.mkMessage({user: userId, room: roomId})
|
||||
utils.mkMessage({user: userId, room: roomId}),
|
||||
];
|
||||
eventData[1].__prev_event = USER_MEMBERSHIP_EVENT;
|
||||
setNextSyncData(eventData);
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
httpBackend.flush("/sync", 1).then(function() {
|
||||
var preNameEvent = room.timeline[room.timeline.length - 3];
|
||||
var postNameEvent = room.timeline[room.timeline.length - 1];
|
||||
const preNameEvent = room.timeline[room.timeline.length - 3];
|
||||
const postNameEvent = room.timeline[room.timeline.length - 1];
|
||||
expect(preNameEvent.sender.name).toEqual(userName);
|
||||
expect(postNameEvent.sender.name).toEqual("New Name");
|
||||
}).catch(utils.failTest).done(done);
|
||||
@@ -431,18 +446,20 @@ describe("MatrixClient room timelines", function() {
|
||||
});
|
||||
|
||||
it("should set the right room.name", function(done) {
|
||||
var secondRoomNameEvent = utils.mkEvent({
|
||||
const secondRoomNameEvent = utils.mkEvent({
|
||||
user: userId, room: roomId, type: "m.room.name", content: {
|
||||
name: "Room 2"
|
||||
}
|
||||
name: "Room 2",
|
||||
},
|
||||
});
|
||||
secondRoomNameEvent.__prev_event = ROOM_NAME_EVENT;
|
||||
setNextSyncData([secondRoomNameEvent]);
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
var nameEmitCount = 0;
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
let nameEmitCount = 0;
|
||||
client.on("Room.name", function(rm) {
|
||||
nameEmitCount += 1;
|
||||
});
|
||||
@@ -451,10 +468,10 @@ describe("MatrixClient room timelines", function() {
|
||||
expect(nameEmitCount).toEqual(1);
|
||||
expect(room.name).toEqual("Room 2");
|
||||
// do another round
|
||||
var thirdRoomNameEvent = utils.mkEvent({
|
||||
const thirdRoomNameEvent = utils.mkEvent({
|
||||
user: userId, room: roomId, type: "m.room.name", content: {
|
||||
name: "Room 3"
|
||||
}
|
||||
name: "Room 3",
|
||||
},
|
||||
});
|
||||
thirdRoomNameEvent.__prev_event = secondRoomNameEvent;
|
||||
setNextSyncData([thirdRoomNameEvent]);
|
||||
@@ -470,32 +487,34 @@ describe("MatrixClient room timelines", function() {
|
||||
});
|
||||
|
||||
it("should set the right room members", function(done) {
|
||||
var userC = "@cee:bar";
|
||||
var userD = "@dee:bar";
|
||||
var eventData = [
|
||||
const userC = "@cee:bar";
|
||||
const userD = "@dee:bar";
|
||||
const eventData = [
|
||||
utils.mkMembership({
|
||||
user: userC, room: roomId, mship: "join", name: "C"
|
||||
user: userC, room: roomId, mship: "join", name: "C",
|
||||
}),
|
||||
utils.mkMembership({
|
||||
user: userC, room: roomId, mship: "invite", skey: userD
|
||||
})
|
||||
user: userC, room: roomId, mship: "invite", skey: userD,
|
||||
}),
|
||||
];
|
||||
eventData[0].__prev_event = null;
|
||||
eventData[1].__prev_event = null;
|
||||
setNextSyncData(eventData);
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
httpBackend.flush("/sync", 1).then(function() {
|
||||
expect(room.currentState.getMembers().length).toEqual(4);
|
||||
expect(room.currentState.getMember(userC).name).toEqual("C");
|
||||
expect(room.currentState.getMember(userC).membership).toEqual(
|
||||
"join"
|
||||
"join",
|
||||
);
|
||||
expect(room.currentState.getMember(userD).name).toEqual(userD);
|
||||
expect(room.currentState.getMember(userD).membership).toEqual(
|
||||
"invite"
|
||||
"invite",
|
||||
);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
@@ -505,15 +524,17 @@ describe("MatrixClient room timelines", function() {
|
||||
|
||||
describe("gappy sync", function() {
|
||||
it("should copy the last known state to the new timeline", function(done) {
|
||||
var eventData = [
|
||||
const eventData = [
|
||||
utils.mkMessage({user: userId, room: roomId}),
|
||||
];
|
||||
setNextSyncData(eventData);
|
||||
NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true;
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
|
||||
httpBackend.flush("/messages", 1);
|
||||
httpBackend.flush("/sync", 1).done(function() {
|
||||
@@ -522,11 +543,11 @@ describe("MatrixClient room timelines", function() {
|
||||
expect(room.currentState.getMembers().length).toEqual(2);
|
||||
expect(room.currentState.getMember(userId).name).toEqual(userName);
|
||||
expect(room.currentState.getMember(userId).membership).toEqual(
|
||||
"join"
|
||||
"join",
|
||||
);
|
||||
expect(room.currentState.getMember(otherUserId).name).toEqual("Bob");
|
||||
expect(room.currentState.getMember(otherUserId).membership).toEqual(
|
||||
"join"
|
||||
"join",
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -535,17 +556,19 @@ describe("MatrixClient room timelines", function() {
|
||||
});
|
||||
|
||||
it("should emit a 'Room.timelineReset' event", function(done) {
|
||||
var eventData = [
|
||||
const eventData = [
|
||||
utils.mkMessage({user: userId, room: roomId}),
|
||||
];
|
||||
setNextSyncData(eventData);
|
||||
NEXT_SYNC_DATA.rooms.join[roomId].timeline.limited = true;
|
||||
|
||||
client.on("sync", function(state) {
|
||||
if (state !== "PREPARED") { return; }
|
||||
var room = client.getRoom(roomId);
|
||||
if (state !== "PREPARED") {
|
||||
return;
|
||||
}
|
||||
const room = client.getRoom(roomId);
|
||||
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
client.on("Room.timelineReset", function(emitRoom) {
|
||||
expect(emitRoom).toEqual(room);
|
||||
emitCount++;
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
var MatrixEvent = sdk.MatrixEvent;
|
||||
var EventTimeline = sdk.EventTimeline;
|
||||
const sdk = require("../..");
|
||||
const HttpBackend = require("../mock-request");
|
||||
const utils = require("../test-utils");
|
||||
const MatrixEvent = sdk.MatrixEvent;
|
||||
const EventTimeline = sdk.EventTimeline;
|
||||
|
||||
describe("MatrixClient syncing", function() {
|
||||
var baseUrl = "http://localhost.or.something";
|
||||
var client, httpBackend;
|
||||
var selfUserId = "@alice:localhost";
|
||||
var selfAccessToken = "aseukfgwef";
|
||||
var otherUserId = "@bob:localhost";
|
||||
var userA = "@alice:bar";
|
||||
var userB = "@bob:bar";
|
||||
var userC = "@claire:bar";
|
||||
var roomOne = "!foo:localhost";
|
||||
var roomTwo = "!bar:localhost";
|
||||
const baseUrl = "http://localhost.or.something";
|
||||
let client = null;
|
||||
let httpBackend = null;
|
||||
const selfUserId = "@alice:localhost";
|
||||
const selfAccessToken = "aseukfgwef";
|
||||
const otherUserId = "@bob:localhost";
|
||||
const userA = "@alice:bar";
|
||||
const userB = "@bob:bar";
|
||||
const userC = "@claire:bar";
|
||||
const roomOne = "!foo:localhost";
|
||||
const roomTwo = "!bar:localhost";
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
httpBackend = new HttpBackend();
|
||||
sdk.request(httpBackend.requestFn);
|
||||
client = sdk.createClient({
|
||||
baseUrl: baseUrl,
|
||||
userId: selfUserId,
|
||||
accessToken: selfAccessToken
|
||||
accessToken: selfAccessToken,
|
||||
});
|
||||
httpBackend.when("GET", "/pushrules").respond(200, {});
|
||||
httpBackend.when("POST", "/filter").respond(200, { filter_id: "a filter id" });
|
||||
@@ -36,10 +37,10 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
describe("startClient", function() {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "batch_token",
|
||||
rooms: {},
|
||||
presence: {}
|
||||
presence: {},
|
||||
};
|
||||
|
||||
it("should /sync after /pushrules and /filter.", function(done) {
|
||||
@@ -68,17 +69,16 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
describe("resolving invites to profile info", function() {
|
||||
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "s_5_3",
|
||||
presence: {
|
||||
events: []
|
||||
events: [],
|
||||
},
|
||||
rooms: {
|
||||
join: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -87,53 +87,53 @@ describe("MatrixClient syncing", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomOne, user: otherUserId, msg: "hello"
|
||||
})
|
||||
]
|
||||
room: roomOne, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
],
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "join", user: otherUserId
|
||||
room: roomOne, mship: "join", user: otherUserId,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "join", user: selfUserId
|
||||
room: roomOne, mship: "join", user: selfUserId,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomOne, user: selfUserId,
|
||||
content: {
|
||||
creator: selfUserId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
creator: selfUserId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it("should resolve incoming invites from /sync", function(done) {
|
||||
syncData.rooms.join[roomOne].state.events.push(
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "invite", user: userC
|
||||
})
|
||||
room: roomOne, mship: "invite", user: userC,
|
||||
}),
|
||||
);
|
||||
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
httpBackend.when("GET", "/profile/" + encodeURIComponent(userC)).respond(
|
||||
200, {
|
||||
avatar_url: "mxc://flibble/wibble",
|
||||
displayname: "The Boss"
|
||||
}
|
||||
displayname: "The Boss",
|
||||
},
|
||||
);
|
||||
|
||||
client.startClient({
|
||||
resolveInvitesToProfiles: true
|
||||
resolveInvitesToProfiles: true,
|
||||
});
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var member = client.getRoom(roomOne).getMember(userC);
|
||||
const member = client.getRoom(roomOne).getMember(userC);
|
||||
expect(member.name).toEqual("The Boss");
|
||||
expect(
|
||||
member.getAvatarUrl("home.server.url", null, null, null, false)
|
||||
member.getAvatarUrl("home.server.url", null, null, null, false),
|
||||
).toBeDefined();
|
||||
done();
|
||||
});
|
||||
@@ -142,23 +142,23 @@ describe("MatrixClient syncing", function() {
|
||||
it("should use cached values from m.presence wherever possible", function(done) {
|
||||
syncData.presence.events = [
|
||||
utils.mkPresence({
|
||||
user: userC, presence: "online", name: "The Ghost"
|
||||
user: userC, presence: "online", name: "The Ghost",
|
||||
}),
|
||||
];
|
||||
syncData.rooms.join[roomOne].state.events.push(
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "invite", user: userC
|
||||
})
|
||||
room: roomOne, mship: "invite", user: userC,
|
||||
}),
|
||||
);
|
||||
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
client.startClient({
|
||||
resolveInvitesToProfiles: true
|
||||
resolveInvitesToProfiles: true,
|
||||
});
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var member = client.getRoom(roomOne).getMember(userC);
|
||||
const member = client.getRoom(roomOne).getMember(userC);
|
||||
expect(member.name).toEqual("The Ghost");
|
||||
done();
|
||||
});
|
||||
@@ -167,18 +167,18 @@ describe("MatrixClient syncing", function() {
|
||||
it("should result in events on the room member firing", function(done) {
|
||||
syncData.presence.events = [
|
||||
utils.mkPresence({
|
||||
user: userC, presence: "online", name: "The Ghost"
|
||||
})
|
||||
user: userC, presence: "online", name: "The Ghost",
|
||||
}),
|
||||
];
|
||||
syncData.rooms.join[roomOne].state.events.push(
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "invite", user: userC
|
||||
})
|
||||
room: roomOne, mship: "invite", user: userC,
|
||||
}),
|
||||
);
|
||||
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
var latestFiredName = null;
|
||||
let latestFiredName = null;
|
||||
client.on("RoomMember.name", function(event, m) {
|
||||
if (m.userId === userC && m.roomId === roomOne) {
|
||||
latestFiredName = m.name;
|
||||
@@ -186,7 +186,7 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
client.startClient({
|
||||
resolveInvitesToProfiles: true
|
||||
resolveInvitesToProfiles: true,
|
||||
});
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
@@ -198,8 +198,8 @@ describe("MatrixClient syncing", function() {
|
||||
it("should no-op if resolveInvitesToProfiles is not set", function(done) {
|
||||
syncData.rooms.join[roomOne].state.events.push(
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "invite", user: userC
|
||||
})
|
||||
room: roomOne, mship: "invite", user: userC,
|
||||
}),
|
||||
);
|
||||
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
@@ -207,10 +207,10 @@ describe("MatrixClient syncing", function() {
|
||||
client.startClient();
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var member = client.getRoom(roomOne).getMember(userC);
|
||||
const member = client.getRoom(roomOne).getMember(userC);
|
||||
expect(member.name).toEqual(userC);
|
||||
expect(
|
||||
member.getAvatarUrl("home.server.url", null, null, null, false)
|
||||
member.getAvatarUrl("home.server.url", null, null, null, false),
|
||||
).toBeNull();
|
||||
done();
|
||||
});
|
||||
@@ -218,18 +218,18 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
describe("users", function() {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "nb",
|
||||
presence: {
|
||||
events: [
|
||||
utils.mkPresence({
|
||||
user: userA, presence: "online"
|
||||
user: userA, presence: "online",
|
||||
}),
|
||||
utils.mkPresence({
|
||||
user: userB, presence: "unavailable"
|
||||
})
|
||||
]
|
||||
}
|
||||
user: userB, presence: "unavailable",
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
it("should create users for presence events from /sync",
|
||||
@@ -247,80 +247,80 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
describe("room state", function() {
|
||||
var msgText = "some text here";
|
||||
var otherDisplayName = "Bob Smith";
|
||||
const msgText = "some text here";
|
||||
const otherDisplayName = "Bob Smith";
|
||||
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
rooms: {
|
||||
join: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
syncData.rooms.join[roomOne] = {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomOne, user: otherUserId, msg: "hello"
|
||||
})
|
||||
]
|
||||
room: roomOne, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
],
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
utils.mkEvent({
|
||||
type: "m.room.name", room: roomOne, user: otherUserId,
|
||||
content: {
|
||||
name: "Old room name"
|
||||
}
|
||||
name: "Old room name",
|
||||
},
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "join", user: otherUserId
|
||||
room: roomOne, mship: "join", user: otherUserId,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "join", user: selfUserId
|
||||
room: roomOne, mship: "join", user: selfUserId,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomOne, user: selfUserId,
|
||||
content: {
|
||||
creator: selfUserId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
creator: selfUserId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
syncData.rooms.join[roomTwo] = {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomTwo, user: otherUserId, msg: "hiii"
|
||||
})
|
||||
]
|
||||
room: roomTwo, user: otherUserId, msg: "hiii",
|
||||
}),
|
||||
],
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
utils.mkMembership({
|
||||
room: roomTwo, mship: "join", user: otherUserId,
|
||||
name: otherDisplayName
|
||||
name: otherDisplayName,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomTwo, mship: "join", user: selfUserId
|
||||
room: roomTwo, mship: "join", user: selfUserId,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomTwo, user: selfUserId,
|
||||
content: {
|
||||
creator: selfUserId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
creator: selfUserId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
var nextSyncData = {
|
||||
const nextSyncData = {
|
||||
rooms: {
|
||||
join: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
nextSyncData.rooms.join[roomOne] = {
|
||||
@@ -328,28 +328,28 @@ describe("MatrixClient syncing", function() {
|
||||
events: [
|
||||
utils.mkEvent({
|
||||
type: "m.room.name", room: roomOne, user: selfUserId,
|
||||
content: { name: "A new room name" }
|
||||
})
|
||||
]
|
||||
}
|
||||
content: { name: "A new room name" },
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
nextSyncData.rooms.join[roomTwo] = {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomTwo, user: otherUserId, msg: msgText
|
||||
})
|
||||
]
|
||||
room: roomTwo, user: otherUserId, msg: msgText,
|
||||
}),
|
||||
],
|
||||
},
|
||||
ephemeral: {
|
||||
events: [
|
||||
utils.mkEvent({
|
||||
type: "m.typing", room: roomTwo,
|
||||
content: { user_ids: [otherUserId] }
|
||||
})
|
||||
]
|
||||
}
|
||||
content: { user_ids: [otherUserId] },
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
it("should continually recalculate the right room name.", function(done) {
|
||||
@@ -359,10 +359,10 @@ describe("MatrixClient syncing", function() {
|
||||
client.startClient();
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var room = client.getRoom(roomOne);
|
||||
const room = client.getRoom(roomOne);
|
||||
// should have clobbered the name to the one from /events
|
||||
expect(room.name).toEqual(
|
||||
nextSyncData.rooms.join[roomOne].state.events[0].content.name
|
||||
nextSyncData.rooms.join[roomOne].state.events[0].content.name,
|
||||
);
|
||||
done();
|
||||
});
|
||||
@@ -375,7 +375,7 @@ describe("MatrixClient syncing", function() {
|
||||
client.startClient();
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var room = client.getRoom(roomTwo);
|
||||
const room = client.getRoom(roomTwo);
|
||||
// should have added the message from /events
|
||||
expect(room.timeline.length).toEqual(2);
|
||||
expect(room.timeline[1].getContent().body).toEqual(msgText);
|
||||
@@ -389,7 +389,7 @@ describe("MatrixClient syncing", function() {
|
||||
|
||||
client.startClient();
|
||||
httpBackend.flush().done(function() {
|
||||
var room = client.getRoom(roomTwo);
|
||||
const room = client.getRoom(roomTwo);
|
||||
// should use the display name of the other person.
|
||||
expect(room.name).toEqual(otherDisplayName);
|
||||
done();
|
||||
@@ -403,8 +403,8 @@ describe("MatrixClient syncing", function() {
|
||||
client.startClient();
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var room = client.getRoom(roomTwo);
|
||||
var member = room.getMember(otherUserId);
|
||||
const room = client.getRoom(roomTwo);
|
||||
let member = room.getMember(otherUserId);
|
||||
expect(member).toBeDefined();
|
||||
expect(member.typing).toEqual(true);
|
||||
member = room.getMember(selfUserId);
|
||||
@@ -425,7 +425,7 @@ describe("MatrixClient syncing", function() {
|
||||
|
||||
describe("timeline", function() {
|
||||
beforeEach(function() {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "batch_token",
|
||||
rooms: {
|
||||
join: {},
|
||||
@@ -435,7 +435,7 @@ describe("MatrixClient syncing", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomOne, user: otherUserId, msg: "hello"
|
||||
room: roomOne, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
],
|
||||
prev_batch: "pagTok",
|
||||
@@ -449,7 +449,7 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
it("should set the back-pagination token on new rooms", function(done) {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "batch_token",
|
||||
rooms: {
|
||||
join: {},
|
||||
@@ -459,7 +459,7 @@ describe("MatrixClient syncing", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomTwo, user: otherUserId, msg: "roomtwo"
|
||||
room: roomTwo, user: otherUserId, msg: "roomtwo",
|
||||
}),
|
||||
],
|
||||
prev_batch: "roomtwotok",
|
||||
@@ -469,8 +469,8 @@ describe("MatrixClient syncing", function() {
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
httpBackend.flush().then(function() {
|
||||
var room = client.getRoom(roomTwo);
|
||||
var tok = room.getLiveTimeline()
|
||||
const room = client.getRoom(roomTwo);
|
||||
const tok = room.getLiveTimeline()
|
||||
.getPaginationToken(EventTimeline.BACKWARDS);
|
||||
expect(tok).toEqual("roomtwotok");
|
||||
done();
|
||||
@@ -478,7 +478,7 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
it("should set the back-pagination token on gappy syncs", function(done) {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "batch_token",
|
||||
rooms: {
|
||||
join: {},
|
||||
@@ -488,7 +488,7 @@ describe("MatrixClient syncing", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomOne, user: otherUserId, msg: "world"
|
||||
room: roomOne, user: otherUserId, msg: "world",
|
||||
}),
|
||||
],
|
||||
limited: true,
|
||||
@@ -497,20 +497,20 @@ describe("MatrixClient syncing", function() {
|
||||
};
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
var resetCallCount = 0;
|
||||
let resetCallCount = 0;
|
||||
// the token should be set *before* timelineReset is emitted
|
||||
client.on("Room.timelineReset", function(room) {
|
||||
resetCallCount++;
|
||||
|
||||
var tl = room.getLiveTimeline();
|
||||
const tl = room.getLiveTimeline();
|
||||
expect(tl.getEvents().length).toEqual(0);
|
||||
var tok = tl.getPaginationToken(EventTimeline.BACKWARDS);
|
||||
const tok = tl.getPaginationToken(EventTimeline.BACKWARDS);
|
||||
expect(tok).toEqual("newerTok");
|
||||
});
|
||||
|
||||
httpBackend.flush().then(function() {
|
||||
var room = client.getRoom(roomOne);
|
||||
var tl = room.getLiveTimeline();
|
||||
const room = client.getRoom(roomOne);
|
||||
const tl = room.getLiveTimeline();
|
||||
expect(tl.getEvents().length).toEqual(1);
|
||||
expect(resetCallCount).toEqual(1);
|
||||
done();
|
||||
@@ -519,80 +519,80 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
describe("receipts", function() {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
rooms: {
|
||||
join: {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
syncData.rooms.join[roomOne] = {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomOne, user: otherUserId, msg: "hello"
|
||||
room: roomOne, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
utils.mkMessage({
|
||||
room: roomOne, user: otherUserId, msg: "world"
|
||||
})
|
||||
]
|
||||
room: roomOne, user: otherUserId, msg: "world",
|
||||
}),
|
||||
],
|
||||
},
|
||||
state: {
|
||||
events: [
|
||||
utils.mkEvent({
|
||||
type: "m.room.name", room: roomOne, user: otherUserId,
|
||||
content: {
|
||||
name: "Old room name"
|
||||
}
|
||||
name: "Old room name",
|
||||
},
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "join", user: otherUserId
|
||||
room: roomOne, mship: "join", user: otherUserId,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
room: roomOne, mship: "join", user: selfUserId
|
||||
room: roomOne, mship: "join", user: selfUserId,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.create", room: roomOne, user: selfUserId,
|
||||
content: {
|
||||
creator: selfUserId
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
creator: selfUserId,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
syncData.rooms.join[roomOne].ephemeral = {
|
||||
events: []
|
||||
events: [],
|
||||
};
|
||||
});
|
||||
|
||||
it("should sync receipts from /sync.", function(done) {
|
||||
var ackEvent = syncData.rooms.join[roomOne].timeline.events[0];
|
||||
var receipt = {};
|
||||
const ackEvent = syncData.rooms.join[roomOne].timeline.events[0];
|
||||
const receipt = {};
|
||||
receipt[ackEvent.event_id] = {
|
||||
"m.read": {}
|
||||
"m.read": {},
|
||||
};
|
||||
receipt[ackEvent.event_id]["m.read"][userC] = {
|
||||
ts: 176592842636
|
||||
ts: 176592842636,
|
||||
};
|
||||
syncData.rooms.join[roomOne].ephemeral.events = [{
|
||||
content: receipt,
|
||||
room_id: roomOne,
|
||||
type: "m.receipt"
|
||||
type: "m.receipt",
|
||||
}];
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
client.startClient();
|
||||
|
||||
httpBackend.flush().done(function() {
|
||||
var room = client.getRoom(roomOne);
|
||||
const room = client.getRoom(roomOne);
|
||||
expect(room.getReceiptsForEvent(new MatrixEvent(ackEvent))).toEqual([{
|
||||
type: "m.read",
|
||||
userId: userC,
|
||||
data: {
|
||||
ts: 176592842636
|
||||
}
|
||||
ts: 176592842636,
|
||||
},
|
||||
}]);
|
||||
done();
|
||||
});
|
||||
@@ -647,10 +647,10 @@ describe("MatrixClient syncing", function() {
|
||||
});
|
||||
|
||||
it("should set the back-pagination token on left rooms", function(done) {
|
||||
var syncData = {
|
||||
const syncData = {
|
||||
next_batch: "batch_token",
|
||||
rooms: {
|
||||
leave: {}
|
||||
leave: {},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -658,7 +658,7 @@ describe("MatrixClient syncing", function() {
|
||||
timeline: {
|
||||
events: [
|
||||
utils.mkMessage({
|
||||
room: roomTwo, user: otherUserId, msg: "hello"
|
||||
room: roomTwo, user: otherUserId, msg: "hello",
|
||||
}),
|
||||
],
|
||||
prev_batch: "pagTok",
|
||||
@@ -666,14 +666,14 @@ describe("MatrixClient syncing", function() {
|
||||
};
|
||||
|
||||
httpBackend.when("POST", "/filter").respond(200, {
|
||||
filter_id: "another_id"
|
||||
filter_id: "another_id",
|
||||
});
|
||||
|
||||
httpBackend.when("GET", "/sync").respond(200, syncData);
|
||||
|
||||
client.syncLeftRooms().then(function() {
|
||||
var room = client.getRoom(roomTwo);
|
||||
var tok = room.getLiveTimeline().getPaginationToken(
|
||||
const room = client.getRoom(roomTwo);
|
||||
const tok = room.getLiveTimeline().getPaginationToken(
|
||||
EventTimeline.BACKWARDS);
|
||||
|
||||
expect(tok).toEqual("pagTok");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+53
-41
@@ -1,5 +1,5 @@
|
||||
"use strict";
|
||||
var q = require("q");
|
||||
const q = require("q");
|
||||
|
||||
/**
|
||||
* Construct a mock HTTP backend, heavily inspired by Angular.js.
|
||||
@@ -8,15 +8,15 @@ var q = require("q");
|
||||
function HttpBackend() {
|
||||
this.requests = [];
|
||||
this.expectedRequests = [];
|
||||
var self = this;
|
||||
const self = this;
|
||||
// the request function dependency that the SDK needs.
|
||||
this.requestFn = function(opts, callback) {
|
||||
var req = new Request(opts, callback);
|
||||
const req = new Request(opts, callback);
|
||||
console.log("HTTP backend received request: %s", req);
|
||||
self.requests.push(req);
|
||||
|
||||
var abort = function() {
|
||||
var idx = self.requests.indexOf(req);
|
||||
const abort = function() {
|
||||
const idx = self.requests.indexOf(req);
|
||||
if (idx >= 0) {
|
||||
console.log("Aborting HTTP request: %s %s", opts.method,
|
||||
opts.uri);
|
||||
@@ -26,7 +26,7 @@ function HttpBackend() {
|
||||
};
|
||||
|
||||
return {
|
||||
abort: abort
|
||||
abort: abort,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -38,18 +38,18 @@ HttpBackend.prototype = {
|
||||
* @return {Promise} resolved when there is nothing left to flush.
|
||||
*/
|
||||
flush: function(path, numToFlush) {
|
||||
var defer = q.defer();
|
||||
var self = this;
|
||||
var flushed = 0;
|
||||
var triedWaiting = false;
|
||||
const defer = q.defer();
|
||||
const self = this;
|
||||
let flushed = 0;
|
||||
let triedWaiting = false;
|
||||
console.log(
|
||||
"HTTP backend flushing... (path=%s numToFlush=%s)", path, numToFlush
|
||||
"HTTP backend flushing... (path=%s numToFlush=%s)", path, numToFlush,
|
||||
);
|
||||
var tryFlush = function() {
|
||||
const tryFlush = function() {
|
||||
// if there's more real requests and more expected requests, flush 'em.
|
||||
console.log(
|
||||
" trying to flush queue => reqs=%s expected=%s [%s]",
|
||||
self.requests.length, self.expectedRequests.length, path
|
||||
self.requests.length, self.expectedRequests.length, path,
|
||||
);
|
||||
if (self._takeFromQueue(path)) {
|
||||
// try again on the next tick.
|
||||
@@ -58,18 +58,15 @@ HttpBackend.prototype = {
|
||||
if (numToFlush && flushed === numToFlush) {
|
||||
console.log(" [%s] Flushed assigned amount: %s", path, numToFlush);
|
||||
defer.resolve();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setTimeout(tryFlush, 0);
|
||||
}
|
||||
}
|
||||
else if (flushed === 0 && !triedWaiting) {
|
||||
} else if (flushed === 0 && !triedWaiting) {
|
||||
// we may not have made the request yet, wait a generous amount of
|
||||
// time before giving up.
|
||||
setTimeout(tryFlush, 5);
|
||||
triedWaiting = true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
console.log(" no more flushes. [%s]", path);
|
||||
defer.resolve();
|
||||
}
|
||||
@@ -86,14 +83,19 @@ HttpBackend.prototype = {
|
||||
* @return {boolean} true if something was resolved.
|
||||
*/
|
||||
_takeFromQueue: function(path) {
|
||||
var req = null;
|
||||
var i, j;
|
||||
var matchingReq, expectedReq, testResponse = null;
|
||||
let req = null;
|
||||
let i;
|
||||
let j;
|
||||
let matchingReq = null;
|
||||
let expectedReq = null;
|
||||
let testResponse = null;
|
||||
for (i = 0; i < this.requests.length; i++) {
|
||||
req = this.requests[i];
|
||||
for (j = 0; j < this.expectedRequests.length; j++) {
|
||||
expectedReq = this.expectedRequests[j];
|
||||
if (path && path !== expectedReq.path) { continue; }
|
||||
if (path && path !== expectedReq.path) {
|
||||
continue;
|
||||
}
|
||||
if (expectedReq.method === req.method &&
|
||||
req.path.indexOf(expectedReq.path) !== -1) {
|
||||
if (!expectedReq.data || (JSON.stringify(expectedReq.data) ===
|
||||
@@ -115,12 +117,12 @@ HttpBackend.prototype = {
|
||||
}
|
||||
testResponse = matchingReq.response;
|
||||
console.log(" responding to %s", matchingReq.path);
|
||||
var body = testResponse.body;
|
||||
let body = testResponse.body;
|
||||
if (Object.prototype.toString.call(body) == "[object Function]") {
|
||||
body = body(req.path, req.data);
|
||||
}
|
||||
req.callback(
|
||||
testResponse.err, testResponse.response, body
|
||||
testResponse.err, testResponse.response, body,
|
||||
);
|
||||
matchingReq = null;
|
||||
}
|
||||
@@ -135,10 +137,10 @@ HttpBackend.prototype = {
|
||||
* Makes sure that the SDK hasn't sent any more requests to the backend.
|
||||
*/
|
||||
verifyNoOutstandingRequests: function() {
|
||||
var firstOutstandingReq = this.requests[0] || {};
|
||||
const firstOutstandingReq = this.requests[0] || {};
|
||||
expect(this.requests.length).toEqual(0,
|
||||
"Expected no more HTTP requests but received request to " +
|
||||
firstOutstandingReq.path
|
||||
firstOutstandingReq.path,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -146,9 +148,9 @@ HttpBackend.prototype = {
|
||||
* Makes sure that the test doesn't have any unresolved requests.
|
||||
*/
|
||||
verifyNoOutstandingExpectation: function() {
|
||||
var firstOutstandingExpectation = this.expectedRequests[0] || {};
|
||||
const firstOutstandingExpectation = this.expectedRequests[0] || {};
|
||||
expect(this.expectedRequests.length).toEqual(0,
|
||||
"Expected to see HTTP request for " + firstOutstandingExpectation.path
|
||||
"Expected to see HTTP request for " + firstOutstandingExpectation.path,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -160,10 +162,10 @@ HttpBackend.prototype = {
|
||||
* @return {Request} An expected request.
|
||||
*/
|
||||
when: function(method, path, data) {
|
||||
var pendingReq = new ExpectedRequest(method, path, data);
|
||||
const pendingReq = new ExpectedRequest(method, path, data);
|
||||
this.expectedRequests.push(pendingReq);
|
||||
return pendingReq;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -206,10 +208,10 @@ ExpectedRequest.prototype = {
|
||||
this.response = {
|
||||
response: {
|
||||
statusCode: code,
|
||||
headers: {}
|
||||
headers: {},
|
||||
},
|
||||
body: data,
|
||||
err: null
|
||||
err: null,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -222,12 +224,12 @@ ExpectedRequest.prototype = {
|
||||
this.response = {
|
||||
response: {
|
||||
statusCode: code,
|
||||
headers: {}
|
||||
headers: {},
|
||||
},
|
||||
body: null,
|
||||
err: err
|
||||
err: err,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -242,23 +244,33 @@ function Request(opts, callback) {
|
||||
this.callback = callback;
|
||||
|
||||
Object.defineProperty(this, 'method', {
|
||||
get: function() { return opts.method; }
|
||||
get: function() {
|
||||
return opts.method;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'path', {
|
||||
get: function() { return opts.uri; }
|
||||
get: function() {
|
||||
return opts.uri;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'data', {
|
||||
get: function() { return opts.body; }
|
||||
get: function() {
|
||||
return opts.body;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'queryParams', {
|
||||
get: function() { return opts.qs; }
|
||||
get: function() {
|
||||
return opts.qs;
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'headers', {
|
||||
get: function() { return opts.headers || {}; }
|
||||
get: function() {
|
||||
return opts.headers || {};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+61
-23
@@ -1,6 +1,6 @@
|
||||
"use strict";
|
||||
var sdk = require("..");
|
||||
var MatrixEvent = sdk.MatrixEvent;
|
||||
const sdk = require("..");
|
||||
const MatrixEvent = sdk.MatrixEvent;
|
||||
|
||||
/**
|
||||
* Perform common actions before each test case, e.g. printing the test case
|
||||
@@ -8,7 +8,7 @@ var MatrixEvent = sdk.MatrixEvent;
|
||||
* @param {TestCase} testCase The test case that is about to be run.
|
||||
*/
|
||||
module.exports.beforeEach = function(testCase) {
|
||||
var desc = testCase.suite.description + " : " + testCase.description;
|
||||
const desc = testCase.suite.description + " : " + testCase.description;
|
||||
console.log(desc);
|
||||
console.log(new Array(1 + desc.length).join("="));
|
||||
};
|
||||
@@ -22,19 +22,18 @@ module.exports.beforeEach = function(testCase) {
|
||||
module.exports.mock = function(constr, name) {
|
||||
// By Tim Buschtöns
|
||||
// http://eclipsesource.com/blogs/2014/03/27/mocks-in-jasmine-tests/
|
||||
var HelperConstr = new Function(); // jshint ignore:line
|
||||
const HelperConstr = new Function(); // jshint ignore:line
|
||||
HelperConstr.prototype = constr.prototype;
|
||||
var result = new HelperConstr();
|
||||
const result = new HelperConstr();
|
||||
result.jasmineToString = function() {
|
||||
return "mock" + (name ? " of " + name : "");
|
||||
};
|
||||
for (var key in constr.prototype) { // jshint ignore:line
|
||||
for (const key in constr.prototype) { // eslint-disable-line guard-for-in
|
||||
try {
|
||||
if (constr.prototype[key] instanceof Function) {
|
||||
result[key] = jasmine.createSpy((name || "mock") + '.' + key);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
} catch (ex) {
|
||||
// Direct access to some non-function fields of DOM prototypes may
|
||||
// cause exceptions.
|
||||
// Overwriting will not work either in that case.
|
||||
@@ -58,17 +57,16 @@ module.exports.mkEvent = function(opts) {
|
||||
if (!opts.type || !opts.content) {
|
||||
throw new Error("Missing .type or .content =>" + JSON.stringify(opts));
|
||||
}
|
||||
var event = {
|
||||
const event = {
|
||||
type: opts.type,
|
||||
room_id: opts.room,
|
||||
sender: opts.sender || opts.user, // opts.user for backwards-compat
|
||||
content: opts.content,
|
||||
event_id: "$" + Math.random() + "-" + Math.random()
|
||||
event_id: "$" + Math.random() + "-" + Math.random(),
|
||||
};
|
||||
if (opts.skey) {
|
||||
if (opts.skey !== undefined) {
|
||||
event.state_key = opts.skey;
|
||||
}
|
||||
else if (["m.room.name", "m.room.topic", "m.room.create", "m.room.join_rules",
|
||||
} else if (["m.room.name", "m.room.topic", "m.room.create", "m.room.join_rules",
|
||||
"m.room.power_levels", "m.room.topic",
|
||||
"com.example.state"].indexOf(opts.type) !== -1) {
|
||||
event.state_key = "";
|
||||
@@ -85,7 +83,7 @@ module.exports.mkPresence = function(opts) {
|
||||
if (!opts.user) {
|
||||
throw new Error("Missing user");
|
||||
}
|
||||
var event = {
|
||||
const event = {
|
||||
event_id: "$" + Math.random() + "-" + Math.random(),
|
||||
type: "m.presence",
|
||||
sender: opts.sender || opts.user, // opts.user for backwards-compat
|
||||
@@ -93,8 +91,8 @@ module.exports.mkPresence = function(opts) {
|
||||
avatar_url: opts.url,
|
||||
displayname: opts.name,
|
||||
last_active_ago: opts.ago,
|
||||
presence: opts.presence || "offline"
|
||||
}
|
||||
presence: opts.presence || "offline",
|
||||
},
|
||||
};
|
||||
return opts.event ? new MatrixEvent(event) : event;
|
||||
};
|
||||
@@ -121,10 +119,14 @@ module.exports.mkMembership = function(opts) {
|
||||
throw new Error("Missing .mship => " + JSON.stringify(opts));
|
||||
}
|
||||
opts.content = {
|
||||
membership: opts.mship
|
||||
membership: opts.mship,
|
||||
};
|
||||
if (opts.name) { opts.content.displayname = opts.name; }
|
||||
if (opts.url) { opts.content.avatar_url = opts.url; }
|
||||
if (opts.name) {
|
||||
opts.content.displayname = opts.name;
|
||||
}
|
||||
if (opts.url) {
|
||||
opts.content.avatar_url = opts.url;
|
||||
}
|
||||
return module.exports.mkEvent(opts);
|
||||
};
|
||||
|
||||
@@ -147,7 +149,7 @@ module.exports.mkMessage = function(opts) {
|
||||
}
|
||||
opts.content = {
|
||||
msgtype: "m.text",
|
||||
body: opts.msg
|
||||
body: opts.msg,
|
||||
};
|
||||
return module.exports.mkEvent(opts);
|
||||
};
|
||||
@@ -159,7 +161,16 @@ module.exports.mkMessage = function(opts) {
|
||||
* <p>This is useful for use with integration tests which use asyncronous
|
||||
* methods: it can be added as a 'catch' handler in a promise chain.
|
||||
*
|
||||
* @param {Error} error exception to be reported
|
||||
* @param {Error} err exception to be reported
|
||||
*
|
||||
* @deprecated
|
||||
* It turns out there are easier ways of doing this. Just use nodeify():
|
||||
*
|
||||
* it("should not throw", function(done) {
|
||||
* asynchronousMethod().then(function() {
|
||||
* // some tests
|
||||
* }).nodeify(done);
|
||||
* });
|
||||
*
|
||||
* @example
|
||||
* it("should not throw", function(done) {
|
||||
@@ -168,6 +179,33 @@ module.exports.mkMessage = function(opts) {
|
||||
* }).catch(utils.failTest).done(done);
|
||||
* });
|
||||
*/
|
||||
module.exports.failTest = function(error) {
|
||||
expect(error.stack).toBe(null);
|
||||
module.exports.failTest = function(err) {
|
||||
expect(true).toBe(false, "Testfunc threw: " + err.stack);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A mock implementation of webstorage
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
module.exports.MockStorageApi = function() {
|
||||
this.data = {};
|
||||
};
|
||||
module.exports.MockStorageApi.prototype = {
|
||||
get length() {
|
||||
return Object.keys(this.data).length;
|
||||
},
|
||||
key: function(i) {
|
||||
return Object.keys(this.data)[i];
|
||||
},
|
||||
setItem: function(k, v) {
|
||||
this.data[k] = v;
|
||||
},
|
||||
getItem: function(k) {
|
||||
return this.data[k] || null;
|
||||
},
|
||||
removeItem: function(k) {
|
||||
delete this.data[k];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
"use strict";
|
||||
var ContentRepo = require("../../lib/content-repo");
|
||||
var testUtils = require("../test-utils");
|
||||
const ContentRepo = require("../../lib/content-repo");
|
||||
const testUtils = require("../test-utils");
|
||||
|
||||
describe("ContentRepo", function() {
|
||||
var baseUrl = "https://my.home.server";
|
||||
const baseUrl = "https://my.home.server";
|
||||
|
||||
beforeEach(function() {
|
||||
testUtils.beforeEach(this);
|
||||
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
describe("getHttpUriForMxc", function() {
|
||||
it("should do nothing to HTTP URLs when allowing direct links", function() {
|
||||
var httpUrl = "http://example.com/image.jpeg";
|
||||
const httpUrl = "http://example.com/image.jpeg";
|
||||
expect(
|
||||
ContentRepo.getHttpUriForMxc(
|
||||
baseUrl, httpUrl, undefined, undefined, undefined, true
|
||||
)
|
||||
baseUrl, httpUrl, undefined, undefined, undefined, true,
|
||||
),
|
||||
).toEqual(httpUrl);
|
||||
});
|
||||
|
||||
it("should return the empty string HTTP URLs by default", function() {
|
||||
var httpUrl = "http://example.com/image.jpeg";
|
||||
const httpUrl = "http://example.com/image.jpeg";
|
||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, httpUrl)).toEqual("");
|
||||
});
|
||||
|
||||
it("should return a download URL if no width/height/resize are specified",
|
||||
function() {
|
||||
var mxcUri = "mxc://server.name/resourceid";
|
||||
const mxcUri = "mxc://server.name/resourceid";
|
||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/download/server.name/resourceid"
|
||||
baseUrl + "/_matrix/media/v1/download/server.name/resourceid",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -38,27 +38,27 @@ describe("ContentRepo", function() {
|
||||
|
||||
it("should return a thumbnail URL if a width/height/resize is specified",
|
||||
function() {
|
||||
var mxcUri = "mxc://server.name/resourceid";
|
||||
const mxcUri = "mxc://server.name/resourceid";
|
||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32, 64, "crop")).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/thumbnail/server.name/resourceid" +
|
||||
"?width=32&height=64&method=crop"
|
||||
"?width=32&height=64&method=crop",
|
||||
);
|
||||
});
|
||||
|
||||
it("should put fragments from mxc:// URIs after any query parameters",
|
||||
function() {
|
||||
var mxcUri = "mxc://server.name/resourceid#automade";
|
||||
const mxcUri = "mxc://server.name/resourceid#automade";
|
||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri, 32)).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/thumbnail/server.name/resourceid" +
|
||||
"?width=32#automade"
|
||||
"?width=32#automade",
|
||||
);
|
||||
});
|
||||
|
||||
it("should put fragments from mxc:// URIs at the end of the HTTP URI",
|
||||
function() {
|
||||
var mxcUri = "mxc://server.name/resourceid#automade";
|
||||
const mxcUri = "mxc://server.name/resourceid#automade";
|
||||
expect(ContentRepo.getHttpUriForMxc(baseUrl, mxcUri)).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/download/server.name/resourceid#automade"
|
||||
baseUrl + "/_matrix/media/v1/download/server.name/resourceid#automade",
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -71,21 +71,21 @@ describe("ContentRepo", function() {
|
||||
it("should set w/h by default to 96", function() {
|
||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar")).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/identicon/foobar" +
|
||||
"?width=96&height=96"
|
||||
"?width=96&height=96",
|
||||
);
|
||||
});
|
||||
|
||||
it("should be able to set custom w/h", function() {
|
||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foobar", 32, 64)).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/identicon/foobar" +
|
||||
"?width=32&height=64"
|
||||
"?width=32&height=64",
|
||||
);
|
||||
});
|
||||
|
||||
it("should URL encode the identicon string", function() {
|
||||
expect(ContentRepo.getIdenticonUri(baseUrl, "foo#bar", 32, 64)).toEqual(
|
||||
baseUrl + "/_matrix/media/v1/identicon/foo%23bar" +
|
||||
"?width=32&height=64"
|
||||
"?width=32&height=64",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
"use strict";
|
||||
var Crypto = require("../../lib/crypto");
|
||||
var sdk = require("../..");
|
||||
const Crypto = require("../../lib/crypto");
|
||||
const sdk = require("../..");
|
||||
|
||||
describe("Crypto", function() {
|
||||
if (!sdk.CRYPTO_ENABLED) {
|
||||
@@ -9,6 +9,6 @@ describe("Crypto", function() {
|
||||
}
|
||||
|
||||
it("Crypto exposes the correct olm library version", function() {
|
||||
expect(Crypto.getOlmVersion()).toEqual([1, 3, 0]);
|
||||
expect(Crypto.getOlmVersion()[0]).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var EventTimeline = sdk.EventTimeline;
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const EventTimeline = sdk.EventTimeline;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
function mockRoomStates(timeline) {
|
||||
timeline._startState = utils.mock(sdk.RoomState, "startState");
|
||||
@@ -9,24 +9,26 @@ function mockRoomStates(timeline) {
|
||||
}
|
||||
|
||||
describe("EventTimeline", function() {
|
||||
var roomId = "!foo:bar";
|
||||
var userA = "@alice:bar";
|
||||
var userB = "@bertha:bar";
|
||||
var timeline;
|
||||
const roomId = "!foo:bar";
|
||||
const userA = "@alice:bar";
|
||||
const userB = "@bertha:bar";
|
||||
let timeline;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
|
||||
// XXX: this is a horrid hack; should use sinon or something instead to mock
|
||||
var timelineSet = { room: { roomId: roomId }};
|
||||
timelineSet.room.getUnfilteredTimelineSet = function() { return timelineSet; };
|
||||
const timelineSet = { room: { roomId: roomId }};
|
||||
timelineSet.room.getUnfilteredTimelineSet = function() {
|
||||
return timelineSet;
|
||||
};
|
||||
|
||||
timeline = new EventTimeline(timelineSet);
|
||||
});
|
||||
|
||||
describe("construction", function() {
|
||||
it("getRoomId should get room id", function() {
|
||||
var v = timeline.getRoomId();
|
||||
const v = timeline.getRoomId();
|
||||
expect(v).toEqual(roomId);
|
||||
});
|
||||
});
|
||||
@@ -37,7 +39,7 @@ describe("EventTimeline", function() {
|
||||
});
|
||||
|
||||
it("should copy state events to start and end state", function() {
|
||||
var events = [
|
||||
const events = [
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "invite", user: userB, skey: userA,
|
||||
event: true,
|
||||
@@ -46,34 +48,38 @@ describe("EventTimeline", function() {
|
||||
type: "m.room.name", room: roomId, user: userB,
|
||||
event: true,
|
||||
content: { name: "New room" },
|
||||
})
|
||||
}),
|
||||
];
|
||||
timeline.initialiseState(events);
|
||||
expect(timeline._startState.setStateEvents).toHaveBeenCalledWith(
|
||||
events
|
||||
events,
|
||||
);
|
||||
expect(timeline._endState.setStateEvents).toHaveBeenCalledWith(
|
||||
events
|
||||
events,
|
||||
);
|
||||
});
|
||||
|
||||
it("should raise an exception if called after events are added", function() {
|
||||
var event =
|
||||
const event =
|
||||
utils.mkMessage({
|
||||
room: roomId, user: userA, msg: "Adam stole the plushies",
|
||||
event: true,
|
||||
});
|
||||
|
||||
var state = [
|
||||
const state = [
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "invite", user: userB, skey: userA,
|
||||
event: true,
|
||||
})
|
||||
}),
|
||||
];
|
||||
|
||||
expect(function() { timeline.initialiseState(state); }).not.toThrow();
|
||||
expect(function() {
|
||||
timeline.initialiseState(state);
|
||||
}).not.toThrow();
|
||||
timeline.addEvent(event, false);
|
||||
expect(function() { timeline.initialiseState(state); }).toThrow();
|
||||
expect(function() {
|
||||
timeline.initialiseState(state);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -99,8 +105,8 @@ describe("EventTimeline", function() {
|
||||
});
|
||||
|
||||
it("setNeighbouringTimeline should set neighbour", function() {
|
||||
var prev = {a: "a"};
|
||||
var next = {b: "b"};
|
||||
const prev = {a: "a"};
|
||||
const next = {b: "b"};
|
||||
timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS);
|
||||
timeline.setNeighbouringTimeline(next, EventTimeline.FORWARDS);
|
||||
expect(timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)).toBe(prev);
|
||||
@@ -108,8 +114,8 @@ describe("EventTimeline", function() {
|
||||
});
|
||||
|
||||
it("setNeighbouringTimeline should throw if called twice", function() {
|
||||
var prev = {a: "a"};
|
||||
var next = {b: "b"};
|
||||
const prev = {a: "a"};
|
||||
const next = {b: "b"};
|
||||
expect(function() {
|
||||
timeline.setNeighbouringTimeline(prev, EventTimeline.BACKWARDS);
|
||||
}).not.toThrow();
|
||||
@@ -135,7 +141,7 @@ describe("EventTimeline", function() {
|
||||
mockRoomStates(timeline);
|
||||
});
|
||||
|
||||
var events = [
|
||||
const events = [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: userA, msg: "hungry hungry hungry",
|
||||
event: true,
|
||||
@@ -148,7 +154,7 @@ describe("EventTimeline", function() {
|
||||
|
||||
it("should be able to add events to the end", function() {
|
||||
timeline.addEvent(events[0], false);
|
||||
var initialIndex = timeline.getBaseIndex();
|
||||
const initialIndex = timeline.getBaseIndex();
|
||||
timeline.addEvent(events[1], false);
|
||||
expect(timeline.getBaseIndex()).toEqual(initialIndex);
|
||||
expect(timeline.getEvents().length).toEqual(2);
|
||||
@@ -158,7 +164,7 @@ describe("EventTimeline", function() {
|
||||
|
||||
it("should be able to add events to the start", function() {
|
||||
timeline.addEvent(events[0], true);
|
||||
var initialIndex = timeline.getBaseIndex();
|
||||
const initialIndex = timeline.getBaseIndex();
|
||||
timeline.addEvent(events[1], true);
|
||||
expect(timeline.getBaseIndex()).toEqual(initialIndex + 1);
|
||||
expect(timeline.getEvents().length).toEqual(2);
|
||||
@@ -167,15 +173,15 @@ describe("EventTimeline", function() {
|
||||
});
|
||||
|
||||
it("should set event.sender for new and old events", function() {
|
||||
var sentinel = {
|
||||
const sentinel = {
|
||||
userId: userA,
|
||||
membership: "join",
|
||||
name: "Alice"
|
||||
name: "Alice",
|
||||
};
|
||||
var oldSentinel = {
|
||||
const oldSentinel = {
|
||||
userId: userA,
|
||||
membership: "join",
|
||||
name: "Old Alice"
|
||||
name: "Old Alice",
|
||||
};
|
||||
timeline.getState(EventTimeline.FORWARDS).getSentinelMember
|
||||
.andCallFake(function(uid) {
|
||||
@@ -192,13 +198,13 @@ describe("EventTimeline", function() {
|
||||
return null;
|
||||
});
|
||||
|
||||
var newEv = utils.mkEvent({
|
||||
const newEv = utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: userA, event: true,
|
||||
content: { name: "New Room Name" }
|
||||
content: { name: "New Room Name" },
|
||||
});
|
||||
var oldEv = utils.mkEvent({
|
||||
const oldEv = utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: userA, event: true,
|
||||
content: { name: "Old Room Name" }
|
||||
content: { name: "Old Room Name" },
|
||||
});
|
||||
|
||||
timeline.addEvent(newEv, false);
|
||||
@@ -209,15 +215,15 @@ describe("EventTimeline", function() {
|
||||
|
||||
it("should set event.target for new and old m.room.member events",
|
||||
function() {
|
||||
var sentinel = {
|
||||
const sentinel = {
|
||||
userId: userA,
|
||||
membership: "join",
|
||||
name: "Alice"
|
||||
name: "Alice",
|
||||
};
|
||||
var oldSentinel = {
|
||||
const oldSentinel = {
|
||||
userId: userA,
|
||||
membership: "join",
|
||||
name: "Old Alice"
|
||||
name: "Old Alice",
|
||||
};
|
||||
timeline.getState(EventTimeline.FORWARDS).getSentinelMember
|
||||
.andCallFake(function(uid) {
|
||||
@@ -234,11 +240,11 @@ describe("EventTimeline", function() {
|
||||
return null;
|
||||
});
|
||||
|
||||
var newEv = utils.mkMembership({
|
||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
||||
const newEv = utils.mkMembership({
|
||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
|
||||
});
|
||||
var oldEv = utils.mkMembership({
|
||||
room: roomId, mship: "ban", user: userB, skey: userA, event: true
|
||||
const oldEv = utils.mkMembership({
|
||||
room: roomId, mship: "ban", user: userB, skey: userA, event: true,
|
||||
});
|
||||
timeline.addEvent(newEv, false);
|
||||
expect(newEv.target).toEqual(sentinel);
|
||||
@@ -248,16 +254,16 @@ describe("EventTimeline", function() {
|
||||
|
||||
it("should call setStateEvents on the right RoomState with the right " +
|
||||
"forwardLooking value for new events", function() {
|
||||
var events = [
|
||||
const events = [
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: userB, event: true,
|
||||
content: {
|
||||
name: "New room"
|
||||
}
|
||||
})
|
||||
name: "New room",
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
timeline.addEvent(events[0], false);
|
||||
@@ -278,16 +284,16 @@ describe("EventTimeline", function() {
|
||||
|
||||
it("should call setStateEvents on the right RoomState with the right " +
|
||||
"forwardLooking value for old events", function() {
|
||||
var events = [
|
||||
const events = [
|
||||
utils.mkMembership({
|
||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true
|
||||
room: roomId, mship: "invite", user: userB, skey: userA, event: true,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
type: "m.room.name", room: roomId, user: userB, event: true,
|
||||
content: {
|
||||
name: "New room"
|
||||
}
|
||||
})
|
||||
name: "New room",
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
timeline.addEvent(events[0], true);
|
||||
@@ -307,7 +313,7 @@ describe("EventTimeline", function() {
|
||||
});
|
||||
|
||||
describe("removeEvent", function() {
|
||||
var events = [
|
||||
const events = [
|
||||
utils.mkMessage({
|
||||
room: roomId, user: userA, msg: "hungry hungry hungry",
|
||||
event: true,
|
||||
@@ -327,7 +333,7 @@ describe("EventTimeline", function() {
|
||||
timeline.addEvent(events[1], false);
|
||||
expect(timeline.getEvents().length).toEqual(2);
|
||||
|
||||
var ev = timeline.removeEvent(events[0].getId());
|
||||
let ev = timeline.removeEvent(events[0].getId());
|
||||
expect(ev).toBe(events[0]);
|
||||
expect(timeline.getEvents().length).toEqual(1);
|
||||
|
||||
@@ -359,7 +365,7 @@ describe("EventTimeline", function() {
|
||||
function() {
|
||||
timeline.addEvent(events[0], true);
|
||||
timeline.removeEvent(events[0].getId());
|
||||
var initialIndex = timeline.getBaseIndex();
|
||||
const initialIndex = timeline.getBaseIndex();
|
||||
timeline.addEvent(events[1], false);
|
||||
timeline.addEvent(events[2], false);
|
||||
expect(timeline.getBaseIndex()).toEqual(initialIndex);
|
||||
|
||||
+15
-15
@@ -1,24 +1,24 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var Filter = sdk.Filter;
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const Filter = sdk.Filter;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("Filter", function() {
|
||||
var filterId = "f1lt3ring15g00d4ursoul";
|
||||
var userId = "@sir_arthur_david:humming.tiger";
|
||||
var filter;
|
||||
const filterId = "f1lt3ring15g00d4ursoul";
|
||||
const userId = "@sir_arthur_david:humming.tiger";
|
||||
let filter;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
filter = new Filter(userId);
|
||||
});
|
||||
|
||||
describe("fromJson", function() {
|
||||
it("create a new Filter from the provided values", function() {
|
||||
var definition = {
|
||||
event_fields: ["type", "content"]
|
||||
const definition = {
|
||||
event_fields: ["type", "content"],
|
||||
};
|
||||
var f = Filter.fromJson(userId, filterId, definition);
|
||||
const f = Filter.fromJson(userId, filterId, definition);
|
||||
expect(f.getDefinition()).toEqual(definition);
|
||||
expect(f.userId).toEqual(userId);
|
||||
expect(f.filterId).toEqual(filterId);
|
||||
@@ -31,17 +31,17 @@ describe("Filter", function() {
|
||||
expect(filter.getDefinition()).toEqual({
|
||||
room: {
|
||||
timeline: {
|
||||
limit: 10
|
||||
}
|
||||
}
|
||||
limit: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setDefinition/getDefinition", function() {
|
||||
it("should set and get the filter body", function() {
|
||||
var definition = {
|
||||
event_format: "client"
|
||||
const definition = {
|
||||
event_format: "client",
|
||||
};
|
||||
filter.setDefinition(definition);
|
||||
expect(filter.getDefinition()).toEqual(definition);
|
||||
|
||||
@@ -15,23 +15,23 @@ limitations under the License.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var q = require("q");
|
||||
var sdk = require("../..");
|
||||
var utils = require("../test-utils");
|
||||
const q = require("q");
|
||||
const sdk = require("../..");
|
||||
const utils = require("../test-utils");
|
||||
|
||||
var InteractiveAuth = sdk.InteractiveAuth;
|
||||
var MatrixError = sdk.MatrixError;
|
||||
const InteractiveAuth = sdk.InteractiveAuth;
|
||||
const MatrixError = sdk.MatrixError;
|
||||
|
||||
describe("InteractiveAuth", function() {
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
it("should start an auth stage and complete it", function(done) {
|
||||
var doRequest = jasmine.createSpy('doRequest');
|
||||
var startAuthStage = jasmine.createSpy('startAuthStage');
|
||||
const doRequest = jasmine.createSpy('doRequest');
|
||||
const startAuthStage = jasmine.createSpy('startAuthStage');
|
||||
|
||||
var ia = new InteractiveAuth({
|
||||
const ia = new InteractiveAuth({
|
||||
doRequest: doRequest,
|
||||
startAuthStage: startAuthStage,
|
||||
authData: {
|
||||
@@ -60,7 +60,7 @@ describe("InteractiveAuth", function() {
|
||||
});
|
||||
|
||||
// .. which should trigger a call here
|
||||
var requestRes = {"a": "b"};
|
||||
const requestRes = {"a": "b"};
|
||||
doRequest.andCallFake(function(authData) {
|
||||
expect(authData).toEqual({
|
||||
session: "sessionId",
|
||||
@@ -78,10 +78,10 @@ describe("InteractiveAuth", function() {
|
||||
});
|
||||
|
||||
it("should make a request if no authdata is provided", function(done) {
|
||||
var doRequest = jasmine.createSpy('doRequest');
|
||||
var startAuthStage = jasmine.createSpy('startAuthStage');
|
||||
const doRequest = jasmine.createSpy('doRequest');
|
||||
const startAuthStage = jasmine.createSpy('startAuthStage');
|
||||
|
||||
var ia = new InteractiveAuth({
|
||||
const ia = new InteractiveAuth({
|
||||
doRequest: doRequest,
|
||||
startAuthStage: startAuthStage,
|
||||
});
|
||||
@@ -93,7 +93,7 @@ describe("InteractiveAuth", function() {
|
||||
doRequest.andCallFake(function(authData) {
|
||||
console.log("request1", authData);
|
||||
expect(authData).toBe(null);
|
||||
var err = new MatrixError({
|
||||
const err = new MatrixError({
|
||||
session: "sessionId",
|
||||
flows: [
|
||||
{ stages: ["logintype"] },
|
||||
@@ -107,7 +107,7 @@ describe("InteractiveAuth", function() {
|
||||
});
|
||||
|
||||
// .. which should be followed by a call to startAuthStage
|
||||
var requestRes = {"a": "b"};
|
||||
const requestRes = {"a": "b"};
|
||||
startAuthStage.andCallFake(function(stage) {
|
||||
expect(stage).toEqual("logintype");
|
||||
expect(ia.getSessionId()).toEqual("sessionId");
|
||||
|
||||
@@ -1,44 +1,46 @@
|
||||
"use strict";
|
||||
var q = require("q");
|
||||
var sdk = require("../..");
|
||||
var MatrixClient = sdk.MatrixClient;
|
||||
var utils = require("../test-utils");
|
||||
const q = require("q");
|
||||
const sdk = require("../..");
|
||||
const MatrixClient = sdk.MatrixClient;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("MatrixClient", function() {
|
||||
var userId = "@alice:bar";
|
||||
var identityServerUrl = "https://identity.server";
|
||||
var identityServerDomain = "identity.server";
|
||||
var client, store, scheduler;
|
||||
const userId = "@alice:bar";
|
||||
const identityServerUrl = "https://identity.server";
|
||||
const identityServerDomain = "identity.server";
|
||||
let client;
|
||||
let store;
|
||||
let scheduler;
|
||||
|
||||
var KEEP_ALIVE_PATH = "/_matrix/client/versions";
|
||||
const KEEP_ALIVE_PATH = "/_matrix/client/versions";
|
||||
|
||||
var PUSH_RULES_RESPONSE = {
|
||||
const PUSH_RULES_RESPONSE = {
|
||||
method: "GET",
|
||||
path: "/pushrules/",
|
||||
data: {}
|
||||
data: {},
|
||||
};
|
||||
|
||||
var FILTER_PATH = "/user/" + encodeURIComponent(userId) + "/filter";
|
||||
const FILTER_PATH = "/user/" + encodeURIComponent(userId) + "/filter";
|
||||
|
||||
var FILTER_RESPONSE = {
|
||||
const FILTER_RESPONSE = {
|
||||
method: "POST",
|
||||
path: FILTER_PATH,
|
||||
data: { filter_id: "f1lt3r" }
|
||||
data: { filter_id: "f1lt3r" },
|
||||
};
|
||||
|
||||
var SYNC_DATA = {
|
||||
const SYNC_DATA = {
|
||||
next_batch: "s_5_3",
|
||||
presence: { events: [] },
|
||||
rooms: {}
|
||||
rooms: {},
|
||||
};
|
||||
|
||||
var SYNC_RESPONSE = {
|
||||
const SYNC_RESPONSE = {
|
||||
method: "GET",
|
||||
path: "/sync",
|
||||
data: SYNC_DATA
|
||||
data: SYNC_DATA,
|
||||
};
|
||||
|
||||
var httpLookups = [
|
||||
let httpLookups = [
|
||||
// items are objects which look like:
|
||||
// {
|
||||
// method: "GET",
|
||||
@@ -51,14 +53,14 @@ describe("MatrixClient", function() {
|
||||
// }
|
||||
// items are popped off when processed and block if no items left.
|
||||
];
|
||||
var accept_keepalives;
|
||||
var pendingLookup = null;
|
||||
let acceptKeepalives;
|
||||
let pendingLookup = null;
|
||||
function httpReq(cb, method, path, qp, data, prefix) {
|
||||
if (path === KEEP_ALIVE_PATH && accept_keepalives) {
|
||||
if (path === KEEP_ALIVE_PATH && acceptKeepalives) {
|
||||
return q();
|
||||
}
|
||||
var next = httpLookups.shift();
|
||||
var logLine = (
|
||||
const next = httpLookups.shift();
|
||||
const logLine = (
|
||||
"MatrixClient[UT] RECV " + method + " " + path + " " +
|
||||
"EXPECT " + (next ? next.method : next) + " " + (next ? next.path : next)
|
||||
);
|
||||
@@ -73,20 +75,20 @@ describe("MatrixClient", function() {
|
||||
expect(false).toBe(
|
||||
true, ">1 pending request. You should probably handle them. " +
|
||||
"PENDING: " + JSON.stringify(pendingLookup) + " JUST GOT: " +
|
||||
method + " " + path
|
||||
method + " " + path,
|
||||
);
|
||||
}
|
||||
pendingLookup = {
|
||||
promise: q.defer().promise,
|
||||
method: method,
|
||||
path: path
|
||||
path: path,
|
||||
};
|
||||
return pendingLookup.promise;
|
||||
}
|
||||
if (next.path === path && next.method === method) {
|
||||
console.log(
|
||||
"MatrixClient[UT] Matched. Returning " +
|
||||
(next.error ? "BAD" : "GOOD") + " response"
|
||||
(next.error ? "BAD" : "GOOD") + " response",
|
||||
);
|
||||
if (next.expectBody) {
|
||||
expect(next.expectBody).toEqual(data);
|
||||
@@ -107,7 +109,7 @@ describe("MatrixClient", function() {
|
||||
httpStatus: next.error.httpStatus,
|
||||
name: next.error.errcode,
|
||||
message: "Expected testing error",
|
||||
data: next.error
|
||||
data: next.error,
|
||||
});
|
||||
}
|
||||
return q(next.data);
|
||||
@@ -117,16 +119,16 @@ describe("MatrixClient", function() {
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
jasmine.Clock.useMock();
|
||||
scheduler = jasmine.createSpyObj("scheduler", [
|
||||
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
|
||||
"setProcessFunction"
|
||||
"setProcessFunction",
|
||||
]);
|
||||
store = jasmine.createSpyObj("store", [
|
||||
"getRoom", "getRooms", "getUser", "getSyncToken", "scrollback",
|
||||
"setSyncToken", "storeEvents", "storeRoom", "storeUser",
|
||||
"getFilterIdByName", "setFilterIdByName", "getFilter", "storeFilter"
|
||||
"getFilterIdByName", "setFilterIdByName", "getFilter", "storeFilter",
|
||||
]);
|
||||
client = new MatrixClient({
|
||||
baseUrl: "https://my.home.server",
|
||||
@@ -135,12 +137,12 @@ describe("MatrixClient", function() {
|
||||
request: function() {}, // NOP
|
||||
store: store,
|
||||
scheduler: scheduler,
|
||||
userId: userId
|
||||
userId: userId,
|
||||
});
|
||||
// FIXME: We shouldn't be yanking _http like this.
|
||||
client._http = jasmine.createSpyObj("httpApi", [
|
||||
"authedRequest", "authedRequestWithPrefix", "getContentUri",
|
||||
"request", "requestWithPrefix", "uploadContent"
|
||||
"request", "requestWithPrefix", "uploadContent",
|
||||
]);
|
||||
client._http.authedRequest.andCallFake(httpReq);
|
||||
client._http.authedRequestWithPrefix.andCallFake(httpReq);
|
||||
@@ -148,7 +150,7 @@ describe("MatrixClient", function() {
|
||||
client._http.request.andCallFake(httpReq);
|
||||
|
||||
// set reasonable working defaults
|
||||
accept_keepalives = true;
|
||||
acceptKeepalives = true;
|
||||
pendingLookup = null;
|
||||
httpLookups = [];
|
||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||
@@ -174,9 +176,9 @@ describe("MatrixClient", function() {
|
||||
httpLookups = [];
|
||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||
httpLookups.push(SYNC_RESPONSE);
|
||||
var filterId = "ehfewf";
|
||||
const filterId = "ehfewf";
|
||||
store.getFilterIdByName.andReturn(filterId);
|
||||
var filter = new sdk.Filter(0, filterId);
|
||||
const filter = new sdk.Filter(0, filterId);
|
||||
filter.setDefinition({"room": {"timeline": {"limit": 8}}});
|
||||
store.getFilter.andReturn(filter);
|
||||
client.startClient();
|
||||
@@ -191,7 +193,6 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("getSyncState", function() {
|
||||
|
||||
it("should return null if the client isn't started", function() {
|
||||
expect(client.getSyncState()).toBeNull();
|
||||
});
|
||||
@@ -219,7 +220,7 @@ describe("MatrixClient", function() {
|
||||
// and they all need to be stored!
|
||||
return "FILTER_SYNC_" + userId + (suffix ? "_" + suffix : "");
|
||||
}
|
||||
var invalidFilterId = 'invalidF1lt3r';
|
||||
const invalidFilterId = 'invalidF1lt3r';
|
||||
httpLookups = [];
|
||||
httpLookups.push({
|
||||
method: "GET",
|
||||
@@ -229,15 +230,15 @@ describe("MatrixClient", function() {
|
||||
name: "M_UNKNOWN",
|
||||
message: "No row found",
|
||||
data: { errcode: "M_UNKNOWN", error: "No row found" },
|
||||
httpStatus: 404
|
||||
}
|
||||
httpStatus: 404,
|
||||
},
|
||||
});
|
||||
httpLookups.push(FILTER_RESPONSE);
|
||||
store.getFilterIdByName.andReturn(invalidFilterId);
|
||||
|
||||
var filterName = getFilterName(client.credentials.userId);
|
||||
const filterName = getFilterName(client.credentials.userId);
|
||||
client.store.setFilterIdByName(filterName, invalidFilterId);
|
||||
var filter = new sdk.Filter(client.credentials.userId);
|
||||
const filter = new sdk.Filter(client.credentials.userId);
|
||||
|
||||
client.getOrCreateFilter(filterName, filter).then(function(filterId) {
|
||||
expect(filterId).toEqual(FILTER_RESPONSE.data.filter_id);
|
||||
@@ -256,7 +257,7 @@ describe("MatrixClient", function() {
|
||||
httpLookups = [];
|
||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||
httpLookups.push({
|
||||
method: "POST", path: FILTER_PATH, error: { errcode: "NOPE_NOPE_NOPE" }
|
||||
method: "POST", path: FILTER_PATH, error: { errcode: "NOPE_NOPE_NOPE" },
|
||||
});
|
||||
httpLookups.push(FILTER_RESPONSE);
|
||||
httpLookups.push(SYNC_RESPONSE);
|
||||
@@ -279,17 +280,17 @@ describe("MatrixClient", function() {
|
||||
|
||||
it("should work on /sync", function(done) {
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", error: { errcode: "NOPE_NOPE_NOPE" }
|
||||
method: "GET", path: "/sync", error: { errcode: "NOPE_NOPE_NOPE" },
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", data: SYNC_DATA
|
||||
method: "GET", path: "/sync", data: SYNC_DATA,
|
||||
});
|
||||
|
||||
client.on("sync", function syncListener(state) {
|
||||
if (state === "ERROR" && httpLookups.length > 0) {
|
||||
expect(httpLookups.length).toEqual(1);
|
||||
expect(client.retryImmediately()).toBe(
|
||||
true, "retryImmediately returned false"
|
||||
true, "retryImmediately returned false",
|
||||
);
|
||||
jasmine.Clock.tick(1);
|
||||
} else if (state === "RECONNECTING" && httpLookups.length > 0) {
|
||||
@@ -305,7 +306,7 @@ describe("MatrixClient", function() {
|
||||
it("should work on /pushrules", function(done) {
|
||||
httpLookups = [];
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/pushrules/", error: { errcode: "NOPE_NOPE_NOPE" }
|
||||
method: "GET", path: "/pushrules/", error: { errcode: "NOPE_NOPE_NOPE" },
|
||||
});
|
||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||
httpLookups.push(FILTER_RESPONSE);
|
||||
@@ -329,12 +330,11 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("emitted sync events", function() {
|
||||
|
||||
function syncChecker(expectedStates, done) {
|
||||
return function syncListener(state, old) {
|
||||
var expected = expectedStates.shift();
|
||||
const expected = expectedStates.shift();
|
||||
console.log(
|
||||
"'sync' curr=%s old=%s EXPECT=%s", state, old, expected
|
||||
"'sync' curr=%s old=%s EXPECT=%s", state, old, expected,
|
||||
);
|
||||
if (!expected) {
|
||||
done();
|
||||
@@ -352,18 +352,18 @@ describe("MatrixClient", function() {
|
||||
}
|
||||
|
||||
it("should transition null -> PREPARED after the first /sync", function(done) {
|
||||
var expectedStates = [];
|
||||
const expectedStates = [];
|
||||
expectedStates.push(["PREPARED", null]);
|
||||
client.on("sync", syncChecker(expectedStates, done));
|
||||
client.startClient();
|
||||
});
|
||||
|
||||
it("should transition null -> ERROR after a failed /filter", function(done) {
|
||||
var expectedStates = [];
|
||||
const expectedStates = [];
|
||||
httpLookups = [];
|
||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||
httpLookups.push({
|
||||
method: "POST", path: FILTER_PATH, error: { errcode: "NOPE_NOPE_NOPE" }
|
||||
method: "POST", path: FILTER_PATH, error: { errcode: "NOPE_NOPE_NOPE" },
|
||||
});
|
||||
expectedStates.push(["ERROR", null]);
|
||||
client.on("sync", syncChecker(expectedStates, done));
|
||||
@@ -372,22 +372,23 @@ describe("MatrixClient", function() {
|
||||
|
||||
it("should transition ERROR -> PREPARED after /sync if prev failed",
|
||||
function(done) {
|
||||
var expectedStates = [];
|
||||
accept_keepalives = false;
|
||||
const expectedStates = [];
|
||||
acceptKeepalives = false;
|
||||
httpLookups = [];
|
||||
httpLookups.push(PUSH_RULES_RESPONSE);
|
||||
httpLookups.push(FILTER_RESPONSE);
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", error: { errcode: "NOPE_NOPE_NOPE" }
|
||||
method: "GET", path: "/sync", error: { errcode: "NOPE_NOPE_NOPE" },
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: KEEP_ALIVE_PATH, error: { errcode: "KEEPALIVE_FAIL" }
|
||||
method: "GET", path: KEEP_ALIVE_PATH,
|
||||
error: { errcode: "KEEPALIVE_FAIL" },
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: KEEP_ALIVE_PATH, data: {}
|
||||
method: "GET", path: KEEP_ALIVE_PATH, data: {},
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", data: SYNC_DATA
|
||||
method: "GET", path: "/sync", data: SYNC_DATA,
|
||||
});
|
||||
|
||||
expectedStates.push(["RECONNECTING", null]);
|
||||
@@ -398,7 +399,7 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
it("should transition PREPARED -> SYNCING after /sync", function(done) {
|
||||
var expectedStates = [];
|
||||
const expectedStates = [];
|
||||
expectedStates.push(["PREPARED", null]);
|
||||
expectedStates.push(["SYNCING", "PREPARED"]);
|
||||
client.on("sync", syncChecker(expectedStates, done));
|
||||
@@ -406,13 +407,14 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
it("should transition SYNCING -> ERROR after a failed /sync", function(done) {
|
||||
accept_keepalives = false;
|
||||
var expectedStates = [];
|
||||
acceptKeepalives = false;
|
||||
const expectedStates = [];
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", error: { errcode: "NONONONONO" }
|
||||
method: "GET", path: "/sync", error: { errcode: "NONONONONO" },
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: KEEP_ALIVE_PATH, error: { errcode: "KEEPALIVE_FAIL" }
|
||||
method: "GET", path: KEEP_ALIVE_PATH,
|
||||
error: { errcode: "KEEPALIVE_FAIL" },
|
||||
});
|
||||
|
||||
expectedStates.push(["PREPARED", null]);
|
||||
@@ -425,9 +427,9 @@ describe("MatrixClient", function() {
|
||||
|
||||
xit("should transition ERROR -> SYNCING after /sync if prev failed",
|
||||
function(done) {
|
||||
var expectedStates = [];
|
||||
const expectedStates = [];
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", error: { errcode: "NONONONONO" }
|
||||
method: "GET", path: "/sync", error: { errcode: "NONONONONO" },
|
||||
});
|
||||
httpLookups.push(SYNC_RESPONSE);
|
||||
|
||||
@@ -440,7 +442,7 @@ describe("MatrixClient", function() {
|
||||
|
||||
it("should transition SYNCING -> SYNCING on subsequent /sync successes",
|
||||
function(done) {
|
||||
var expectedStates = [];
|
||||
const expectedStates = [];
|
||||
httpLookups.push(SYNC_RESPONSE);
|
||||
httpLookups.push(SYNC_RESPONSE);
|
||||
|
||||
@@ -452,16 +454,18 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
it("should transition ERROR -> ERROR if keepalive keeps failing", function(done) {
|
||||
accept_keepalives = false;
|
||||
var expectedStates = [];
|
||||
acceptKeepalives = false;
|
||||
const expectedStates = [];
|
||||
httpLookups.push({
|
||||
method: "GET", path: "/sync", error: { errcode: "NONONONONO" }
|
||||
method: "GET", path: "/sync", error: { errcode: "NONONONONO" },
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: KEEP_ALIVE_PATH, error: { errcode: "KEEPALIVE_FAIL" }
|
||||
method: "GET", path: KEEP_ALIVE_PATH,
|
||||
error: { errcode: "KEEPALIVE_FAIL" },
|
||||
});
|
||||
httpLookups.push({
|
||||
method: "GET", path: KEEP_ALIVE_PATH, error: { errcode: "KEEPALIVE_FAIL" }
|
||||
method: "GET", path: KEEP_ALIVE_PATH,
|
||||
error: { errcode: "KEEPALIVE_FAIL" },
|
||||
});
|
||||
|
||||
expectedStates.push(["PREPARED", null]);
|
||||
@@ -475,7 +479,7 @@ describe("MatrixClient", function() {
|
||||
});
|
||||
|
||||
describe("inviteByEmail", function() {
|
||||
var roomId = "!foo:bar";
|
||||
const roomId = "!foo:bar";
|
||||
|
||||
it("should send an invite HTTP POST", function() {
|
||||
httpLookups = [{
|
||||
@@ -485,17 +489,15 @@ describe("MatrixClient", function() {
|
||||
expectBody: {
|
||||
id_server: identityServerDomain,
|
||||
medium: "email",
|
||||
address: "alice@gmail.com"
|
||||
}
|
||||
address: "alice@gmail.com",
|
||||
},
|
||||
}];
|
||||
client.inviteByEmail(roomId, "alice@gmail.com");
|
||||
expect(httpLookups.length).toEqual(0);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("guest rooms", function() {
|
||||
|
||||
it("should only do /sync calls (without filter/pushrules)", function(done) {
|
||||
httpLookups = []; // no /pushrules or /filter
|
||||
httpLookups.push({
|
||||
@@ -504,7 +506,7 @@ describe("MatrixClient", function() {
|
||||
data: SYNC_DATA,
|
||||
thenCall: function() {
|
||||
done();
|
||||
}
|
||||
},
|
||||
});
|
||||
client.setGuest(true);
|
||||
client.startClient();
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
"use strict";
|
||||
var PushProcessor = require("../../lib/pushprocessor");
|
||||
var MatrixEvent = MatrixEvent;
|
||||
var utils = require("../test-utils");
|
||||
const PushProcessor = require("../../lib/pushprocessor");
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe('NotificationService', function() {
|
||||
var testUserId = "@ali:matrix.org";
|
||||
var testDisplayName = "Alice M";
|
||||
var testRoomId = "!fl1bb13:localhost";
|
||||
const testUserId = "@ali:matrix.org";
|
||||
const testDisplayName = "Alice M";
|
||||
const testRoomId = "!fl1bb13:localhost";
|
||||
|
||||
var testEvent;
|
||||
let testEvent;
|
||||
|
||||
var pushProcessor;
|
||||
let pushProcessor;
|
||||
|
||||
// These would be better if individual rules were configured in the tests themselves.
|
||||
var matrixClient = {
|
||||
const matrixClient = {
|
||||
getRoom: function() {
|
||||
return {
|
||||
currentState: {
|
||||
getMember: function() {
|
||||
return {
|
||||
name: testDisplayName
|
||||
name: testDisplayName,
|
||||
};
|
||||
},
|
||||
members: {}
|
||||
}
|
||||
members: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
credentials: {
|
||||
userId: testUserId
|
||||
userId: testUserId,
|
||||
},
|
||||
pushRules: {
|
||||
"device": {},
|
||||
@@ -38,91 +37,91 @@ describe('NotificationService', function() {
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"pattern": "ali",
|
||||
"rule_id": ".m.rule.contains_user_name"
|
||||
"rule_id": ".m.rule.contains_user_name",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"pattern": "coffee",
|
||||
"rule_id": "coffee"
|
||||
"rule_id": "coffee",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"pattern": "foo*bar",
|
||||
"rule_id": "foobar"
|
||||
"rule_id": "foobar",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"pattern": "p[io]ng",
|
||||
"rule_id": "pingpong"
|
||||
"rule_id": "pingpong",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"pattern": "I ate [0-9] pies",
|
||||
"rule_id": "pies"
|
||||
"rule_id": "pies",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"pattern": "b[!ai]ke",
|
||||
"rule_id": "bakebike"
|
||||
}
|
||||
"rule_id": "bakebike",
|
||||
},
|
||||
],
|
||||
"override": [
|
||||
{
|
||||
@@ -130,70 +129,70 @@ describe('NotificationService', function() {
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
"value": "default",
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
"set_tweak": "highlight",
|
||||
},
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "contains_display_name"
|
||||
}
|
||||
"kind": "contains_display_name",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.contains_display_name"
|
||||
"rule_id": ".m.rule.contains_display_name",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
}
|
||||
"value": "default",
|
||||
},
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"is": "2",
|
||||
"kind": "room_member_count"
|
||||
}
|
||||
"kind": "room_member_count",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.room_one_to_one"
|
||||
}
|
||||
"rule_id": ".m.rule.room_one_to_one",
|
||||
},
|
||||
],
|
||||
"room": [],
|
||||
"sender": [],
|
||||
"underride": [
|
||||
{
|
||||
"actions": [
|
||||
"dont-notify"
|
||||
"dont-notify",
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "content.msgtype",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.notice"
|
||||
}
|
||||
"pattern": "m.notice",
|
||||
},
|
||||
],
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.suppress_notices"
|
||||
"rule_id": ".m.rule.suppress_notices",
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
"value": false,
|
||||
},
|
||||
],
|
||||
"conditions": [],
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.fallback"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"rule_id": ".m.rule.fallback",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -204,8 +203,8 @@ describe('NotificationService', function() {
|
||||
event: true,
|
||||
content: {
|
||||
body: "",
|
||||
msgtype: "m.text"
|
||||
}
|
||||
msgtype: "m.text",
|
||||
},
|
||||
});
|
||||
pushProcessor = new PushProcessor(matrixClient);
|
||||
});
|
||||
@@ -214,25 +213,25 @@ describe('NotificationService', function() {
|
||||
|
||||
it('should bing on a user ID.', function() {
|
||||
testEvent.event.content.body = "Hello @ali:matrix.org, how are you?";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on a partial user ID with an @.', function() {
|
||||
testEvent.event.content.body = "Hello @ali, how are you?";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on a partial user ID without @.', function() {
|
||||
testEvent.event.content.body = "Hello ali, how are you?";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on a case-insensitive user ID.', function() {
|
||||
testEvent.event.content.body = "Hello @AlI:matrix.org, how are you?";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -240,13 +239,13 @@ describe('NotificationService', function() {
|
||||
|
||||
it('should bing on a display name.', function() {
|
||||
testEvent.event.content.body = "Hello Alice M, how are you?";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on a case-insensitive display name.', function() {
|
||||
testEvent.event.content.body = "Hello ALICE M, how are you?";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
@@ -254,25 +253,25 @@ describe('NotificationService', function() {
|
||||
|
||||
it('should bing on a bing word.', function() {
|
||||
testEvent.event.content.body = "I really like coffee";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on case-insensitive bing words.', function() {
|
||||
testEvent.event.content.body = "Coffee is great";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on wildcard (.*) bing words.', function() {
|
||||
testEvent.event.content.body = "It was foomahbar I think.";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on character group ([abc]) bing words.', function() {
|
||||
testEvent.event.content.body = "Ping!";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
let actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
testEvent.event.content.body = "Pong!";
|
||||
actions = pushProcessor.actionsForEvent(testEvent);
|
||||
@@ -281,13 +280,13 @@ describe('NotificationService', function() {
|
||||
|
||||
it('should bing on character range ([a-z]) bing words.', function() {
|
||||
testEvent.event.content.body = "I ate 6 pies";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
});
|
||||
|
||||
it('should bing on character negation ([!a]) bing words.', function() {
|
||||
testEvent.event.content.body = "boke";
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
let actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(true);
|
||||
testEvent.event.content.body = "bake";
|
||||
actions = pushProcessor.actionsForEvent(testEvent);
|
||||
@@ -298,7 +297,7 @@ describe('NotificationService', function() {
|
||||
|
||||
it('should gracefully handle bad input.', function() {
|
||||
testEvent.event.content.body = { "foo": "bar" };
|
||||
var actions = pushProcessor.actionsForEvent(testEvent);
|
||||
const actions = pushProcessor.actionsForEvent(testEvent);
|
||||
expect(actions.tweaks.highlight).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
var callbacks = require("../../lib/realtime-callbacks");
|
||||
var test_utils = require("../test-utils.js");
|
||||
const callbacks = require("../../lib/realtime-callbacks");
|
||||
const testUtils = require("../test-utils.js");
|
||||
|
||||
describe("realtime-callbacks", function() {
|
||||
var clock = jasmine.Clock;
|
||||
var fakeDate;
|
||||
const clock = jasmine.Clock;
|
||||
let fakeDate;
|
||||
|
||||
function tick(millis) {
|
||||
// make sure we tick the fakedate first, otherwise nothing will happen!
|
||||
@@ -14,10 +14,12 @@ describe("realtime-callbacks", function() {
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
test_utils.beforeEach(this);
|
||||
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
clock.useMock();
|
||||
fakeDate = Date.now();
|
||||
callbacks.setNow(function() { return fakeDate; });
|
||||
callbacks.setNow(function() {
|
||||
return fakeDate;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
@@ -26,7 +28,7 @@ describe("realtime-callbacks", function() {
|
||||
|
||||
describe("setTimeout", function() {
|
||||
it("should call the callback after the timeout", function() {
|
||||
var callback = jasmine.createSpy();
|
||||
const callback = jasmine.createSpy();
|
||||
callbacks.setTimeout(callback, 100);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
@@ -36,7 +38,7 @@ describe("realtime-callbacks", function() {
|
||||
|
||||
|
||||
it("should default to a zero timeout", function() {
|
||||
var callback = jasmine.createSpy();
|
||||
const callback = jasmine.createSpy();
|
||||
callbacks.setTimeout(callback);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
@@ -45,17 +47,17 @@ describe("realtime-callbacks", function() {
|
||||
});
|
||||
|
||||
it("should pass any parameters to the callback", function() {
|
||||
var callback = jasmine.createSpy();
|
||||
const callback = jasmine.createSpy();
|
||||
callbacks.setTimeout(callback, 0, "a", "b", "c");
|
||||
tick(0);
|
||||
expect(callback).toHaveBeenCalledWith("a", "b", "c");
|
||||
});
|
||||
|
||||
it("should set 'this' to the global object", function() {
|
||||
var callback = jasmine.createSpy();
|
||||
const callback = jasmine.createSpy();
|
||||
callback.andCallFake(function() {
|
||||
expect(this).toBe(global);
|
||||
expect(this.console).toBeDefined();
|
||||
expect(this).toBe(global); // eslint-disable-line no-invalid-this
|
||||
expect(this.console).toBeDefined(); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
callbacks.setTimeout(callback);
|
||||
tick(0);
|
||||
@@ -63,20 +65,20 @@ describe("realtime-callbacks", function() {
|
||||
});
|
||||
|
||||
it("should handle timeouts of several seconds", function() {
|
||||
var callback = jasmine.createSpy();
|
||||
const callback = jasmine.createSpy();
|
||||
callbacks.setTimeout(callback, 2000);
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
for (var i = 0; i < 4; i++) {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
tick(500);
|
||||
}
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call multiple callbacks in the right order", function() {
|
||||
var callback1 = jasmine.createSpy("callback1");
|
||||
var callback2 = jasmine.createSpy("callback2");
|
||||
var callback3 = jasmine.createSpy("callback3");
|
||||
const callback1 = jasmine.createSpy("callback1");
|
||||
const callback2 = jasmine.createSpy("callback2");
|
||||
const callback3 = jasmine.createSpy("callback3");
|
||||
callbacks.setTimeout(callback2, 200);
|
||||
callbacks.setTimeout(callback1, 100);
|
||||
callbacks.setTimeout(callback3, 300);
|
||||
@@ -99,8 +101,8 @@ describe("realtime-callbacks", function() {
|
||||
});
|
||||
|
||||
it("should treat -ve timeouts the same as a zero timeout", function() {
|
||||
var callback1 = jasmine.createSpy("callback1");
|
||||
var callback2 = jasmine.createSpy("callback2");
|
||||
const callback1 = jasmine.createSpy("callback1");
|
||||
const callback2 = jasmine.createSpy("callback2");
|
||||
|
||||
// check that cb1 is called before cb2
|
||||
callback1.andCallFake(function() {
|
||||
@@ -118,8 +120,8 @@ describe("realtime-callbacks", function() {
|
||||
});
|
||||
|
||||
it("should not get confused by chained calls", function() {
|
||||
var callback2 = jasmine.createSpy("callback2");
|
||||
var callback1 = jasmine.createSpy("callback1");
|
||||
const callback2 = jasmine.createSpy("callback2");
|
||||
const callback1 = jasmine.createSpy("callback1");
|
||||
callback1.andCallFake(function() {
|
||||
callbacks.setTimeout(callback2, 0);
|
||||
expect(callback2).not.toHaveBeenCalled();
|
||||
@@ -134,11 +136,11 @@ describe("realtime-callbacks", function() {
|
||||
});
|
||||
|
||||
it("should be immune to exceptions", function() {
|
||||
var callback1 = jasmine.createSpy("callback1");
|
||||
const callback1 = jasmine.createSpy("callback1");
|
||||
callback1.andCallFake(function() {
|
||||
throw new Error("prepare to die");
|
||||
});
|
||||
var callback2 = jasmine.createSpy("callback2");
|
||||
const callback2 = jasmine.createSpy("callback2");
|
||||
callbacks.setTimeout(callback1, 0);
|
||||
callbacks.setTimeout(callback2, 0);
|
||||
|
||||
@@ -148,24 +150,23 @@ describe("realtime-callbacks", function() {
|
||||
expect(callback1).toHaveBeenCalled();
|
||||
expect(callback2).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("cancelTimeout", function() {
|
||||
it("should cancel a pending timeout", function() {
|
||||
var callback = jasmine.createSpy();
|
||||
var k = callbacks.setTimeout(callback);
|
||||
const callback = jasmine.createSpy();
|
||||
const k = callbacks.setTimeout(callback);
|
||||
callbacks.clearTimeout(k);
|
||||
tick(0);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not affect sooner timeouts", function() {
|
||||
var callback1 = jasmine.createSpy("callback1");
|
||||
var callback2 = jasmine.createSpy("callback2");
|
||||
const callback1 = jasmine.createSpy("callback1");
|
||||
const callback2 = jasmine.createSpy("callback2");
|
||||
|
||||
callbacks.setTimeout(callback1, 100);
|
||||
var k = callbacks.setTimeout(callback2, 200);
|
||||
const k = callbacks.setTimeout(callback2, 200);
|
||||
callbacks.clearTimeout(k);
|
||||
|
||||
tick(100);
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var RoomMember = sdk.RoomMember;
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const RoomMember = sdk.RoomMember;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("RoomMember", function() {
|
||||
var roomId = "!foo:bar";
|
||||
var userA = "@alice:bar";
|
||||
var userB = "@bertha:bar";
|
||||
var userC = "@clarissa:bar";
|
||||
var member;
|
||||
const roomId = "!foo:bar";
|
||||
const userA = "@alice:bar";
|
||||
const userB = "@bertha:bar";
|
||||
const userC = "@clarissa:bar";
|
||||
let member;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
member = new RoomMember(roomId, userA);
|
||||
});
|
||||
|
||||
describe("getAvatarUrl", function() {
|
||||
var hsUrl = "https://my.home.server";
|
||||
const hsUrl = "https://my.home.server";
|
||||
|
||||
it("should return the URL from m.room.member preferentially", function() {
|
||||
member.events.member = utils.mkEvent({
|
||||
@@ -27,10 +27,10 @@ describe("RoomMember", function() {
|
||||
user: userA,
|
||||
content: {
|
||||
membership: "join",
|
||||
avatar_url: "mxc://flibble/wibble"
|
||||
}
|
||||
avatar_url: "mxc://flibble/wibble",
|
||||
},
|
||||
});
|
||||
var url = member.getAvatarUrl(hsUrl);
|
||||
const url = member.getAvatarUrl(hsUrl);
|
||||
// we don't care about how the mxc->http conversion is done, other
|
||||
// than it contains the mxc body.
|
||||
expect(url.indexOf("flibble/wibble")).not.toEqual(-1);
|
||||
@@ -38,20 +38,20 @@ describe("RoomMember", function() {
|
||||
|
||||
it("should return an identicon HTTP URL if allowDefault was set and there " +
|
||||
"was no m.room.member event", function() {
|
||||
var url = member.getAvatarUrl(hsUrl, 64, 64, "crop", true);
|
||||
const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", true);
|
||||
expect(url.indexOf("http")).toEqual(0); // don't care about form
|
||||
});
|
||||
|
||||
it("should return nothing if there is no m.room.member and allowDefault=false",
|
||||
function() {
|
||||
var url = member.getAvatarUrl(hsUrl, 64, 64, "crop", false);
|
||||
const url = member.getAvatarUrl(hsUrl, 64, 64, "crop", false);
|
||||
expect(url).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setPowerLevelEvent", function() {
|
||||
it("should set 'powerLevel' and 'powerLevelNorm'.", function() {
|
||||
var event = utils.mkEvent({
|
||||
const event = utils.mkEvent({
|
||||
type: "m.room.power_levels",
|
||||
room: roomId,
|
||||
user: userA,
|
||||
@@ -59,16 +59,16 @@ describe("RoomMember", function() {
|
||||
users_default: 20,
|
||||
users: {
|
||||
"@bertha:bar": 200,
|
||||
"@invalid:user": 10 // shouldn't barf on this.
|
||||
}
|
||||
"@invalid:user": 10, // shouldn't barf on this.
|
||||
},
|
||||
},
|
||||
event: true
|
||||
event: true,
|
||||
});
|
||||
member.setPowerLevelEvent(event);
|
||||
expect(member.powerLevel).toEqual(20);
|
||||
expect(member.powerLevelNorm).toEqual(10);
|
||||
|
||||
var memberB = new RoomMember(roomId, userB);
|
||||
const memberB = new RoomMember(roomId, userB);
|
||||
memberB.setPowerLevelEvent(event);
|
||||
expect(memberB.powerLevel).toEqual(200);
|
||||
expect(memberB.powerLevelNorm).toEqual(100);
|
||||
@@ -76,7 +76,7 @@ describe("RoomMember", function() {
|
||||
|
||||
it("should emit 'RoomMember.powerLevel' if the power level changes.",
|
||||
function() {
|
||||
var event = utils.mkEvent({
|
||||
const event = utils.mkEvent({
|
||||
type: "m.room.power_levels",
|
||||
room: roomId,
|
||||
user: userA,
|
||||
@@ -84,12 +84,12 @@ describe("RoomMember", function() {
|
||||
users_default: 20,
|
||||
users: {
|
||||
"@bertha:bar": 200,
|
||||
"@invalid:user": 10 // shouldn't barf on this.
|
||||
}
|
||||
"@invalid:user": 10, // shouldn't barf on this.
|
||||
},
|
||||
},
|
||||
event: true
|
||||
event: true,
|
||||
});
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
|
||||
member.on("RoomMember.powerLevel", function(emitEvent, emitMember) {
|
||||
emitCount += 1;
|
||||
@@ -105,7 +105,7 @@ describe("RoomMember", function() {
|
||||
|
||||
it("should honour power levels of zero.",
|
||||
function() {
|
||||
var event = utils.mkEvent({
|
||||
const event = utils.mkEvent({
|
||||
type: "m.room.power_levels",
|
||||
room: roomId,
|
||||
user: userA,
|
||||
@@ -113,11 +113,11 @@ describe("RoomMember", function() {
|
||||
users_default: 20,
|
||||
users: {
|
||||
"@alice:bar": 0,
|
||||
}
|
||||
},
|
||||
},
|
||||
event: true
|
||||
event: true,
|
||||
});
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
|
||||
// set the power level to something other than zero or we
|
||||
// won't get an event
|
||||
@@ -138,21 +138,21 @@ describe("RoomMember", function() {
|
||||
describe("setTypingEvent", function() {
|
||||
it("should set 'typing'", function() {
|
||||
member.typing = false;
|
||||
var memberB = new RoomMember(roomId, userB);
|
||||
const memberB = new RoomMember(roomId, userB);
|
||||
memberB.typing = true;
|
||||
var memberC = new RoomMember(roomId, userC);
|
||||
const memberC = new RoomMember(roomId, userC);
|
||||
memberC.typing = true;
|
||||
|
||||
var event = utils.mkEvent({
|
||||
const event = utils.mkEvent({
|
||||
type: "m.typing",
|
||||
user: userA,
|
||||
room: roomId,
|
||||
content: {
|
||||
user_ids: [
|
||||
userA, userC
|
||||
]
|
||||
userA, userC,
|
||||
],
|
||||
},
|
||||
event: true
|
||||
event: true,
|
||||
});
|
||||
member.setTypingEvent(event);
|
||||
memberB.setTypingEvent(event);
|
||||
@@ -165,17 +165,17 @@ describe("RoomMember", function() {
|
||||
|
||||
it("should emit 'RoomMember.typing' if the typing state changes",
|
||||
function() {
|
||||
var event = utils.mkEvent({
|
||||
const event = utils.mkEvent({
|
||||
type: "m.typing",
|
||||
room: roomId,
|
||||
content: {
|
||||
user_ids: [
|
||||
userA, userC
|
||||
]
|
||||
userA, userC,
|
||||
],
|
||||
},
|
||||
event: true
|
||||
event: true,
|
||||
});
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
member.on("RoomMember.typing", function(ev, mem) {
|
||||
expect(mem).toEqual(member);
|
||||
expect(ev).toEqual(event);
|
||||
@@ -190,20 +190,20 @@ describe("RoomMember", function() {
|
||||
});
|
||||
|
||||
describe("setMembershipEvent", function() {
|
||||
var joinEvent = utils.mkMembership({
|
||||
const joinEvent = utils.mkMembership({
|
||||
event: true,
|
||||
mship: "join",
|
||||
user: userA,
|
||||
room: roomId,
|
||||
name: "Alice"
|
||||
name: "Alice",
|
||||
});
|
||||
|
||||
var inviteEvent = utils.mkMembership({
|
||||
const inviteEvent = utils.mkMembership({
|
||||
event: true,
|
||||
mship: "invite",
|
||||
user: userB,
|
||||
skey: userA,
|
||||
room: roomId
|
||||
room: roomId,
|
||||
});
|
||||
|
||||
it("should set 'membership' and assign the event to 'events.member'.",
|
||||
@@ -218,24 +218,26 @@ describe("RoomMember", function() {
|
||||
|
||||
it("should set 'name' based on user_id, displayname and room state",
|
||||
function() {
|
||||
var roomState = {
|
||||
const roomState = {
|
||||
getStateEvents: function(type) {
|
||||
if (type !== "m.room.member") { return []; }
|
||||
if (type !== "m.room.member") {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
utils.mkMembership({
|
||||
event: true, mship: "join", room: roomId,
|
||||
user: userB
|
||||
user: userB,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
event: true, mship: "join", room: roomId,
|
||||
user: userC, name: "Alice"
|
||||
user: userC, name: "Alice",
|
||||
}),
|
||||
joinEvent
|
||||
joinEvent,
|
||||
];
|
||||
},
|
||||
getUserIdsWithDisplayName: function(displayName) {
|
||||
return [userA, userC];
|
||||
}
|
||||
},
|
||||
};
|
||||
expect(member.name).toEqual(userA); // default = user_id
|
||||
member.setMembershipEvent(joinEvent);
|
||||
@@ -247,7 +249,7 @@ describe("RoomMember", function() {
|
||||
});
|
||||
|
||||
it("should emit 'RoomMember.membership' if the membership changes", function() {
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
member.on("RoomMember.membership", function(ev, mem) {
|
||||
emitCount += 1;
|
||||
expect(mem).toEqual(member);
|
||||
@@ -260,7 +262,7 @@ describe("RoomMember", function() {
|
||||
});
|
||||
|
||||
it("should emit 'RoomMember.name' if the name changes", function() {
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
member.on("RoomMember.name", function(ev, mem) {
|
||||
emitCount += 1;
|
||||
expect(mem).toEqual(member);
|
||||
@@ -271,8 +273,5 @@ describe("RoomMember", function() {
|
||||
member.setMembershipEvent(joinEvent); // no-op
|
||||
expect(emitCount).toEqual(1);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var RoomState = sdk.RoomState;
|
||||
var RoomMember = sdk.RoomMember;
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const RoomState = sdk.RoomState;
|
||||
const RoomMember = sdk.RoomMember;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("RoomState", function() {
|
||||
var roomId = "!foo:bar";
|
||||
var userA = "@alice:bar";
|
||||
var userB = "@bob:bar";
|
||||
var state;
|
||||
const roomId = "!foo:bar";
|
||||
const userA = "@alice:bar";
|
||||
const userB = "@bob:bar";
|
||||
let state;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
state = new RoomState(roomId);
|
||||
state.setStateEvents([
|
||||
utils.mkMembership({ // userA joined
|
||||
event: true, mship: "join", user: userA, room: roomId
|
||||
event: true, mship: "join", user: userA, room: roomId,
|
||||
}),
|
||||
utils.mkMembership({ // userB joined
|
||||
event: true, mship: "join", user: userB, room: roomId
|
||||
event: true, mship: "join", user: userB, room: roomId,
|
||||
}),
|
||||
utils.mkEvent({ // Room name is "Room name goes here"
|
||||
type: "m.room.name", user: userA, room: roomId, event: true, content: {
|
||||
name: "Room name goes here"
|
||||
}
|
||||
name: "Room name goes here",
|
||||
},
|
||||
}),
|
||||
utils.mkEvent({ // Room creation
|
||||
type: "m.room.create", user: userA, room: roomId, event: true, content: {
|
||||
creator: userA
|
||||
}
|
||||
})
|
||||
creator: userA,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("RoomState", function() {
|
||||
});
|
||||
|
||||
it("should return a member for each m.room.member event", function() {
|
||||
var members = state.getMembers();
|
||||
const members = state.getMembers();
|
||||
expect(members.length).toEqual(2);
|
||||
// ordering unimportant
|
||||
expect([userA, userB].indexOf(members[0].userId)).not.toEqual(-1);
|
||||
@@ -58,15 +58,15 @@ describe("RoomState", function() {
|
||||
});
|
||||
|
||||
it("should return a member which changes as state changes", function() {
|
||||
var member = state.getMember(userB);
|
||||
const member = state.getMember(userB);
|
||||
expect(member.membership).toEqual("join");
|
||||
expect(member.name).toEqual(userB);
|
||||
|
||||
state.setStateEvents([
|
||||
utils.mkMembership({
|
||||
room: roomId, user: userB, mship: "leave", event: true,
|
||||
name: "BobGone"
|
||||
})
|
||||
name: "BobGone",
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(member.membership).toEqual("leave");
|
||||
@@ -81,14 +81,14 @@ describe("RoomState", function() {
|
||||
|
||||
it("should return a member which doesn't change when the state is updated",
|
||||
function() {
|
||||
var preLeaveUser = state.getSentinelMember(userA);
|
||||
const preLeaveUser = state.getSentinelMember(userA);
|
||||
state.setStateEvents([
|
||||
utils.mkMembership({
|
||||
room: roomId, user: userA, mship: "leave", event: true,
|
||||
name: "AliceIsGone"
|
||||
})
|
||||
name: "AliceIsGone",
|
||||
}),
|
||||
]);
|
||||
var postLeaveUser = state.getSentinelMember(userA);
|
||||
const postLeaveUser = state.getSentinelMember(userA);
|
||||
|
||||
expect(preLeaveUser.membership).toEqual("join");
|
||||
expect(preLeaveUser.name).toEqual(userA);
|
||||
@@ -111,7 +111,7 @@ describe("RoomState", function() {
|
||||
|
||||
it("should return a list of matching events if no state_key was specified",
|
||||
function() {
|
||||
var events = state.getStateEvents("m.room.member");
|
||||
const events = state.getStateEvents("m.room.member");
|
||||
expect(events.length).toEqual(2);
|
||||
// ordering unimportant
|
||||
expect([userA, userB].indexOf(events[0].getStateKey())).not.toEqual(-1);
|
||||
@@ -120,24 +120,24 @@ describe("RoomState", function() {
|
||||
|
||||
it("should return a single MatrixEvent if a state_key was specified",
|
||||
function() {
|
||||
var event = state.getStateEvents("m.room.member", userA);
|
||||
const event = state.getStateEvents("m.room.member", userA);
|
||||
expect(event.getContent()).toEqual({
|
||||
membership: "join"
|
||||
membership: "join",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setStateEvents", function() {
|
||||
it("should emit 'RoomState.members' for each m.room.member event", function() {
|
||||
var memberEvents = [
|
||||
const memberEvents = [
|
||||
utils.mkMembership({
|
||||
user: "@cleo:bar", mship: "invite", room: roomId, event: true
|
||||
user: "@cleo:bar", mship: "invite", room: roomId, event: true,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
user: "@daisy:bar", mship: "join", room: roomId, event: true
|
||||
})
|
||||
user: "@daisy:bar", mship: "join", room: roomId, event: true,
|
||||
}),
|
||||
];
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
state.on("RoomState.members", function(ev, st, mem) {
|
||||
expect(ev).toEqual(memberEvents[emitCount]);
|
||||
expect(st).toEqual(state);
|
||||
@@ -149,15 +149,15 @@ describe("RoomState", function() {
|
||||
});
|
||||
|
||||
it("should emit 'RoomState.newMember' for each new member added", function() {
|
||||
var memberEvents = [
|
||||
const memberEvents = [
|
||||
utils.mkMembership({
|
||||
user: "@cleo:bar", mship: "invite", room: roomId, event: true
|
||||
user: "@cleo:bar", mship: "invite", room: roomId, event: true,
|
||||
}),
|
||||
utils.mkMembership({
|
||||
user: "@daisy:bar", mship: "join", room: roomId, event: true
|
||||
})
|
||||
user: "@daisy:bar", mship: "join", room: roomId, event: true,
|
||||
}),
|
||||
];
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
state.on("RoomState.newMember", function(ev, st, mem) {
|
||||
expect(mem.userId).toEqual(memberEvents[emitCount].getSender());
|
||||
expect(mem.membership).toBeFalsy(); // not defined yet
|
||||
@@ -168,21 +168,21 @@ describe("RoomState", function() {
|
||||
});
|
||||
|
||||
it("should emit 'RoomState.events' for each state event", function() {
|
||||
var events = [
|
||||
const events = [
|
||||
utils.mkMembership({
|
||||
user: "@cleo:bar", mship: "invite", room: roomId, event: true
|
||||
user: "@cleo:bar", mship: "invite", room: roomId, event: true,
|
||||
}),
|
||||
utils.mkEvent({
|
||||
user: userB, room: roomId, type: "m.room.topic", event: true,
|
||||
content: {
|
||||
topic: "boo!"
|
||||
}
|
||||
topic: "boo!",
|
||||
},
|
||||
}),
|
||||
utils.mkMessage({ // Not a state event
|
||||
user: userA, room: roomId, event: true
|
||||
})
|
||||
user: userA, room: roomId, event: true,
|
||||
}),
|
||||
];
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
state.on("RoomState.events", function(ev, st) {
|
||||
expect(ev).toEqual(events[emitCount]);
|
||||
expect(st).toEqual(state);
|
||||
@@ -198,39 +198,39 @@ describe("RoomState", function() {
|
||||
state.members[userA] = utils.mock(RoomMember);
|
||||
state.members[userB] = utils.mock(RoomMember);
|
||||
|
||||
var powerLevelEvent = utils.mkEvent({
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
users_default: 10,
|
||||
state_default: 50,
|
||||
events_default: 25
|
||||
}
|
||||
});
|
||||
|
||||
state.setStateEvents([powerLevelEvent]);
|
||||
|
||||
expect(state.members[userA].setPowerLevelEvent).toHaveBeenCalledWith(
|
||||
powerLevelEvent
|
||||
);
|
||||
expect(state.members[userB].setPowerLevelEvent).toHaveBeenCalledWith(
|
||||
powerLevelEvent
|
||||
);
|
||||
});
|
||||
|
||||
it("should call setPowerLevelEvent on a new RoomMember if power levels exist",
|
||||
function() {
|
||||
var userC = "@cleo:bar";
|
||||
var memberEvent = utils.mkMembership({
|
||||
mship: "join", user: userC, room: roomId, event: true
|
||||
});
|
||||
var powerLevelEvent = utils.mkEvent({
|
||||
const powerLevelEvent = utils.mkEvent({
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
users_default: 10,
|
||||
state_default: 50,
|
||||
events_default: 25,
|
||||
users: {}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
state.setStateEvents([powerLevelEvent]);
|
||||
|
||||
expect(state.members[userA].setPowerLevelEvent).toHaveBeenCalledWith(
|
||||
powerLevelEvent,
|
||||
);
|
||||
expect(state.members[userB].setPowerLevelEvent).toHaveBeenCalledWith(
|
||||
powerLevelEvent,
|
||||
);
|
||||
});
|
||||
|
||||
it("should call setPowerLevelEvent on a new RoomMember if power levels exist",
|
||||
function() {
|
||||
const userC = "@cleo:bar";
|
||||
const memberEvent = utils.mkMembership({
|
||||
mship: "join", user: userC, room: roomId, event: true,
|
||||
});
|
||||
const powerLevelEvent = utils.mkEvent({
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
users_default: 10,
|
||||
state_default: 50,
|
||||
events_default: 25,
|
||||
users: {},
|
||||
},
|
||||
});
|
||||
|
||||
state.setStateEvents([powerLevelEvent]);
|
||||
@@ -247,24 +247,24 @@ describe("RoomState", function() {
|
||||
state.members[userA] = utils.mock(RoomMember);
|
||||
state.members[userB] = utils.mock(RoomMember);
|
||||
|
||||
var memberEvent = utils.mkMembership({
|
||||
user: userB, mship: "leave", room: roomId, event: true
|
||||
const memberEvent = utils.mkMembership({
|
||||
user: userB, mship: "leave", room: roomId, event: true,
|
||||
});
|
||||
state.setStateEvents([memberEvent]);
|
||||
|
||||
expect(state.members[userA].setMembershipEvent).not.toHaveBeenCalled();
|
||||
expect(state.members[userB].setMembershipEvent).toHaveBeenCalledWith(
|
||||
memberEvent, state
|
||||
memberEvent, state,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("setTypingEvent", function() {
|
||||
it("should call setTypingEvent on each RoomMember", function() {
|
||||
var typingEvent = utils.mkEvent({
|
||||
const typingEvent = utils.mkEvent({
|
||||
type: "m.typing", room: roomId, event: true, content: {
|
||||
user_ids: [userA]
|
||||
}
|
||||
user_ids: [userA],
|
||||
},
|
||||
});
|
||||
// mock up the room members
|
||||
state.members[userA] = utils.mock(RoomMember);
|
||||
@@ -272,10 +272,10 @@ describe("RoomState", function() {
|
||||
state.setTypingEvent(typingEvent);
|
||||
|
||||
expect(state.members[userA].setTypingEvent).toHaveBeenCalledWith(
|
||||
typingEvent
|
||||
typingEvent,
|
||||
);
|
||||
expect(state.members[userB].setTypingEvent).toHaveBeenCalledWith(
|
||||
typingEvent
|
||||
typingEvent,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -284,7 +284,7 @@ describe("RoomState", function() {
|
||||
it("should say non-joined members may not send state",
|
||||
function() {
|
||||
expect(state.maySendStateEvent(
|
||||
'm.room.name', "@nobody:nowhere"
|
||||
'm.room.name', "@nobody:nowhere",
|
||||
)).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -296,15 +296,15 @@ describe("RoomState", function() {
|
||||
it("should say members with power >=50 may send state with power level event " +
|
||||
"but no state default",
|
||||
function() {
|
||||
var powerLevelEvent = {
|
||||
const powerLevelEvent = {
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
users_default: 10,
|
||||
// state_default: 50, "intentionally left blank"
|
||||
events_default: 25,
|
||||
users: {
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
powerLevelEvent.content.users[userA] = 50;
|
||||
|
||||
@@ -316,15 +316,15 @@ describe("RoomState", function() {
|
||||
|
||||
it("should obey state_default",
|
||||
function() {
|
||||
var powerLevelEvent = {
|
||||
const powerLevelEvent = {
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
users_default: 10,
|
||||
state_default: 30,
|
||||
events_default: 25,
|
||||
users: {
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
powerLevelEvent.content.users[userA] = 30;
|
||||
powerLevelEvent.content.users[userB] = 29;
|
||||
@@ -337,18 +337,18 @@ describe("RoomState", function() {
|
||||
|
||||
it("should honour explicit event power levels in the power_levels event",
|
||||
function() {
|
||||
var powerLevelEvent = {
|
||||
const powerLevelEvent = {
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
events: {
|
||||
"m.room.other_thing": 76
|
||||
"m.room.other_thing": 76,
|
||||
},
|
||||
users_default: 10,
|
||||
state_default: 50,
|
||||
events_default: 25,
|
||||
users: {
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
powerLevelEvent.content.users[userA] = 80;
|
||||
powerLevelEvent.content.users[userB] = 50;
|
||||
@@ -367,7 +367,7 @@ describe("RoomState", function() {
|
||||
it("should say non-joined members may not send events",
|
||||
function() {
|
||||
expect(state.maySendEvent(
|
||||
'm.room.message', "@nobody:nowhere"
|
||||
'm.room.message', "@nobody:nowhere",
|
||||
)).toEqual(false);
|
||||
expect(state.maySendMessage("@nobody:nowhere")).toEqual(false);
|
||||
});
|
||||
@@ -380,15 +380,15 @@ describe("RoomState", function() {
|
||||
|
||||
it("should obey events_default",
|
||||
function() {
|
||||
var powerLevelEvent = {
|
||||
const powerLevelEvent = {
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
users_default: 10,
|
||||
state_default: 30,
|
||||
events_default: 25,
|
||||
users: {
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
powerLevelEvent.content.users[userA] = 26;
|
||||
powerLevelEvent.content.users[userB] = 24;
|
||||
@@ -404,18 +404,18 @@ describe("RoomState", function() {
|
||||
|
||||
it("should honour explicit event power levels in the power_levels event",
|
||||
function() {
|
||||
var powerLevelEvent = {
|
||||
const powerLevelEvent = {
|
||||
type: "m.room.power_levels", room: roomId, user: userA, event: true,
|
||||
content: {
|
||||
events: {
|
||||
"m.room.other_thing": 33
|
||||
"m.room.other_thing": 33,
|
||||
},
|
||||
users_default: 10,
|
||||
state_default: 50,
|
||||
events_default: 25,
|
||||
users: {
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
powerLevelEvent.content.users[userA] = 40;
|
||||
powerLevelEvent.content.users[userB] = 30;
|
||||
|
||||
+256
-258
File diff suppressed because it is too large
Load Diff
+64
-60
@@ -1,24 +1,27 @@
|
||||
"use strict";
|
||||
var q = require("q");
|
||||
var sdk = require("../..");
|
||||
var MatrixScheduler = sdk.MatrixScheduler;
|
||||
var MatrixError = sdk.MatrixError;
|
||||
var utils = require("../test-utils");
|
||||
// This file had a function whose name is all caps, which displeases eslint
|
||||
/* eslint new-cap: "off" */
|
||||
|
||||
const q = require("q");
|
||||
const sdk = require("../..");
|
||||
const MatrixScheduler = sdk.MatrixScheduler;
|
||||
const MatrixError = sdk.MatrixError;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("MatrixScheduler", function() {
|
||||
var scheduler;
|
||||
var retryFn, queueFn;
|
||||
var defer;
|
||||
var roomId = "!foo:bar";
|
||||
var eventA = utils.mkMessage({
|
||||
user: "@alice:bar", room: roomId, event: true
|
||||
let scheduler;
|
||||
let retryFn;
|
||||
let queueFn;
|
||||
let defer;
|
||||
const roomId = "!foo:bar";
|
||||
const eventA = utils.mkMessage({
|
||||
user: "@alice:bar", room: roomId, event: true,
|
||||
});
|
||||
var eventB = utils.mkMessage({
|
||||
user: "@alice:bar", room: roomId, event: true
|
||||
const eventB = utils.mkMessage({
|
||||
user: "@alice:bar", room: roomId, event: true,
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
jasmine.Clock.useMock();
|
||||
scheduler = new MatrixScheduler(function(ev, attempts, err) {
|
||||
if (retryFn) {
|
||||
@@ -43,15 +46,14 @@ describe("MatrixScheduler", function() {
|
||||
queueFn = function() {
|
||||
return "one_big_queue";
|
||||
};
|
||||
var deferA = q.defer();
|
||||
var deferB = q.defer();
|
||||
var resolvedA = false;
|
||||
const deferA = q.defer();
|
||||
const deferB = q.defer();
|
||||
let resolvedA = false;
|
||||
scheduler.setProcessFunction(function(event) {
|
||||
if (resolvedA) {
|
||||
expect(event).toEqual(eventB);
|
||||
return deferB.promise;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
expect(event).toEqual(eventA);
|
||||
return deferA.promise;
|
||||
}
|
||||
@@ -68,22 +70,23 @@ describe("MatrixScheduler", function() {
|
||||
|
||||
it("should invoke the retryFn on failure and wait the amount of time specified",
|
||||
function(done) {
|
||||
var waitTimeMs = 1500;
|
||||
var retryDefer = q.defer();
|
||||
const waitTimeMs = 1500;
|
||||
const retryDefer = q.defer();
|
||||
retryFn = function() {
|
||||
retryDefer.resolve();
|
||||
return waitTimeMs;
|
||||
};
|
||||
queueFn = function() { return "yep"; };
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
|
||||
var procCount = 0;
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
if (procCount === 1) {
|
||||
expect(ev).toEqual(eventA);
|
||||
return defer.promise;
|
||||
}
|
||||
else if (procCount === 2) {
|
||||
} else if (procCount === 2) {
|
||||
// don't care about this defer
|
||||
return q.defer().promise;
|
||||
}
|
||||
@@ -109,25 +112,26 @@ describe("MatrixScheduler", function() {
|
||||
retryFn = function() {
|
||||
return -1;
|
||||
};
|
||||
queueFn = function() { return "yep"; };
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
|
||||
var deferA = q.defer();
|
||||
var deferB = q.defer();
|
||||
var procCount = 0;
|
||||
const deferA = q.defer();
|
||||
const deferB = q.defer();
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
if (procCount === 1) {
|
||||
expect(ev).toEqual(eventA);
|
||||
return deferA.promise;
|
||||
}
|
||||
else if (procCount === 2) {
|
||||
} else if (procCount === 2) {
|
||||
expect(ev).toEqual(eventB);
|
||||
return deferB.promise;
|
||||
}
|
||||
expect(procCount).toBeLessThan(3);
|
||||
});
|
||||
|
||||
var globalA = scheduler.queueEvent(eventA);
|
||||
const globalA = scheduler.queueEvent(eventA);
|
||||
scheduler.queueEvent(eventB);
|
||||
|
||||
expect(procCount).toEqual(1);
|
||||
@@ -145,10 +149,10 @@ describe("MatrixScheduler", function() {
|
||||
// Expect to have processFn invoked for A&B.
|
||||
// Resolve A.
|
||||
// Expect to have processFn invoked for D.
|
||||
var eventC = utils.mkMessage({user: "@a:bar", room: roomId, event: true});
|
||||
var eventD = utils.mkMessage({user: "@b:bar", room: roomId, event: true});
|
||||
const eventC = utils.mkMessage({user: "@a:bar", room: roomId, event: true});
|
||||
const eventD = utils.mkMessage({user: "@b:bar", room: roomId, event: true});
|
||||
|
||||
var buckets = {};
|
||||
const buckets = {};
|
||||
buckets[eventA.getId()] = "queue_A";
|
||||
buckets[eventD.getId()] = "queue_A";
|
||||
buckets[eventB.getId()] = "queue_B";
|
||||
@@ -161,12 +165,12 @@ describe("MatrixScheduler", function() {
|
||||
return buckets[event.getId()];
|
||||
};
|
||||
|
||||
var expectOrder = [
|
||||
eventA.getId(), eventB.getId(), eventD.getId()
|
||||
const expectOrder = [
|
||||
eventA.getId(), eventB.getId(), eventD.getId(),
|
||||
];
|
||||
var deferA = q.defer();
|
||||
const deferA = q.defer();
|
||||
scheduler.setProcessFunction(function(event) {
|
||||
var id = expectOrder.shift();
|
||||
const id = expectOrder.shift();
|
||||
expect(id).toEqual(event.getId());
|
||||
if (expectOrder.length === 0) {
|
||||
done();
|
||||
@@ -197,7 +201,7 @@ describe("MatrixScheduler", function() {
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
var prom = scheduler.queueEvent(eventA);
|
||||
const prom = scheduler.queueEvent(eventA);
|
||||
expect(prom).toBeDefined();
|
||||
expect(prom.then).toBeDefined();
|
||||
});
|
||||
@@ -225,15 +229,15 @@ describe("MatrixScheduler", function() {
|
||||
};
|
||||
scheduler.queueEvent(eventA);
|
||||
scheduler.queueEvent(eventB);
|
||||
var queue = scheduler.getQueueForEvent(eventA);
|
||||
const queue = scheduler.getQueueForEvent(eventA);
|
||||
expect(queue.length).toEqual(2);
|
||||
expect(queue).toEqual([eventA, eventB]);
|
||||
// modify the queue
|
||||
var eventC = utils.mkMessage(
|
||||
{user: "@a:bar", room: roomId, event: true}
|
||||
const eventC = utils.mkMessage(
|
||||
{user: "@a:bar", room: roomId, event: true},
|
||||
);
|
||||
queue.push(eventC);
|
||||
var queueAgain = scheduler.getQueueForEvent(eventA);
|
||||
const queueAgain = scheduler.getQueueForEvent(eventA);
|
||||
expect(queueAgain.length).toEqual(2);
|
||||
});
|
||||
|
||||
@@ -244,9 +248,9 @@ describe("MatrixScheduler", function() {
|
||||
};
|
||||
scheduler.queueEvent(eventA);
|
||||
scheduler.queueEvent(eventB);
|
||||
var queue = scheduler.getQueueForEvent(eventA);
|
||||
const queue = scheduler.getQueueForEvent(eventA);
|
||||
queue[1].event.content.body = "foo";
|
||||
var queueAgain = scheduler.getQueueForEvent(eventA);
|
||||
const queueAgain = scheduler.getQueueForEvent(eventA);
|
||||
expect(queueAgain[1].event.content.body).toEqual("foo");
|
||||
});
|
||||
});
|
||||
@@ -280,7 +284,7 @@ describe("MatrixScheduler", function() {
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
var procCount = 0;
|
||||
let procCount = 0;
|
||||
scheduler.queueEvent(eventA);
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
@@ -294,7 +298,7 @@ describe("MatrixScheduler", function() {
|
||||
queueFn = function() {
|
||||
return "yep";
|
||||
};
|
||||
var procCount = 0;
|
||||
let procCount = 0;
|
||||
scheduler.setProcessFunction(function(ev) {
|
||||
procCount += 1;
|
||||
return defer.promise;
|
||||
@@ -308,41 +312,41 @@ describe("MatrixScheduler", function() {
|
||||
expect(MatrixScheduler.QUEUE_MESSAGES(eventA)).toEqual("message");
|
||||
expect(MatrixScheduler.QUEUE_MESSAGES(
|
||||
utils.mkMembership({
|
||||
user: "@alice:bar", room: roomId, mship: "join", event: true
|
||||
})
|
||||
user: "@alice:bar", room: roomId, mship: "join", event: true,
|
||||
}),
|
||||
)).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RETRY_BACKOFF_RATELIMIT", function() {
|
||||
it("should wait at least the time given on M_LIMIT_EXCEEDED", function() {
|
||||
var res = MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
const res = MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 1, new MatrixError({
|
||||
errcode: "M_LIMIT_EXCEEDED", retry_after_ms: 5000
|
||||
})
|
||||
errcode: "M_LIMIT_EXCEEDED", retry_after_ms: 5000,
|
||||
}),
|
||||
);
|
||||
expect(res >= 500).toBe(true, "Didn't wait long enough.");
|
||||
});
|
||||
|
||||
it("should give up after 5 attempts", function() {
|
||||
var res = MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 5, {}
|
||||
const res = MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 5, {},
|
||||
);
|
||||
expect(res).toBe(-1, "Didn't give up.");
|
||||
});
|
||||
|
||||
it("should do exponential backoff", function() {
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 1, {}
|
||||
eventA, 1, {},
|
||||
)).toEqual(2000);
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 2, {}
|
||||
eventA, 2, {},
|
||||
)).toEqual(4000);
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 3, {}
|
||||
eventA, 3, {},
|
||||
)).toEqual(8000);
|
||||
expect(MatrixScheduler.RETRY_BACKOFF_RATELIMIT(
|
||||
eventA, 4, {}
|
||||
eventA, 4, {},
|
||||
)).toEqual(16000);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
"use strict";
|
||||
var q = require("q");
|
||||
var sdk = require("../..");
|
||||
var EventTimeline = sdk.EventTimeline;
|
||||
var TimelineWindow = sdk.TimelineWindow;
|
||||
var TimelineIndex = require("../../lib/timeline-window").TimelineIndex;
|
||||
const q = require("q");
|
||||
const sdk = require("../..");
|
||||
const EventTimeline = sdk.EventTimeline;
|
||||
const TimelineWindow = sdk.TimelineWindow;
|
||||
const TimelineIndex = require("../../lib/timeline-window").TimelineIndex;
|
||||
|
||||
var utils = require("../test-utils");
|
||||
const utils = require("../test-utils");
|
||||
|
||||
var ROOM_ID = "roomId";
|
||||
var USER_ID = "userId";
|
||||
const ROOM_ID = "roomId";
|
||||
const USER_ID = "userId";
|
||||
|
||||
/*
|
||||
* create a timeline with a bunch (default 3) events.
|
||||
* baseIndex is 1 by default.
|
||||
*/
|
||||
function createTimeline(numEvents, baseIndex) {
|
||||
if (numEvents === undefined) { numEvents = 3; }
|
||||
if (baseIndex === undefined) { baseIndex = 1; }
|
||||
if (numEvents === undefined) {
|
||||
numEvents = 3;
|
||||
}
|
||||
if (baseIndex === undefined) {
|
||||
baseIndex = 1;
|
||||
}
|
||||
|
||||
// XXX: this is a horrid hack
|
||||
var timelineSet = { room: { roomId: ROOM_ID }};
|
||||
timelineSet.room.getUnfilteredTimelineSet = function() { return timelineSet; };
|
||||
const timelineSet = { room: { roomId: ROOM_ID }};
|
||||
timelineSet.room.getUnfilteredTimelineSet = function() {
|
||||
return timelineSet;
|
||||
};
|
||||
|
||||
var timeline = new EventTimeline(timelineSet);
|
||||
const timeline = new EventTimeline(timelineSet);
|
||||
|
||||
// add the events after the baseIndex first
|
||||
addEventsToTimeline(timeline, numEvents - baseIndex, false);
|
||||
@@ -35,12 +41,12 @@ function createTimeline(numEvents, baseIndex) {
|
||||
}
|
||||
|
||||
function addEventsToTimeline(timeline, numEvents, atStart) {
|
||||
for (var i = 0; i < numEvents; i++) {
|
||||
for (let i = 0; i < numEvents; i++) {
|
||||
timeline.addEvent(
|
||||
utils.mkMessage({
|
||||
room: ROOM_ID, user: USER_ID,
|
||||
event: true,
|
||||
}), atStart
|
||||
}), atStart,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,8 +56,8 @@ function addEventsToTimeline(timeline, numEvents, atStart) {
|
||||
* create a pair of linked timelines
|
||||
*/
|
||||
function createLinkedTimelines() {
|
||||
var tl1 = createTimeline();
|
||||
var tl2 = createTimeline();
|
||||
const tl1 = createTimeline();
|
||||
const tl2 = createTimeline();
|
||||
tl1.setNeighbouringTimeline(tl2, EventTimeline.FORWARDS);
|
||||
tl2.setNeighbouringTimeline(tl1, EventTimeline.BACKWARDS);
|
||||
return [tl1, tl2];
|
||||
@@ -60,46 +66,47 @@ function createLinkedTimelines() {
|
||||
|
||||
describe("TimelineIndex", function() {
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
describe("minIndex", function() {
|
||||
it("should return the min index relative to BaseIndex", function() {
|
||||
var timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
expect(timelineIndex.minIndex()).toEqual(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("maxIndex", function() {
|
||||
it("should return the max index relative to BaseIndex", function() {
|
||||
var timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
expect(timelineIndex.maxIndex()).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("advance", function() {
|
||||
it("should advance up to the end of the timeline", function() {
|
||||
var timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
var result = timelineIndex.advance(3);
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.advance(3);
|
||||
expect(result).toEqual(2);
|
||||
expect(timelineIndex.index).toEqual(2);
|
||||
});
|
||||
|
||||
it("should retreat back to the start of the timeline", function() {
|
||||
var timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
var result = timelineIndex.advance(-2);
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.advance(-2);
|
||||
expect(result).toEqual(-1);
|
||||
expect(timelineIndex.index).toEqual(-1);
|
||||
});
|
||||
|
||||
it("should advance into the next timeline", function() {
|
||||
var timelines = createLinkedTimelines();
|
||||
var tl1 = timelines[0], tl2 = timelines[1];
|
||||
const timelines = createLinkedTimelines();
|
||||
const tl1 = timelines[0];
|
||||
const tl2 = timelines[1];
|
||||
|
||||
// initialise the index pointing at the end of the first timeline
|
||||
var timelineIndex = new TimelineIndex(tl1, 2);
|
||||
const timelineIndex = new TimelineIndex(tl1, 2);
|
||||
|
||||
var result = timelineIndex.advance(1);
|
||||
const result = timelineIndex.advance(1);
|
||||
expect(result).toEqual(1);
|
||||
expect(timelineIndex.timeline).toBe(tl2);
|
||||
|
||||
@@ -110,14 +117,15 @@ describe("TimelineIndex", function() {
|
||||
});
|
||||
|
||||
it("should retreat into the previous timeline", function() {
|
||||
var timelines = createLinkedTimelines();
|
||||
var tl1 = timelines[0], tl2 = timelines[1];
|
||||
const timelines = createLinkedTimelines();
|
||||
const tl1 = timelines[0];
|
||||
const tl2 = timelines[1];
|
||||
|
||||
// initialise the index pointing at the start of the second
|
||||
// timeline
|
||||
var timelineIndex = new TimelineIndex(tl2, -1);
|
||||
const timelineIndex = new TimelineIndex(tl2, -1);
|
||||
|
||||
var result = timelineIndex.advance(-1);
|
||||
const result = timelineIndex.advance(-1);
|
||||
expect(result).toEqual(-1);
|
||||
expect(timelineIndex.timeline).toBe(tl1);
|
||||
expect(timelineIndex.index).toEqual(1);
|
||||
@@ -126,8 +134,8 @@ describe("TimelineIndex", function() {
|
||||
|
||||
describe("retreat", function() {
|
||||
it("should retreat up to the start of the timeline", function() {
|
||||
var timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
var result = timelineIndex.retreat(2);
|
||||
const timelineIndex = new TimelineIndex(createTimeline(), 0);
|
||||
const result = timelineIndex.retreat(2);
|
||||
expect(result).toEqual(1);
|
||||
expect(timelineIndex.index).toEqual(-1);
|
||||
});
|
||||
@@ -140,7 +148,8 @@ describe("TimelineWindow", function() {
|
||||
* create a dummy eventTimelineSet and client, and a TimelineWindow
|
||||
* attached to them.
|
||||
*/
|
||||
var timelineSet, client;
|
||||
let timelineSet;
|
||||
let client;
|
||||
function createWindow(timeline, opts) {
|
||||
timelineSet = {};
|
||||
client = {};
|
||||
@@ -153,53 +162,55 @@ describe("TimelineWindow", function() {
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
describe("load", function() {
|
||||
it("should initialise from the live timeline", function(done) {
|
||||
var liveTimeline = createTimeline();
|
||||
var room = {};
|
||||
room.getLiveTimeline = function() { return liveTimeline; };
|
||||
const liveTimeline = createTimeline();
|
||||
const room = {};
|
||||
room.getLiveTimeline = function() {
|
||||
return liveTimeline;
|
||||
};
|
||||
|
||||
var timelineWindow = new TimelineWindow(undefined, room);
|
||||
const timelineWindow = new TimelineWindow(undefined, room);
|
||||
timelineWindow.load(undefined, 2).then(function() {
|
||||
var expectedEvents = liveTimeline.getEvents().slice(1);
|
||||
const expectedEvents = liveTimeline.getEvents().slice(1);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("should initialise from a specific event", function(done) {
|
||||
var timeline = createTimeline();
|
||||
var eventId = timeline.getEvents()[1].getId();
|
||||
const timeline = createTimeline();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
var timelineSet = {};
|
||||
var client = {};
|
||||
const timelineSet = {};
|
||||
const client = {};
|
||||
client.getEventTimeline = function(timelineSet0, eventId0) {
|
||||
expect(timelineSet0).toBe(timelineSet);
|
||||
expect(eventId0).toEqual(eventId);
|
||||
return q(timeline);
|
||||
};
|
||||
|
||||
var timelineWindow = new TimelineWindow(client, timelineSet);
|
||||
const timelineWindow = new TimelineWindow(client, timelineSet);
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = timeline.getEvents();
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("canPaginate should return false until load has returned",
|
||||
function(done) {
|
||||
var timeline = createTimeline();
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok1", EventTimeline.BACKWARDS);
|
||||
timeline.setPaginationToken("toktok2", EventTimeline.FORWARDS);
|
||||
|
||||
var eventId = timeline.getEvents()[1].getId();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
var timelineSet = {};
|
||||
var client = {};
|
||||
const timelineSet = {};
|
||||
const client = {};
|
||||
|
||||
var timelineWindow = new TimelineWindow(client, timelineSet);
|
||||
const timelineWindow = new TimelineWindow(client, timelineSet);
|
||||
|
||||
client.getEventTimeline = function(timelineSet0, eventId0) {
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -210,7 +221,7 @@ describe("TimelineWindow", function() {
|
||||
};
|
||||
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = timeline.getEvents();
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
.toBe(true);
|
||||
@@ -223,12 +234,12 @@ describe("TimelineWindow", function() {
|
||||
describe("pagination", function() {
|
||||
it("should be able to advance across the initial timeline",
|
||||
function(done) {
|
||||
var timeline = createTimeline();
|
||||
var eventId = timeline.getEvents()[1].getId();
|
||||
var timelineWindow = createWindow(timeline);
|
||||
const timeline = createTimeline();
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(timeline);
|
||||
|
||||
timelineWindow.load(eventId, 1).then(function() {
|
||||
var expectedEvents = [timeline.getEvents()[1]];
|
||||
const expectedEvents = [timeline.getEvents()[1]];
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -239,7 +250,7 @@ describe("TimelineWindow", function() {
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
var expectedEvents = timeline.getEvents().slice(1);
|
||||
const expectedEvents = timeline.getEvents().slice(1);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -254,7 +265,7 @@ describe("TimelineWindow", function() {
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
var expectedEvents = timeline.getEvents();
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -268,12 +279,12 @@ describe("TimelineWindow", function() {
|
||||
});
|
||||
|
||||
it("should advance into next timeline", function(done) {
|
||||
var tls = createLinkedTimelines();
|
||||
var eventId = tls[0].getEvents()[1].getId();
|
||||
var timelineWindow = createWindow(tls[0], {windowLimit: 5});
|
||||
const tls = createLinkedTimelines();
|
||||
const eventId = tls[0].getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(tls[0], {windowLimit: 5});
|
||||
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = tls[0].getEvents();
|
||||
const expectedEvents = tls[0].getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -284,7 +295,7 @@ describe("TimelineWindow", function() {
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
var expectedEvents = tls[0].getEvents()
|
||||
const expectedEvents = tls[0].getEvents()
|
||||
.concat(tls[1].getEvents().slice(0, 2));
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
@@ -298,7 +309,7 @@ describe("TimelineWindow", function() {
|
||||
expect(success).toBe(true);
|
||||
// the windowLimit should have made us drop an event from
|
||||
// tls[0]
|
||||
var expectedEvents = tls[0].getEvents().slice(1)
|
||||
const expectedEvents = tls[0].getEvents().slice(1)
|
||||
.concat(tls[1].getEvents());
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
@@ -313,12 +324,12 @@ describe("TimelineWindow", function() {
|
||||
});
|
||||
|
||||
it("should retreat into previous timeline", function(done) {
|
||||
var tls = createLinkedTimelines();
|
||||
var eventId = tls[1].getEvents()[1].getId();
|
||||
var timelineWindow = createWindow(tls[1], {windowLimit: 5});
|
||||
const tls = createLinkedTimelines();
|
||||
const eventId = tls[1].getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(tls[1], {windowLimit: 5});
|
||||
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = tls[1].getEvents();
|
||||
const expectedEvents = tls[1].getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -329,7 +340,7 @@ describe("TimelineWindow", function() {
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
var expectedEvents = tls[0].getEvents().slice(1, 3)
|
||||
const expectedEvents = tls[0].getEvents().slice(1, 3)
|
||||
.concat(tls[1].getEvents());
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
@@ -343,7 +354,7 @@ describe("TimelineWindow", function() {
|
||||
expect(success).toBe(true);
|
||||
// the windowLimit should have made us drop an event from
|
||||
// tls[1]
|
||||
var expectedEvents = tls[0].getEvents()
|
||||
const expectedEvents = tls[0].getEvents()
|
||||
.concat(tls[1].getEvents().slice(0, 2));
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
@@ -358,11 +369,11 @@ describe("TimelineWindow", function() {
|
||||
});
|
||||
|
||||
it("should make forward pagination requests", function(done) {
|
||||
var timeline = createTimeline();
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.FORWARDS);
|
||||
|
||||
var timelineWindow = createWindow(timeline, {windowLimit: 5});
|
||||
var eventId = timeline.getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(timeline, {windowLimit: 5});
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
client.paginateEventTimeline = function(timeline0, opts) {
|
||||
expect(timeline0).toBe(timeline);
|
||||
@@ -374,7 +385,7 @@ describe("TimelineWindow", function() {
|
||||
};
|
||||
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = timeline.getEvents();
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -384,18 +395,18 @@ describe("TimelineWindow", function() {
|
||||
return timelineWindow.paginate(EventTimeline.FORWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
var expectedEvents = timeline.getEvents().slice(0, 5);
|
||||
const expectedEvents = timeline.getEvents().slice(0, 5);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
|
||||
|
||||
it("should make backward pagination requests", function(done) {
|
||||
var timeline = createTimeline();
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.BACKWARDS);
|
||||
|
||||
var timelineWindow = createWindow(timeline, {windowLimit: 5});
|
||||
var eventId = timeline.getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(timeline, {windowLimit: 5});
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
client.paginateEventTimeline = function(timeline0, opts) {
|
||||
expect(timeline0).toBe(timeline);
|
||||
@@ -407,7 +418,7 @@ describe("TimelineWindow", function() {
|
||||
};
|
||||
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = timeline.getEvents();
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -417,20 +428,20 @@ describe("TimelineWindow", function() {
|
||||
return timelineWindow.paginate(EventTimeline.BACKWARDS, 2);
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(true);
|
||||
var expectedEvents = timeline.getEvents().slice(1, 6);
|
||||
const expectedEvents = timeline.getEvents().slice(1, 6);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
}).catch(utils.failTest).done(done);
|
||||
});
|
||||
|
||||
it("should limit the number of unsuccessful pagination requests",
|
||||
function(done) {
|
||||
var timeline = createTimeline();
|
||||
const timeline = createTimeline();
|
||||
timeline.setPaginationToken("toktok", EventTimeline.FORWARDS);
|
||||
|
||||
var timelineWindow = createWindow(timeline, {windowLimit: 5});
|
||||
var eventId = timeline.getEvents()[1].getId();
|
||||
const timelineWindow = createWindow(timeline, {windowLimit: 5});
|
||||
const eventId = timeline.getEvents()[1].getId();
|
||||
|
||||
var paginateCount = 0;
|
||||
let paginateCount = 0;
|
||||
client.paginateEventTimeline = function(timeline0, opts) {
|
||||
expect(timeline0).toBe(timeline);
|
||||
expect(opts.backwards).toBe(false);
|
||||
@@ -440,7 +451,7 @@ describe("TimelineWindow", function() {
|
||||
};
|
||||
|
||||
timelineWindow.load(eventId, 3).then(function() {
|
||||
var expectedEvents = timeline.getEvents();
|
||||
const expectedEvents = timeline.getEvents();
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
@@ -451,7 +462,7 @@ describe("TimelineWindow", function() {
|
||||
}).then(function(success) {
|
||||
expect(success).toBe(false);
|
||||
expect(paginateCount).toEqual(3);
|
||||
var expectedEvents = timeline.getEvents().slice(0, 3);
|
||||
const expectedEvents = timeline.getEvents().slice(0, 3);
|
||||
expect(timelineWindow.getEvents()).toEqual(expectedEvents);
|
||||
|
||||
expect(timelineWindow.canPaginate(EventTimeline.BACKWARDS))
|
||||
|
||||
+12
-12
@@ -1,30 +1,30 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var User = sdk.User;
|
||||
var utils = require("../test-utils");
|
||||
const sdk = require("../..");
|
||||
const User = sdk.User;
|
||||
const utils = require("../test-utils");
|
||||
|
||||
describe("User", function() {
|
||||
var userId = "@alice:bar";
|
||||
var user;
|
||||
const userId = "@alice:bar";
|
||||
let user;
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
utils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
user = new User(userId);
|
||||
});
|
||||
|
||||
describe("setPresenceEvent", function() {
|
||||
var event = utils.mkEvent({
|
||||
const event = utils.mkEvent({
|
||||
type: "m.presence", content: {
|
||||
presence: "online",
|
||||
user_id: userId,
|
||||
displayname: "Alice",
|
||||
last_active_ago: 1085,
|
||||
avatar_url: "mxc://foo/bar"
|
||||
}, event: true
|
||||
avatar_url: "mxc://foo/bar",
|
||||
}, event: true,
|
||||
});
|
||||
|
||||
it("should emit 'User.displayName' if the display name changes", function() {
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
user.on("User.displayName", function(ev, usr) {
|
||||
emitCount += 1;
|
||||
});
|
||||
@@ -35,7 +35,7 @@ describe("User", function() {
|
||||
});
|
||||
|
||||
it("should emit 'User.avatarUrl' if the avatar URL changes", function() {
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
user.on("User.avatarUrl", function(ev, usr) {
|
||||
emitCount += 1;
|
||||
});
|
||||
@@ -46,7 +46,7 @@ describe("User", function() {
|
||||
});
|
||||
|
||||
it("should emit 'User.presence' if the presence changes", function() {
|
||||
var emitCount = 0;
|
||||
let emitCount = 0;
|
||||
user.on("User.presence", function(ev, usr) {
|
||||
emitCount += 1;
|
||||
});
|
||||
|
||||
+83
-55
@@ -1,40 +1,40 @@
|
||||
"use strict";
|
||||
var utils = require("../../lib/utils");
|
||||
var testUtils = require("../test-utils");
|
||||
const utils = require("../../lib/utils");
|
||||
const testUtils = require("../test-utils");
|
||||
|
||||
describe("utils", function() {
|
||||
beforeEach(function() {
|
||||
testUtils.beforeEach(this);
|
||||
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
describe("encodeParams", function() {
|
||||
it("should url encode and concat with &s", function() {
|
||||
var params = {
|
||||
const params = {
|
||||
foo: "bar",
|
||||
baz: "beer@"
|
||||
baz: "beer@",
|
||||
};
|
||||
expect(utils.encodeParams(params)).toEqual(
|
||||
"foo=bar&baz=beer%40"
|
||||
"foo=bar&baz=beer%40",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("encodeUri", function() {
|
||||
it("should replace based on object keys and url encode", function() {
|
||||
var path = "foo/bar/%something/%here";
|
||||
var vals = {
|
||||
const path = "foo/bar/%something/%here";
|
||||
const vals = {
|
||||
"%something": "baz",
|
||||
"%here": "beer@"
|
||||
"%here": "beer@",
|
||||
};
|
||||
expect(utils.encodeUri(path, vals)).toEqual(
|
||||
"foo/bar/baz/beer%40"
|
||||
"foo/bar/baz/beer%40",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("forEach", function() {
|
||||
it("should be invoked for each element", function() {
|
||||
var arr = [];
|
||||
const arr = [];
|
||||
utils.forEach([55, 66, 77], function(element) {
|
||||
arr.push(element);
|
||||
});
|
||||
@@ -44,38 +44,50 @@ describe("utils", function() {
|
||||
|
||||
describe("findElement", function() {
|
||||
it("should find only 1 element if there is a match", function() {
|
||||
var matchFn = function() { return true; };
|
||||
var arr = [55, 66, 77];
|
||||
const matchFn = function() {
|
||||
return true;
|
||||
};
|
||||
const arr = [55, 66, 77];
|
||||
expect(utils.findElement(arr, matchFn)).toEqual(55);
|
||||
});
|
||||
it("should be able to find in reverse order", function() {
|
||||
var matchFn = function() { return true; };
|
||||
var arr = [55, 66, 77];
|
||||
const matchFn = function() {
|
||||
return true;
|
||||
};
|
||||
const arr = [55, 66, 77];
|
||||
expect(utils.findElement(arr, matchFn, true)).toEqual(77);
|
||||
});
|
||||
it("should find nothing if the function never returns true", function() {
|
||||
var matchFn = function() { return false; };
|
||||
var arr = [55, 66, 77];
|
||||
const matchFn = function() {
|
||||
return false;
|
||||
};
|
||||
const arr = [55, 66, 77];
|
||||
expect(utils.findElement(arr, matchFn)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("removeElement", function() {
|
||||
it("should remove only 1 element if there is a match", function() {
|
||||
var matchFn = function() { return true; };
|
||||
var arr = [55, 66, 77];
|
||||
const matchFn = function() {
|
||||
return true;
|
||||
};
|
||||
const arr = [55, 66, 77];
|
||||
utils.removeElement(arr, matchFn);
|
||||
expect(arr).toEqual([66, 77]);
|
||||
});
|
||||
it("should be able to remove in reverse order", function() {
|
||||
var matchFn = function() { return true; };
|
||||
var arr = [55, 66, 77];
|
||||
const matchFn = function() {
|
||||
return true;
|
||||
};
|
||||
const arr = [55, 66, 77];
|
||||
utils.removeElement(arr, matchFn, true);
|
||||
expect(arr).toEqual([55, 66]);
|
||||
});
|
||||
it("should remove nothing if the function never returns true", function() {
|
||||
var matchFn = function() { return false; };
|
||||
var arr = [55, 66, 77];
|
||||
const matchFn = function() {
|
||||
return false;
|
||||
};
|
||||
const arr = [55, 66, 77];
|
||||
utils.removeElement(arr, matchFn);
|
||||
expect(arr).toEqual(arr);
|
||||
});
|
||||
@@ -92,7 +104,7 @@ describe("utils", function() {
|
||||
expect(utils.isFunction(555)).toBe(false);
|
||||
|
||||
expect(utils.isFunction(function() {})).toBe(true);
|
||||
var s = { foo: function() {} };
|
||||
const s = { foo: function() {} };
|
||||
expect(utils.isFunction(s.foo)).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -113,30 +125,42 @@ describe("utils", function() {
|
||||
|
||||
describe("checkObjectHasKeys", function() {
|
||||
it("should throw for missing keys", function() {
|
||||
expect(function() { utils.checkObjectHasKeys({}, ["foo"]); }).toThrow();
|
||||
expect(function() { utils.checkObjectHasKeys({
|
||||
foo: "bar"
|
||||
}, ["foo"]); }).not.toThrow();
|
||||
expect(function() {
|
||||
utils.checkObjectHasKeys({}, ["foo"]);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
utils.checkObjectHasKeys({
|
||||
foo: "bar",
|
||||
}, ["foo"]);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("checkObjectHasNoAdditionalKeys", function() {
|
||||
it("should throw for extra keys", function() {
|
||||
expect(function() { utils.checkObjectHasNoAdditionalKeys({
|
||||
foo: "bar",
|
||||
baz: 4
|
||||
}, ["foo"]); }).toThrow();
|
||||
expect(function() {
|
||||
utils.checkObjectHasNoAdditionalKeys({
|
||||
foo: "bar",
|
||||
baz: 4,
|
||||
}, ["foo"]);
|
||||
}).toThrow();
|
||||
|
||||
expect(function() { utils.checkObjectHasNoAdditionalKeys({
|
||||
foo: "bar"
|
||||
}, ["foo"]); }).not.toThrow();
|
||||
expect(function() {
|
||||
utils.checkObjectHasNoAdditionalKeys({
|
||||
foo: "bar",
|
||||
}, ["foo"]);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("deepCompare", function() {
|
||||
var assert = {
|
||||
isTrue: function(x) { expect(x).toBe(true); },
|
||||
isFalse: function(x) { expect(x).toBe(false); },
|
||||
const assert = {
|
||||
isTrue: function(x) {
|
||||
expect(x).toBe(true);
|
||||
},
|
||||
isFalse: function(x) {
|
||||
expect(x).toBe(false);
|
||||
},
|
||||
};
|
||||
|
||||
it("should handle primitives", function() {
|
||||
@@ -150,7 +174,7 @@ describe("utils", function() {
|
||||
it("should handle regexps", function() {
|
||||
assert.isTrue(utils.deepCompare(/abc/, /abc/));
|
||||
assert.isFalse(utils.deepCompare(/abc/, /123/));
|
||||
var r = /abc/;
|
||||
const r = /abc/;
|
||||
assert.isTrue(utils.deepCompare(r, r));
|
||||
});
|
||||
|
||||
@@ -192,8 +216,12 @@ describe("utils", function() {
|
||||
// no two different function is equal really, they capture their
|
||||
// context variables so even if they have same toString(), they
|
||||
// won't have same functionality
|
||||
var func = function(x) { return true; };
|
||||
var func2 = function(x) { return true; };
|
||||
const func = function(x) {
|
||||
return true;
|
||||
};
|
||||
const func2 = function(x) {
|
||||
return true;
|
||||
};
|
||||
assert.isTrue(utils.deepCompare(func, func));
|
||||
assert.isFalse(utils.deepCompare(func, func2));
|
||||
assert.isTrue(utils.deepCompare({ a: { b: func } }, { a: { b: func } }));
|
||||
@@ -203,58 +231,58 @@ describe("utils", function() {
|
||||
|
||||
|
||||
describe("extend", function() {
|
||||
var SOURCE = { "prop2": 1, "string2": "x", "newprop": "new" };
|
||||
const SOURCE = { "prop2": 1, "string2": "x", "newprop": "new" };
|
||||
|
||||
it("should extend", function() {
|
||||
var target = {
|
||||
const target = {
|
||||
"prop1": 5, "prop2": 7, "string1": "baz", "string2": "foo",
|
||||
};
|
||||
var merged = {
|
||||
const merged = {
|
||||
"prop1": 5, "prop2": 1, "string1": "baz", "string2": "x",
|
||||
"newprop": "new",
|
||||
};
|
||||
var source_orig = JSON.stringify(SOURCE);
|
||||
const sourceOrig = JSON.stringify(SOURCE);
|
||||
|
||||
utils.extend(target, SOURCE);
|
||||
expect(JSON.stringify(target)).toEqual(JSON.stringify(merged));
|
||||
|
||||
// check the originial wasn't modified
|
||||
expect(JSON.stringify(SOURCE)).toEqual(source_orig);
|
||||
expect(JSON.stringify(SOURCE)).toEqual(sourceOrig);
|
||||
});
|
||||
|
||||
it("should ignore null", function() {
|
||||
var target = {
|
||||
const target = {
|
||||
"prop1": 5, "prop2": 7, "string1": "baz", "string2": "foo",
|
||||
};
|
||||
var merged = {
|
||||
const merged = {
|
||||
"prop1": 5, "prop2": 1, "string1": "baz", "string2": "x",
|
||||
"newprop": "new",
|
||||
};
|
||||
var source_orig = JSON.stringify(SOURCE);
|
||||
const sourceOrig = JSON.stringify(SOURCE);
|
||||
|
||||
utils.extend(target, null, SOURCE);
|
||||
expect(JSON.stringify(target)).toEqual(JSON.stringify(merged));
|
||||
|
||||
// check the originial wasn't modified
|
||||
expect(JSON.stringify(SOURCE)).toEqual(source_orig);
|
||||
expect(JSON.stringify(SOURCE)).toEqual(sourceOrig);
|
||||
});
|
||||
|
||||
it("should handle properties created with defineProperties", function() {
|
||||
var source = Object.defineProperties({}, {
|
||||
const source = Object.defineProperties({}, {
|
||||
"enumerableProp": {
|
||||
get: function() {
|
||||
return true;
|
||||
},
|
||||
enumerable: true
|
||||
enumerable: true,
|
||||
},
|
||||
"nonenumerableProp": {
|
||||
get: function() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var target = {};
|
||||
const target = {};
|
||||
utils.extend(target, source);
|
||||
expect(target.enumerableProp).toBe(true);
|
||||
expect(target.nonenumerableProp).toBe(undefined);
|
||||
|
||||
@@ -1,580 +0,0 @@
|
||||
"use strict";
|
||||
var sdk = require("../..");
|
||||
var WebStorageStore = sdk.WebStorageStore;
|
||||
var Room = sdk.Room;
|
||||
var User = sdk.User;
|
||||
var utils = require("../test-utils");
|
||||
|
||||
var MockStorageApi = require("../MockStorageApi");
|
||||
|
||||
describe("WebStorageStore", function() {
|
||||
var store, room;
|
||||
var roomId = "!foo:bar";
|
||||
var userId = "@alice:bar";
|
||||
var mockStorageApi;
|
||||
var batchNum = 3;
|
||||
// web storage api keys
|
||||
var prefix = "room_" + roomId + "_timeline_";
|
||||
var stateKeyName = "room_" + roomId + "_state";
|
||||
|
||||
// stored state events
|
||||
var stateEventMap = {
|
||||
"m.room.member": {},
|
||||
"m.room.name": {}
|
||||
};
|
||||
stateEventMap["m.room.member"][userId] = utils.mkMembership(
|
||||
{user: userId, room: roomId, mship: "join"}
|
||||
);
|
||||
stateEventMap["m.room.name"][""] = utils.mkEvent(
|
||||
{user: userId, room: roomId, type: "m.room.name",
|
||||
content: {
|
||||
name: "foo"
|
||||
}}
|
||||
);
|
||||
|
||||
beforeEach(function() {
|
||||
utils.beforeEach(this);
|
||||
mockStorageApi = new MockStorageApi();
|
||||
store = new WebStorageStore(mockStorageApi, batchNum);
|
||||
room = new Room(roomId);
|
||||
});
|
||||
|
||||
describe("constructor", function() {
|
||||
it("should throw if the WebStorage API functions are missing", function() {
|
||||
expect(function() {
|
||||
store = new WebStorageStore({}, 5);
|
||||
}).toThrow();
|
||||
expect(function() {
|
||||
mockStorageApi.length = undefined;
|
||||
store = new WebStorageStore(mockStorageApi, 5);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("syncToken", function() {
|
||||
it("get: should return the token from the store", function() {
|
||||
var token = "flibble";
|
||||
store.setSyncToken(token);
|
||||
expect(store.getSyncToken()).toEqual(token);
|
||||
expect(mockStorageApi.length).toEqual(1);
|
||||
});
|
||||
it("get: should return null if the token does not exist", function() {
|
||||
expect(store.getSyncToken()).toEqual(null);
|
||||
expect(mockStorageApi.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("storeRoom", function() {
|
||||
it("should persist the room state correctly", function() {
|
||||
var stateEvents = [
|
||||
utils.mkEvent({
|
||||
event: true, type: "m.room.create", user: userId, room: roomId,
|
||||
content: {
|
||||
creator: userId
|
||||
}
|
||||
}),
|
||||
utils.mkMembership({
|
||||
event: true, user: userId, room: roomId, mship: "join"
|
||||
})
|
||||
];
|
||||
room.currentState.setStateEvents(stateEvents);
|
||||
store.storeRoom(room);
|
||||
var storedEvents = getItem(mockStorageApi,
|
||||
"room_" + roomId + "_state"
|
||||
).events;
|
||||
expect(storedEvents["m.room.create"][""]).toEqual(stateEvents[0].event);
|
||||
});
|
||||
|
||||
it("should persist timeline events correctly", function() {
|
||||
var timelineEvents = [];
|
||||
var entries = batchNum + batchNum - 1;
|
||||
var i = 0;
|
||||
for (i = 0; i < entries; i++) {
|
||||
timelineEvents.push(
|
||||
utils.mkMessage({room: roomId, user: userId, event: true})
|
||||
);
|
||||
}
|
||||
room.timeline = timelineEvents;
|
||||
store.storeRoom(room);
|
||||
expect(getItem(mockStorageApi, prefix + "-1")).toBe(null);
|
||||
expect(getItem(mockStorageApi, prefix + "2")).toBe(null);
|
||||
expect(getItem(mockStorageApi, prefix + "live")).toBe(null);
|
||||
var timeline0 = getItem(mockStorageApi, prefix + "0");
|
||||
var timeline1 = getItem(mockStorageApi, prefix + "1");
|
||||
expect(timeline0.length).toEqual(batchNum);
|
||||
expect(timeline1.length).toEqual(batchNum - 1);
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
expect(timeline0[i]).toEqual(timelineEvents[i].event);
|
||||
if ((i + batchNum) < timelineEvents.length) {
|
||||
expect(timeline1[i]).toEqual(timelineEvents[i + batchNum].event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("should persist timeline events in one bucket if batchNum=0", function() {
|
||||
store = new WebStorageStore(mockStorageApi, 0);
|
||||
var timelineEvents = [];
|
||||
var entries = batchNum + batchNum - 1;
|
||||
var i = 0;
|
||||
for (i = 0; i < entries; i++) {
|
||||
timelineEvents.push(
|
||||
utils.mkMessage({room: roomId, user: userId, event: true})
|
||||
);
|
||||
}
|
||||
room.timeline = timelineEvents;
|
||||
store.storeRoom(room);
|
||||
expect(getItem(mockStorageApi, prefix + "-1")).toBe(null);
|
||||
expect(getItem(mockStorageApi, prefix + "1")).toBe(null);
|
||||
expect(getItem(mockStorageApi, prefix + "live")).toBe(null);
|
||||
var timeline = getItem(mockStorageApi, prefix + "0");
|
||||
expect(timeline.length).toEqual(timelineEvents.length);
|
||||
for (i = 0; i < timeline.length; i++) {
|
||||
expect(timeline[i]).toEqual(
|
||||
timelineEvents[i].event
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRoom", function() {
|
||||
// stored timeline events
|
||||
var timeline0, timeline1, i;
|
||||
|
||||
beforeEach(function() {
|
||||
timeline0 = [];
|
||||
timeline1 = [];
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
timeline1[i] = utils.mkMessage({user: userId, room: roomId});
|
||||
if (i !== (batchNum - 1)) { // miss last one
|
||||
timeline0[i] = utils.mkMessage({user: userId, room: roomId});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("should reconstruct room state", function() {
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(
|
||||
storedRoom.currentState.getStateEvents("m.room.name", "").event
|
||||
).toEqual(stateEventMap["m.room.name"][""]);
|
||||
expect(
|
||||
storedRoom.currentState.getStateEvents("m.room.member", userId).event
|
||||
).toEqual(stateEventMap["m.room.member"][userId]);
|
||||
});
|
||||
|
||||
it("should reconstruct old room state", function() {
|
||||
var inviteEvent = utils.mkMembership({
|
||||
user: userId, room: roomId, mship: "invite"
|
||||
});
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", [inviteEvent]);
|
||||
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(
|
||||
storedRoom.currentState.getStateEvents("m.room.member", userId).event
|
||||
).toEqual(stateEventMap["m.room.member"][userId]);
|
||||
expect(
|
||||
storedRoom.oldState.getStateEvents("m.room.member", userId).event
|
||||
).toEqual(inviteEvent);
|
||||
});
|
||||
|
||||
it("should reconstruct the room timeline", function() {
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", timeline0);
|
||||
setItem(mockStorageApi, prefix + "1", timeline1);
|
||||
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom).not.toBeNull();
|
||||
// should only get up to the batch num timeline events
|
||||
expect(storedRoom.timeline.length).toEqual(batchNum);
|
||||
var timeline = timeline0.concat(timeline1);
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
expect(storedRoom.timeline[batchNum - 1 - i].event).toEqual(
|
||||
timeline[timeline.length - 1 - i]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should sync the timeline for 'live' events " +
|
||||
"(full hi batch; 1+bit live batches)", function() {
|
||||
// 1 and a bit events go into _live
|
||||
var timelineLive = [];
|
||||
timelineLive.push(utils.mkMessage({user: userId, room: roomId}));
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
timelineLive.push(
|
||||
utils.mkMessage({user: userId, room: roomId})
|
||||
);
|
||||
}
|
||||
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", timeline0);
|
||||
setItem(mockStorageApi, prefix + "1", timeline1);
|
||||
setItem(mockStorageApi,
|
||||
// deep copy the timeline via parse/stringify else items will
|
||||
// be shift()ed from timelineLive and we can't compare!
|
||||
prefix + "live", JSON.parse(JSON.stringify(timelineLive))
|
||||
);
|
||||
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom).not.toBeNull();
|
||||
// should only get up to the batch num timeline events (highest
|
||||
// index of timelineLive is the newest message)
|
||||
expect(storedRoom.timeline.length).toEqual(batchNum);
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
expect(storedRoom.timeline[i].event).toEqual(
|
||||
timelineLive[i + 1]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should sync the timeline for 'live' events " +
|
||||
"(no low batch; 1 live batches)", function() {
|
||||
var timelineLive = [];
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
timelineLive.push(
|
||||
utils.mkMessage({user: userId, room: roomId})
|
||||
);
|
||||
}
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", []);
|
||||
setItem(mockStorageApi,
|
||||
// deep copy the timeline via parse/stringify else items will
|
||||
// be shift()ed from timelineLive and we can't compare!
|
||||
prefix + "live", JSON.parse(JSON.stringify(timelineLive))
|
||||
);
|
||||
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom).not.toBeNull();
|
||||
// should only get up to the batch num timeline events (highest
|
||||
// index of timelineLive is the newest message)
|
||||
expect(storedRoom.timeline.length).toEqual(batchNum);
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
expect(storedRoom.timeline[i].event).toEqual(
|
||||
timelineLive[i]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should be able to reconstruct the timeline with negative indices",
|
||||
function() {
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "-5", timeline0);
|
||||
setItem(mockStorageApi, prefix + "-4", timeline1);
|
||||
var timeline = timeline0.concat(timeline1);
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom).not.toBeNull();
|
||||
// should only get up to the batch num timeline events
|
||||
expect(storedRoom.timeline.length).toEqual(batchNum);
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
expect(storedRoom.timeline[batchNum - 1 - i].event).toEqual(
|
||||
timeline[timeline.length - 1 - i]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return null if the room doesn't exist", function() {
|
||||
expect(store.getRoom("nothing")).toEqual(null);
|
||||
});
|
||||
|
||||
it("should assign a storageToken to the Room", function() {
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", timeline0);
|
||||
setItem(mockStorageApi, prefix + "1", timeline1);
|
||||
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom.storageToken).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("scrollback", function() {
|
||||
// stored timeline events
|
||||
var timeline0, timeline1, timeline2;
|
||||
|
||||
beforeEach(function() {
|
||||
// batch size is 3
|
||||
store = new WebStorageStore(mockStorageApi, 3);
|
||||
timeline0 = [
|
||||
// _
|
||||
utils.mkMessage({user: userId, room: roomId}), // 1 OLDEST
|
||||
utils.mkMessage({user: userId, room: roomId}) // 2
|
||||
];
|
||||
timeline1 = [
|
||||
utils.mkMessage({user: userId, room: roomId}), // 3
|
||||
utils.mkMessage({user: userId, room: roomId}), // 4
|
||||
utils.mkMessage({user: userId, room: roomId}) // 5
|
||||
];
|
||||
timeline2 = [
|
||||
utils.mkMessage({user: userId, room: roomId}), // 6
|
||||
utils.mkMessage({user: userId, room: roomId}), // 7
|
||||
utils.mkMessage({user: userId, room: roomId}) // 8 NEWEST
|
||||
];
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", timeline0);
|
||||
setItem(mockStorageApi, prefix + "1", timeline1);
|
||||
setItem(mockStorageApi, prefix + "2", timeline2);
|
||||
});
|
||||
|
||||
it("should scroll back locally giving 'limit' events", function() {
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom.timeline.length).toEqual(3);
|
||||
var events = store.scrollback(storedRoom, 3);
|
||||
expect(events.length).toEqual(3);
|
||||
expect(events.reverse()).toEqual(timeline1);
|
||||
});
|
||||
|
||||
it("should give less than 'limit' events near the end of the stored timeline",
|
||||
function() {
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom.timeline.length).toEqual(3);
|
||||
var events = store.scrollback(storedRoom, 7);
|
||||
expect(events.length).toEqual(5);
|
||||
expect(events.reverse()).toEqual(timeline0.concat(timeline1));
|
||||
});
|
||||
|
||||
it("should progressively give older messages the more times scrollback is called",
|
||||
function() {
|
||||
var events;
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom.timeline.length).toEqual(3);
|
||||
|
||||
events = store.scrollback(storedRoom, 2);
|
||||
expect(events.reverse()).toEqual([timeline1[1], timeline1[2]]);
|
||||
expect(storedRoom.timeline.length).toEqual(5);
|
||||
|
||||
events = store.scrollback(storedRoom, 2);
|
||||
expect(events.reverse()).toEqual([timeline0[1], timeline1[0]]);
|
||||
expect(storedRoom.timeline.length).toEqual(7);
|
||||
|
||||
events = store.scrollback(storedRoom, 2);
|
||||
expect(events).toEqual([timeline0[0]]);
|
||||
expect(storedRoom.timeline.length).toEqual(8);
|
||||
|
||||
events = store.scrollback(storedRoom, 2);
|
||||
expect(events).toEqual([]);
|
||||
expect(storedRoom.timeline.length).toEqual(8);
|
||||
});
|
||||
|
||||
it("should give 0 events if there is no token on the room", function() {
|
||||
var r = new Room(roomId);
|
||||
expect(store.scrollback(r, 3)).toEqual([]);
|
||||
});
|
||||
|
||||
it("should give 0 events for unknown rooms", function() {
|
||||
var r = new Room("!unknown:room");
|
||||
r.storageToken = "foo";
|
||||
expect(store.scrollback(r, 3)).toEqual([]);
|
||||
});
|
||||
|
||||
it("should give 0 events if the boundary event is the last in the timeline",
|
||||
function() {
|
||||
var events;
|
||||
var storedRoom = store.getRoom(roomId);
|
||||
expect(storedRoom.timeline.length).toEqual(3);
|
||||
|
||||
// go up to the boundary (8 messages total)
|
||||
events = store.scrollback(storedRoom, 5);
|
||||
expect(events.length).toEqual(5);
|
||||
|
||||
events = store.scrollback(storedRoom, 5);
|
||||
expect(events.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("storeEvents", function() {
|
||||
var timeline0, i;
|
||||
|
||||
beforeEach(function() {
|
||||
timeline0 = [];
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
timeline0.push(utils.mkMessage({user: userId, room: roomId}));
|
||||
}
|
||||
setItem(mockStorageApi, stateKeyName, {
|
||||
events: stateEventMap,
|
||||
pagination_token: "tok"
|
||||
});
|
||||
setItem(mockStorageApi, prefix + "0", timeline0);
|
||||
});
|
||||
|
||||
it("should add to the live batch", function() {
|
||||
var events = [
|
||||
utils.mkMessage({user: userId, room: roomId, event: true}),
|
||||
utils.mkMessage({user: userId, room: roomId, event: true})
|
||||
];
|
||||
store.storeEvents(room, events, "atoken");
|
||||
var liveEvents = getItem(mockStorageApi, prefix + "live");
|
||||
expect(liveEvents.length).toEqual(2);
|
||||
expect(liveEvents[0]).toEqual(events[0].event);
|
||||
expect(liveEvents[1]).toEqual(events[1].event);
|
||||
});
|
||||
|
||||
it("should preserve existing live events in the store", function() {
|
||||
var existingEvent = utils.mkMessage({user: userId, room: roomId});
|
||||
setItem(mockStorageApi, prefix + "live", [existingEvent]);
|
||||
var events = [
|
||||
utils.mkMessage({user: userId, room: roomId, event: true}),
|
||||
utils.mkMessage({user: userId, room: roomId, event: true})
|
||||
];
|
||||
store.storeEvents(room, events, "atoken");
|
||||
var liveEvents = getItem(mockStorageApi, prefix + "live");
|
||||
expect(liveEvents.length).toEqual(3);
|
||||
expect(liveEvents[0]).toEqual(existingEvent);
|
||||
expect(liveEvents[1]).toEqual(events[0].event);
|
||||
expect(liveEvents[2]).toEqual(events[1].event);
|
||||
});
|
||||
|
||||
it("should add to the lowest batch index if toStart=true", function() {
|
||||
var events = [
|
||||
utils.mkMessage({user: userId, room: roomId, event: true}),
|
||||
utils.mkMessage({user: userId, room: roomId, event: true})
|
||||
];
|
||||
store.storeEvents(room, events, "atoken", true);
|
||||
var timelineNeg1 = getItem(mockStorageApi, prefix + "-1");
|
||||
expect(timelineNeg1.length).toEqual(2);
|
||||
expect(timelineNeg1[0]).toEqual(events[1].event);
|
||||
expect(timelineNeg1[1]).toEqual(events[0].event);
|
||||
});
|
||||
|
||||
it("should add multiple batches to the lowest batch index if toStart=true",
|
||||
function() {
|
||||
var timelineNeg1 = [];
|
||||
var timelineNeg2 = [];
|
||||
for (i = 0; i < batchNum; i++) {
|
||||
timelineNeg1.push(
|
||||
utils.mkMessage({user: userId, room: roomId, event: true})
|
||||
);
|
||||
timelineNeg2.push(
|
||||
utils.mkMessage({user: userId, room: roomId, event: true})
|
||||
);
|
||||
}
|
||||
|
||||
var events = timelineNeg2.concat(timelineNeg1).reverse();
|
||||
store.storeEvents(room, events, "atoken", true);
|
||||
|
||||
var storedNeg1 = getItem(mockStorageApi, prefix + "-1");
|
||||
var storedNeg2 = getItem(mockStorageApi, prefix + "-2");
|
||||
expect(timelineNeg1.length).toEqual(storedNeg1.length);
|
||||
expect(timelineNeg2.length).toEqual(storedNeg2.length);
|
||||
for (i = 0; i < timelineNeg1.length; i++) {
|
||||
expect(timelineNeg1[i].event).toEqual(storedNeg1[i]);
|
||||
expect(timelineNeg2[i].event).toEqual(storedNeg2[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should update stored state if state events exist", function() {
|
||||
var events = [
|
||||
utils.mkEvent({
|
||||
user: userId, room: roomId, type: "m.room.name", event: true,
|
||||
content: {
|
||||
name: "Room Name Here for updates"
|
||||
}
|
||||
})
|
||||
];
|
||||
room.currentState.setStateEvents(events);
|
||||
store.storeEvents(room, events, "atoken");
|
||||
|
||||
var liveEvents = getItem(mockStorageApi, prefix + "live");
|
||||
expect(liveEvents.length).toEqual(1);
|
||||
expect(liveEvents[0]).toEqual(events[0].event);
|
||||
|
||||
var stateEvents = getItem(mockStorageApi, stateKeyName);
|
||||
expect(stateEvents.events["m.room.name"][""]).toEqual(events[0].event);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRooms", function() {
|
||||
var mkState = function(id) {
|
||||
return [
|
||||
utils.mkEvent({
|
||||
event: true, type: "m.room.create", user: userId, room: id,
|
||||
content: {
|
||||
creator: userId
|
||||
}
|
||||
}),
|
||||
utils.mkMembership({
|
||||
event: true, user: userId, room: id, mship: "join"
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
it("should get all rooms in the store", function() {
|
||||
var roomIds = [
|
||||
"!alpha:bet", "!beta:fet"
|
||||
];
|
||||
// store 2 dynamically
|
||||
var roomA = new Room(roomIds[0]);
|
||||
roomA.currentState.setStateEvents(mkState(roomIds[0]));
|
||||
var roomB = new Room(roomIds[1]);
|
||||
roomB.currentState.setStateEvents(mkState(roomIds[1]));
|
||||
store.storeRoom(roomA);
|
||||
store.storeRoom(roomB);
|
||||
|
||||
var rooms = store.getRooms();
|
||||
expect(rooms.length).toEqual(2);
|
||||
for (var i = 0; i < rooms.length; i++) {
|
||||
var index = roomIds.indexOf(rooms[i].roomId);
|
||||
expect(index).not.toEqual(
|
||||
-1, "Unknown room"
|
||||
);
|
||||
roomIds.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getUser", function() {
|
||||
it("should be able to retrieve a stored user", function() {
|
||||
var user = new User(userId);
|
||||
store.storeUser(user);
|
||||
var result = store.getUser(userId);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.userId).toEqual(userId);
|
||||
});
|
||||
|
||||
it("should be able to retrieve a stored user with name data", function() {
|
||||
var presence = utils.mkEvent({
|
||||
type: "m.presence", event: true, content: {
|
||||
user_id: userId,
|
||||
displayname: "Flibble"
|
||||
}
|
||||
});
|
||||
var user = new User(userId);
|
||||
user.setPresenceEvent(presence);
|
||||
store.storeUser(user);
|
||||
var result = store.getUser(userId);
|
||||
console.log(result);
|
||||
expect(result.events.presence).toEqual(presence);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getItem(store, key) {
|
||||
return JSON.parse(store.getItem(key));
|
||||
}
|
||||
|
||||
function setItem(store, key, val) {
|
||||
store.setItem(key, JSON.stringify(val));
|
||||
}
|
||||
+231
-129
@@ -22,8 +22,8 @@ limitations under the License.
|
||||
* @module base-apis
|
||||
*/
|
||||
|
||||
var httpApi = require("./http-api");
|
||||
var utils = require("./utils");
|
||||
const httpApi = require("./http-api");
|
||||
const utils = require("./utils");
|
||||
|
||||
/**
|
||||
* Low-level wrappers for the Matrix APIs
|
||||
@@ -45,6 +45,10 @@ var utils = require("./utils");
|
||||
*
|
||||
* @param {string} opts.accessToken The access_token for this user.
|
||||
*
|
||||
* @param {Number=} opts.localTimeoutMs Optional. The default maximum amount of
|
||||
* time to wait before timing out HTTP requests. If not specified, there is no
|
||||
* timeout.
|
||||
*
|
||||
* @param {Object} opts.queryParams Optional. Extra query parameters to append
|
||||
* to all requests with this client. Useful for application services which require
|
||||
* <code>?user_id=</code>.
|
||||
@@ -56,14 +60,15 @@ function MatrixBaseApis(opts) {
|
||||
this.baseUrl = opts.baseUrl;
|
||||
this.idBaseUrl = opts.idBaseUrl;
|
||||
|
||||
var httpOpts = {
|
||||
const httpOpts = {
|
||||
baseUrl: opts.baseUrl,
|
||||
idBaseUrl: opts.idBaseUrl,
|
||||
accessToken: opts.accessToken,
|
||||
request: opts.request,
|
||||
prefix: httpApi.PREFIX_R0,
|
||||
onlyData: true,
|
||||
extraParams: opts.queryParams
|
||||
extraParams: opts.queryParams,
|
||||
localTimeoutMs: opts.localTimeoutMs,
|
||||
};
|
||||
this._http = new httpApi.MatrixHttpApi(this, httpOpts);
|
||||
|
||||
@@ -128,17 +133,27 @@ MatrixBaseApis.prototype.makeTxnId = function() {
|
||||
MatrixBaseApis.prototype.register = function(
|
||||
username, password,
|
||||
sessionId, auth, bindEmail, guestAccessToken,
|
||||
callback
|
||||
callback,
|
||||
) {
|
||||
if (auth === undefined) { auth = {}; }
|
||||
if (sessionId) { auth.session = sessionId; }
|
||||
if (auth === undefined) {
|
||||
auth = {};
|
||||
}
|
||||
if (sessionId) {
|
||||
auth.session = sessionId;
|
||||
}
|
||||
|
||||
var params = {
|
||||
auth: auth
|
||||
const params = {
|
||||
auth: auth,
|
||||
};
|
||||
if (username !== undefined && username !== null) { params.username = username; }
|
||||
if (password !== undefined && password !== null) { params.password = password; }
|
||||
if (bindEmail !== undefined && bindEmail !== null) { params.bind_email = bindEmail; }
|
||||
if (username !== undefined && username !== null) {
|
||||
params.username = username;
|
||||
}
|
||||
if (password !== undefined && password !== null) {
|
||||
params.password = password;
|
||||
}
|
||||
if (bindEmail !== undefined && bindEmail !== null) {
|
||||
params.bind_email = bindEmail;
|
||||
}
|
||||
if (guestAccessToken !== undefined && guestAccessToken !== null) {
|
||||
params.guest_access_token = guestAccessToken;
|
||||
}
|
||||
@@ -168,11 +183,13 @@ MatrixBaseApis.prototype.registerGuest = function(opts, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.registerRequest = function(data, kind, callback) {
|
||||
var params = {};
|
||||
if (kind) { params.kind = kind; }
|
||||
const params = {};
|
||||
if (kind) {
|
||||
params.kind = kind;
|
||||
}
|
||||
|
||||
return this._http.request(
|
||||
callback, "POST", "/register", params, data
|
||||
callback, "POST", "/register", params, data,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -193,7 +210,7 @@ MatrixBaseApis.prototype.loginFlows = function(callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.login = function(loginType, data, callback) {
|
||||
var login_data = {
|
||||
const login_data = {
|
||||
type: loginType,
|
||||
};
|
||||
|
||||
@@ -201,7 +218,7 @@ MatrixBaseApis.prototype.login = function(loginType, data, callback) {
|
||||
utils.extend(login_data, data);
|
||||
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", "/login", undefined, login_data
|
||||
callback, "POST", "/login", undefined, login_data,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -215,7 +232,7 @@ MatrixBaseApis.prototype.login = function(loginType, data, callback) {
|
||||
MatrixBaseApis.prototype.loginWithPassword = function(user, password, callback) {
|
||||
return this.login("m.login.password", {
|
||||
user: user,
|
||||
password: password
|
||||
password: password,
|
||||
}, callback);
|
||||
};
|
||||
|
||||
@@ -227,7 +244,7 @@ MatrixBaseApis.prototype.loginWithPassword = function(user, password, callback)
|
||||
*/
|
||||
MatrixBaseApis.prototype.loginWithSAML2 = function(relayState, callback) {
|
||||
return this.login("m.login.saml2", {
|
||||
relay_state: relayState
|
||||
relay_state: relayState,
|
||||
}, callback);
|
||||
};
|
||||
|
||||
@@ -238,7 +255,7 @@ MatrixBaseApis.prototype.loginWithSAML2 = function(relayState, callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.getCasLoginUrl = function(redirectUrl) {
|
||||
return this._http.getUrl("/login/cas/redirect", {
|
||||
"redirectUrl": redirectUrl
|
||||
"redirectUrl": redirectUrl,
|
||||
}, httpApi.PREFIX_UNSTABLE);
|
||||
};
|
||||
|
||||
@@ -250,7 +267,7 @@ MatrixBaseApis.prototype.getCasLoginUrl = function(redirectUrl) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.loginWithToken = function(token, callback) {
|
||||
return this.login("m.login.token", {
|
||||
token: token
|
||||
token: token,
|
||||
}, callback);
|
||||
};
|
||||
|
||||
@@ -266,7 +283,7 @@ MatrixBaseApis.prototype.loginWithToken = function(token, callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.logout = function(callback) {
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", '/logout'
|
||||
callback, "POST", '/logout',
|
||||
);
|
||||
};
|
||||
|
||||
@@ -281,14 +298,14 @@ MatrixBaseApis.prototype.logout = function(callback) {
|
||||
* @return {module:client.Promise} Resolves: On success, the empty object
|
||||
*/
|
||||
MatrixBaseApis.prototype.deactivateAccount = function(auth, callback) {
|
||||
var body = {};
|
||||
let body = {};
|
||||
if (auth) {
|
||||
body = {
|
||||
auth: auth,
|
||||
};
|
||||
}
|
||||
return this._http.authedRequestWithPrefix(
|
||||
callback, "POST", '/account/deactivate', undefined, body, httpApi.PREFIX_UNSTABLE
|
||||
callback, "POST", '/account/deactivate', undefined, body, httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -301,7 +318,7 @@ MatrixBaseApis.prototype.deactivateAccount = function(auth, callback) {
|
||||
* @return {string} HS URL to hit to for the fallback interface
|
||||
*/
|
||||
MatrixBaseApis.prototype.getFallbackAuthUrl = function(loginType, authSessionId) {
|
||||
var path = utils.encodeUri("/auth/$loginType/fallback/web", {
|
||||
const path = utils.encodeUri("/auth/$loginType/fallback/web", {
|
||||
$loginType: loginType,
|
||||
});
|
||||
|
||||
@@ -330,7 +347,7 @@ MatrixBaseApis.prototype.getFallbackAuthUrl = function(loginType, authSessionId)
|
||||
MatrixBaseApis.prototype.createRoom = function(options, callback) {
|
||||
// valid options include: room_alias_name, visibility, invite
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", "/createRoom", undefined, options
|
||||
callback, "POST", "/createRoom", undefined, options,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -341,7 +358,7 @@ MatrixBaseApis.prototype.createRoom = function(options, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.roomState = function(roomId, callback) {
|
||||
var path = utils.encodeUri("/rooms/$roomId/state", {$roomId: roomId});
|
||||
const path = utils.encodeUri("/rooms/$roomId/state", {$roomId: roomId});
|
||||
return this._http.authedRequest(callback, "GET", path);
|
||||
};
|
||||
|
||||
@@ -355,17 +372,17 @@ MatrixBaseApis.prototype.roomState = function(roomId, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.getStateEvent = function(roomId, eventType, stateKey, callback) {
|
||||
var pathParams = {
|
||||
const pathParams = {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$stateKey: stateKey
|
||||
$stateKey: stateKey,
|
||||
};
|
||||
var path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
if (stateKey !== undefined) {
|
||||
path = utils.encodeUri(path + "/$stateKey", pathParams);
|
||||
}
|
||||
return this._http.authedRequest(
|
||||
callback, "GET", path
|
||||
callback, "GET", path,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -380,17 +397,17 @@ MatrixBaseApis.prototype.getStateEvent = function(roomId, eventType, stateKey, c
|
||||
*/
|
||||
MatrixBaseApis.prototype.sendStateEvent = function(roomId, eventType, content, stateKey,
|
||||
callback) {
|
||||
var pathParams = {
|
||||
const pathParams = {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$stateKey: stateKey
|
||||
$stateKey: stateKey,
|
||||
};
|
||||
var path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
let path = utils.encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
if (stateKey !== undefined) {
|
||||
path = utils.encodeUri(path + "/$stateKey", pathParams);
|
||||
}
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
callback, "PUT", path, undefined, content,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -402,9 +419,9 @@ MatrixBaseApis.prototype.sendStateEvent = function(roomId, eventType, content, s
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.redactEvent = function(roomId, eventId, callback) {
|
||||
var path = utils.encodeUri("/rooms/$roomId/redact/$eventId", {
|
||||
const path = utils.encodeUri("/rooms/$roomId/redact/$eventId", {
|
||||
$roomId: roomId,
|
||||
$eventId: eventId
|
||||
$eventId: eventId,
|
||||
});
|
||||
return this._http.authedRequest(callback, "POST", path, undefined, {});
|
||||
};
|
||||
@@ -417,15 +434,17 @@ MatrixBaseApis.prototype.redactEvent = function(roomId, eventId, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.roomInitialSync = function(roomId, limit, callback) {
|
||||
if (utils.isFunction(limit)) { callback = limit; limit = undefined; }
|
||||
var path = utils.encodeUri("/rooms/$roomId/initialSync",
|
||||
{$roomId: roomId}
|
||||
if (utils.isFunction(limit)) {
|
||||
callback = limit; limit = undefined;
|
||||
}
|
||||
const path = utils.encodeUri("/rooms/$roomId/initialSync",
|
||||
{$roomId: roomId},
|
||||
);
|
||||
if (!limit) {
|
||||
limit = 30;
|
||||
}
|
||||
return this._http.authedRequest(
|
||||
callback, "GET", path, { limit: limit }
|
||||
callback, "GET", path, { limit: limit },
|
||||
);
|
||||
};
|
||||
|
||||
@@ -434,6 +453,7 @@ MatrixBaseApis.prototype.roomInitialSync = function(roomId, limit, callback) {
|
||||
// =========================
|
||||
|
||||
/**
|
||||
* @param {Object} options Options for this request
|
||||
* @param {string} options.server The remote server to query for the room list.
|
||||
* Optional. If unspecified, get the local home
|
||||
* server's public room list.
|
||||
@@ -454,7 +474,7 @@ MatrixBaseApis.prototype.publicRooms = function(options, callback) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
var query_params = {};
|
||||
const query_params = {};
|
||||
if (options.server) {
|
||||
query_params.server = options.server;
|
||||
delete options.server;
|
||||
@@ -464,7 +484,7 @@ MatrixBaseApis.prototype.publicRooms = function(options, callback) {
|
||||
return this._http.authedRequest(callback, "GET", "/publicRooms");
|
||||
} else {
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", "/publicRooms", query_params, options
|
||||
callback, "POST", "/publicRooms", query_params, options,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -478,14 +498,14 @@ MatrixBaseApis.prototype.publicRooms = function(options, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.createAlias = function(alias, roomId, callback) {
|
||||
var path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias
|
||||
const path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias,
|
||||
});
|
||||
var data = {
|
||||
room_id: roomId
|
||||
const data = {
|
||||
room_id: roomId,
|
||||
};
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, data
|
||||
callback, "PUT", path, undefined, data,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -498,11 +518,11 @@ MatrixBaseApis.prototype.createAlias = function(alias, roomId, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.deleteAlias = function(alias, callback) {
|
||||
var path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias
|
||||
const path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "DELETE", path, undefined, undefined
|
||||
callback, "DELETE", path, undefined, undefined,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -515,11 +535,11 @@ MatrixBaseApis.prototype.deleteAlias = function(alias, callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.getRoomIdForAlias = function(alias, callback) {
|
||||
// TODO: deprecate this or resolveRoomAlias
|
||||
var path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias
|
||||
const path = utils.encodeUri("/directory/room/$alias", {
|
||||
$alias: alias,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "GET", path
|
||||
callback, "GET", path,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -531,7 +551,7 @@ MatrixBaseApis.prototype.getRoomIdForAlias = function(alias, callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.resolveRoomAlias = function(roomAlias, callback) {
|
||||
// TODO: deprecate this or getRoomIdForAlias
|
||||
var path = utils.encodeUri("/directory/room/$alias", {$alias: roomAlias});
|
||||
const path = utils.encodeUri("/directory/room/$alias", {$alias: roomAlias});
|
||||
return this._http.request(callback, "GET", path);
|
||||
};
|
||||
|
||||
@@ -544,8 +564,8 @@ MatrixBaseApis.prototype.resolveRoomAlias = function(roomAlias, callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.getRoomDirectoryVisibility =
|
||||
function(roomId, callback) {
|
||||
var path = utils.encodeUri("/directory/list/room/$roomId", {
|
||||
$roomId: roomId
|
||||
const path = utils.encodeUri("/directory/list/room/$roomId", {
|
||||
$roomId: roomId,
|
||||
});
|
||||
return this._http.authedRequest(callback, "GET", path);
|
||||
};
|
||||
@@ -562,11 +582,35 @@ MatrixBaseApis.prototype.getRoomDirectoryVisibility =
|
||||
*/
|
||||
MatrixBaseApis.prototype.setRoomDirectoryVisibility =
|
||||
function(roomId, visibility, callback) {
|
||||
var path = utils.encodeUri("/directory/list/room/$roomId", {
|
||||
$roomId: roomId
|
||||
const path = utils.encodeUri("/directory/list/room/$roomId", {
|
||||
$roomId: roomId,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, { "visibility": visibility }
|
||||
callback, "PUT", path, undefined, { "visibility": visibility },
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the visbility of a room bridged to a 3rd party network in
|
||||
* the current HS's room directory.
|
||||
* @param {string} networkId the network ID of the 3rd party
|
||||
* instance under which this room is published under.
|
||||
* @param {string} roomId
|
||||
* @param {string} visibility "public" to make the room visible
|
||||
* in the public directory, or "private" to make
|
||||
* it invisible.
|
||||
* @param {module:client.callback} callback Optional.
|
||||
* @return {module:client.Promise} Resolves: result object
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.setRoomDirectoryVisibilityAppService =
|
||||
function(networkId, roomId, visibility, callback) {
|
||||
const path = utils.encodeUri("/directory/list/appservice/$networkId/$roomId", {
|
||||
$networkId: networkId,
|
||||
$roomId: roomId,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, { "visibility": visibility },
|
||||
);
|
||||
};
|
||||
|
||||
@@ -644,9 +688,11 @@ MatrixBaseApis.prototype.getCurrentUploads = function() {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.getProfileInfo = function(userId, info, callback) {
|
||||
if (utils.isFunction(info)) { callback = info; info = undefined; }
|
||||
if (utils.isFunction(info)) {
|
||||
callback = info; info = undefined;
|
||||
}
|
||||
|
||||
var path = info ?
|
||||
const path = info ?
|
||||
utils.encodeUri("/profile/$userId/$info",
|
||||
{ $userId: userId, $info: info }) :
|
||||
utils.encodeUri("/profile/$userId",
|
||||
@@ -664,9 +710,9 @@ MatrixBaseApis.prototype.getProfileInfo = function(userId, info, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.getThreePids = function(callback) {
|
||||
var path = "/account/3pid";
|
||||
const path = "/account/3pid";
|
||||
return this._http.authedRequest(
|
||||
callback, "GET", path, undefined, undefined
|
||||
callback, "GET", path, undefined, undefined,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -678,13 +724,32 @@ MatrixBaseApis.prototype.getThreePids = function(callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
|
||||
var path = "/account/3pid";
|
||||
var data = {
|
||||
const path = "/account/3pid";
|
||||
const data = {
|
||||
'threePidCreds': creds,
|
||||
'bind': bind
|
||||
'bind': bind,
|
||||
};
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", path, null, data
|
||||
callback, "POST", path, null, data,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} medium The threepid medium (eg. 'email')
|
||||
* @param {string} address The threepid address (eg. 'bob@example.com')
|
||||
* this must be as returned by getThreePids.
|
||||
* @return {module:client.Promise} Resolves: The server response on success
|
||||
* (generally the empty JSON object)
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.deleteThreePid = function(medium, address) {
|
||||
const path = "/account/3pid/delete";
|
||||
const data = {
|
||||
'medium': medium,
|
||||
'address': address,
|
||||
};
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "POST", path, null, data, httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -697,14 +762,14 @@ MatrixBaseApis.prototype.addThreePid = function(creds, bind, callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.setPassword = function(authDict, newPassword, callback) {
|
||||
var path = "/account/password";
|
||||
var data = {
|
||||
const path = "/account/password";
|
||||
const data = {
|
||||
'auth': authDict,
|
||||
'new_password': newPassword
|
||||
'new_password': newPassword,
|
||||
};
|
||||
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", path, null, data
|
||||
callback, "POST", path, null, data,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -718,10 +783,10 @@ MatrixBaseApis.prototype.setPassword = function(authDict, newPassword, callback)
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.getDevices = function() {
|
||||
var path = "/devices";
|
||||
const path = "/devices";
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "GET", path, undefined, undefined,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -734,14 +799,14 @@ MatrixBaseApis.prototype.getDevices = function() {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.setDeviceDetails = function(device_id, body) {
|
||||
var path = utils.encodeUri("/devices/$device_id", {
|
||||
const path = utils.encodeUri("/devices/$device_id", {
|
||||
$device_id: device_id,
|
||||
});
|
||||
|
||||
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "PUT", path, undefined, body,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -754,11 +819,11 @@ MatrixBaseApis.prototype.setDeviceDetails = function(device_id, body) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.deleteDevice = function(device_id, auth) {
|
||||
var path = utils.encodeUri("/devices/$device_id", {
|
||||
const path = utils.encodeUri("/devices/$device_id", {
|
||||
$device_id: device_id,
|
||||
});
|
||||
|
||||
var body = {};
|
||||
const body = {};
|
||||
|
||||
if (auth) {
|
||||
body.auth = auth;
|
||||
@@ -766,7 +831,7 @@ MatrixBaseApis.prototype.deleteDevice = function(device_id, auth) {
|
||||
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "DELETE", path, undefined, body,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -782,9 +847,9 @@ MatrixBaseApis.prototype.deleteDevice = function(device_id, auth) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.getPushers = function(callback) {
|
||||
var path = "/pushers";
|
||||
const path = "/pushers";
|
||||
return this._http.authedRequest(
|
||||
callback, "GET", path, undefined, undefined
|
||||
callback, "GET", path, undefined, undefined,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -797,9 +862,9 @@ MatrixBaseApis.prototype.getPushers = function(callback) {
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.setPusher = function(pusher, callback) {
|
||||
var path = "/pushers/set";
|
||||
const path = "/pushers/set";
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", path, null, pusher
|
||||
callback, "POST", path, null, pusher,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -823,12 +888,12 @@ MatrixBaseApis.prototype.getPushRules = function(callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.addPushRule = function(scope, kind, ruleId, body, callback) {
|
||||
// NB. Scope not uri encoded because devices need the '/'
|
||||
var path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", {
|
||||
const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
$ruleId: ruleId,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, body
|
||||
callback, "PUT", path, undefined, body,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -842,9 +907,9 @@ MatrixBaseApis.prototype.addPushRule = function(scope, kind, ruleId, body, callb
|
||||
*/
|
||||
MatrixBaseApis.prototype.deletePushRule = function(scope, kind, ruleId, callback) {
|
||||
// NB. Scope not uri encoded because devices need the '/'
|
||||
var path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", {
|
||||
const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
$ruleId: ruleId,
|
||||
});
|
||||
return this._http.authedRequest(callback, "DELETE", path);
|
||||
};
|
||||
@@ -861,12 +926,12 @@ MatrixBaseApis.prototype.deletePushRule = function(scope, kind, ruleId, callback
|
||||
*/
|
||||
MatrixBaseApis.prototype.setPushRuleEnabled = function(scope, kind,
|
||||
ruleId, enabled, callback) {
|
||||
var path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/enabled", {
|
||||
const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/enabled", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
$ruleId: ruleId,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, {"enabled": enabled}
|
||||
callback, "PUT", path, undefined, {"enabled": enabled},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -882,12 +947,12 @@ MatrixBaseApis.prototype.setPushRuleEnabled = function(scope, kind,
|
||||
*/
|
||||
MatrixBaseApis.prototype.setPushRuleActions = function(scope, kind,
|
||||
ruleId, actions, callback) {
|
||||
var path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/actions", {
|
||||
const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/actions", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
$ruleId: ruleId,
|
||||
});
|
||||
return this._http.authedRequest(
|
||||
callback, "PUT", path, undefined, {"actions": actions}
|
||||
callback, "PUT", path, undefined, {"actions": actions},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -905,12 +970,12 @@ MatrixBaseApis.prototype.setPushRuleActions = function(scope, kind,
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.search = function(opts, callback) {
|
||||
var queryparams = {};
|
||||
const queryparams = {};
|
||||
if (opts.next_batch) {
|
||||
queryparams.next_batch = opts.next_batch;
|
||||
}
|
||||
return this._http.authedRequest(
|
||||
callback, "POST", "/search", queryparams, opts.body
|
||||
callback, "POST", "/search", queryparams, opts.body,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -934,8 +999,8 @@ MatrixBaseApis.prototype.search = function(opts, callback) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.uploadKeysRequest = function(content, opts, callback) {
|
||||
opts = opts || {};
|
||||
var deviceId = opts.device_id;
|
||||
var path;
|
||||
const deviceId = opts.device_id;
|
||||
let path;
|
||||
if (deviceId) {
|
||||
path = utils.encodeUri("/keys/upload/$deviceId", {
|
||||
$deviceId: deviceId,
|
||||
@@ -944,7 +1009,7 @@ MatrixBaseApis.prototype.uploadKeysRequest = function(content, opts, callback) {
|
||||
path = "/keys/upload";
|
||||
}
|
||||
return this._http.authedRequestWithPrefix(
|
||||
callback, "POST", path, undefined, content, httpApi.PREFIX_UNSTABLE
|
||||
callback, "POST", path, undefined, content, httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -953,28 +1018,43 @@ MatrixBaseApis.prototype.uploadKeysRequest = function(content, opts, callback) {
|
||||
*
|
||||
* @param {string[]} userIds list of users to get keys for
|
||||
*
|
||||
* @param {module:client.callback=} callback
|
||||
* @param {Object=} opts
|
||||
*
|
||||
* @param {string=} opts.token sync token to pass in the query request, to help
|
||||
* the HS give the most recent results
|
||||
*
|
||||
* @return {module:client.Promise} Resolves: result object. Rejects: with
|
||||
* an error response ({@link module:http-api.MatrixError}).
|
||||
*/
|
||||
MatrixBaseApis.prototype.downloadKeysForUsers = function(userIds, callback) {
|
||||
var downloadQuery = {};
|
||||
|
||||
for (var i = 0; i < userIds.length; ++i) {
|
||||
downloadQuery[userIds[i]] = {};
|
||||
MatrixBaseApis.prototype.downloadKeysForUsers = function(userIds, opts) {
|
||||
if (utils.isFunction(opts)) {
|
||||
// opts used to be 'callback'.
|
||||
throw new Error(
|
||||
'downloadKeysForUsers no longer accepts a callback parameter',
|
||||
);
|
||||
}
|
||||
var content = {device_keys: downloadQuery};
|
||||
opts = opts || {};
|
||||
|
||||
const content = {
|
||||
device_keys: {},
|
||||
};
|
||||
if ('token' in opts) {
|
||||
content.token = opts.token;
|
||||
}
|
||||
userIds.forEach((u) => {
|
||||
content.device_keys[u] = {};
|
||||
});
|
||||
|
||||
return this._http.authedRequestWithPrefix(
|
||||
callback, "POST", "/keys/query", undefined, content,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
undefined, "POST", "/keys/query", undefined, content,
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Claim one-time keys
|
||||
*
|
||||
* @param {string[][]} devices a list of [userId, deviceId] pairs
|
||||
* @param {string[]} devices a list of [userId, deviceId] pairs
|
||||
*
|
||||
* @param {string} [key_algorithm = signed_curve25519] desired key type
|
||||
*
|
||||
@@ -982,23 +1062,45 @@ MatrixBaseApis.prototype.downloadKeysForUsers = function(userIds, callback) {
|
||||
* an error response ({@link module:http-api.MatrixError}).
|
||||
*/
|
||||
MatrixBaseApis.prototype.claimOneTimeKeys = function(devices, key_algorithm) {
|
||||
var queries = {};
|
||||
const queries = {};
|
||||
|
||||
if (key_algorithm === undefined) {
|
||||
key_algorithm = "signed_curve25519";
|
||||
}
|
||||
|
||||
for (var i = 0; i < devices.length; ++i) {
|
||||
var userId = devices[i][0];
|
||||
var deviceId = devices[i][1];
|
||||
var query = queries[userId] || {};
|
||||
for (let i = 0; i < devices.length; ++i) {
|
||||
const userId = devices[i][0];
|
||||
const deviceId = devices[i][1];
|
||||
const query = queries[userId] || {};
|
||||
queries[userId] = query;
|
||||
query[deviceId] = key_algorithm;
|
||||
}
|
||||
var content = {one_time_keys: queries};
|
||||
const content = {one_time_keys: queries};
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "POST", "/keys/claim", undefined, content,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask the server for a list of users who have changed their device lists
|
||||
* between a pair of sync tokens
|
||||
*
|
||||
* @param {string} oldToken
|
||||
* @param {string} newToken
|
||||
*
|
||||
* @return {module:client.Promise} Resolves: result object. Rejects: with
|
||||
* an error response ({@link module:http-api.MatrixError}).
|
||||
*/
|
||||
MatrixBaseApis.prototype.getKeyChanges = function(oldToken, newToken) {
|
||||
const qps = {
|
||||
from: oldToken,
|
||||
to: newToken,
|
||||
};
|
||||
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "GET", "/keys/changes", qps, undefined,
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1028,15 +1130,15 @@ MatrixBaseApis.prototype.claimOneTimeKeys = function(devices, key_algorithm) {
|
||||
*/
|
||||
MatrixBaseApis.prototype.requestEmailToken = function(email, clientSecret,
|
||||
sendAttempt, nextLink, callback) {
|
||||
var params = {
|
||||
const params = {
|
||||
client_secret: clientSecret,
|
||||
email: email,
|
||||
send_attempt: sendAttempt,
|
||||
next_link: nextLink
|
||||
next_link: nextLink,
|
||||
};
|
||||
return this._http.idServerRequest(
|
||||
callback, "POST", "/validate/email/requestToken",
|
||||
params, httpApi.PREFIX_IDENTITY_V1
|
||||
params, httpApi.PREFIX_IDENTITY_V1,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1052,13 +1154,13 @@ MatrixBaseApis.prototype.requestEmailToken = function(email, clientSecret,
|
||||
* @return {module:http-api.MatrixError} Rejects: with an error response.
|
||||
*/
|
||||
MatrixBaseApis.prototype.lookupThreePid = function(medium, address, callback) {
|
||||
var params = {
|
||||
const params = {
|
||||
medium: medium,
|
||||
address: address,
|
||||
};
|
||||
return this._http.idServerRequest(
|
||||
callback, "GET", "/lookup",
|
||||
params, httpApi.PREFIX_IDENTITY_V1
|
||||
params, httpApi.PREFIX_IDENTITY_V1,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1077,20 +1179,20 @@ MatrixBaseApis.prototype.lookupThreePid = function(medium, address, callback) {
|
||||
* @return {module:client.Promise} Resolves to the result object
|
||||
*/
|
||||
MatrixBaseApis.prototype.sendToDevice = function(
|
||||
eventType, contentMap, txnId
|
||||
eventType, contentMap, txnId,
|
||||
) {
|
||||
var path = utils.encodeUri("/sendToDevice/$eventType/$txnId", {
|
||||
const path = utils.encodeUri("/sendToDevice/$eventType/$txnId", {
|
||||
$eventType: eventType,
|
||||
$txnId: txnId ? txnId : this.makeTxnId(),
|
||||
});
|
||||
|
||||
var body = {
|
||||
const body = {
|
||||
messages: contentMap,
|
||||
};
|
||||
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "PUT", path, undefined, body,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1105,7 +1207,7 @@ MatrixBaseApis.prototype.sendToDevice = function(
|
||||
MatrixBaseApis.prototype.getThirdpartyProtocols = function() {
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "GET", "/thirdparty/protocols", undefined, undefined,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1118,13 +1220,13 @@ MatrixBaseApis.prototype.getThirdpartyProtocols = function() {
|
||||
* @return {module:client.Promise} Resolves to the result object
|
||||
*/
|
||||
MatrixBaseApis.prototype.getThirdpartyLocation = function(protocol, params) {
|
||||
var path = utils.encodeUri("/thirdparty/location/$protocol", {
|
||||
$protocol: protocol
|
||||
const path = utils.encodeUri("/thirdparty/location/$protocol", {
|
||||
$protocol: protocol,
|
||||
});
|
||||
|
||||
return this._http.authedRequestWithPrefix(
|
||||
undefined, "GET", path, params, undefined,
|
||||
httpApi.PREFIX_UNSTABLE
|
||||
httpApi.PREFIX_UNSTABLE,
|
||||
);
|
||||
};
|
||||
|
||||
+472
-333
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
/**
|
||||
* @module content-repo
|
||||
*/
|
||||
var utils = require("./utils");
|
||||
const utils = require("./utils");
|
||||
|
||||
/** Content Repo utility functions */
|
||||
module.exports = {
|
||||
@@ -46,9 +46,9 @@ module.exports = {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
var serverAndMediaId = mxc.slice(6); // strips mxc://
|
||||
var prefix = "/_matrix/media/v1/download/";
|
||||
var params = {};
|
||||
let serverAndMediaId = mxc.slice(6); // strips mxc://
|
||||
let prefix = "/_matrix/media/v1/download/";
|
||||
const params = {};
|
||||
|
||||
if (width) {
|
||||
params.width = width;
|
||||
@@ -65,8 +65,8 @@ module.exports = {
|
||||
prefix = "/_matrix/media/v1/thumbnail/";
|
||||
}
|
||||
|
||||
var fragmentOffset = serverAndMediaId.indexOf("#"),
|
||||
fragment = "";
|
||||
const fragmentOffset = serverAndMediaId.indexOf("#");
|
||||
let fragment = "";
|
||||
if (fragmentOffset >= 0) {
|
||||
fragment = serverAndMediaId.substr(fragmentOffset);
|
||||
serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
|
||||
@@ -88,18 +88,22 @@ module.exports = {
|
||||
if (!identiconString) {
|
||||
return null;
|
||||
}
|
||||
if (!width) { width = 96; }
|
||||
if (!height) { height = 96; }
|
||||
var params = {
|
||||
if (!width) {
|
||||
width = 96;
|
||||
}
|
||||
if (!height) {
|
||||
height = 96;
|
||||
}
|
||||
const params = {
|
||||
width: width,
|
||||
height: height
|
||||
height: height,
|
||||
};
|
||||
|
||||
var path = utils.encodeUri("/_matrix/media/v1/identicon/$ident", {
|
||||
$ident: identiconString
|
||||
const path = utils.encodeUri("/_matrix/media/v1/identicon/$ident", {
|
||||
$ident: identiconString,
|
||||
});
|
||||
return baseUrl + path +
|
||||
(utils.keys(params).length === 0 ? "" :
|
||||
("?" + utils.encodeParams(params)));
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @module crypto/DeviceList
|
||||
*
|
||||
* Manages the list of other users' devices
|
||||
*/
|
||||
|
||||
import q from 'q';
|
||||
|
||||
import DeviceInfo from './deviceinfo';
|
||||
import olmlib from './olmlib';
|
||||
|
||||
/**
|
||||
* @alias module:crypto/DeviceList
|
||||
*/
|
||||
export default class DeviceList {
|
||||
constructor(baseApis, sessionStore, olmDevice) {
|
||||
this._baseApis = baseApis;
|
||||
this._sessionStore = sessionStore;
|
||||
this._olmDevice = olmDevice;
|
||||
|
||||
// users with outdated device lists
|
||||
// userId -> true
|
||||
this._pendingUsersWithNewDevices = {};
|
||||
|
||||
// userId -> promise
|
||||
this._keyDownloadsInProgressByUser = {};
|
||||
|
||||
this.lastKnownSyncToken = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the keys for a list of users and stores the keys in the session
|
||||
* store.
|
||||
* @param {Array} userIds The users to fetch.
|
||||
* @param {bool} forceDownload Always download the keys even if cached.
|
||||
*
|
||||
* @return {Promise} A promise which resolves to a map userId->deviceId->{@link
|
||||
* module:crypto/deviceinfo|DeviceInfo}.
|
||||
*/
|
||||
downloadKeys(userIds, forceDownload) {
|
||||
// promises we need to wait for while the download happens
|
||||
const promises = [];
|
||||
|
||||
let needsRefresh = false;
|
||||
userIds.forEach((u) => {
|
||||
if (this._keyDownloadsInProgressByUser[u]) {
|
||||
// just wait for the existing download to complete
|
||||
promises.push(this._keyDownloadsInProgressByUser[u]);
|
||||
} else {
|
||||
if (forceDownload || !this.getStoredDevicesForUser(u)) {
|
||||
this.invalidateUserDeviceList(u);
|
||||
}
|
||||
if (this._pendingUsersWithNewDevices[u]) {
|
||||
needsRefresh = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (needsRefresh) {
|
||||
promises.push(this.refreshOutdatedDeviceLists(true));
|
||||
}
|
||||
|
||||
return q.all(promises).then(() => {
|
||||
return this._getDevicesFromStore(userIds);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stored device keys for a list of user ids
|
||||
*
|
||||
* @param {string[]} userIds the list of users to list keys for.
|
||||
*
|
||||
* @return {Object} userId->deviceId->{@link module:crypto/deviceinfo|DeviceInfo}.
|
||||
*/
|
||||
_getDevicesFromStore(userIds) {
|
||||
const stored = {};
|
||||
const self = this;
|
||||
userIds.map(function(u) {
|
||||
stored[u] = {};
|
||||
const devices = self.getStoredDevicesForUser(u) || [];
|
||||
devices.map(function(dev) {
|
||||
stored[u][dev.deviceId] = dev;
|
||||
});
|
||||
});
|
||||
return stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stored device keys for a user id
|
||||
*
|
||||
* @param {string} userId the user to list keys for.
|
||||
*
|
||||
* @return {module:crypto/deviceinfo[]|null} list of devices, or null if we haven't
|
||||
* managed to get a list of devices for this user yet.
|
||||
*/
|
||||
getStoredDevicesForUser(userId) {
|
||||
const devs = this._sessionStore.getEndToEndDevicesForUser(userId);
|
||||
if (!devs) {
|
||||
return null;
|
||||
}
|
||||
const res = [];
|
||||
for (const deviceId in devs) {
|
||||
if (devs.hasOwnProperty(deviceId)) {
|
||||
res.push(DeviceInfo.fromStorage(devs[deviceId], deviceId));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stored keys for a single device
|
||||
*
|
||||
* @param {string} userId
|
||||
* @param {string} deviceId
|
||||
*
|
||||
* @return {module:crypto/deviceinfo?} device, or undefined
|
||||
* if we don't know about this device
|
||||
*/
|
||||
getStoredDevice(userId, deviceId) {
|
||||
const devs = this._sessionStore.getEndToEndDevicesForUser(userId);
|
||||
if (!devs || !devs[deviceId]) {
|
||||
return undefined;
|
||||
}
|
||||
return DeviceInfo.fromStorage(devs[deviceId], deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a device by curve25519 identity key
|
||||
*
|
||||
* @param {string} userId owner of the device
|
||||
* @param {string} algorithm encryption algorithm
|
||||
* @param {string} sender_key curve25519 key to match
|
||||
*
|
||||
* @return {module:crypto/deviceinfo?}
|
||||
*/
|
||||
getDeviceByIdentityKey(userId, algorithm, sender_key) {
|
||||
if (
|
||||
algorithm !== olmlib.OLM_ALGORITHM &&
|
||||
algorithm !== olmlib.MEGOLM_ALGORITHM
|
||||
) {
|
||||
// we only deal in olm keys
|
||||
return null;
|
||||
}
|
||||
|
||||
const devices = this._sessionStore.getEndToEndDevicesForUser(userId);
|
||||
if (!devices) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const deviceId in devices) {
|
||||
if (!devices.hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const device = devices[deviceId];
|
||||
for (const keyId in device.keys) {
|
||||
if (!device.keys.hasOwnProperty(keyId)) {
|
||||
continue;
|
||||
}
|
||||
if (keyId.indexOf("curve25519:") !== 0) {
|
||||
continue;
|
||||
}
|
||||
const deviceKey = device.keys[keyId];
|
||||
if (deviceKey == sender_key) {
|
||||
return DeviceInfo.fromStorage(device, deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// doesn't match a known device
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the cached device list for the given user outdated.
|
||||
*
|
||||
* This doesn't set off an update, so that several users can be batched
|
||||
* together. Call refreshOutdatedDeviceLists() for that.
|
||||
*
|
||||
* @param {String} userId
|
||||
*/
|
||||
invalidateUserDeviceList(userId) {
|
||||
// sanity-check the userId. This is mostly paranoia, but if synapse
|
||||
// can't parse the userId we give it as an mxid, it 500s the whole
|
||||
// request and we can never update the device lists again (because
|
||||
// the broken userId is always 'invalid' and always included in any
|
||||
// refresh request).
|
||||
// By checking it is at least a string, we can eliminate a class of
|
||||
// silly errors.
|
||||
if (typeof userId !== 'string') {
|
||||
throw new Error('userId must be a string; was '+userId);
|
||||
}
|
||||
this._pendingUsersWithNewDevices[userId] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start device queries for any users with outdated device lists
|
||||
*
|
||||
* We tolerate multiple concurrent device queries, but only one query per
|
||||
* user.
|
||||
*
|
||||
* If any users already have downloads in progress, they are ignored - they
|
||||
* will be refreshed when the current download completes anyway, so
|
||||
* each user with outdated device lists will be updated eventually.
|
||||
*
|
||||
* The returned promise resolves immediately if there are no users with
|
||||
* outdated device lists, or if all users with outdated device lists already
|
||||
* have a query in progress.
|
||||
*
|
||||
* Otherwise, a new query request is made, and the promise resolves
|
||||
* once that query completes. If the query fails, the promise will reject
|
||||
* if rejectOnFailure was truthy, otherwise it will still resolve.
|
||||
*
|
||||
* @param {Boolean?} rejectOnFailure true to make the returned promise
|
||||
* reject if the device list query fails.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
refreshOutdatedDeviceLists(rejectOnFailure) {
|
||||
const users = Object.keys(this._pendingUsersWithNewDevices).filter(
|
||||
(u) => !this._keyDownloadsInProgressByUser[u],
|
||||
);
|
||||
|
||||
if (users.length === 0) {
|
||||
return q();
|
||||
}
|
||||
|
||||
let prom = this._doKeyDownloadForUsers(users).then(() => {
|
||||
users.forEach((u) => {
|
||||
delete this._keyDownloadsInProgressByUser[u];
|
||||
});
|
||||
|
||||
// flush out any more requests that were blocked up while that
|
||||
// was going on, but let the initial promise complete now.
|
||||
//
|
||||
this.refreshOutdatedDeviceLists().done();
|
||||
}, (e) => {
|
||||
console.error(
|
||||
'Error updating device key cache for ' + users + ":", e,
|
||||
);
|
||||
|
||||
// reinstate the pending flags on any users which failed; this will
|
||||
// mean that we will do another download in the future, but won't
|
||||
// tight-loop.
|
||||
//
|
||||
users.forEach((u) => {
|
||||
delete this._keyDownloadsInProgressByUser[u];
|
||||
this._pendingUsersWithNewDevices[u] = true;
|
||||
});
|
||||
|
||||
// TODO: schedule a retry.
|
||||
throw e;
|
||||
});
|
||||
|
||||
users.forEach((u) => {
|
||||
delete this._pendingUsersWithNewDevices[u];
|
||||
this._keyDownloadsInProgressByUser[u] = prom;
|
||||
});
|
||||
|
||||
if (!rejectOnFailure) {
|
||||
// normally we just want to swallow the exception - we've already
|
||||
// logged it futher up.
|
||||
prom = prom.catch((e) => {});
|
||||
}
|
||||
return prom;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} downloadUsers list of userIds
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
_doKeyDownloadForUsers(downloadUsers) {
|
||||
console.log('Starting key download for ' + downloadUsers);
|
||||
|
||||
const token = this.lastKnownSyncToken;
|
||||
const opts = {};
|
||||
if (token) {
|
||||
opts.token = token;
|
||||
}
|
||||
return this._baseApis.downloadKeysForUsers(
|
||||
downloadUsers, opts,
|
||||
).then((res) => {
|
||||
const dk = res.device_keys || {};
|
||||
|
||||
for (const userId of downloadUsers) {
|
||||
console.log('got keys for ' + userId + ':', dk[userId]);
|
||||
|
||||
// map from deviceid -> deviceinfo for this user
|
||||
const userStore = {};
|
||||
const devs = this._sessionStore.getEndToEndDevicesForUser(userId);
|
||||
if (devs) {
|
||||
Object.keys(devs).forEach((deviceId) => {
|
||||
const d = DeviceInfo.fromStorage(devs[deviceId], deviceId);
|
||||
userStore[deviceId] = d;
|
||||
});
|
||||
}
|
||||
|
||||
_updateStoredDeviceKeysForUser(
|
||||
this._olmDevice, userId, userStore, dk[userId] || {},
|
||||
);
|
||||
|
||||
// update the session store
|
||||
const storage = {};
|
||||
Object.keys(userStore).forEach((deviceId) => {
|
||||
storage[deviceId] = userStore[deviceId].toStorage();
|
||||
});
|
||||
|
||||
this._sessionStore.storeEndToEndDevicesForUser(
|
||||
userId, storage,
|
||||
);
|
||||
|
||||
if (token) {
|
||||
this._sessionStore.storeEndToEndDeviceSyncToken(token);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function _updateStoredDeviceKeysForUser(_olmDevice, userId, userStore,
|
||||
userResult) {
|
||||
let updated = false;
|
||||
|
||||
// remove any devices in the store which aren't in the response
|
||||
for (var deviceId in userStore) {
|
||||
if (!userStore.hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(deviceId in userResult)) {
|
||||
console.log("Device " + userId + ":" + deviceId +
|
||||
" has been removed");
|
||||
delete userStore[deviceId];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (deviceId in userResult) {
|
||||
if (!userResult.hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const deviceResult = userResult[deviceId];
|
||||
|
||||
// check that the user_id and device_id in the response object are
|
||||
// correct
|
||||
if (deviceResult.user_id !== userId) {
|
||||
console.warn("Mismatched user_id " + deviceResult.user_id +
|
||||
" in keys from " + userId + ":" + deviceId);
|
||||
continue;
|
||||
}
|
||||
if (deviceResult.device_id !== deviceId) {
|
||||
console.warn("Mismatched device_id " + deviceResult.device_id +
|
||||
" in keys from " + userId + ":" + deviceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_storeDeviceKeys(_olmDevice, userStore, deviceResult)) {
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a device in a /query response, and add it to the userStore
|
||||
*
|
||||
* returns true if a change was made, else false
|
||||
*/
|
||||
function _storeDeviceKeys(_olmDevice, userStore, deviceResult) {
|
||||
if (!deviceResult.keys) {
|
||||
// no keys?
|
||||
return false;
|
||||
}
|
||||
|
||||
const deviceId = deviceResult.device_id;
|
||||
const userId = deviceResult.user_id;
|
||||
|
||||
const signKeyId = "ed25519:" + deviceId;
|
||||
const signKey = deviceResult.keys[signKeyId];
|
||||
if (!signKey) {
|
||||
console.log("Device " + userId + ":" + deviceId +
|
||||
" has no ed25519 key");
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned = deviceResult.unsigned || {};
|
||||
|
||||
try {
|
||||
olmlib.verifySignature(_olmDevice, deviceResult, userId, deviceId, signKey);
|
||||
} catch (e) {
|
||||
console.log("Unable to verify signature on device " +
|
||||
userId + ":" + deviceId + ":", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DeviceInfo
|
||||
let deviceStore;
|
||||
|
||||
if (deviceId in userStore) {
|
||||
// already have this device.
|
||||
deviceStore = userStore[deviceId];
|
||||
|
||||
if (deviceStore.getFingerprint() != signKey) {
|
||||
// this should only happen if the list has been MITMed; we are
|
||||
// best off sticking with the original keys.
|
||||
//
|
||||
// Should we warn the user about it somehow?
|
||||
console.warn("Ed25519 key for device" + userId + ": " +
|
||||
deviceId + " has changed");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
userStore[deviceId] = deviceStore = new DeviceInfo(deviceId);
|
||||
}
|
||||
|
||||
deviceStore.keys = deviceResult.keys || {};
|
||||
deviceStore.algorithms = deviceResult.algorithms || [];
|
||||
deviceStore.unsigned = unsigned;
|
||||
return true;
|
||||
}
|
||||
@@ -20,9 +20,49 @@ limitations under the License.
|
||||
*
|
||||
* @module crypto/OlmDevice
|
||||
*/
|
||||
const Olm = require("olm");
|
||||
if (!Olm) {
|
||||
// this happens if we were loaded via browserify and the Olm module was not
|
||||
// loaded.
|
||||
throw new Error("Olm is not defined");
|
||||
}
|
||||
const utils = require("../utils");
|
||||
|
||||
|
||||
// The maximum size of an event is 65K, and we base64 the content, so this is a
|
||||
// reasonable approximation to the biggest plaintext we can encrypt.
|
||||
const MAX_PLAINTEXT_LENGTH = 65536 * 3 / 4;
|
||||
|
||||
function checkPayloadLength(payloadString) {
|
||||
if (payloadString === undefined) {
|
||||
throw new Error("payloadString undefined");
|
||||
}
|
||||
|
||||
if (payloadString.length > MAX_PLAINTEXT_LENGTH) {
|
||||
// might as well fail early here rather than letting the olm library throw
|
||||
// a cryptic memory allocation error.
|
||||
//
|
||||
// Note that even if we manage to do the encryption, the message send may fail,
|
||||
// because by the time we've wrapped the ciphertext in the event object, it may
|
||||
// exceed 65K. But at least we won't just fail with "abort()" in that case.
|
||||
throw new Error("Message too long (" + payloadString.length + " bytes). " +
|
||||
"The maximum for an encrypted message is " +
|
||||
MAX_PLAINTEXT_LENGTH + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The type of object we use for importing and exporting megolm session data.
|
||||
*
|
||||
* @typedef {Object} module:crypto/OlmDevice.MegolmSessionData
|
||||
* @property {String} sender_key Sender's Curve25519 device key
|
||||
* @property {Object<string, string>} sender_claimed_keys Other keys the sender claims.
|
||||
* @property {String} room_id Room this session is used in
|
||||
* @property {String} session_id Unique id for the session
|
||||
* @property {String} session_key Base64'ed key data
|
||||
*/
|
||||
|
||||
var Olm = require("olm");
|
||||
var utils = require("../utils");
|
||||
|
||||
/**
|
||||
* Manages the olm cryptography functions. Each OlmDevice has a single
|
||||
@@ -43,8 +83,8 @@ function OlmDevice(sessionStore) {
|
||||
this._sessionStore = sessionStore;
|
||||
this._pickleKey = "DEFAULT_KEY";
|
||||
|
||||
var e2eKeys;
|
||||
var account = new Olm.Account();
|
||||
let e2eKeys;
|
||||
const account = new Olm.Account();
|
||||
try {
|
||||
_initialise_account(this._sessionStore, this._pickleKey, account);
|
||||
e2eKeys = JSON.parse(account.identity_keys());
|
||||
@@ -73,14 +113,14 @@ function OlmDevice(sessionStore) {
|
||||
}
|
||||
|
||||
function _initialise_account(sessionStore, pickleKey, account) {
|
||||
var e2eAccount = sessionStore.getEndToEndAccount();
|
||||
const e2eAccount = sessionStore.getEndToEndAccount();
|
||||
if (e2eAccount !== null) {
|
||||
account.unpickle(pickleKey, e2eAccount);
|
||||
return;
|
||||
}
|
||||
|
||||
account.create();
|
||||
var pickled = account.pickle(pickleKey);
|
||||
const pickled = account.pickle(pickleKey);
|
||||
sessionStore.storeEndToEndAccount(pickled);
|
||||
}
|
||||
|
||||
@@ -100,9 +140,9 @@ OlmDevice.getOlmVersion = function() {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._getAccount = function(func) {
|
||||
var account = new Olm.Account();
|
||||
const account = new Olm.Account();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
const pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
return func(account);
|
||||
} finally {
|
||||
@@ -118,7 +158,7 @@ OlmDevice.prototype._getAccount = function(func) {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._saveAccount = function(account) {
|
||||
var pickledAccount = account.pickle(this._pickleKey);
|
||||
const pickledAccount = account.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndAccount(pickledAccount);
|
||||
};
|
||||
|
||||
@@ -133,10 +173,10 @@ OlmDevice.prototype._saveAccount = function(account) {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._getSession = function(deviceKey, sessionId, func) {
|
||||
var sessions = this._sessionStore.getEndToEndSessions(deviceKey);
|
||||
var pickledSession = sessions[sessionId];
|
||||
const sessions = this._sessionStore.getEndToEndSessions(deviceKey);
|
||||
const pickledSession = sessions[sessionId];
|
||||
|
||||
var session = new Olm.Session();
|
||||
const session = new Olm.Session();
|
||||
try {
|
||||
session.unpickle(this._pickleKey, pickledSession);
|
||||
return func(session);
|
||||
@@ -154,9 +194,9 @@ OlmDevice.prototype._getSession = function(deviceKey, sessionId, func) {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._saveSession = function(deviceKey, session) {
|
||||
var pickledSession = session.pickle(this._pickleKey);
|
||||
const pickledSession = session.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndSession(
|
||||
deviceKey, session.session_id(), pickledSession
|
||||
deviceKey, session.session_id(), pickledSession,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -169,7 +209,7 @@ OlmDevice.prototype._saveSession = function(deviceKey, session) {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._getUtility = function(func) {
|
||||
var utility = new Olm.Utility();
|
||||
const utility = new Olm.Utility();
|
||||
try {
|
||||
return func(utility);
|
||||
} finally {
|
||||
@@ -219,7 +259,7 @@ OlmDevice.prototype.maxNumberOfOneTimeKeys = function() {
|
||||
* Marks all of the one-time keys as published.
|
||||
*/
|
||||
OlmDevice.prototype.markKeysAsPublished = function() {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this._getAccount(function(account) {
|
||||
account.mark_keys_as_published();
|
||||
self._saveAccount(account);
|
||||
@@ -232,7 +272,7 @@ OlmDevice.prototype.markKeysAsPublished = function() {
|
||||
* @param {number} numKeys number of keys to generate
|
||||
*/
|
||||
OlmDevice.prototype.generateOneTimeKeys = function(numKeys) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this._getAccount(function(account) {
|
||||
account.generate_one_time_keys(numKeys);
|
||||
self._saveAccount(account);
|
||||
@@ -249,11 +289,11 @@ OlmDevice.prototype.generateOneTimeKeys = function(numKeys) {
|
||||
* @return {string} sessionId for the outbound session.
|
||||
*/
|
||||
OlmDevice.prototype.createOutboundSession = function(
|
||||
theirIdentityKey, theirOneTimeKey
|
||||
theirIdentityKey, theirOneTimeKey,
|
||||
) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
return this._getAccount(function(account) {
|
||||
var session = new Olm.Session();
|
||||
const session = new Olm.Session();
|
||||
try {
|
||||
session.create_outbound(account, theirIdentityKey, theirOneTimeKey);
|
||||
self._saveSession(theirIdentityKey, session);
|
||||
@@ -279,21 +319,21 @@ OlmDevice.prototype.createOutboundSession = function(
|
||||
* didn't use a valid one-time key).
|
||||
*/
|
||||
OlmDevice.prototype.createInboundSession = function(
|
||||
theirDeviceIdentityKey, message_type, ciphertext
|
||||
theirDeviceIdentityKey, message_type, ciphertext,
|
||||
) {
|
||||
if (message_type !== 0) {
|
||||
throw new Error("Need message_type == 0 to create inbound session");
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
return this._getAccount(function(account) {
|
||||
var session = new Olm.Session();
|
||||
const session = new Olm.Session();
|
||||
try {
|
||||
session.create_inbound_from(account, theirDeviceIdentityKey, ciphertext);
|
||||
account.remove_one_time_keys(session);
|
||||
self._saveAccount(account);
|
||||
|
||||
var payloadString = session.decrypt(message_type, ciphertext);
|
||||
const payloadString = session.decrypt(message_type, ciphertext);
|
||||
|
||||
self._saveSession(theirDeviceIdentityKey, session);
|
||||
|
||||
@@ -316,8 +356,8 @@ OlmDevice.prototype.createInboundSession = function(
|
||||
* @return {string[]} a list of known session ids for the device
|
||||
*/
|
||||
OlmDevice.prototype.getSessionIdsForDevice = function(theirDeviceIdentityKey) {
|
||||
var sessions = this._sessionStore.getEndToEndSessions(
|
||||
theirDeviceIdentityKey
|
||||
const sessions = this._sessionStore.getEndToEndSessions(
|
||||
theirDeviceIdentityKey,
|
||||
);
|
||||
return utils.keys(sessions);
|
||||
};
|
||||
@@ -330,7 +370,7 @@ OlmDevice.prototype.getSessionIdsForDevice = function(theirDeviceIdentityKey) {
|
||||
* @return {string?} session id, or null if no established session
|
||||
*/
|
||||
OlmDevice.prototype.getSessionIdForDevice = function(theirDeviceIdentityKey) {
|
||||
var sessionIds = this.getSessionIdsForDevice(theirDeviceIdentityKey);
|
||||
const sessionIds = this.getSessionIdsForDevice(theirDeviceIdentityKey);
|
||||
if (sessionIds.length === 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -351,20 +391,20 @@ OlmDevice.prototype.getSessionIdForDevice = function(theirDeviceIdentityKey) {
|
||||
* @return {Array.<{sessionId: string, hasReceivedMessage: Boolean}>}
|
||||
*/
|
||||
OlmDevice.prototype.getSessionInfoForDevice = function(deviceIdentityKey) {
|
||||
var sessionIds = this.getSessionIdsForDevice(deviceIdentityKey);
|
||||
const sessionIds = this.getSessionIdsForDevice(deviceIdentityKey);
|
||||
sessionIds.sort();
|
||||
|
||||
var info = [];
|
||||
const info = [];
|
||||
|
||||
function getSessionInfo(session) {
|
||||
return {
|
||||
hasReceivedMessage: session.has_received_message()
|
||||
hasReceivedMessage: session.has_received_message(),
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < sessionIds.length; i++) {
|
||||
var sessionId = sessionIds[i];
|
||||
var res = this._getSession(deviceIdentityKey, sessionId, getSessionInfo);
|
||||
for (let i = 0; i < sessionIds.length; i++) {
|
||||
const sessionId = sessionIds[i];
|
||||
const res = this._getSession(deviceIdentityKey, sessionId, getSessionInfo);
|
||||
res.sessionId = sessionId;
|
||||
info.push(res);
|
||||
}
|
||||
@@ -382,16 +422,14 @@ OlmDevice.prototype.getSessionInfoForDevice = function(deviceIdentityKey) {
|
||||
* @return {string} ciphertext
|
||||
*/
|
||||
OlmDevice.prototype.encryptMessage = function(
|
||||
theirDeviceIdentityKey, sessionId, payloadString
|
||||
theirDeviceIdentityKey, sessionId, payloadString,
|
||||
) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
if (payloadString === undefined) {
|
||||
throw new Error("payloadString undefined");
|
||||
}
|
||||
checkPayloadLength(payloadString);
|
||||
|
||||
return this._getSession(theirDeviceIdentityKey, sessionId, function(session) {
|
||||
var res = session.encrypt(payloadString);
|
||||
const res = session.encrypt(payloadString);
|
||||
self._saveSession(theirDeviceIdentityKey, session);
|
||||
return res;
|
||||
});
|
||||
@@ -409,12 +447,12 @@ OlmDevice.prototype.encryptMessage = function(
|
||||
* @return {string} decrypted payload.
|
||||
*/
|
||||
OlmDevice.prototype.decryptMessage = function(
|
||||
theirDeviceIdentityKey, sessionId, message_type, ciphertext
|
||||
theirDeviceIdentityKey, sessionId, message_type, ciphertext,
|
||||
) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
return this._getSession(theirDeviceIdentityKey, sessionId, function(session) {
|
||||
var payloadString = session.decrypt(message_type, ciphertext);
|
||||
const payloadString = session.decrypt(message_type, ciphertext);
|
||||
self._saveSession(theirDeviceIdentityKey, session);
|
||||
|
||||
return payloadString;
|
||||
@@ -434,7 +472,7 @@ OlmDevice.prototype.decryptMessage = function(
|
||||
* the given session.
|
||||
*/
|
||||
OlmDevice.prototype.matchesSession = function(
|
||||
theirDeviceIdentityKey, sessionId, message_type, ciphertext
|
||||
theirDeviceIdentityKey, sessionId, message_type, ciphertext,
|
||||
) {
|
||||
if (message_type !== 0) {
|
||||
return false;
|
||||
@@ -446,7 +484,6 @@ OlmDevice.prototype.matchesSession = function(
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Outbound group session
|
||||
// ======================
|
||||
|
||||
@@ -457,7 +494,7 @@ OlmDevice.prototype.matchesSession = function(
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._saveOutboundGroupSession = function(session) {
|
||||
var pickledSession = session.pickle(this._pickleKey);
|
||||
const pickledSession = session.pickle(this._pickleKey);
|
||||
this._outboundGroupSessionStore[session.session_id()] = pickledSession;
|
||||
};
|
||||
|
||||
@@ -472,12 +509,12 @@ OlmDevice.prototype._saveOutboundGroupSession = function(session) {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._getOutboundGroupSession = function(sessionId, func) {
|
||||
var pickled = this._outboundGroupSessionStore[sessionId];
|
||||
const pickled = this._outboundGroupSessionStore[sessionId];
|
||||
if (pickled === null) {
|
||||
throw new Error("Unknown outbound group session " + sessionId);
|
||||
}
|
||||
|
||||
var session = new Olm.OutboundGroupSession();
|
||||
const session = new Olm.OutboundGroupSession();
|
||||
try {
|
||||
session.unpickle(this._pickleKey, pickled);
|
||||
return func(session);
|
||||
@@ -493,7 +530,7 @@ OlmDevice.prototype._getOutboundGroupSession = function(sessionId, func) {
|
||||
* @return {string} sessionId for the outbound session.
|
||||
*/
|
||||
OlmDevice.prototype.createOutboundGroupSession = function() {
|
||||
var session = new Olm.OutboundGroupSession();
|
||||
const session = new Olm.OutboundGroupSession();
|
||||
try {
|
||||
session.create();
|
||||
this._saveOutboundGroupSession(session);
|
||||
@@ -513,10 +550,12 @@ OlmDevice.prototype.createOutboundGroupSession = function() {
|
||||
* @return {string} ciphertext
|
||||
*/
|
||||
OlmDevice.prototype.encryptGroupMessage = function(sessionId, payloadString) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
checkPayloadLength(payloadString);
|
||||
|
||||
return this._getOutboundGroupSession(sessionId, function(session) {
|
||||
var res = session.encrypt(payloadString);
|
||||
const res = session.encrypt(payloadString);
|
||||
self._saveOutboundGroupSession(session);
|
||||
return res;
|
||||
});
|
||||
@@ -554,16 +593,16 @@ OlmDevice.prototype.getOutboundGroupSessionKey = function(sessionId) {
|
||||
* @private
|
||||
*/
|
||||
OlmDevice.prototype._saveInboundGroupSession = function(
|
||||
roomId, senderCurve25519Key, sessionId, session, keysClaimed
|
||||
roomId, senderCurve25519Key, sessionId, session, keysClaimed,
|
||||
) {
|
||||
var r = {
|
||||
const r = {
|
||||
room_id: roomId,
|
||||
session: session.pickle(this._pickleKey),
|
||||
keysClaimed: keysClaimed,
|
||||
};
|
||||
|
||||
this._sessionStore.storeEndToEndInboundGroupSession(
|
||||
senderCurve25519Key, sessionId, JSON.stringify(r)
|
||||
senderCurve25519Key, sessionId, JSON.stringify(r),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -584,10 +623,10 @@ OlmDevice.prototype._saveInboundGroupSession = function(
|
||||
* @template {T}
|
||||
*/
|
||||
OlmDevice.prototype._getInboundGroupSession = function(
|
||||
roomId, senderKey, sessionId, func
|
||||
roomId, senderKey, sessionId, func,
|
||||
) {
|
||||
var r = this._sessionStore.getEndToEndInboundGroupSession(
|
||||
senderKey, sessionId
|
||||
let r = this._sessionStore.getEndToEndInboundGroupSession(
|
||||
senderKey, sessionId,
|
||||
);
|
||||
|
||||
if (r === null) {
|
||||
@@ -601,11 +640,11 @@ OlmDevice.prototype._getInboundGroupSession = function(
|
||||
if (roomId !== r.room_id) {
|
||||
throw new Error(
|
||||
"Mismatched room_id for inbound group session (expected " + r.room_id +
|
||||
", was " + roomId + ")"
|
||||
", was " + roomId + ")",
|
||||
);
|
||||
}
|
||||
|
||||
var session = new Olm.InboundGroupSession();
|
||||
const session = new Olm.InboundGroupSession();
|
||||
try {
|
||||
session.unpickle(this._pickleKey, r.session);
|
||||
return func(session, r.keysClaimed || {});
|
||||
@@ -624,19 +663,79 @@ OlmDevice.prototype._getInboundGroupSession = function(
|
||||
* @param {Object<string, string>} keysClaimed Other keys the sender claims.
|
||||
*/
|
||||
OlmDevice.prototype.addInboundGroupSession = function(
|
||||
roomId, senderKey, sessionId, sessionKey, keysClaimed
|
||||
roomId, senderKey, sessionId, sessionKey, keysClaimed,
|
||||
) {
|
||||
var self = this;
|
||||
var session = new Olm.InboundGroupSession();
|
||||
const self = this;
|
||||
|
||||
/* if we already have this session, consider updating it */
|
||||
function updateSession(session) {
|
||||
console.log("Update for megolm session " + senderKey + "/" + sessionId);
|
||||
// for now we just ignore updates. TODO: implement something here
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const r = this._getInboundGroupSession(
|
||||
roomId, senderKey, sessionId, updateSession,
|
||||
);
|
||||
|
||||
if (r !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// new session.
|
||||
const session = new Olm.InboundGroupSession();
|
||||
try {
|
||||
session.create(sessionKey);
|
||||
if (sessionId != session.session_id()) {
|
||||
throw new Error(
|
||||
"Mismatched group session ID from senderKey: " + senderKey
|
||||
"Mismatched group session ID from senderKey: " + senderKey,
|
||||
);
|
||||
}
|
||||
self._saveInboundGroupSession(
|
||||
roomId, senderKey, sessionId, session, keysClaimed
|
||||
roomId, senderKey, sessionId, session, keysClaimed,
|
||||
);
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add a previously-exported inbound group session to the session store
|
||||
*
|
||||
* @param {module:crypto/OlmDevice.MegolmSessionData} data session data
|
||||
*/
|
||||
OlmDevice.prototype.importInboundGroupSession = function(data) {
|
||||
/* if we already have this session, consider updating it */
|
||||
function updateSession(session) {
|
||||
console.log("Update for megolm session " + data.sender_key + "|" +
|
||||
data.session_id);
|
||||
// for now we just ignore updates. TODO: implement something here
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const r = this._getInboundGroupSession(
|
||||
data.room_id, data.sender_key, data.session_id, updateSession,
|
||||
);
|
||||
|
||||
if (r !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// new session.
|
||||
const session = new Olm.InboundGroupSession();
|
||||
try {
|
||||
session.import_session(data.session_key);
|
||||
if (data.session_id != session.session_id()) {
|
||||
throw new Error(
|
||||
"Mismatched group session ID from senderKey: " + data.sender_key,
|
||||
);
|
||||
}
|
||||
this._saveInboundGroupSession(
|
||||
data.room_id, data.sender_key, data.session_id, session,
|
||||
data.sender_claimed_keys,
|
||||
);
|
||||
} finally {
|
||||
session.free();
|
||||
@@ -657,24 +756,24 @@ OlmDevice.prototype.addInboundGroupSession = function(
|
||||
* Object<string, string>}} result
|
||||
*/
|
||||
OlmDevice.prototype.decryptGroupMessage = function(
|
||||
roomId, senderKey, sessionId, body
|
||||
roomId, senderKey, sessionId, body,
|
||||
) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
function decrypt(session, keysClaimed) {
|
||||
var res = session.decrypt(body);
|
||||
const res = session.decrypt(body);
|
||||
|
||||
var plaintext = res.plaintext;
|
||||
let plaintext = res.plaintext;
|
||||
if (plaintext === undefined) {
|
||||
// Compatibility for older olm versions.
|
||||
plaintext = res;
|
||||
} else {
|
||||
// Check if we have seen this message index before to detect replay attacks.
|
||||
var messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index;
|
||||
const messageIndexKey = senderKey + "|" + sessionId + "|" + res.message_index;
|
||||
if (messageIndexKey in self._inboundGroupSessionMessageIndexes) {
|
||||
throw new Error(
|
||||
"Duplicate message index, possible replay attack: " +
|
||||
messageIndexKey
|
||||
messageIndexKey,
|
||||
);
|
||||
}
|
||||
self._inboundGroupSessionMessageIndexes[messageIndexKey] = true;
|
||||
@@ -682,10 +781,10 @@ OlmDevice.prototype.decryptGroupMessage = function(
|
||||
|
||||
// the sender must have had the senderKey to persuade us to save the
|
||||
// session.
|
||||
var keysProved = {curve25519: senderKey};
|
||||
const keysProved = {curve25519: senderKey};
|
||||
|
||||
self._saveInboundGroupSession(
|
||||
roomId, senderKey, sessionId, session, keysClaimed
|
||||
roomId, senderKey, sessionId, session, keysClaimed,
|
||||
);
|
||||
return {
|
||||
result: plaintext,
|
||||
@@ -695,10 +794,45 @@ OlmDevice.prototype.decryptGroupMessage = function(
|
||||
}
|
||||
|
||||
return this._getInboundGroupSession(
|
||||
roomId, senderKey, sessionId, decrypt
|
||||
roomId, senderKey, sessionId, decrypt,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Export an inbound group session
|
||||
*
|
||||
* @param {string} senderKey base64-encoded curve25519 key of the sender
|
||||
* @param {string} sessionId session identifier
|
||||
* @return {module:crypto/OlmDevice.MegolmSessionData} exported session data
|
||||
*/
|
||||
OlmDevice.prototype.exportInboundGroupSession = function(senderKey, sessionId) {
|
||||
const s = this._sessionStore.getEndToEndInboundGroupSession(
|
||||
senderKey, sessionId,
|
||||
);
|
||||
|
||||
if (s === null) {
|
||||
throw new Error("Unknown inbound group session [" + senderKey + "," +
|
||||
sessionId + "]");
|
||||
}
|
||||
const r = JSON.parse(s);
|
||||
|
||||
const session = new Olm.InboundGroupSession();
|
||||
try {
|
||||
session.unpickle(this._pickleKey, r.session);
|
||||
|
||||
const messageIndex = session.first_known_index();
|
||||
|
||||
return {
|
||||
"sender_key": senderKey,
|
||||
"sender_claimed_keys": r.keysClaimed,
|
||||
"room_id": r.room_id,
|
||||
"session_id": sessionId,
|
||||
"session_key": session.export_session(messageIndex),
|
||||
};
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
};
|
||||
|
||||
// Utilities
|
||||
// =========
|
||||
@@ -715,7 +849,7 @@ OlmDevice.prototype.decryptGroupMessage = function(
|
||||
* was invalid then the message will be "OLM.BAD_MESSAGE_MAC".
|
||||
*/
|
||||
OlmDevice.prototype.verifySignature = function(
|
||||
key, message, signature
|
||||
key, message, signature,
|
||||
) {
|
||||
this._getUtility(function(util) {
|
||||
util.ed25519_verify(key, message, signature);
|
||||
@@ -20,7 +20,7 @@ limitations under the License.
|
||||
*
|
||||
* @module crypto/algorithms/base
|
||||
*/
|
||||
var utils = require("../../utils");
|
||||
const utils = require("../../utils");
|
||||
|
||||
/**
|
||||
* map of registered encryption algorithm classes. A map from string to {@link
|
||||
@@ -53,7 +53,7 @@ module.exports.DECRYPTION_CLASSES = {};
|
||||
* @param {string} params.roomId The ID of the room we will be sending to
|
||||
* @param {object} params.config The body of the m.room.encryption event
|
||||
*/
|
||||
var EncryptionAlgorithm = function(params) {
|
||||
const EncryptionAlgorithm = function(params) {
|
||||
this._userId = params.userId;
|
||||
this._deviceId = params.deviceId;
|
||||
this._crypto = params.crypto;
|
||||
@@ -85,18 +85,9 @@ module.exports.EncryptionAlgorithm = EncryptionAlgorithm;
|
||||
* @param {string=} oldMembership previous membership
|
||||
*/
|
||||
EncryptionAlgorithm.prototype.onRoomMembership = function(
|
||||
event, member, oldMembership
|
||||
event, member, oldMembership,
|
||||
) {};
|
||||
|
||||
/**
|
||||
* Called when a new device announces itself in the room
|
||||
*
|
||||
* @param {string} userId owner of the device
|
||||
* @param {string} deviceId deviceId of the device
|
||||
*/
|
||||
EncryptionAlgorithm.prototype.onNewDevice = function(userId, deviceId) {};
|
||||
|
||||
|
||||
/**
|
||||
* base type for decryption implementations
|
||||
*
|
||||
@@ -105,11 +96,16 @@ EncryptionAlgorithm.prototype.onNewDevice = function(userId, deviceId) {};
|
||||
*
|
||||
* @param {object} params parameters
|
||||
* @param {string} params.userId The UserID for the local user
|
||||
* @param {module:crypto} params.crypto crypto core
|
||||
* @param {module:crypto/OlmDevice} params.olmDevice olm.js wrapper
|
||||
* @param {string=} params.roomId The ID of the room we will be receiving
|
||||
* from. Null for to-device events.
|
||||
*/
|
||||
var DecryptionAlgorithm = function(params) {
|
||||
const DecryptionAlgorithm = function(params) {
|
||||
this._userId = params.userId;
|
||||
this._crypto = params.crypto;
|
||||
this._olmDevice = params.olmDevice;
|
||||
this._roomId = params.roomId;
|
||||
};
|
||||
/** */
|
||||
module.exports.DecryptionAlgorithm = DecryptionAlgorithm;
|
||||
@@ -134,12 +130,21 @@ module.exports.DecryptionAlgorithm = DecryptionAlgorithm;
|
||||
*
|
||||
* @method module:crypto/algorithms/base.DecryptionAlgorithm#onRoomKeyEvent
|
||||
*
|
||||
* @param {module:models/event.MatrixEvent} event key event
|
||||
* @param {module:models/event.MatrixEvent} params event key event
|
||||
*/
|
||||
DecryptionAlgorithm.prototype.onRoomKeyEvent = function(params) {
|
||||
// ignore by default
|
||||
};
|
||||
|
||||
/**
|
||||
* Import a room key
|
||||
*
|
||||
* @param {module:crypto/OlmDevice.MegolmSessionData} session
|
||||
*/
|
||||
DecryptionAlgorithm.prototype.importRoomKey = function(session) {
|
||||
// ignore by default
|
||||
};
|
||||
|
||||
/**
|
||||
* Exception thrown when decryption fails
|
||||
*
|
||||
@@ -152,6 +157,23 @@ module.exports.DecryptionError = function(msg) {
|
||||
};
|
||||
utils.inherits(module.exports.DecryptionError, Error);
|
||||
|
||||
/**
|
||||
* Exception thrown specifically when we want to warn the user to consider
|
||||
* the security of their conversation before continuing
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} msg message describing the problem
|
||||
* @param {Object} devices userId -> {deviceId -> object}
|
||||
* set of unknown devices per user we're warning about
|
||||
* @extends Error
|
||||
*/
|
||||
module.exports.UnknownDeviceError = function(msg, devices) {
|
||||
this.name = "UnknownDeviceError";
|
||||
this.message = msg;
|
||||
this.devices = devices;
|
||||
};
|
||||
utils.inherits(module.exports.UnknownDeviceError, Error);
|
||||
|
||||
/**
|
||||
* Registers an encryption/decryption class for a particular algorithm
|
||||
*
|
||||
@@ -19,7 +19,7 @@ limitations under the License.
|
||||
* @module crypto/algorithms
|
||||
*/
|
||||
|
||||
var base = require("./base");
|
||||
const base = require("./base");
|
||||
|
||||
require("./olm");
|
||||
require("./megolm");
|
||||
@@ -0,0 +1,657 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Defines m.olm encryption/decryption
|
||||
*
|
||||
* @module crypto/algorithms/megolm
|
||||
*/
|
||||
|
||||
const q = require("q");
|
||||
|
||||
const utils = require("../../utils");
|
||||
const olmlib = require("../olmlib");
|
||||
const base = require("./base");
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @constructor
|
||||
*
|
||||
* @param {string} sessionId
|
||||
*
|
||||
* @property {string} sessionId
|
||||
* @property {Number} useCount number of times this session has been used
|
||||
* @property {Number} creationTime when the session was created (ms since the epoch)
|
||||
*
|
||||
* @property {object} sharedWithDevices
|
||||
* devices with which we have shared the session key
|
||||
* userId -> {deviceId -> msgindex}
|
||||
*/
|
||||
function OutboundSessionInfo(sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
this.useCount = 0;
|
||||
this.creationTime = new Date().getTime();
|
||||
this.sharedWithDevices = {};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if it's time to rotate the session
|
||||
*
|
||||
* @param {Number} rotationPeriodMsgs
|
||||
* @param {Number} rotationPeriodMs
|
||||
* @return {Boolean}
|
||||
*/
|
||||
OutboundSessionInfo.prototype.needsRotation = function(
|
||||
rotationPeriodMsgs, rotationPeriodMs,
|
||||
) {
|
||||
const sessionLifetime = new Date().getTime() - this.creationTime;
|
||||
|
||||
if (this.useCount >= rotationPeriodMsgs ||
|
||||
sessionLifetime >= rotationPeriodMs
|
||||
) {
|
||||
console.log(
|
||||
"Rotating megolm session after " + this.useCount +
|
||||
" messages, " + sessionLifetime + "ms",
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determine if this session has been shared with devices which it shouldn't
|
||||
* have been.
|
||||
*
|
||||
* @param {Object} devicesInRoom userId -> {deviceId -> object}
|
||||
* devices we should shared the session with.
|
||||
*
|
||||
* @return {Boolean} true if we have shared the session with devices which aren't
|
||||
* in devicesInRoom.
|
||||
*/
|
||||
OutboundSessionInfo.prototype.sharedWithTooManyDevices = function(
|
||||
devicesInRoom,
|
||||
) {
|
||||
for (const userId in this.sharedWithDevices) {
|
||||
if (!this.sharedWithDevices.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!devicesInRoom.hasOwnProperty(userId)) {
|
||||
console.log("Starting new session because we shared with " + userId);
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const deviceId in this.sharedWithDevices[userId]) {
|
||||
if (!this.sharedWithDevices[userId].hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!devicesInRoom[userId].hasOwnProperty(deviceId)) {
|
||||
console.log(
|
||||
"Starting new session because we shared with " +
|
||||
userId + ":" + deviceId,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Megolm encryption implementation
|
||||
*
|
||||
* @constructor
|
||||
* @extends {module:crypto/algorithms/base.EncryptionAlgorithm}
|
||||
*
|
||||
* @param {object} params parameters, as per
|
||||
* {@link module:crypto/algorithms/base.EncryptionAlgorithm}
|
||||
*/
|
||||
function MegolmEncryption(params) {
|
||||
base.EncryptionAlgorithm.call(this, params);
|
||||
|
||||
// the most recent attempt to set up a session. This is used to serialise
|
||||
// the session setups, so that we have a race-free view of which session we
|
||||
// are using, and which devices we have shared the keys with. It resolves
|
||||
// with an OutboundSessionInfo (or undefined, for the first message in the
|
||||
// room).
|
||||
this._setupPromise = q();
|
||||
|
||||
// default rotation periods
|
||||
this._sessionRotationPeriodMsgs = 100;
|
||||
this._sessionRotationPeriodMs = 7 * 24 * 3600 * 1000;
|
||||
|
||||
if (params.config.rotation_period_ms !== undefined) {
|
||||
this._sessionRotationPeriodMs = params.config.rotation_period_ms;
|
||||
}
|
||||
|
||||
if (params.config.rotation_period_msgs !== undefined) {
|
||||
this._sessionRotationPeriodMsgs = params.config.rotation_period_msgs;
|
||||
}
|
||||
}
|
||||
utils.inherits(MegolmEncryption, base.EncryptionAlgorithm);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {Object} devicesInRoom The devices in this room, indexed by user ID
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves to the
|
||||
* OutboundSessionInfo when setup is complete.
|
||||
*/
|
||||
MegolmEncryption.prototype._ensureOutboundSession = function(devicesInRoom) {
|
||||
const self = this;
|
||||
|
||||
let session;
|
||||
|
||||
// takes the previous OutboundSessionInfo, and considers whether to create
|
||||
// a new one. Also shares the key with any (new) devices in the room.
|
||||
// Updates `session` to hold the final OutboundSessionInfo.
|
||||
//
|
||||
// returns a promise which resolves once the keyshare is successful.
|
||||
function prepareSession(oldSession) {
|
||||
session = oldSession;
|
||||
|
||||
// need to make a brand new session?
|
||||
if (session && session.needsRotation(self._sessionRotationPeriodMsgs,
|
||||
self._sessionRotationPeriodMs)
|
||||
) {
|
||||
console.log("Starting new megolm session because we need to rotate.");
|
||||
session = null;
|
||||
}
|
||||
|
||||
// determine if we have shared with anyone we shouldn't have
|
||||
if (session && session.sharedWithTooManyDevices(devicesInRoom)) {
|
||||
session = null;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
session = self._prepareNewSession();
|
||||
}
|
||||
|
||||
// now check if we need to share with any devices
|
||||
const shareMap = {};
|
||||
|
||||
for (const userId in devicesInRoom) {
|
||||
if (!devicesInRoom.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const userDevices = devicesInRoom[userId];
|
||||
|
||||
for (const deviceId in userDevices) {
|
||||
if (!userDevices.hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const deviceInfo = userDevices[deviceId];
|
||||
|
||||
const key = deviceInfo.getIdentityKey();
|
||||
if (key == self._olmDevice.deviceCurve25519Key) {
|
||||
// don't bother sending to ourself
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!session.sharedWithDevices[userId] ||
|
||||
session.sharedWithDevices[userId][deviceId] === undefined
|
||||
) {
|
||||
shareMap[userId] = shareMap[userId] || [];
|
||||
shareMap[userId].push(deviceInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self._shareKeyWithDevices(
|
||||
session, shareMap,
|
||||
);
|
||||
}
|
||||
|
||||
// helper which returns the session prepared by prepareSession
|
||||
function returnSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
// first wait for the previous share to complete
|
||||
const prom = this._setupPromise.then(prepareSession);
|
||||
|
||||
// _setupPromise resolves to `session` whether or not the share succeeds
|
||||
this._setupPromise = prom.then(returnSession, returnSession);
|
||||
|
||||
// but we return a promise which only resolves if the share was successful.
|
||||
return prom.then(returnSession);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @return {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
||||
*/
|
||||
MegolmEncryption.prototype._prepareNewSession = function() {
|
||||
const session_id = this._olmDevice.createOutboundGroupSession();
|
||||
const key = this._olmDevice.getOutboundGroupSessionKey(session_id);
|
||||
|
||||
this._olmDevice.addInboundGroupSession(
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, session_id,
|
||||
key.key, {ed25519: this._olmDevice.deviceEd25519Key},
|
||||
);
|
||||
|
||||
return new OutboundSessionInfo(session_id);
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {module:crypto/algorithms/megolm.OutboundSessionInfo} session
|
||||
*
|
||||
* @param {object<string, module:crypto/deviceinfo[]>} devicesByUser
|
||||
* map from userid to list of devices
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves once the key sharing
|
||||
* message has been sent.
|
||||
*/
|
||||
MegolmEncryption.prototype._shareKeyWithDevices = function(session, devicesByUser) {
|
||||
const self = this;
|
||||
|
||||
const key = this._olmDevice.getOutboundGroupSessionKey(session.sessionId);
|
||||
const payload = {
|
||||
type: "m.room_key",
|
||||
content: {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
room_id: this._roomId,
|
||||
session_id: session.sessionId,
|
||||
session_key: key.key,
|
||||
chain_index: key.chain_index,
|
||||
},
|
||||
};
|
||||
|
||||
const contentMap = {};
|
||||
|
||||
return olmlib.ensureOlmSessionsForDevices(
|
||||
this._olmDevice, this._baseApis, devicesByUser,
|
||||
).then(function(devicemap) {
|
||||
let haveTargets = false;
|
||||
|
||||
for (const userId in devicesByUser) {
|
||||
if (!devicesByUser.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const devicesToShareWith = devicesByUser[userId];
|
||||
const sessionResults = devicemap[userId];
|
||||
|
||||
for (let i = 0; i < devicesToShareWith.length; i++) {
|
||||
const deviceInfo = devicesToShareWith[i];
|
||||
const deviceId = deviceInfo.deviceId;
|
||||
|
||||
const sessionResult = sessionResults[deviceId];
|
||||
if (!sessionResult.sessionId) {
|
||||
// no session with this device, probably because there
|
||||
// were no one-time keys.
|
||||
//
|
||||
// we could send them a to_device message anyway, as a
|
||||
// signal that they have missed out on the key sharing
|
||||
// message because of the lack of keys, but there's not
|
||||
// much point in that really; it will mostly serve to clog
|
||||
// up to_device inboxes.
|
||||
//
|
||||
// ensureOlmSessionsForUsers has already done the logging,
|
||||
// so just skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"sharing keys with device " + userId + ":" + deviceId,
|
||||
);
|
||||
|
||||
const encryptedContent = {
|
||||
algorithm: olmlib.OLM_ALGORITHM,
|
||||
sender_key: self._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: {},
|
||||
};
|
||||
|
||||
olmlib.encryptMessageForDevice(
|
||||
encryptedContent.ciphertext,
|
||||
self._userId,
|
||||
self._deviceId,
|
||||
self._olmDevice,
|
||||
userId,
|
||||
deviceInfo,
|
||||
payload,
|
||||
);
|
||||
|
||||
if (!contentMap[userId]) {
|
||||
contentMap[userId] = {};
|
||||
}
|
||||
|
||||
contentMap[userId][deviceId] = encryptedContent;
|
||||
haveTargets = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveTargets) {
|
||||
return q();
|
||||
}
|
||||
|
||||
// TODO: retries
|
||||
return self._baseApis.sendToDevice("m.room.encrypted", contentMap);
|
||||
}).then(function() {
|
||||
// Add the devices we have shared with to session.sharedWithDevices.
|
||||
//
|
||||
// we deliberately iterate over devicesByUser (ie, the devices we
|
||||
// attempted to share with) rather than the contentMap (those we did
|
||||
// share with), because we don't want to try to claim a one-time-key
|
||||
// for dead devices on every message.
|
||||
for (const userId in devicesByUser) {
|
||||
if (!devicesByUser.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
if (!session.sharedWithDevices[userId]) {
|
||||
session.sharedWithDevices[userId] = {};
|
||||
}
|
||||
const devicesToShareWith = devicesByUser[userId];
|
||||
for (let i = 0; i < devicesToShareWith.length; i++) {
|
||||
const deviceInfo = devicesToShareWith[i];
|
||||
session.sharedWithDevices[userId][deviceInfo.deviceId] =
|
||||
key.chain_index;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {module:models/room} room
|
||||
* @param {string} eventType
|
||||
* @param {object} content plaintext event content
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||
*/
|
||||
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
const self = this;
|
||||
return this._getDevicesInRoom(room).then(function(devicesInRoom) {
|
||||
// check if any of these devices are not yet known to the user.
|
||||
// if so, warn the user so they can verify or ignore.
|
||||
self._checkForUnknownDevices(devicesInRoom);
|
||||
|
||||
return self._ensureOutboundSession(devicesInRoom);
|
||||
}).then(function(session) {
|
||||
const payloadJson = {
|
||||
room_id: self._roomId,
|
||||
type: eventType,
|
||||
content: content,
|
||||
};
|
||||
|
||||
const ciphertext = self._olmDevice.encryptGroupMessage(
|
||||
session.sessionId, JSON.stringify(payloadJson),
|
||||
);
|
||||
|
||||
const encryptedContent = {
|
||||
algorithm: olmlib.MEGOLM_ALGORITHM,
|
||||
sender_key: self._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: ciphertext,
|
||||
session_id: session.sessionId,
|
||||
// Include our device ID so that recipients can send us a
|
||||
// m.new_device message if they don't have our session key.
|
||||
device_id: self._deviceId,
|
||||
};
|
||||
|
||||
session.useCount++;
|
||||
return encryptedContent;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the devices we're about to send to and see if any are entirely
|
||||
* unknown to the user. If so, warn the user, and mark them as known to
|
||||
* give the user a chance to go verify them before re-sending this message.
|
||||
*
|
||||
* @param {Object} devicesInRoom userId -> {deviceId -> object}
|
||||
* devices we should shared the session with.
|
||||
*/
|
||||
MegolmEncryption.prototype._checkForUnknownDevices = function(devicesInRoom) {
|
||||
const unknownDevices = {};
|
||||
|
||||
Object.keys(devicesInRoom).forEach((userId)=>{
|
||||
Object.keys(devicesInRoom[userId]).forEach((deviceId)=>{
|
||||
const device = devicesInRoom[userId][deviceId];
|
||||
if (device.isUnverified() && !device.isKnown()) {
|
||||
if (!unknownDevices[userId]) {
|
||||
unknownDevices[userId] = {};
|
||||
}
|
||||
unknownDevices[userId][deviceId] = device;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (Object.keys(unknownDevices).length) {
|
||||
// it'd be kind to pass unknownDevices up to the user in this error
|
||||
throw new base.UnknownDeviceError(
|
||||
"This room contains unknown devices which have not been verified. " +
|
||||
"We strongly recommend you verify them before continuing.", unknownDevices);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of unblocked devices for all users in the room
|
||||
*
|
||||
* @param {module:models/room} room
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves to a map
|
||||
* from userId to deviceId to deviceInfo
|
||||
*/
|
||||
MegolmEncryption.prototype._getDevicesInRoom = function(room) {
|
||||
// XXX what about rooms where invitees can see the content?
|
||||
const roomMembers = utils.map(room.getJoinedMembers(), function(u) {
|
||||
return u.userId;
|
||||
});
|
||||
|
||||
// We are happy to use a cached version here: we assume that if we already
|
||||
// have a list of the user's devices, then we already share an e2e room
|
||||
// with them, which means that they will have announced any new devices via
|
||||
// an m.new_device.
|
||||
//
|
||||
// XXX: what if the cache is stale, and the user left the room we had in
|
||||
// common and then added new devices before joining this one? --Matthew
|
||||
//
|
||||
// yup, see https://github.com/vector-im/riot-web/issues/2305 --richvdh
|
||||
return this._crypto.downloadKeys(roomMembers, false).then((devices) => {
|
||||
// remove any blocked devices
|
||||
for (const userId in devices) {
|
||||
if (!devices.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const userDevices = devices[userId];
|
||||
for (const deviceId in userDevices) {
|
||||
if (!userDevices.hasOwnProperty(deviceId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (userDevices[deviceId].isBlocked() ||
|
||||
(userDevices[deviceId].isUnverified() &&
|
||||
(room.getBlacklistUnverifiedDevices() ||
|
||||
this._crypto.getGlobalBlacklistUnverifiedDevices()))
|
||||
) {
|
||||
delete userDevices[deviceId];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Megolm decryption implementation
|
||||
*
|
||||
* @constructor
|
||||
* @extends {module:crypto/algorithms/base.DecryptionAlgorithm}
|
||||
*
|
||||
* @param {object} params parameters, as per
|
||||
* {@link module:crypto/algorithms/base.DecryptionAlgorithm}
|
||||
*/
|
||||
function MegolmDecryption(params) {
|
||||
base.DecryptionAlgorithm.call(this, params);
|
||||
|
||||
// events which we couldn't decrypt due to unknown sessions / indexes: map from
|
||||
// senderKey|sessionId to list of MatrixEvents
|
||||
this._pendingEvents = {};
|
||||
}
|
||||
utils.inherits(MegolmDecryption, base.DecryptionAlgorithm);
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {MatrixEvent} event
|
||||
*
|
||||
* @throws {module:crypto/algorithms/base.DecryptionError} if there is a
|
||||
* problem decrypting the event
|
||||
*/
|
||||
MegolmDecryption.prototype.decryptEvent = function(event) {
|
||||
const content = event.getWireContent();
|
||||
|
||||
if (!content.sender_key || !content.session_id ||
|
||||
!content.ciphertext
|
||||
) {
|
||||
throw new base.DecryptionError("Missing fields in input");
|
||||
}
|
||||
|
||||
let res;
|
||||
try {
|
||||
res = this._olmDevice.decryptGroupMessage(
|
||||
event.getRoomId(), content.sender_key, content.session_id, content.ciphertext,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e.message === 'OLM.UNKNOWN_MESSAGE_INDEX') {
|
||||
this._addEventToPendingList(event);
|
||||
}
|
||||
throw new base.DecryptionError(e);
|
||||
}
|
||||
|
||||
if (res === null) {
|
||||
// We've got a message for a session we don't have.
|
||||
this._addEventToPendingList(event);
|
||||
throw new base.DecryptionError(
|
||||
"The sender's device has not sent us the keys for this message.",
|
||||
);
|
||||
}
|
||||
|
||||
const payload = JSON.parse(res.result);
|
||||
|
||||
// belt-and-braces check that the room id matches that indicated by the HS
|
||||
// (this is somewhat redundant, since the megolm session is scoped to the
|
||||
// room, so neither the sender nor a MITM can lie about the room_id).
|
||||
if (payload.room_id !== event.getRoomId()) {
|
||||
throw new base.DecryptionError(
|
||||
"Message intended for room " + payload.room_id,
|
||||
);
|
||||
}
|
||||
|
||||
event.setClearData(payload, res.keysProved, res.keysClaimed);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Add an event to the list of those we couldn't decrypt the first time we
|
||||
* saw them.
|
||||
*
|
||||
* @private
|
||||
*
|
||||
* @param {module:models/event.MatrixEvent} event
|
||||
*/
|
||||
MegolmDecryption.prototype._addEventToPendingList = function(event) {
|
||||
const content = event.getWireContent();
|
||||
const k = content.sender_key + "|" + content.session_id;
|
||||
if (!this._pendingEvents[k]) {
|
||||
this._pendingEvents[k] = [];
|
||||
}
|
||||
this._pendingEvents[k].push(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {module:models/event.MatrixEvent} event key event
|
||||
*/
|
||||
MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
|
||||
console.log("Adding key from ", event);
|
||||
const content = event.getContent();
|
||||
|
||||
if (!content.room_id ||
|
||||
!content.session_id ||
|
||||
!content.session_key
|
||||
) {
|
||||
console.error("key event is missing fields");
|
||||
return;
|
||||
}
|
||||
|
||||
this._olmDevice.addInboundGroupSession(
|
||||
content.room_id, event.getSenderKey(), content.session_id,
|
||||
content.session_key, event.getKeysClaimed(),
|
||||
);
|
||||
|
||||
// have another go at decrypting events sent with this session.
|
||||
this._retryDecryption(event.getSenderKey, content.session_id);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {module:crypto/OlmDevice.MegolmSessionData} session
|
||||
*/
|
||||
MegolmDecryption.prototype.importRoomKey = function(session) {
|
||||
this._olmDevice.importInboundGroupSession(session);
|
||||
|
||||
// have another go at decrypting events sent with this session.
|
||||
this._retryDecryption(session.sender_key, session.session_id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Have another go at decrypting events after we receive a key
|
||||
*
|
||||
* @private
|
||||
* @param {String} senderKey
|
||||
* @param {String} sessionId
|
||||
*/
|
||||
MegolmDecryption.prototype._retryDecryption = function(senderKey, sessionId) {
|
||||
const k = senderKey + "|" + sessionId;
|
||||
const pending = this._pendingEvents[k];
|
||||
if (!pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
delete this._pendingEvents[k];
|
||||
|
||||
for (let i = 0; i < pending.length; i++) {
|
||||
try {
|
||||
this.decryptEvent(pending[i]);
|
||||
console.log("successful re-decryption of", pending[i]);
|
||||
} catch (e) {
|
||||
console.log("Still can't decrypt", pending[i], e.stack || e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
base.registerAlgorithm(
|
||||
olmlib.MEGOLM_ALGORITHM, MegolmEncryption, MegolmDecryption,
|
||||
);
|
||||
@@ -20,15 +20,15 @@ limitations under the License.
|
||||
*
|
||||
* @module crypto/algorithms/olm
|
||||
*/
|
||||
var q = require('q');
|
||||
const q = require('q');
|
||||
|
||||
var utils = require("../../utils");
|
||||
var olmlib = require("../olmlib");
|
||||
var DeviceInfo = require("../deviceinfo");
|
||||
var DeviceVerification = DeviceInfo.DeviceVerification;
|
||||
const utils = require("../../utils");
|
||||
const olmlib = require("../olmlib");
|
||||
const DeviceInfo = require("../deviceinfo");
|
||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||
|
||||
|
||||
var base = require("./base");
|
||||
const base = require("./base");
|
||||
|
||||
/**
|
||||
* Olm encryption implementation
|
||||
@@ -63,7 +63,7 @@ OlmEncryption.prototype._ensureSession = function(roomMembers) {
|
||||
return q();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
this._prepPromise = self._crypto.downloadKeys(roomMembers, true).then(function(res) {
|
||||
return self._crypto.ensureOlmSessionsForUsers(roomMembers);
|
||||
}).then(function() {
|
||||
@@ -79,7 +79,7 @@ OlmEncryption.prototype._ensureSession = function(roomMembers) {
|
||||
*
|
||||
* @param {module:models/room} room
|
||||
* @param {string} eventType
|
||||
* @param {object} plaintext event content
|
||||
* @param {object} content plaintext event content
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||
*/
|
||||
@@ -89,31 +89,31 @@ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
// TODO: there is a race condition here! What if a new user turns up
|
||||
// just as you are sending a secret message?
|
||||
|
||||
var users = utils.map(room.getJoinedMembers(), function(u) {
|
||||
const users = utils.map(room.getJoinedMembers(), function(u) {
|
||||
return u.userId;
|
||||
});
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
return this._ensureSession(users).then(function() {
|
||||
var payloadFields = {
|
||||
const payloadFields = {
|
||||
room_id: room.roomId,
|
||||
type: eventType,
|
||||
content: content,
|
||||
};
|
||||
|
||||
var encryptedContent = {
|
||||
const encryptedContent = {
|
||||
algorithm: olmlib.OLM_ALGORITHM,
|
||||
sender_key: self._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: {},
|
||||
};
|
||||
|
||||
for (var i = 0; i < users.length; ++i) {
|
||||
var userId = users[i];
|
||||
var devices = self._crypto.getStoredDevicesForUser(userId);
|
||||
for (let i = 0; i < users.length; ++i) {
|
||||
const userId = users[i];
|
||||
const devices = self._crypto.getStoredDevicesForUser(userId);
|
||||
|
||||
for (var j = 0; j < devices.length; ++j) {
|
||||
var deviceInfo = devices[j];
|
||||
var key = deviceInfo.getIdentityKey();
|
||||
for (let j = 0; j < devices.length; ++j) {
|
||||
const deviceInfo = devices[j];
|
||||
const key = deviceInfo.getIdentityKey();
|
||||
if (key == self._olmDevice.deviceCurve25519Key) {
|
||||
// don't bother sending to ourself
|
||||
continue;
|
||||
@@ -126,7 +126,7 @@ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
olmlib.encryptMessageForDevice(
|
||||
encryptedContent.ciphertext,
|
||||
self._userId, self._deviceId, self._olmDevice,
|
||||
userId, deviceInfo, payloadFields
|
||||
userId, deviceInfo, payloadFields,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -151,17 +151,15 @@ utils.inherits(OlmDecryption, base.DecryptionAlgorithm);
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @param {object} event raw event
|
||||
*
|
||||
* @return {module:crypto.DecryptionResult} decryption result
|
||||
* @param {MatrixEvent} event
|
||||
*
|
||||
* @throws {module:crypto/algorithms/base.DecryptionError} if there is a
|
||||
* problem decrypting the event
|
||||
*/
|
||||
OlmDecryption.prototype.decryptEvent = function(event) {
|
||||
var content = event.content;
|
||||
var deviceKey = content.sender_key;
|
||||
var ciphertext = content.ciphertext;
|
||||
const content = event.getWireContent();
|
||||
const deviceKey = content.sender_key;
|
||||
const ciphertext = content.ciphertext;
|
||||
|
||||
if (!ciphertext) {
|
||||
throw new base.DecryptionError("Missing ciphertext");
|
||||
@@ -170,50 +168,39 @@ OlmDecryption.prototype.decryptEvent = function(event) {
|
||||
if (!(this._olmDevice.deviceCurve25519Key in ciphertext)) {
|
||||
throw new base.DecryptionError("Not included in recipients");
|
||||
}
|
||||
var message = ciphertext[this._olmDevice.deviceCurve25519Key];
|
||||
var payloadString;
|
||||
const message = ciphertext[this._olmDevice.deviceCurve25519Key];
|
||||
let payloadString;
|
||||
|
||||
try {
|
||||
payloadString = this._decryptMessage(deviceKey, message);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"Failed to decrypt Olm event (id=" +
|
||||
event.event_id + ") from " + deviceKey +
|
||||
": " + e.message
|
||||
event.getId() + ") from " + deviceKey +
|
||||
": " + e.message,
|
||||
);
|
||||
throw new base.DecryptionError("Bad Encrypted Message");
|
||||
}
|
||||
|
||||
var payload = JSON.parse(payloadString);
|
||||
const payload = JSON.parse(payloadString);
|
||||
|
||||
// check that we were the intended recipient, to avoid unknown-key attack
|
||||
// https://github.com/vector-im/vector-web/issues/2483
|
||||
if (payload.recipient === undefined) {
|
||||
// older versions of riot did not set this field, so we cannot make
|
||||
// this check. TODO: kill this off once our users have updated
|
||||
if (payload.recipient != this._userId) {
|
||||
console.warn(
|
||||
"Olm event (id=" + event.event_id + ") contains no 'recipient' " +
|
||||
"property; cannot prevent unknown-key attack");
|
||||
} else if (payload.recipient != this._userId) {
|
||||
console.warn(
|
||||
"Event " + event.event_id + ": Intended recipient " +
|
||||
payload.recipient + " does not match our id " + this._userId
|
||||
"Event " + event.getId() + ": Intended recipient " +
|
||||
payload.recipient + " does not match our id " + this._userId,
|
||||
);
|
||||
throw new base.DecryptionError(
|
||||
"Message was intented for " + payload.recipient
|
||||
"Message was intented for " + payload.recipient,
|
||||
);
|
||||
}
|
||||
|
||||
if (payload.recipient_keys === undefined) {
|
||||
// ditto
|
||||
console.warn(
|
||||
"Olm event (id=" + event.event_id + ") contains no " +
|
||||
"'recipient_keys' property; cannot prevent unknown-key attack");
|
||||
} else if (payload.recipient_keys.ed25519 !=
|
||||
if (payload.recipient_keys.ed25519 !=
|
||||
this._olmDevice.deviceEd25519Key) {
|
||||
console.warn(
|
||||
"Event " + event.event_id + ": Intended recipient ed25519 key " +
|
||||
payload.recipient_keys.ed25519 + " did not match ours"
|
||||
"Event " + event.getId() + ": Intended recipient ed25519 key " +
|
||||
payload.recipient_keys.ed25519 + " did not match ours",
|
||||
);
|
||||
throw new base.DecryptionError("Message not intended for this device");
|
||||
}
|
||||
@@ -222,38 +209,28 @@ OlmDecryption.prototype.decryptEvent = function(event) {
|
||||
// avoid people masquerading as others.
|
||||
// (this check is also provided via the sender's embedded ed25519 key,
|
||||
// which is checked elsewhere).
|
||||
if (payload.sender === undefined) {
|
||||
// ditto
|
||||
if (payload.sender != event.getSender()) {
|
||||
console.warn(
|
||||
"Olm event (id=" + event.event_id + ") contains no " +
|
||||
"'sender' property; cannot prevent unknown-key attack");
|
||||
} else if (payload.sender != event.sender) {
|
||||
console.warn(
|
||||
"Event " + event.event_id + ": original sender " + payload.sender +
|
||||
" does not match reported sender " + event.sender
|
||||
"Event " + event.getId() + ": original sender " + payload.sender +
|
||||
" does not match reported sender " + event.getSender(),
|
||||
);
|
||||
throw new base.DecryptionError(
|
||||
"Message forwarded from " + payload.sender
|
||||
"Message forwarded from " + payload.sender,
|
||||
);
|
||||
}
|
||||
|
||||
// Olm events intended for a room have a room_id.
|
||||
if (payload.room_id !== event.room_id) {
|
||||
if (payload.room_id !== event.getRoomId()) {
|
||||
console.warn(
|
||||
"Event " + event.event_id + ": original room " + payload.room_id +
|
||||
" does not match reported room " + event.room_id
|
||||
"Event " + event.getId() + ": original room " + payload.room_id +
|
||||
" does not match reported room " + event.room_id,
|
||||
);
|
||||
throw new base.DecryptionError(
|
||||
"Message intended for room " + payload.room_id
|
||||
"Message intended for room " + payload.room_id,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
payload: payload,
|
||||
sessionExists: true,
|
||||
keysProved: {curve25519: deviceKey},
|
||||
keysClaimed: payload.keys || {}
|
||||
};
|
||||
event.setClearData(payload, {curve25519: deviceKey}, payload.keys || {});
|
||||
};
|
||||
|
||||
|
||||
@@ -266,24 +243,24 @@ OlmDecryption.prototype.decryptEvent = function(event) {
|
||||
* @return {string} payload, if decrypted successfully.
|
||||
*/
|
||||
OlmDecryption.prototype._decryptMessage = function(theirDeviceIdentityKey, message) {
|
||||
var sessionIds = this._olmDevice.getSessionIdsForDevice(theirDeviceIdentityKey);
|
||||
const sessionIds = this._olmDevice.getSessionIdsForDevice(theirDeviceIdentityKey);
|
||||
|
||||
// try each session in turn.
|
||||
var decryptionErrors = {};
|
||||
for (var i = 0; i < sessionIds.length; i++) {
|
||||
var sessionId = sessionIds[i];
|
||||
const decryptionErrors = {};
|
||||
for (let i = 0; i < sessionIds.length; i++) {
|
||||
const sessionId = sessionIds[i];
|
||||
try {
|
||||
var payload = this._olmDevice.decryptMessage(
|
||||
theirDeviceIdentityKey, sessionId, message.type, message.body
|
||||
const payload = this._olmDevice.decryptMessage(
|
||||
theirDeviceIdentityKey, sessionId, message.type, message.body,
|
||||
);
|
||||
console.log(
|
||||
"Decrypted Olm message from " + theirDeviceIdentityKey +
|
||||
" with session " + sessionId
|
||||
" with session " + sessionId,
|
||||
);
|
||||
return payload;
|
||||
} catch (e) {
|
||||
var foundSession = this._olmDevice.matchesSession(
|
||||
theirDeviceIdentityKey, sessionId, message.type, message.body
|
||||
const foundSession = this._olmDevice.matchesSession(
|
||||
theirDeviceIdentityKey, sessionId, message.type, message.body,
|
||||
);
|
||||
|
||||
if (foundSession) {
|
||||
@@ -291,7 +268,7 @@ OlmDecryption.prototype._decryptMessage = function(theirDeviceIdentityKey, messa
|
||||
// session, so it should have worked.
|
||||
throw new Error(
|
||||
"Error decrypting prekey message with existing session id " +
|
||||
sessionId + ": " + e.message
|
||||
sessionId + ": " + e.message,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -311,29 +288,29 @@ OlmDecryption.prototype._decryptMessage = function(theirDeviceIdentityKey, messa
|
||||
|
||||
throw new Error(
|
||||
"Error decrypting non-prekey message with existing sessions: " +
|
||||
JSON.stringify(decryptionErrors)
|
||||
JSON.stringify(decryptionErrors),
|
||||
);
|
||||
}
|
||||
|
||||
// prekey message which doesn't match any existing sessions: make a new
|
||||
// session.
|
||||
|
||||
var res;
|
||||
let res;
|
||||
try {
|
||||
res = this._olmDevice.createInboundSession(
|
||||
theirDeviceIdentityKey, message.type, message.body
|
||||
theirDeviceIdentityKey, message.type, message.body,
|
||||
);
|
||||
} catch (e) {
|
||||
decryptionErrors["(new)"] = e.message;
|
||||
throw new Error(
|
||||
"Error decrypting prekey message: " +
|
||||
JSON.stringify(decryptionErrors)
|
||||
JSON.stringify(decryptionErrors),
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
"created new inbound Olm session ID " +
|
||||
res.session_id + " with " + theirDeviceIdentityKey
|
||||
res.session_id + " with " + theirDeviceIdentityKey,
|
||||
);
|
||||
return res.payload;
|
||||
};
|
||||
@@ -34,7 +34,11 @@ limitations under the License.
|
||||
* <key type>:<id> -> <base64-encoded key>>
|
||||
*
|
||||
* @property {module:crypto/deviceinfo.DeviceVerification} verified
|
||||
* whether the device has been verified by the user
|
||||
* whether the device has been verified/blocked by the user
|
||||
*
|
||||
* @property {boolean} known
|
||||
* whether the user knows of this device's existence (useful when warning
|
||||
* the user that a user has added new devices)
|
||||
*
|
||||
* @property {Object} unsigned additional data from the homeserver
|
||||
*
|
||||
@@ -50,6 +54,7 @@ function DeviceInfo(deviceId) {
|
||||
this.algorithms = [];
|
||||
this.keys = {};
|
||||
this.verified = DeviceVerification.UNVERIFIED;
|
||||
this.known = false;
|
||||
this.unsigned = {};
|
||||
}
|
||||
|
||||
@@ -62,8 +67,8 @@ function DeviceInfo(deviceId) {
|
||||
* @return {module:crypto~DeviceInfo} new DeviceInfo
|
||||
*/
|
||||
DeviceInfo.fromStorage = function(obj, deviceId) {
|
||||
var res = new DeviceInfo(deviceId);
|
||||
for (var prop in obj) {
|
||||
const res = new DeviceInfo(deviceId);
|
||||
for (const prop in obj) {
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
res[prop] = obj[prop];
|
||||
}
|
||||
@@ -81,6 +86,7 @@ DeviceInfo.prototype.toStorage = function() {
|
||||
algorithms: this.algorithms,
|
||||
keys: this.keys,
|
||||
verified: this.verified,
|
||||
known: this.known,
|
||||
unsigned: this.unsigned,
|
||||
};
|
||||
};
|
||||
@@ -130,6 +136,24 @@ DeviceInfo.prototype.isVerified = function() {
|
||||
return this.verified == DeviceVerification.VERIFIED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if this device is unverified
|
||||
*
|
||||
* @return {Boolean} true if unverified
|
||||
*/
|
||||
DeviceInfo.prototype.isUnverified = function() {
|
||||
return this.verified == DeviceVerification.UNVERIFIED;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the user knows about this device's existence
|
||||
*
|
||||
* @return {Boolean} true if known
|
||||
*/
|
||||
DeviceInfo.prototype.isKnown = function() {
|
||||
return this.known == true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum
|
||||
*/
|
||||
@@ -139,7 +163,7 @@ DeviceInfo.DeviceVerification = {
|
||||
BLOCKED: -1,
|
||||
};
|
||||
|
||||
var DeviceVerification = DeviceInfo.DeviceVerification;
|
||||
const DeviceVerification = DeviceInfo.DeviceVerification;
|
||||
|
||||
/** */
|
||||
module.exports = DeviceInfo;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module olmlib
|
||||
*
|
||||
* Utilities common to olm encryption algorithms
|
||||
*/
|
||||
|
||||
const q = require('q');
|
||||
const anotherjson = require('another-json');
|
||||
|
||||
const utils = require("../utils");
|
||||
|
||||
/**
|
||||
* matrix algorithm tag for olm
|
||||
*/
|
||||
module.exports.OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
||||
|
||||
/**
|
||||
* matrix algorithm tag for megolm
|
||||
*/
|
||||
module.exports.MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt an event payload for an Olm device
|
||||
*
|
||||
* @param {Object<string, string>} resultsObject The `ciphertext` property
|
||||
* of the m.room.encrypted event to which to add our result
|
||||
*
|
||||
* @param {string} ourUserId
|
||||
* @param {string} ourDeviceId
|
||||
* @param {module:crypto/OlmDevice} olmDevice olm.js wrapper
|
||||
* @param {string} recipientUserId
|
||||
* @param {module:crypto/deviceinfo} recipientDevice
|
||||
* @param {object} payloadFields fields to include in the encrypted payload
|
||||
*/
|
||||
module.exports.encryptMessageForDevice = function(
|
||||
resultsObject,
|
||||
ourUserId, ourDeviceId, olmDevice, recipientUserId, recipientDevice,
|
||||
payloadFields,
|
||||
) {
|
||||
const deviceKey = recipientDevice.getIdentityKey();
|
||||
const sessionId = olmDevice.getSessionIdForDevice(deviceKey);
|
||||
if (sessionId === null) {
|
||||
// If we don't have a session for a device then
|
||||
// we can't encrypt a message for it.
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
"Using sessionid " + sessionId + " for device " +
|
||||
recipientUserId + ":" + recipientDevice.deviceId,
|
||||
);
|
||||
|
||||
const payload = {
|
||||
sender: ourUserId,
|
||||
sender_device: ourDeviceId,
|
||||
|
||||
// Include the Ed25519 key so that the recipient knows what
|
||||
// device this message came from.
|
||||
// We don't need to include the curve25519 key since the
|
||||
// recipient will already know this from the olm headers.
|
||||
// When combined with the device keys retrieved from the
|
||||
// homeserver signed by the ed25519 key this proves that
|
||||
// the curve25519 key and the ed25519 key are owned by
|
||||
// the same device.
|
||||
keys: {
|
||||
"ed25519": olmDevice.deviceEd25519Key,
|
||||
},
|
||||
|
||||
// include the recipient device details in the payload,
|
||||
// to avoid unknown key attacks, per
|
||||
// https://github.com/vector-im/vector-web/issues/2483
|
||||
recipient: recipientUserId,
|
||||
recipient_keys: {
|
||||
"ed25519": recipientDevice.getFingerprint(),
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: technically, a bunch of that stuff only needs to be included for
|
||||
// pre-key messages: after that, both sides know exactly which devices are
|
||||
// involved in the session. If we're looking to reduce data transfer in the
|
||||
// future, we could elide them for subsequent messages.
|
||||
|
||||
utils.extend(payload, payloadFields);
|
||||
|
||||
resultsObject[deviceKey] = olmDevice.encryptMessage(
|
||||
deviceKey, sessionId, JSON.stringify(payload),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to make sure we have established olm sessions for the given devices.
|
||||
*
|
||||
* @param {module:crypto/OlmDevice} olmDevice
|
||||
*
|
||||
* @param {module:base-apis~MatrixBaseApis} baseApis
|
||||
*
|
||||
* @param {object<string, module:crypto/deviceinfo[]>} devicesByUser
|
||||
* map from userid to list of devices
|
||||
*
|
||||
* @return {module:client.Promise} resolves once the sessions are complete, to
|
||||
* an Object mapping from userId to deviceId to
|
||||
* {@link module:crypto~OlmSessionResult}
|
||||
*/
|
||||
module.exports.ensureOlmSessionsForDevices = function(
|
||||
olmDevice, baseApis, devicesByUser,
|
||||
) {
|
||||
const devicesWithoutSession = [
|
||||
// [userId, deviceId], ...
|
||||
];
|
||||
const result = {};
|
||||
|
||||
for (const userId in devicesByUser) {
|
||||
if (!devicesByUser.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
result[userId] = {};
|
||||
const devices = devicesByUser[userId];
|
||||
for (let j = 0; j < devices.length; j++) {
|
||||
const deviceInfo = devices[j];
|
||||
const deviceId = deviceInfo.deviceId;
|
||||
const key = deviceInfo.getIdentityKey();
|
||||
const sessionId = olmDevice.getSessionIdForDevice(key);
|
||||
if (sessionId === null) {
|
||||
devicesWithoutSession.push([userId, deviceId]);
|
||||
}
|
||||
result[userId][deviceId] = {
|
||||
device: deviceInfo,
|
||||
sessionId: sessionId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (devicesWithoutSession.length === 0) {
|
||||
return q(result);
|
||||
}
|
||||
|
||||
// TODO: this has a race condition - if we try to send another message
|
||||
// while we are claiming a key, we will end up claiming two and setting up
|
||||
// two sessions.
|
||||
//
|
||||
// That should eventually resolve itself, but it's poor form.
|
||||
|
||||
const oneTimeKeyAlgorithm = "signed_curve25519";
|
||||
return baseApis.claimOneTimeKeys(
|
||||
devicesWithoutSession, oneTimeKeyAlgorithm,
|
||||
).then(function(res) {
|
||||
const otk_res = res.one_time_keys || {};
|
||||
for (const userId in devicesByUser) {
|
||||
if (!devicesByUser.hasOwnProperty(userId)) {
|
||||
continue;
|
||||
}
|
||||
const userRes = otk_res[userId] || {};
|
||||
const devices = devicesByUser[userId];
|
||||
for (let j = 0; j < devices.length; j++) {
|
||||
const deviceInfo = devices[j];
|
||||
const deviceId = deviceInfo.deviceId;
|
||||
if (result[userId][deviceId].sessionId) {
|
||||
// we already have a result for this device
|
||||
continue;
|
||||
}
|
||||
|
||||
const deviceRes = userRes[deviceId] || {};
|
||||
let oneTimeKey = null;
|
||||
for (const keyId in deviceRes) {
|
||||
if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) {
|
||||
oneTimeKey = deviceRes[keyId];
|
||||
}
|
||||
}
|
||||
|
||||
if (!oneTimeKey) {
|
||||
console.warn(
|
||||
"No one-time keys (alg=" + oneTimeKeyAlgorithm +
|
||||
") for device " + userId + ":" + deviceId,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const sid = _verifyKeyAndStartSession(
|
||||
olmDevice, oneTimeKey, userId, deviceInfo,
|
||||
);
|
||||
result[userId][deviceId].sessionId = sid;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function _verifyKeyAndStartSession(olmDevice, oneTimeKey, userId, deviceInfo) {
|
||||
const deviceId = deviceInfo.deviceId;
|
||||
try {
|
||||
_verifySignature(
|
||||
olmDevice, oneTimeKey, userId, deviceId,
|
||||
deviceInfo.getFingerprint(),
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(
|
||||
"Unable to verify signature on one-time key for device " +
|
||||
userId + ":" + deviceId + ":", e,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
let sid;
|
||||
try {
|
||||
sid = olmDevice.createOutboundSession(
|
||||
deviceInfo.getIdentityKey(), oneTimeKey.key,
|
||||
);
|
||||
} catch (e) {
|
||||
// possibly a bad key
|
||||
console.error("Error starting session with device " +
|
||||
userId + ":" + deviceId + ": " + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log("Started new sessionid " + sid +
|
||||
" for device " + userId + ":" + deviceId);
|
||||
return sid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify the signature on an object
|
||||
*
|
||||
* @param {module:crypto/OlmDevice} olmDevice olm wrapper to use for verify op
|
||||
*
|
||||
* @param {Object} obj object to check signature on. Note that this will be
|
||||
* stripped of its 'signatures' and 'unsigned' properties.
|
||||
*
|
||||
* @param {string} signingUserId ID of the user whose signature should be checked
|
||||
*
|
||||
* @param {string} signingDeviceId ID of the device whose signature should be checked
|
||||
*
|
||||
* @param {string} signingKey base64-ed ed25519 public key
|
||||
*/
|
||||
const _verifySignature = module.exports.verifySignature = function(
|
||||
olmDevice, obj, signingUserId, signingDeviceId, signingKey,
|
||||
) {
|
||||
const signKeyId = "ed25519:" + signingDeviceId;
|
||||
const signatures = obj.signatures || {};
|
||||
const userSigs = signatures[signingUserId] || {};
|
||||
const signature = userSigs[signKeyId];
|
||||
if (!signature) {
|
||||
throw Error("No signature");
|
||||
}
|
||||
|
||||
// prepare the canonical json: remove unsigned and signatures, and stringify with
|
||||
// anotherjson
|
||||
delete obj.unsigned;
|
||||
delete obj.signatures;
|
||||
const json = anotherjson.stringify(obj);
|
||||
|
||||
olmDevice.verifySignature(
|
||||
signingKey, json, signature,
|
||||
);
|
||||
};
|
||||
@@ -27,10 +27,9 @@ limitations under the License.
|
||||
*/
|
||||
function _matches_wildcard(actual_value, filter_value) {
|
||||
if (filter_value.endsWith("*")) {
|
||||
var type_prefix = filter_value.slice(0, -1);
|
||||
const type_prefix = filter_value.slice(0, -1);
|
||||
return actual_value.substr(0, type_prefix.length) === type_prefix;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return actual_value === filter_value;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +43,7 @@ function _matches_wildcard(actual_value, filter_value) {
|
||||
* 'Filters' are referred to as 'FilterCollections'.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} the definition of this filter JSON, e.g. { 'contains_url': true }
|
||||
* @param {Object} filter_json the definition of this filter JSON, e.g. { 'contains_url': true }
|
||||
*/
|
||||
function FilterComponent(filter_json) {
|
||||
this.filter_json = filter_json;
|
||||
@@ -71,7 +70,7 @@ FilterComponent.prototype.check = function(event) {
|
||||
event.getRoomId(),
|
||||
event.getSender(),
|
||||
event.getType(),
|
||||
event.getContent() ? event.getContent().url !== undefined : false
|
||||
event.getContent() ? event.getContent().url !== undefined : false,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -84,24 +83,29 @@ FilterComponent.prototype.check = function(event) {
|
||||
* @return {bool} true if the event fields match the filter
|
||||
*/
|
||||
FilterComponent.prototype._checkFields =
|
||||
function(room_id, sender, event_type, contains_url)
|
||||
{
|
||||
var literal_keys = {
|
||||
"rooms": function(v) { return room_id === v; },
|
||||
"senders": function(v) { return sender === v; },
|
||||
"types": function(v) { return _matches_wildcard(event_type, v); },
|
||||
function(room_id, sender, event_type, contains_url) {
|
||||
const literal_keys = {
|
||||
"rooms": function(v) {
|
||||
return room_id === v;
|
||||
},
|
||||
"senders": function(v) {
|
||||
return sender === v;
|
||||
},
|
||||
"types": function(v) {
|
||||
return _matches_wildcard(event_type, v);
|
||||
},
|
||||
};
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
Object.keys(literal_keys).forEach(function(name) {
|
||||
var match_func = literal_keys[name];
|
||||
var not_name = "not_" + name;
|
||||
var disallowed_values = self[not_name];
|
||||
const match_func = literal_keys[name];
|
||||
const not_name = "not_" + name;
|
||||
const disallowed_values = self[not_name];
|
||||
if (disallowed_values.map(match_func)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var allowed_values = self[name];
|
||||
const allowed_values = self[name];
|
||||
if (allowed_values) {
|
||||
if (!allowed_values.map(match_func)) {
|
||||
return false;
|
||||
@@ -109,7 +113,7 @@ FilterComponent.prototype._checkFields =
|
||||
}
|
||||
});
|
||||
|
||||
var contains_url_filter = this.filter_json.contains_url;
|
||||
const contains_url_filter = this.filter_json.contains_url;
|
||||
if (contains_url_filter !== undefined) {
|
||||
if (contains_url_filter !== contains_url) {
|
||||
return false;
|
||||
@@ -18,7 +18,7 @@ limitations under the License.
|
||||
* @module filter
|
||||
*/
|
||||
|
||||
var FilterComponent = require("./filter-component");
|
||||
const FilterComponent = require("./filter-component");
|
||||
|
||||
/**
|
||||
* @param {Object} obj
|
||||
@@ -26,9 +26,9 @@ var FilterComponent = require("./filter-component");
|
||||
* @param {*} val
|
||||
*/
|
||||
function setProp(obj, keyNesting, val) {
|
||||
var nestedKeys = keyNesting.split(".");
|
||||
var currentObj = obj;
|
||||
for (var i = 0; i < (nestedKeys.length - 1); i++) {
|
||||
const nestedKeys = keyNesting.split(".");
|
||||
let currentObj = obj;
|
||||
for (let i = 0; i < (nestedKeys.length - 1); i++) {
|
||||
if (!currentObj[nestedKeys[i]]) {
|
||||
currentObj[nestedKeys[i]] = {};
|
||||
}
|
||||
@@ -106,10 +106,10 @@ Filter.prototype.setDefinition = function(definition) {
|
||||
// "event_fields": ["type", "content", "sender"]
|
||||
// }
|
||||
|
||||
var room_filter_json = definition.room;
|
||||
const room_filter_json = definition.room;
|
||||
|
||||
// consider the top level rooms/not_rooms filter
|
||||
var room_filter_fields = {};
|
||||
const room_filter_fields = {};
|
||||
if (room_filter_json) {
|
||||
if (room_filter_json.rooms) {
|
||||
room_filter_fields.rooms = room_filter_json.rooms;
|
||||
@@ -123,7 +123,7 @@ Filter.prototype.setDefinition = function(definition) {
|
||||
|
||||
this._room_filter = new FilterComponent(room_filter_fields);
|
||||
this._room_timeline_filter = new FilterComponent(
|
||||
room_filter_json ? (room_filter_json.timeline || {}) : {}
|
||||
room_filter_json ? (room_filter_json.timeline || {}) : {},
|
||||
);
|
||||
|
||||
// don't bother porting this from synapse yet:
|
||||
@@ -183,7 +183,7 @@ Filter.prototype.setIncludeLeaveRooms = function(includeLeave) {
|
||||
* @return {Filter}
|
||||
*/
|
||||
Filter.fromJson = function(userId, filterId, jsonObj) {
|
||||
var filter = new Filter(userId, filterId);
|
||||
const filter = new Filter(userId, filterId);
|
||||
filter.setDefinition(jsonObj);
|
||||
return filter;
|
||||
};
|
||||
@@ -18,13 +18,13 @@ limitations under the License.
|
||||
* This is an internal module. See {@link MatrixHttpApi} for the public class.
|
||||
* @module http-api
|
||||
*/
|
||||
var q = require("q");
|
||||
var utils = require("./utils");
|
||||
const q = require("q");
|
||||
const utils = require("./utils");
|
||||
|
||||
// we use our own implementation of setTimeout, so that if we get suspended in
|
||||
// the middle of a /sync, we cancel the sync as soon as we awake, rather than
|
||||
// waiting for the delay to elapse.
|
||||
var callbacks = require("./realtime-callbacks");
|
||||
const callbacks = require("./realtime-callbacks");
|
||||
|
||||
/*
|
||||
TODO:
|
||||
@@ -70,8 +70,10 @@ module.exports.PREFIX_MEDIA_R0 = "/_matrix/media/r0";
|
||||
*
|
||||
* @param {string} opts.accessToken The access_token to send with requests. Can be
|
||||
* null to not send an access token.
|
||||
* @param {Object} opts.extraParams Optional. Extra query parameters to send on
|
||||
* @param {Object=} opts.extraParams Optional. Extra query parameters to send on
|
||||
* requests.
|
||||
* @param {Number=} opts.localTimeoutMs The default maximum amount of time to wait
|
||||
* before timing out the request. If not specified, there is no timeout.
|
||||
*/
|
||||
module.exports.MatrixHttpApi = function MatrixHttpApi(event_emitter, opts) {
|
||||
utils.checkObjectHasKeys(opts, ["baseUrl", "request", "prefix"]);
|
||||
@@ -89,13 +91,13 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
* path and query parameters respectively.
|
||||
*/
|
||||
getContentUri: function() {
|
||||
var params = {
|
||||
access_token: this.opts.accessToken
|
||||
const params = {
|
||||
access_token: this.opts.accessToken,
|
||||
};
|
||||
return {
|
||||
base: this.opts.baseUrl,
|
||||
path: "/_matrix/media/v1/upload",
|
||||
params: params
|
||||
params: params,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -104,7 +106,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
*
|
||||
* @param {object} file The object to upload. On a browser, something that
|
||||
* can be sent to XMLHttpRequest.send (typically a File). Under node.js,
|
||||
* a a Buffer, String or ReadStream.
|
||||
* a Buffer, String or ReadStream.
|
||||
*
|
||||
* @param {object} opts options object
|
||||
*
|
||||
@@ -143,16 +145,16 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
|
||||
// if the file doesn't have a mime type, use a default since
|
||||
// the HS errors if we don't supply one.
|
||||
var contentType = opts.type || file.type || 'application/octet-stream';
|
||||
var fileName = opts.name || file.name;
|
||||
const contentType = opts.type || file.type || 'application/octet-stream';
|
||||
const fileName = opts.name || file.name;
|
||||
|
||||
// we used to recommend setting file.stream to the thing to upload on
|
||||
// nodejs.
|
||||
var body = file.stream ? file.stream : file;
|
||||
const body = file.stream ? file.stream : file;
|
||||
|
||||
// backwards-compatibility hacks where we used to do different things
|
||||
// between browser and node.
|
||||
var rawResponse = opts.rawResponse;
|
||||
let rawResponse = opts.rawResponse;
|
||||
if (rawResponse === undefined) {
|
||||
if (global.XMLHttpRequest) {
|
||||
rawResponse = false;
|
||||
@@ -161,20 +163,20 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
"Returning the raw JSON from uploadContent(). Future " +
|
||||
"versions of the js-sdk will change this default, to " +
|
||||
"return the parsed object. Set opts.rawResponse=false " +
|
||||
"to change this behaviour now."
|
||||
"to change this behaviour now.",
|
||||
);
|
||||
rawResponse = true;
|
||||
}
|
||||
}
|
||||
|
||||
var onlyContentUri = opts.onlyContentUri;
|
||||
let onlyContentUri = opts.onlyContentUri;
|
||||
if (!rawResponse && onlyContentUri === undefined) {
|
||||
if (global.XMLHttpRequest) {
|
||||
console.warn(
|
||||
"Returning only the content-uri from uploadContent(). " +
|
||||
"Future versions of the js-sdk will change this " +
|
||||
"default, to return the whole response object. Set " +
|
||||
"opts.onlyContentUri=false to change this behaviour now."
|
||||
"opts.onlyContentUri=false to change this behaviour now.",
|
||||
);
|
||||
onlyContentUri = true;
|
||||
} else {
|
||||
@@ -190,17 +192,17 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
// (browser-request doesn't support progress either, which is also kind
|
||||
// of important here)
|
||||
|
||||
var upload = { loaded: 0, total: 0 };
|
||||
var promise;
|
||||
const upload = { loaded: 0, total: 0 };
|
||||
let promise;
|
||||
|
||||
// XMLHttpRequest doesn't parse JSON for us. request normally does, but
|
||||
// we're setting opts.json=false so that it doesn't JSON-encode the
|
||||
// request, which also means it doesn't JSON-decode the response. Either
|
||||
// way, we have to JSON-parse the response ourselves.
|
||||
var bodyParser = null;
|
||||
let bodyParser = null;
|
||||
if (!rawResponse) {
|
||||
bodyParser = function(rawBody) {
|
||||
var body = JSON.parse(rawBody);
|
||||
let body = JSON.parse(rawBody);
|
||||
if (onlyContentUri) {
|
||||
body = body.content_uri;
|
||||
if (body === undefined) {
|
||||
@@ -212,12 +214,12 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
}
|
||||
|
||||
if (global.XMLHttpRequest) {
|
||||
var defer = q.defer();
|
||||
var xhr = new global.XMLHttpRequest();
|
||||
const defer = q.defer();
|
||||
const xhr = new global.XMLHttpRequest();
|
||||
upload.xhr = xhr;
|
||||
var cb = requestCallback(defer, opts.callback, this.opts.onlyData);
|
||||
const cb = requestCallback(defer, opts.callback, this.opts.onlyData);
|
||||
|
||||
var timeout_fn = function() {
|
||||
const timeout_fn = function() {
|
||||
xhr.abort();
|
||||
cb(new Error('Timeout'));
|
||||
};
|
||||
@@ -255,7 +257,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
xhr.timeout_timer = callbacks.setTimeout(timeout_fn, 30000);
|
||||
defer.notify(ev);
|
||||
});
|
||||
var url = this.opts.baseUrl + "/_matrix/media/v1/upload";
|
||||
let url = this.opts.baseUrl + "/_matrix/media/v1/upload";
|
||||
url += "?access_token=" + encodeURIComponent(this.opts.accessToken);
|
||||
url += "&filename=" + encodeURIComponent(fileName);
|
||||
|
||||
@@ -267,7 +269,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
// dirty hack (as per _request) to allow the upload to be cancelled.
|
||||
promise.abort = xhr.abort.bind(xhr);
|
||||
} else {
|
||||
var queryParams = {
|
||||
const queryParams = {
|
||||
filename: fileName,
|
||||
};
|
||||
|
||||
@@ -277,15 +279,15 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
headers: {"Content-Type": contentType},
|
||||
json: false,
|
||||
bodyParser: bodyParser,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
// remove the upload from the list on completion
|
||||
var promise0 = promise.finally(function() {
|
||||
for (var i = 0; i < self.uploads.length; ++i) {
|
||||
const promise0 = promise.finally(function() {
|
||||
for (let i = 0; i < self.uploads.length; ++i) {
|
||||
if (self.uploads[i] === upload) {
|
||||
self.uploads.splice(i, 1);
|
||||
return;
|
||||
@@ -315,20 +317,20 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
},
|
||||
|
||||
idServerRequest: function(callback, method, path, params, prefix) {
|
||||
var fullUri = this.opts.idBaseUrl + prefix + path;
|
||||
const fullUri = this.opts.idBaseUrl + prefix + path;
|
||||
|
||||
if (callback !== undefined && !utils.isFunction(callback)) {
|
||||
throw Error(
|
||||
"Expected callback to be a function but got " + typeof callback
|
||||
"Expected callback to be a function but got " + typeof callback,
|
||||
);
|
||||
}
|
||||
|
||||
var opts = {
|
||||
const opts = {
|
||||
uri: fullUri,
|
||||
method: method,
|
||||
withCredentials: false,
|
||||
json: false,
|
||||
_matrix_opts: this.opts
|
||||
_matrix_opts: this.opts,
|
||||
};
|
||||
if (method == 'GET') {
|
||||
opts.qs = params;
|
||||
@@ -336,10 +338,10 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
opts.form = params;
|
||||
}
|
||||
|
||||
var defer = q.defer();
|
||||
const defer = q.defer();
|
||||
this.opts.request(
|
||||
opts,
|
||||
requestCallback(defer, callback, this.opts.onlyData)
|
||||
requestCallback(defer, callback, this.opts.onlyData),
|
||||
);
|
||||
// ID server does not always take JSON, so we can't use requests' 'json'
|
||||
// option as we do with the home server, but it does return JSON, so
|
||||
@@ -387,11 +389,11 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
queryParams.access_token = this.opts.accessToken;
|
||||
}
|
||||
|
||||
var request_promise = this.request(
|
||||
callback, method, path, queryParams, data, opts
|
||||
const request_promise = this.request(
|
||||
callback, method, path, queryParams, data, opts,
|
||||
);
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
request_promise.catch(function(err) {
|
||||
if (err.errcode == 'M_UNKNOWN_TOKEN') {
|
||||
self.event_emitter.emit("Session.logged_out");
|
||||
@@ -435,11 +437,11 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
*/
|
||||
request: function(callback, method, path, queryParams, data, opts) {
|
||||
opts = opts || {};
|
||||
var prefix = opts.prefix !== undefined ? opts.prefix : this.opts.prefix;
|
||||
var fullUri = this.opts.baseUrl + prefix + path;
|
||||
const prefix = opts.prefix !== undefined ? opts.prefix : this.opts.prefix;
|
||||
const fullUri = this.opts.baseUrl + prefix + path;
|
||||
|
||||
return this.requestOtherUrl(
|
||||
callback, method, fullUri, queryParams, data, opts
|
||||
callback, method, fullUri, queryParams, data, opts,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -474,7 +476,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
callback, method, path, queryParams, data, {
|
||||
localTimeoutMs: localTimeoutMs,
|
||||
prefix: prefix,
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -509,7 +511,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
callback, method, path, queryParams, data, {
|
||||
localTimeoutMs: localTimeoutMs,
|
||||
prefix: prefix,
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -549,12 +551,12 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
} else if (isFinite(opts)) {
|
||||
// opts used to be localTimeoutMs
|
||||
opts = {
|
||||
localTimeoutMs: opts
|
||||
localTimeoutMs: opts,
|
||||
};
|
||||
}
|
||||
|
||||
return this._request(
|
||||
callback, method, uri, queryParams, data, opts
|
||||
callback, method, uri, queryParams, data, opts,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -570,7 +572,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
* @return {string} URL
|
||||
*/
|
||||
getUrl: function(path, queryParams, prefix) {
|
||||
var queryString = "";
|
||||
let queryString = "";
|
||||
if (queryParams) {
|
||||
queryString = "?" + utils.encodeParams(queryParams);
|
||||
}
|
||||
@@ -594,7 +596,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
* @param {object=} opts.headers extra request headers
|
||||
*
|
||||
* @param {number=} opts.localTimeoutMs client-side timeout for the
|
||||
* request. No timeout if undefined.
|
||||
* request. Default timeout if falsy.
|
||||
*
|
||||
* @param {function=} opts.bodyParser function to parse the body of the
|
||||
* response before passing it to the promise and callback.
|
||||
@@ -606,27 +608,29 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
_request: function(callback, method, uri, queryParams, data, opts) {
|
||||
if (callback !== undefined && !utils.isFunction(callback)) {
|
||||
throw Error(
|
||||
"Expected callback to be a function but got " + typeof callback
|
||||
"Expected callback to be a function but got " + typeof callback,
|
||||
);
|
||||
}
|
||||
opts = opts || {};
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (this.opts.extraParams) {
|
||||
for (var key in this.opts.extraParams) {
|
||||
if (!this.opts.extraParams.hasOwnProperty(key)) { continue; }
|
||||
for (const key in this.opts.extraParams) {
|
||||
if (!this.opts.extraParams.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
queryParams[key] = this.opts.extraParams[key];
|
||||
}
|
||||
}
|
||||
|
||||
var json = opts.json === undefined ? true : opts.json;
|
||||
const json = opts.json === undefined ? true : opts.json;
|
||||
|
||||
var defer = q.defer();
|
||||
const defer = q.defer();
|
||||
|
||||
var timeoutId;
|
||||
var timedOut = false;
|
||||
var req;
|
||||
var localTimeoutMs = opts.localTimeoutMs;
|
||||
let timeoutId;
|
||||
let timedOut = false;
|
||||
let req;
|
||||
const localTimeoutMs = opts.localTimeoutMs || this.opts.localTimeoutMs;
|
||||
if (localTimeoutMs) {
|
||||
timeoutId = callbacks.setTimeout(function() {
|
||||
timedOut = true;
|
||||
@@ -636,12 +640,12 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
defer.reject(new module.exports.MatrixError({
|
||||
error: "Locally timed out waiting for a response",
|
||||
errcode: "ORG.MATRIX.JSSDK_TIMEOUT",
|
||||
timeout: localTimeoutMs
|
||||
timeout: localTimeoutMs,
|
||||
}));
|
||||
}, localTimeoutMs);
|
||||
}
|
||||
|
||||
var reqPromise = defer.promise;
|
||||
const reqPromise = defer.promise;
|
||||
|
||||
try {
|
||||
req = this.opts.request(
|
||||
@@ -654,7 +658,7 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
json: json,
|
||||
timeout: localTimeoutMs,
|
||||
headers: opts.headers || {},
|
||||
_matrix_opts: this.opts
|
||||
_matrix_opts: this.opts,
|
||||
},
|
||||
function(err, response, body) {
|
||||
if (localTimeoutMs) {
|
||||
@@ -666,29 +670,28 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
|
||||
// if json is falsy, we won't parse any error response, so need
|
||||
// to do so before turning it into a MatrixError
|
||||
var parseErrorJson = !json;
|
||||
var handlerFn = requestCallback(
|
||||
const parseErrorJson = !json;
|
||||
const handlerFn = requestCallback(
|
||||
defer, callback, self.opts.onlyData,
|
||||
parseErrorJson,
|
||||
opts.bodyParser
|
||||
opts.bodyParser,
|
||||
);
|
||||
handlerFn(err, response, body);
|
||||
}
|
||||
},
|
||||
);
|
||||
if (req && req.abort) {
|
||||
// FIXME: This is EVIL, but I can't think of a better way to expose
|
||||
// abort() operations on underlying HTTP requests :(
|
||||
reqPromise.abort = req.abort.bind(req);
|
||||
}
|
||||
}
|
||||
catch (ex) {
|
||||
} catch (ex) {
|
||||
defer.reject(ex);
|
||||
if (callback) {
|
||||
callback(ex);
|
||||
}
|
||||
}
|
||||
return reqPromise;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -702,9 +705,9 @@ module.exports.MatrixHttpApi.prototype = {
|
||||
* If parseErrorJson is true, we will JSON.parse the body if we get a 4xx error.
|
||||
*
|
||||
*/
|
||||
var requestCallback = function(
|
||||
const requestCallback = function(
|
||||
defer, userDefinedCallback, onlyData,
|
||||
parseErrorJson, bodyParser
|
||||
parseErrorJson, bodyParser,
|
||||
) {
|
||||
userDefinedCallback = userDefinedCallback || function() {};
|
||||
|
||||
@@ -731,12 +734,11 @@ var requestCallback = function(
|
||||
if (err) {
|
||||
defer.reject(err);
|
||||
userDefinedCallback(err);
|
||||
}
|
||||
else {
|
||||
var res = {
|
||||
} else {
|
||||
const res = {
|
||||
code: response.statusCode,
|
||||
headers: response.headers,
|
||||
data: body
|
||||
data: body,
|
||||
};
|
||||
defer.resolve(onlyData ? body : res);
|
||||
userDefinedCallback(null, onlyData ? body : res);
|
||||
@@ -16,9 +16,9 @@ limitations under the License.
|
||||
"use strict";
|
||||
|
||||
/** @module interactive-auth */
|
||||
var q = require("q");
|
||||
const q = require("q");
|
||||
|
||||
var utils = require("./utils");
|
||||
const utils = require("./utils");
|
||||
|
||||
/**
|
||||
* Abstracts the logic used to drive the interactive auth process.
|
||||
@@ -89,11 +89,11 @@ InteractiveAuth.prototype = {
|
||||
/**
|
||||
* get the server params for a given stage
|
||||
*
|
||||
* @param {string} login type for the stage
|
||||
* @return {object?} any parameters from the server for this stage
|
||||
* @param {string} loginType login type for the stage
|
||||
* @return {object?} any parameters from the server for this stage
|
||||
*/
|
||||
getStageParams: function(loginType) {
|
||||
var params = {};
|
||||
let params = {};
|
||||
if (this._data && this._data.params) {
|
||||
params = this._data.params;
|
||||
}
|
||||
@@ -115,7 +115,7 @@ InteractiveAuth.prototype = {
|
||||
}
|
||||
|
||||
// use the sessionid from the last request.
|
||||
var auth = {
|
||||
const auth = {
|
||||
session: this._data.session,
|
||||
};
|
||||
utils.extend(auth, authData);
|
||||
@@ -131,12 +131,12 @@ InteractiveAuth.prototype = {
|
||||
* @param {object?} auth new auth dict, including session id
|
||||
*/
|
||||
_doRequest: function(auth) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
// hackery to make sure that synchronous exceptions end up in the catch
|
||||
// handler (without the additional event loop entailed by q.fcall or an
|
||||
// extra q().then)
|
||||
var prom;
|
||||
let prom;
|
||||
try {
|
||||
prom = this._requestCallback(auth);
|
||||
} catch (e) {
|
||||
@@ -154,7 +154,7 @@ InteractiveAuth.prototype = {
|
||||
}
|
||||
self._data = error.data;
|
||||
self._startNextAuthStage();
|
||||
}
|
||||
},
|
||||
).catch(this._completionDeferred.reject).done();
|
||||
},
|
||||
|
||||
@@ -164,12 +164,12 @@ InteractiveAuth.prototype = {
|
||||
* @private
|
||||
*/
|
||||
_startNextAuthStage: function() {
|
||||
var nextStage = this._chooseStage();
|
||||
const nextStage = this._chooseStage();
|
||||
if (!nextStage) {
|
||||
throw new Error("No incomplete flows from the server");
|
||||
}
|
||||
|
||||
var stageError = null;
|
||||
let stageError = null;
|
||||
if (this._data.errcode || this._data.error) {
|
||||
stageError = {
|
||||
errcode: this._data.errcode || "",
|
||||
@@ -186,9 +186,9 @@ InteractiveAuth.prototype = {
|
||||
* @return {string?} login type
|
||||
*/
|
||||
_chooseStage: function() {
|
||||
var flow = this._chooseFlow();
|
||||
const flow = this._chooseFlow();
|
||||
console.log("Active flow => %s", JSON.stringify(flow));
|
||||
var nextStage = this._firstUncompletedStage(flow);
|
||||
const nextStage = this._firstUncompletedStage(flow);
|
||||
console.log("Next stage: %s", nextStage);
|
||||
return nextStage;
|
||||
},
|
||||
@@ -200,7 +200,7 @@ InteractiveAuth.prototype = {
|
||||
* @return {object} flow
|
||||
*/
|
||||
_chooseFlow: function() {
|
||||
var flows = this._data.flows || [];
|
||||
const flows = this._data.flows || [];
|
||||
// always use the first flow for now
|
||||
return flows[0];
|
||||
},
|
||||
@@ -213,9 +213,9 @@ InteractiveAuth.prototype = {
|
||||
* @return {string} login type
|
||||
*/
|
||||
_firstUncompletedStage: function(flow) {
|
||||
var completed = (this._data || {}).completed || [];
|
||||
for (var i = 0; i < flow.stages.length; ++i) {
|
||||
var stageType = flow.stages[i];
|
||||
const completed = (this._data || {}).completed || [];
|
||||
for (let i = 0; i < flow.stages.length; ++i) {
|
||||
const stageType = flow.stages[i];
|
||||
if (completed.indexOf(stageType) === -1) {
|
||||
return stageType;
|
||||
}
|
||||
@@ -21,9 +21,6 @@ module.exports.MatrixEvent = require("./models/event").MatrixEvent;
|
||||
module.exports.EventStatus = require("./models/event").EventStatus;
|
||||
/** The {@link module:store/memory.MatrixInMemoryStore|MatrixInMemoryStore} class. */
|
||||
module.exports.MatrixInMemoryStore = require("./store/memory").MatrixInMemoryStore;
|
||||
/** The {@link module:store/webstorage~WebStorageStore|WebStorageStore} class.
|
||||
* <strong>Work in progress; unstable.</strong> */
|
||||
module.exports.WebStorageStore = require("./store/webstorage");
|
||||
/** The {@link module:http-api.MatrixHttpApi|MatrixHttpApi} class. */
|
||||
module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi;
|
||||
/** The {@link module:http-api.MatrixError|MatrixError} class. */
|
||||
@@ -71,7 +68,7 @@ module.exports.createNewMatrixCall = require("./webrtc/call").createNewMatrixCal
|
||||
|
||||
// expose the underlying request object so different environments can use
|
||||
// different request libs (e.g. request or browser-request)
|
||||
var request;
|
||||
let request;
|
||||
/**
|
||||
* The function used to perform HTTP requests. Only use this if you want to
|
||||
* use a different HTTP library, e.g. Angular's <code>$http</code>. This should
|
||||
@@ -82,6 +79,27 @@ module.exports.request = function(r) {
|
||||
request = r;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the currently-set request function.
|
||||
* @return {requestFunction} The current request function.
|
||||
*/
|
||||
module.exports.getRequest = function() {
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply wrapping code around the request function. The wrapper function is
|
||||
* installed as the new request handler, and when invoked it is passed the
|
||||
* previous value, along with the options and callback arguments.
|
||||
* @param {requestWrapperFunction} wrapper The wrapping function.
|
||||
*/
|
||||
module.exports.wrapRequest = function(wrapper) {
|
||||
const origRequest = request;
|
||||
request = function(options, callback) {
|
||||
return wrapper(origRequest, options, callback);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a Matrix Client. Similar to {@link module:client~MatrixClient}
|
||||
* except that the 'request', 'store' and 'scheduler' dependencies are satisfied.
|
||||
@@ -101,12 +119,12 @@ module.exports.request = function(r) {
|
||||
module.exports.createClient = function(opts) {
|
||||
if (typeof opts === "string") {
|
||||
opts = {
|
||||
"baseUrl": opts
|
||||
"baseUrl": opts,
|
||||
};
|
||||
}
|
||||
opts.request = opts.request || request;
|
||||
opts.store = opts.store || new module.exports.MatrixInMemoryStore({
|
||||
localStorage: global.localStorage
|
||||
localStorage: global.localStorage,
|
||||
});
|
||||
opts.scheduler = opts.scheduler || new module.exports.MatrixScheduler();
|
||||
return new module.exports.MatrixClient(opts);
|
||||
@@ -129,6 +147,16 @@ module.exports.createClient = function(opts) {
|
||||
* @param {requestCallback} callback The request callback.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A wrapper for the request function interface.
|
||||
* @callback requestWrapperFunction
|
||||
* @param {requestFunction} origRequest The underlying request function being
|
||||
* wrapped
|
||||
* @param {Object} opts The options for this HTTP request, given in the same
|
||||
* form as {@link requestFunction}.
|
||||
* @param {requestCallback} callback The request callback.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The request callback interface for performing HTTP requests. This matches the
|
||||
* API for the {@link https://github.com/request/request#requestoptions-callback|
|
||||
@@ -17,18 +17,19 @@ limitations under the License.
|
||||
/**
|
||||
* @module models/event-timeline-set
|
||||
*/
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
var utils = require("../utils");
|
||||
var EventTimeline = require("./event-timeline");
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
const utils = require("../utils");
|
||||
const EventTimeline = require("./event-timeline");
|
||||
|
||||
// var DEBUG = false;
|
||||
var DEBUG = true;
|
||||
const DEBUG = true;
|
||||
|
||||
let debuglog;
|
||||
if (DEBUG) {
|
||||
// using bind means that we get to keep useful line numbers in the console
|
||||
var debuglog = console.log.bind(console);
|
||||
debuglog = console.log.bind(console);
|
||||
} else {
|
||||
var debuglog = function() {};
|
||||
debuglog = function() {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,8 +106,7 @@ EventTimelineSet.prototype.getPendingEvents = function() {
|
||||
|
||||
if (this._filter) {
|
||||
return this._filter.filterRoomTimeline(this.room.getPendingEvents());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return this.room.getPendingEvents();
|
||||
}
|
||||
};
|
||||
@@ -136,7 +136,7 @@ EventTimelineSet.prototype.eventIdToTimeline = function(eventId) {
|
||||
* @param {String} newEventId event ID of the replacement event
|
||||
*/
|
||||
EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) {
|
||||
var existingTimeline = this._eventIdToTimeline[oldEventId];
|
||||
const existingTimeline = this._eventIdToTimeline[oldEventId];
|
||||
if (existingTimeline) {
|
||||
delete this._eventIdToTimeline[oldEventId];
|
||||
this._eventIdToTimeline[newEventId] = existingTimeline;
|
||||
@@ -154,7 +154,7 @@ EventTimelineSet.prototype.replaceEventId = function(oldEventId, newEventId) {
|
||||
* @fires module:client~MatrixClient#event:"Room.timelineReset"
|
||||
*/
|
||||
EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flush) {
|
||||
var newTimeline;
|
||||
let newTimeline;
|
||||
|
||||
if (!this._timelineSupport || flush) {
|
||||
// if timeline support is disabled, forget about the old timelines
|
||||
@@ -166,12 +166,16 @@ EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flu
|
||||
}
|
||||
|
||||
// initialise the state in the new timeline from our last known state
|
||||
var evMap = this._liveTimeline.getState(EventTimeline.FORWARDS).events;
|
||||
var events = [];
|
||||
for (var evtype in evMap) {
|
||||
if (!evMap.hasOwnProperty(evtype)) { continue; }
|
||||
for (var stateKey in evMap[evtype]) {
|
||||
if (!evMap[evtype].hasOwnProperty(stateKey)) { continue; }
|
||||
const evMap = this._liveTimeline.getState(EventTimeline.FORWARDS).events;
|
||||
const events = [];
|
||||
for (const evtype in evMap) {
|
||||
if (!evMap.hasOwnProperty(evtype)) {
|
||||
continue;
|
||||
}
|
||||
for (const stateKey in evMap[evtype]) {
|
||||
if (!evMap[evtype].hasOwnProperty(stateKey)) {
|
||||
continue;
|
||||
}
|
||||
events.push(evMap[evtype][stateKey]);
|
||||
}
|
||||
}
|
||||
@@ -194,7 +198,7 @@ EventTimelineSet.prototype.resetLiveTimeline = function(backPaginationToken, flu
|
||||
* the given event, or null if unknown
|
||||
*/
|
||||
EventTimelineSet.prototype.getTimelineForEvent = function(eventId) {
|
||||
var res = this._eventIdToTimeline[eventId];
|
||||
const res = this._eventIdToTimeline[eventId];
|
||||
return (res === undefined) ? null : res;
|
||||
};
|
||||
|
||||
@@ -205,12 +209,13 @@ EventTimelineSet.prototype.getTimelineForEvent = function(eventId) {
|
||||
* @return {?module:models/event~MatrixEvent} the given event, or undefined if unknown
|
||||
*/
|
||||
EventTimelineSet.prototype.findEventById = function(eventId) {
|
||||
var tl = this.getTimelineForEvent(eventId);
|
||||
const tl = this.getTimelineForEvent(eventId);
|
||||
if (!tl) {
|
||||
return undefined;
|
||||
}
|
||||
return utils.findElement(tl.getEvents(),
|
||||
function(ev) { return ev.getId() == eventId; });
|
||||
return utils.findElement(tl.getEvents(), function(ev) {
|
||||
return ev.getId() == eventId;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -225,7 +230,7 @@ EventTimelineSet.prototype.addTimeline = function() {
|
||||
" it.");
|
||||
}
|
||||
|
||||
var timeline = new EventTimeline(this);
|
||||
const timeline = new EventTimeline(this);
|
||||
this._timelines.push(timeline);
|
||||
return timeline;
|
||||
};
|
||||
@@ -254,14 +259,14 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
|
||||
timeline, paginationToken) {
|
||||
if (!timeline) {
|
||||
throw new Error(
|
||||
"'timeline' not specified for EventTimelineSet.addEventsToTimeline"
|
||||
"'timeline' not specified for EventTimelineSet.addEventsToTimeline",
|
||||
);
|
||||
}
|
||||
|
||||
if (!toStartOfTimeline && timeline == this._liveTimeline) {
|
||||
throw new Error(
|
||||
"EventTimelineSet.addEventsToTimeline cannot be used for adding events to " +
|
||||
"the live timeline - use Room.addLiveEvents instead"
|
||||
"the live timeline - use Room.addLiveEvents instead",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -272,9 +277,9 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
|
||||
}
|
||||
}
|
||||
|
||||
var direction = toStartOfTimeline ? EventTimeline.BACKWARDS :
|
||||
const direction = toStartOfTimeline ? EventTimeline.BACKWARDS :
|
||||
EventTimeline.FORWARDS;
|
||||
var inverseDirection = toStartOfTimeline ? EventTimeline.FORWARDS :
|
||||
const inverseDirection = toStartOfTimeline ? EventTimeline.FORWARDS :
|
||||
EventTimeline.BACKWARDS;
|
||||
|
||||
// Adding events to timelines can be quite complicated. The following
|
||||
@@ -346,13 +351,13 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
|
||||
// paginating with the same token, we might as well use the new pagination
|
||||
// token in the hope that we eventually work our way out of the mess.
|
||||
|
||||
var didUpdate = false;
|
||||
var lastEventWasNew = false;
|
||||
for (var i = 0; i < events.length; i++) {
|
||||
var event = events[i];
|
||||
var eventId = event.getId();
|
||||
let didUpdate = false;
|
||||
let lastEventWasNew = false;
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const event = events[i];
|
||||
const eventId = event.getId();
|
||||
|
||||
var existingTimeline = this._eventIdToTimeline[eventId];
|
||||
const existingTimeline = this._eventIdToTimeline[eventId];
|
||||
|
||||
if (!existingTimeline) {
|
||||
// we don't know about this event yet. Just add it to the timeline.
|
||||
@@ -369,7 +374,7 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
|
||||
continue;
|
||||
}
|
||||
|
||||
var neighbour = timeline.getNeighbouringTimeline(direction);
|
||||
const neighbour = timeline.getNeighbouringTimeline(direction);
|
||||
if (neighbour) {
|
||||
// this timeline already has a neighbour in the relevant direction;
|
||||
// let's assume the timelines are already correctly linked up, and
|
||||
@@ -417,25 +422,25 @@ EventTimelineSet.prototype.addEventsToTimeline = function(events, toStartOfTimel
|
||||
*/
|
||||
EventTimelineSet.prototype.addLiveEvent = function(event, duplicateStrategy) {
|
||||
if (this._filter) {
|
||||
var events = this._filter.filterRoomTimeline([event]);
|
||||
const events = this._filter.filterRoomTimeline([event]);
|
||||
if (!events.length) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var timeline = this._eventIdToTimeline[event.getId()];
|
||||
const timeline = this._eventIdToTimeline[event.getId()];
|
||||
if (timeline) {
|
||||
if (duplicateStrategy === "replace") {
|
||||
debuglog("EventTimelineSet.addLiveEvent: replacing duplicate event " +
|
||||
event.getId());
|
||||
var tlEvents = timeline.getEvents();
|
||||
for (var j = 0; j < tlEvents.length; j++) {
|
||||
const tlEvents = timeline.getEvents();
|
||||
for (let j = 0; j < tlEvents.length; j++) {
|
||||
if (tlEvents[j].getId() === event.getId()) {
|
||||
// still need to set the right metadata on this event
|
||||
EventTimeline.setEventMetadata(
|
||||
event,
|
||||
timeline.getState(EventTimeline.FORWARDS),
|
||||
false
|
||||
false,
|
||||
);
|
||||
|
||||
if (!tlEvents[j].encryptedType) {
|
||||
@@ -470,11 +475,11 @@ EventTimelineSet.prototype.addLiveEvent = function(event, duplicateStrategy) {
|
||||
*/
|
||||
EventTimelineSet.prototype.addEventToTimeline = function(event, timeline,
|
||||
toStartOfTimeline) {
|
||||
var eventId = event.getId();
|
||||
const eventId = event.getId();
|
||||
timeline.addEvent(event, toStartOfTimeline);
|
||||
this._eventIdToTimeline[eventId] = timeline;
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
timeline: timeline,
|
||||
liveEvent: !toStartOfTimeline && timeline == this._liveTimeline,
|
||||
};
|
||||
@@ -495,7 +500,7 @@ EventTimelineSet.prototype.addEventToTimeline = function(event, timeline,
|
||||
EventTimelineSet.prototype.handleRemoteEcho = function(localEvent, oldEventId,
|
||||
newEventId) {
|
||||
// XXX: why don't we infer newEventId from localEvent?
|
||||
var existingTimeline = this._eventIdToTimeline[oldEventId];
|
||||
const existingTimeline = this._eventIdToTimeline[oldEventId];
|
||||
if (existingTimeline) {
|
||||
delete this._eventIdToTimeline[oldEventId];
|
||||
this._eventIdToTimeline[newEventId] = existingTimeline;
|
||||
@@ -504,8 +509,7 @@ EventTimelineSet.prototype.handleRemoteEcho = function(localEvent, oldEventId,
|
||||
if (this._filter.filterRoomTimeline([localEvent]).length) {
|
||||
this.addEventToTimeline(localEvent, this._liveTimeline, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.addEventToTimeline(localEvent, this._liveTimeline, false);
|
||||
}
|
||||
}
|
||||
@@ -520,15 +524,15 @@ EventTimelineSet.prototype.handleRemoteEcho = function(localEvent, oldEventId,
|
||||
* in this room.
|
||||
*/
|
||||
EventTimelineSet.prototype.removeEvent = function(eventId) {
|
||||
var timeline = this._eventIdToTimeline[eventId];
|
||||
const timeline = this._eventIdToTimeline[eventId];
|
||||
if (!timeline) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var removed = timeline.removeEvent(eventId);
|
||||
const removed = timeline.removeEvent(eventId);
|
||||
if (removed) {
|
||||
delete this._eventIdToTimeline[eventId];
|
||||
var data = {
|
||||
const data = {
|
||||
timeline: timeline,
|
||||
};
|
||||
this.emit("Room.timeline", removed, this.room, undefined, true, data);
|
||||
@@ -554,8 +558,8 @@ EventTimelineSet.prototype.compareEventOrdering = function(eventId1, eventId2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var timeline1 = this._eventIdToTimeline[eventId1];
|
||||
var timeline2 = this._eventIdToTimeline[eventId2];
|
||||
const timeline1 = this._eventIdToTimeline[eventId1];
|
||||
const timeline2 = this._eventIdToTimeline[eventId2];
|
||||
|
||||
if (timeline1 === undefined) {
|
||||
return null;
|
||||
@@ -567,11 +571,11 @@ EventTimelineSet.prototype.compareEventOrdering = function(eventId1, eventId2) {
|
||||
if (timeline1 === timeline2) {
|
||||
// both events are in the same timeline - figure out their
|
||||
// relative indices
|
||||
var idx1, idx2;
|
||||
var events = timeline1.getEvents();
|
||||
for (var idx = 0; idx < events.length &&
|
||||
let idx1, idx2;
|
||||
const events = timeline1.getEvents();
|
||||
for (let idx = 0; idx < events.length &&
|
||||
(idx1 === undefined || idx2 === undefined); idx++) {
|
||||
var evId = events[idx].getId();
|
||||
const evId = events[idx].getId();
|
||||
if (evId == eventId1) {
|
||||
idx1 = idx;
|
||||
}
|
||||
@@ -586,7 +590,7 @@ EventTimelineSet.prototype.compareEventOrdering = function(eventId1, eventId2) {
|
||||
// linkedlist to see which comes first.
|
||||
|
||||
// first work forwards from timeline1
|
||||
var tl = timeline1;
|
||||
let tl = timeline1;
|
||||
while (tl) {
|
||||
if (tl === timeline2) {
|
||||
// timeline1 is before timeline2
|
||||
@@ -4,9 +4,9 @@
|
||||
* @module models/event-timeline
|
||||
*/
|
||||
|
||||
var RoomState = require("./room-state");
|
||||
var utils = require("../utils");
|
||||
var MatrixEvent = require("./event").MatrixEvent;
|
||||
const RoomState = require("./room-state");
|
||||
const utils = require("../utils");
|
||||
const MatrixEvent = require("./event").MatrixEvent;
|
||||
|
||||
/**
|
||||
* Construct a new EventTimeline
|
||||
@@ -75,10 +75,15 @@ EventTimeline.prototype.initialiseState = function(stateEvents) {
|
||||
|
||||
// we deep-copy the events here, in case they get changed later - we don't
|
||||
// want changes to the start state leaking through to the end state.
|
||||
var oldStateEvents = utils.map(
|
||||
const oldStateEvents = utils.map(
|
||||
utils.deepCopy(
|
||||
stateEvents.map(function(mxEvent) { return mxEvent.event; })
|
||||
), function(ev) { return new MatrixEvent(ev); });
|
||||
stateEvents.map(function(mxEvent) {
|
||||
return mxEvent.event;
|
||||
}),
|
||||
),
|
||||
function(ev) {
|
||||
return new MatrixEvent(ev);
|
||||
});
|
||||
|
||||
this._startState.setStateEvents(oldStateEvents);
|
||||
this._endState.setStateEvents(stateEvents);
|
||||
@@ -232,13 +237,12 @@ EventTimeline.prototype.setNeighbouringTimeline = function(neighbour, direction)
|
||||
* @param {boolean} atStart true to insert new event at the start
|
||||
*/
|
||||
EventTimeline.prototype.addEvent = function(event, atStart) {
|
||||
var stateContext = atStart ? this._startState : this._endState;
|
||||
const stateContext = atStart ? this._startState : this._endState;
|
||||
|
||||
// only call setEventMetadata on the unfiltered timelineSets
|
||||
var timelineSet = this.getTimelineSet();
|
||||
const timelineSet = this.getTimelineSet();
|
||||
if (timelineSet.room &&
|
||||
timelineSet.room.getUnfilteredTimelineSet() === timelineSet)
|
||||
{
|
||||
timelineSet.room.getUnfilteredTimelineSet() === timelineSet) {
|
||||
EventTimeline.setEventMetadata(event, stateContext, atStart);
|
||||
|
||||
// modify state
|
||||
@@ -260,7 +264,7 @@ EventTimeline.prototype.addEvent = function(event, atStart) {
|
||||
}
|
||||
}
|
||||
|
||||
var insertIndex;
|
||||
let insertIndex;
|
||||
|
||||
if (atStart) {
|
||||
insertIndex = 0;
|
||||
@@ -284,11 +288,11 @@ EventTimeline.prototype.addEvent = function(event, atStart) {
|
||||
EventTimeline.setEventMetadata = function(event, stateContext, toStartOfTimeline) {
|
||||
// set sender and target properties
|
||||
event.sender = stateContext.getSentinelMember(
|
||||
event.getSender()
|
||||
event.getSender(),
|
||||
);
|
||||
if (event.getType() === "m.room.member") {
|
||||
event.target = stateContext.getSentinelMember(
|
||||
event.getStateKey()
|
||||
event.getStateKey(),
|
||||
);
|
||||
}
|
||||
if (event.isState()) {
|
||||
@@ -309,8 +313,8 @@ EventTimeline.setEventMetadata = function(event, stateContext, toStartOfTimeline
|
||||
* @return {?MatrixEvent} removed event, or null if not found
|
||||
*/
|
||||
EventTimeline.prototype.removeEvent = function(eventId) {
|
||||
for (var i = this._events.length - 1; i >= 0; i--) {
|
||||
var ev = this._events[i];
|
||||
for (let i = this._events.length - 1; i >= 0; i--) {
|
||||
const ev = this._events[i];
|
||||
if (ev.getId() == eventId) {
|
||||
this._events.splice(i, 1);
|
||||
if (i < this._baseIndex) {
|
||||
@@ -21,6 +21,10 @@ limitations under the License.
|
||||
* @module models/event
|
||||
*/
|
||||
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
|
||||
const utils = require('../utils.js');
|
||||
|
||||
/**
|
||||
* Enum for event statuses.
|
||||
* @readonly
|
||||
@@ -51,15 +55,6 @@ module.exports.EventStatus = {
|
||||
*
|
||||
* @param {Object} event The raw event to be wrapped in this DAO
|
||||
*
|
||||
* @param {Object=} clearEvent For encrypted events, the plaintext payload for
|
||||
* the event (typically containing <tt>type</tt> and <tt>content</tt> fields).
|
||||
*
|
||||
* @param {Object=} keysProved Keys owned by the sender of this event.
|
||||
* See {@link module:models/event.MatrixEvent#getKeysProved}.
|
||||
*
|
||||
* @param {Object=} keysClaimed Keys the sender of this event claims.
|
||||
* See {@link module:models/event.MatrixEvent#getKeysClaimed}.
|
||||
*
|
||||
* @prop {Object} event The raw (possibly encrypted) event. <b>Do not access
|
||||
* this property</b> directly unless you absolutely have to. Prefer the getter
|
||||
* methods defined on this class. Using the getter methods shields your app
|
||||
@@ -70,27 +65,32 @@ module.exports.EventStatus = {
|
||||
* @prop {RoomMember} target The room member who is the target of this event, e.g.
|
||||
* the invitee, the person being banned, etc.
|
||||
* @prop {EventStatus} status The sending status of the event.
|
||||
* @prop {Error} error most recent error associated with sending the event, if any
|
||||
* @prop {boolean} forwardLooking True if this event is 'forward looking', meaning
|
||||
* that getDirectionalContent() will return event.content and not event.prev_content.
|
||||
* Default: true. <strong>This property is experimental and may change.</strong>
|
||||
*/
|
||||
module.exports.MatrixEvent = function MatrixEvent(
|
||||
event, clearEvent, keysProved, keysClaimed
|
||||
event,
|
||||
) {
|
||||
this.event = event || {};
|
||||
this.sender = null;
|
||||
this.target = null;
|
||||
this.status = null;
|
||||
this.error = null;
|
||||
this.forwardLooking = true;
|
||||
|
||||
this._clearEvent = clearEvent || {};
|
||||
this._pushActions = null;
|
||||
this._date = this.event.origin_server_ts ?
|
||||
new Date(this.event.origin_server_ts) : null;
|
||||
|
||||
this._keysProved = keysProved || {};
|
||||
this._keysClaimed = keysClaimed || {};
|
||||
this._clearEvent = {};
|
||||
this._keysProved = {};
|
||||
this._keysClaimed = {};
|
||||
};
|
||||
utils.inherits(module.exports.MatrixEvent, EventEmitter);
|
||||
|
||||
module.exports.MatrixEvent.prototype = {
|
||||
|
||||
utils.extend(module.exports.MatrixEvent.prototype, {
|
||||
|
||||
/**
|
||||
* Get the event_id for this event.
|
||||
@@ -146,6 +146,14 @@ module.exports.MatrixEvent.prototype = {
|
||||
return this.event.origin_server_ts;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the timestamp of this event, as a Date object.
|
||||
* @return {Date} The event date, e.g. <code>new Date(1433502692297)</code>
|
||||
*/
|
||||
getDate: function() {
|
||||
return this._date;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the (decrypted, if necessary) event content JSON.
|
||||
*
|
||||
@@ -239,12 +247,37 @@ module.exports.MatrixEvent.prototype = {
|
||||
this._keysClaimed = keys;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the cleartext data on this event.
|
||||
*
|
||||
* (This is used after decrypting an event; it should not be used by applications).
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @fires module:models/event.MatrixEvent#"Event.decrypted"
|
||||
*
|
||||
* @param {Object} clearEvent The plaintext payload for the event
|
||||
* (typically containing <tt>type</tt> and <tt>content</tt> fields).
|
||||
*
|
||||
* @param {Object=} keysProved Keys owned by the sender of this event.
|
||||
* See {@link module:models/event.MatrixEvent#getKeysProved}.
|
||||
*
|
||||
* @param {Object=} keysClaimed Keys the sender of this event claims.
|
||||
* See {@link module:models/event.MatrixEvent#getKeysClaimed}.
|
||||
*/
|
||||
setClearData: function(clearEvent, keysProved, keysClaimed) {
|
||||
this._clearEvent = clearEvent;
|
||||
this._keysProved = keysProved || {};
|
||||
this._keysClaimed = keysClaimed || {};
|
||||
this.emit("Event.decrypted", this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the event is encrypted.
|
||||
* @return {boolean} True if this event is encrypted.
|
||||
*/
|
||||
isEncrypted: function() {
|
||||
return Boolean(this._clearEvent.type);
|
||||
return this.event.type === "m.room.encrypted";
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -309,18 +342,22 @@ module.exports.MatrixEvent.prototype = {
|
||||
}
|
||||
this.event.unsigned.redacted_because = redaction_event.event;
|
||||
|
||||
var key;
|
||||
let key;
|
||||
for (key in this.event) {
|
||||
if (!this.event.hasOwnProperty(key)) { continue; }
|
||||
if (!this.event.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
if (!_REDACT_KEEP_KEY_MAP[key]) {
|
||||
delete this.event[key];
|
||||
}
|
||||
}
|
||||
|
||||
var keeps = _REDACT_KEEP_CONTENT_MAP[this.getType()] || {};
|
||||
var content = this.getContent();
|
||||
const keeps = _REDACT_KEEP_CONTENT_MAP[this.getType()] || {};
|
||||
const content = this.getContent();
|
||||
for (key in content) {
|
||||
if (!content.hasOwnProperty(key)) { continue; }
|
||||
if (!content.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
if (!keeps[key]) {
|
||||
delete content[key];
|
||||
}
|
||||
@@ -353,7 +390,18 @@ module.exports.MatrixEvent.prototype = {
|
||||
setPushActions: function(pushActions) {
|
||||
this._pushActions = pushActions;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace the `event` property and recalculate any properties based on it.
|
||||
* @param {Object} event the object to assign to the `event` property
|
||||
*/
|
||||
handleRemoteEcho: function(event) {
|
||||
this.event = event;
|
||||
// successfully sent.
|
||||
this.status = null;
|
||||
this._date = new Date(this.event.origin_server_ts);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
/* http://matrix.org/docs/spec/r0.0.1/client_server.html#redactions says:
|
||||
@@ -378,13 +426,15 @@ module.exports.MatrixEvent.prototype = {
|
||||
* m.room.aliases allows key aliases
|
||||
*/
|
||||
// a map giving the keys we keep when an event is redacted
|
||||
var _REDACT_KEEP_KEY_MAP = [
|
||||
const _REDACT_KEEP_KEY_MAP = [
|
||||
'event_id', 'type', 'room_id', 'user_id', 'state_key', 'prev_state',
|
||||
'content', 'unsigned',
|
||||
].reduce(function(ret, val) { ret[val] = 1; return ret; }, {});
|
||||
].reduce(function(ret, val) {
|
||||
ret[val] = 1; return ret;
|
||||
}, {});
|
||||
|
||||
// a map from event type to the .content keys we keep when an event is redacted
|
||||
var _REDACT_KEEP_CONTENT_MAP = {
|
||||
const _REDACT_KEEP_CONTENT_MAP = {
|
||||
'm.room.member': {'membership': 1},
|
||||
'm.room.create': {'creator': 1},
|
||||
'm.room.join_rules': {'join_rule': 1},
|
||||
@@ -394,3 +444,13 @@ var _REDACT_KEEP_CONTENT_MAP = {
|
||||
},
|
||||
'm.room.aliases': {'aliases': 1},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Fires when an event is decrypted
|
||||
*
|
||||
* @event module:models/event.MatrixEvent#"Event.decrypted"
|
||||
*
|
||||
* @param {module:models/event.MatrixEvent} event
|
||||
* The matrix event which has been decrypted
|
||||
*/
|
||||
@@ -17,10 +17,10 @@ limitations under the License.
|
||||
/**
|
||||
* @module models/room-member
|
||||
*/
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
var ContentRepo = require("../content-repo");
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
const ContentRepo = require("../content-repo");
|
||||
|
||||
var utils = require("../utils");
|
||||
const utils = require("../utils");
|
||||
|
||||
/**
|
||||
* Construct a new room member.
|
||||
@@ -52,7 +52,7 @@ function RoomMember(roomId, userId) {
|
||||
this.user = null;
|
||||
this.membership = null;
|
||||
this.events = {
|
||||
member: null
|
||||
member: null,
|
||||
};
|
||||
this._updateModifiedTime();
|
||||
}
|
||||
@@ -73,10 +73,10 @@ RoomMember.prototype.setMembershipEvent = function(event, roomState) {
|
||||
}
|
||||
this.events.member = event;
|
||||
|
||||
var oldMembership = this.membership;
|
||||
const oldMembership = this.membership;
|
||||
this.membership = event.getDirectionalContent().membership;
|
||||
|
||||
var oldName = this.name;
|
||||
const oldName = this.name;
|
||||
this.name = calculateDisplayName(this, event, roomState);
|
||||
if (oldMembership !== this.membership) {
|
||||
this._updateModifiedTime();
|
||||
@@ -99,12 +99,12 @@ RoomMember.prototype.setPowerLevelEvent = function(powerLevelEvent) {
|
||||
if (powerLevelEvent.getType() !== "m.room.power_levels") {
|
||||
return;
|
||||
}
|
||||
var maxLevel = powerLevelEvent.getContent().users_default || 0;
|
||||
let maxLevel = powerLevelEvent.getContent().users_default || 0;
|
||||
utils.forEach(utils.values(powerLevelEvent.getContent().users), function(lvl) {
|
||||
maxLevel = Math.max(maxLevel, lvl);
|
||||
});
|
||||
var oldPowerLevel = this.powerLevel;
|
||||
var oldPowerLevelNorm = this.powerLevelNorm;
|
||||
const oldPowerLevel = this.powerLevel;
|
||||
const oldPowerLevelNorm = this.powerLevelNorm;
|
||||
|
||||
if (powerLevelEvent.getContent().users[this.userId] !== undefined) {
|
||||
this.powerLevel = powerLevelEvent.getContent().users[this.userId];
|
||||
@@ -136,9 +136,9 @@ RoomMember.prototype.setTypingEvent = function(event) {
|
||||
if (event.getType() !== "m.typing") {
|
||||
return;
|
||||
}
|
||||
var oldTyping = this.typing;
|
||||
const oldTyping = this.typing;
|
||||
this.typing = false;
|
||||
var typingList = event.getContent().user_ids;
|
||||
const typingList = event.getContent().user_ids;
|
||||
if (!utils.isArray(typingList)) {
|
||||
// malformed event :/ bail early. TODO: whine?
|
||||
return;
|
||||
@@ -189,28 +189,29 @@ RoomMember.prototype.getLastModifiedTime = function() {
|
||||
*/
|
||||
RoomMember.prototype.getAvatarUrl =
|
||||
function(baseUrl, width, height, resizeMethod, allowDefault, allowDirectLinks) {
|
||||
if (allowDefault === undefined) { allowDefault = true; }
|
||||
if (allowDefault === undefined) {
|
||||
allowDefault = true;
|
||||
}
|
||||
if (!this.events.member && !allowDefault) {
|
||||
return null;
|
||||
}
|
||||
var rawUrl = this.events.member ? this.events.member.getContent().avatar_url : null;
|
||||
var httpUrl = ContentRepo.getHttpUriForMxc(
|
||||
baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks
|
||||
const rawUrl = this.events.member ? this.events.member.getContent().avatar_url : null;
|
||||
const httpUrl = ContentRepo.getHttpUriForMxc(
|
||||
baseUrl, rawUrl, width, height, resizeMethod, allowDirectLinks,
|
||||
);
|
||||
if (httpUrl) {
|
||||
return httpUrl;
|
||||
}
|
||||
else if (allowDefault) {
|
||||
} else if (allowDefault) {
|
||||
return ContentRepo.getIdenticonUri(
|
||||
baseUrl, this.userId, width, height
|
||||
baseUrl, this.userId, width, height,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
function calculateDisplayName(member, event, roomState) {
|
||||
var displayName = event.getDirectionalContent().displayname;
|
||||
var selfUserId = member.userId;
|
||||
const displayName = event.getDirectionalContent().displayname;
|
||||
const selfUserId = member.userId;
|
||||
|
||||
if (!displayName) {
|
||||
return selfUserId;
|
||||
@@ -220,8 +221,8 @@ function calculateDisplayName(member, event, roomState) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
var userIds = roomState.getUserIdsWithDisplayName(displayName);
|
||||
var otherUsers = userIds.filter(function(u) {
|
||||
const userIds = roomState.getUserIdsWithDisplayName(displayName);
|
||||
const otherUsers = userIds.filter(function(u) {
|
||||
return u !== selfUserId;
|
||||
});
|
||||
if (otherUsers.length > 0) {
|
||||
@@ -17,10 +17,10 @@ limitations under the License.
|
||||
/**
|
||||
* @module models/room-state
|
||||
*/
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
|
||||
var utils = require("../utils");
|
||||
var RoomMember = require("./room-member");
|
||||
const utils = require("../utils");
|
||||
const RoomMember = require("./room-member");
|
||||
|
||||
/**
|
||||
* Construct room state.
|
||||
@@ -100,7 +100,7 @@ RoomState.prototype.getStateEvents = function(eventType, stateKey) {
|
||||
if (stateKey === undefined) { // return all values
|
||||
return utils.values(this.events[eventType]);
|
||||
}
|
||||
var event = this.events[eventType][stateKey];
|
||||
const event = this.events[eventType][stateKey];
|
||||
return event ? event : null;
|
||||
};
|
||||
|
||||
@@ -115,13 +115,17 @@ RoomState.prototype.getStateEvents = function(eventType, stateKey) {
|
||||
* @fires module:client~MatrixClient#event:"RoomState.events"
|
||||
*/
|
||||
RoomState.prototype.setStateEvents = function(stateEvents) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
this._updateModifiedTime();
|
||||
|
||||
// update the core event dict
|
||||
utils.forEach(stateEvents, function(event) {
|
||||
if (event.getRoomId() !== self.roomId) { return; }
|
||||
if (!event.isState()) { return; }
|
||||
if (event.getRoomId() !== self.roomId) {
|
||||
return;
|
||||
}
|
||||
if (!event.isState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.events[event.getType()] === undefined) {
|
||||
self.events[event.getType()] = {};
|
||||
@@ -129,7 +133,7 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
||||
self.events[event.getType()][event.getStateKey()] = event;
|
||||
if (event.getType() === "m.room.member") {
|
||||
_updateDisplayNameCache(
|
||||
self, event.getStateKey(), event.getContent().displayname
|
||||
self, event.getStateKey(), event.getContent().displayname,
|
||||
);
|
||||
_updateThirdPartyTokenCache(self, event);
|
||||
}
|
||||
@@ -141,18 +145,21 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
||||
// the given array (e.g. disambiguating display names in one go to do both
|
||||
// clashing names rather than progressively which only catches 1 of them).
|
||||
utils.forEach(stateEvents, function(event) {
|
||||
if (event.getRoomId() !== self.roomId) { return; }
|
||||
if (!event.isState()) { return; }
|
||||
if (event.getRoomId() !== self.roomId) {
|
||||
return;
|
||||
}
|
||||
if (!event.isState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getType() === "m.room.member") {
|
||||
var userId = event.getStateKey();
|
||||
const userId = event.getStateKey();
|
||||
|
||||
// leave events apparently elide the displayname or avatar_url,
|
||||
// so let's fake one up so that we don't leak user ids
|
||||
// into the timeline
|
||||
if (event.getContent().membership === "leave" ||
|
||||
event.getContent().membership === "ban")
|
||||
{
|
||||
event.getContent().membership === "ban") {
|
||||
event.getContent().avatar_url =
|
||||
event.getContent().avatar_url ||
|
||||
event.getPrevContent().avatar_url;
|
||||
@@ -161,7 +168,7 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
||||
event.getPrevContent().displayname;
|
||||
}
|
||||
|
||||
var member = self.members[userId];
|
||||
let member = self.members[userId];
|
||||
if (!member) {
|
||||
member = new RoomMember(event.getRoomId(), userId);
|
||||
self.emit("RoomState.newMember", event, self, member);
|
||||
@@ -171,11 +178,11 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
||||
// so we don't make assumptions about the properties of RoomMember
|
||||
// (e.g. and manage to break it because deep copying doesn't do
|
||||
// everything).
|
||||
var sentinel = new RoomMember(event.getRoomId(), userId);
|
||||
const sentinel = new RoomMember(event.getRoomId(), userId);
|
||||
utils.forEach([member, sentinel], function(roomMember) {
|
||||
roomMember.setMembershipEvent(event, self);
|
||||
// this member may have a power level already, so set it.
|
||||
var pwrLvlEvent = self.getStateEvents("m.room.power_levels", "");
|
||||
const pwrLvlEvent = self.getStateEvents("m.room.power_levels", "");
|
||||
if (pwrLvlEvent) {
|
||||
roomMember.setPowerLevelEvent(pwrLvlEvent);
|
||||
}
|
||||
@@ -184,9 +191,8 @@ RoomState.prototype.setStateEvents = function(stateEvents) {
|
||||
self._sentinels[userId] = sentinel;
|
||||
self.members[userId] = member;
|
||||
self.emit("RoomState.members", event, self, member);
|
||||
}
|
||||
else if (event.getType() === "m.room.power_levels") {
|
||||
var members = utils.values(self.members);
|
||||
} else if (event.getType() === "m.room.power_levels") {
|
||||
const members = utils.values(self.members);
|
||||
utils.forEach(members, function(member) {
|
||||
member.setPowerLevelEvent(event);
|
||||
self.emit("RoomState.members", event, self, member);
|
||||
@@ -253,7 +259,7 @@ RoomState.prototype.maySendMessage = function(userId) {
|
||||
/**
|
||||
* Returns true if the given user ID has permission to send a normal
|
||||
* event of type `eventType` into this room.
|
||||
* @param {string} type The type of event to test
|
||||
* @param {string} eventType The type of event to test
|
||||
* @param {string} userId The user ID of the user to test permission for
|
||||
* @return {boolean} true if the given user ID should be permitted to send
|
||||
* the given type of event into this room,
|
||||
@@ -267,8 +273,8 @@ RoomState.prototype.maySendEvent = function(eventType, userId) {
|
||||
/**
|
||||
* Returns true if the given MatrixClient has permission to send a state
|
||||
* event of type `stateEventType` into this room.
|
||||
* @param {string} type The type of state events to test
|
||||
* @param {MatrixClient} The client to test permission for
|
||||
* @param {string} stateEventType The type of state events to test
|
||||
* @param {MatrixClient} cli The client to test permission for
|
||||
* @return {boolean} true if the given client should be permitted to send
|
||||
* the given type of state event into this room,
|
||||
* according to the room's state.
|
||||
@@ -283,7 +289,7 @@ RoomState.prototype.mayClientSendStateEvent = function(stateEventType, cli) {
|
||||
/**
|
||||
* Returns true if the given user ID has permission to send a state
|
||||
* event of type `stateEventType` into this room.
|
||||
* @param {string} type The type of state events to test
|
||||
* @param {string} stateEventType The type of state events to test
|
||||
* @param {string} userId The user ID of the user to test permission for
|
||||
* @return {boolean} true if the given user ID should be permitted to send
|
||||
* the given type of state event into this room,
|
||||
@@ -296,7 +302,7 @@ RoomState.prototype.maySendStateEvent = function(stateEventType, userId) {
|
||||
/**
|
||||
* Returns true if the given user ID has permission to send a normal or state
|
||||
* event of type `eventType` into this room.
|
||||
* @param {string} type The type of event to test
|
||||
* @param {string} eventType The type of event to test
|
||||
* @param {string} userId The user ID of the user to test permission for
|
||||
* @param {boolean} state If true, tests if the user may send a state
|
||||
event of this type. Otherwise tests whether
|
||||
@@ -306,26 +312,22 @@ RoomState.prototype.maySendStateEvent = function(stateEventType, userId) {
|
||||
* according to the room's state.
|
||||
*/
|
||||
RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
|
||||
var member = this.getMember(userId);
|
||||
if (!member || member.membership == 'leave') { return false; }
|
||||
const member = this.getMember(userId);
|
||||
if (!member || member.membership == 'leave') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var power_levels_event = this.getStateEvents('m.room.power_levels', '');
|
||||
const power_levels_event = this.getStateEvents('m.room.power_levels', '');
|
||||
|
||||
var power_levels;
|
||||
var events_levels = {};
|
||||
let power_levels;
|
||||
let events_levels = {};
|
||||
|
||||
var default_user_level = 0;
|
||||
var user_levels = [];
|
||||
|
||||
var state_default = 0;
|
||||
var events_default = 0;
|
||||
let state_default = 0;
|
||||
let events_default = 0;
|
||||
if (power_levels_event) {
|
||||
power_levels = power_levels_event.getContent();
|
||||
events_levels = power_levels.events || {};
|
||||
|
||||
default_user_level = parseInt(power_levels.users_default || 0);
|
||||
user_levels = power_levels.users || {};
|
||||
|
||||
if (power_levels.state_default !== undefined) {
|
||||
state_default = power_levels.state_default;
|
||||
} else {
|
||||
@@ -336,7 +338,7 @@ RoomState.prototype._maySendEventOfType = function(eventType, userId, state) {
|
||||
}
|
||||
}
|
||||
|
||||
var required_level = state ? state_default : events_default;
|
||||
let required_level = state ? state_default : events_default;
|
||||
if (events_levels[eventType] !== undefined) {
|
||||
required_level = events_levels[eventType];
|
||||
}
|
||||
@@ -353,12 +355,12 @@ function _updateThirdPartyTokenCache(roomState, memberEvent) {
|
||||
if (!memberEvent.getContent().third_party_invite) {
|
||||
return;
|
||||
}
|
||||
var token = (memberEvent.getContent().third_party_invite.signed || {}).token;
|
||||
const token = (memberEvent.getContent().third_party_invite.signed || {}).token;
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
var threePidInvite = roomState.getStateEvents(
|
||||
"m.room.third_party_invite", token
|
||||
const threePidInvite = roomState.getStateEvents(
|
||||
"m.room.third_party_invite", token,
|
||||
);
|
||||
if (!threePidInvite) {
|
||||
return;
|
||||
@@ -367,15 +369,15 @@ function _updateThirdPartyTokenCache(roomState, memberEvent) {
|
||||
}
|
||||
|
||||
function _updateDisplayNameCache(roomState, userId, displayName) {
|
||||
var oldName = roomState._userIdsToDisplayNames[userId];
|
||||
const oldName = roomState._userIdsToDisplayNames[userId];
|
||||
delete roomState._userIdsToDisplayNames[userId];
|
||||
if (oldName) {
|
||||
// Remove the old name from the cache.
|
||||
// We clobber the user_id > name lookup but the name -> [user_id] lookup
|
||||
// means we need to remove that user ID from that array rather than nuking
|
||||
// the lot.
|
||||
var existingUserIds = roomState._displayNameToUserIds[oldName] || [];
|
||||
for (var i = 0; i < existingUserIds.length; i++) {
|
||||
const existingUserIds = roomState._displayNameToUserIds[oldName] || [];
|
||||
for (let i = 0; i < existingUserIds.length; i++) {
|
||||
if (existingUserIds[i] === userId) {
|
||||
// remove this user ID from this array
|
||||
existingUserIds.splice(i, 1);
|
||||
@@ -17,30 +17,30 @@ limitations under the License.
|
||||
/**
|
||||
* @module models/room
|
||||
*/
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
|
||||
var EventStatus = require("./event").EventStatus;
|
||||
var RoomSummary = require("./room-summary");
|
||||
var MatrixEvent = require("./event").MatrixEvent;
|
||||
var utils = require("../utils");
|
||||
var ContentRepo = require("../content-repo");
|
||||
var EventTimeline = require("./event-timeline");
|
||||
var EventTimelineSet = require("./event-timeline-set");
|
||||
const EventStatus = require("./event").EventStatus;
|
||||
const RoomSummary = require("./room-summary");
|
||||
const MatrixEvent = require("./event").MatrixEvent;
|
||||
const utils = require("../utils");
|
||||
const ContentRepo = require("../content-repo");
|
||||
const EventTimeline = require("./event-timeline");
|
||||
const EventTimelineSet = require("./event-timeline-set");
|
||||
|
||||
|
||||
function synthesizeReceipt(userId, event, receiptType) {
|
||||
// console.log("synthesizing receipt for "+event.getId());
|
||||
// This is really ugly because JS has no way to express an object literal
|
||||
// where the name of a key comes from an expression
|
||||
var fakeReceipt = {
|
||||
const fakeReceipt = {
|
||||
content: {},
|
||||
type: "m.receipt",
|
||||
room_id: event.getRoomId()
|
||||
room_id: event.getRoomId(),
|
||||
};
|
||||
fakeReceipt.content[event.getId()] = {};
|
||||
fakeReceipt.content[event.getId()][receiptType] = {};
|
||||
fakeReceipt.content[event.getId()][receiptType][userId] = {
|
||||
ts: event.getTs()
|
||||
ts: event.getTs(),
|
||||
};
|
||||
return new MatrixEvent(fakeReceipt);
|
||||
}
|
||||
@@ -108,7 +108,7 @@ function Room(roomId, opts) {
|
||||
if (["chronological", "detached"].indexOf(opts.pendingEventOrdering) === -1) {
|
||||
throw new Error(
|
||||
"opts.pendingEventOrdering MUST be either 'chronological' or " +
|
||||
"'detached'. Got: '" + opts.pendingEventOrdering + "'"
|
||||
"'detached'. Got: '" + opts.pendingEventOrdering + "'",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -165,6 +165,8 @@ function Room(roomId, opts) {
|
||||
if (this._opts.pendingEventOrdering == "detached") {
|
||||
this._pendingEventList = [];
|
||||
}
|
||||
|
||||
this._blacklistUnverifiedDevices = false; // read by megolm
|
||||
}
|
||||
utils.inherits(Room, EventEmitter);
|
||||
|
||||
@@ -204,7 +206,7 @@ Room.prototype.getLiveTimeline = function() {
|
||||
* @param {string=} backPaginationToken token for back-paginating the new timeline
|
||||
*/
|
||||
Room.prototype.resetLiveTimeline = function(backPaginationToken) {
|
||||
for (var i = 0; i < this._timelineSets.length; i++) {
|
||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||
this._timelineSets[i].resetLiveTimeline(backPaginationToken);
|
||||
}
|
||||
|
||||
@@ -294,6 +296,24 @@ Room.prototype.setUnreadNotificationCount = function(type, count) {
|
||||
this._notificationCounts[type] = count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether to send encrypted messages to devices within this room.
|
||||
* Will be ignored if MatrixClient's blacklistUnverifiedDevices setting is true.
|
||||
* @param {boolean} value if true, blacklist unverified devices.
|
||||
*/
|
||||
Room.prototype.setBlacklistUnverifiedDevices = function(value) {
|
||||
this._blacklistUnverifiedDevices = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether to send encrypted messages to devices within this room.
|
||||
* Will be ignored if MatrixClient's blacklistUnverifiedDevices setting is true.
|
||||
* @return {boolean} true if blacklisting unverified devices.
|
||||
*/
|
||||
Room.prototype.getBlacklistUnverifiedDevices = function() {
|
||||
return this._blacklistUnverifiedDevices;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the avatar URL for a room if one was set.
|
||||
* @param {String} baseUrl The homeserver base URL. See
|
||||
@@ -308,21 +328,22 @@ Room.prototype.setUnreadNotificationCount = function(type, count) {
|
||||
*/
|
||||
Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
|
||||
allowDefault) {
|
||||
var roomAvatarEvent = this.currentState.getStateEvents("m.room.avatar", "");
|
||||
if (allowDefault === undefined) { allowDefault = true; }
|
||||
const roomAvatarEvent = this.currentState.getStateEvents("m.room.avatar", "");
|
||||
if (allowDefault === undefined) {
|
||||
allowDefault = true;
|
||||
}
|
||||
if (!roomAvatarEvent && !allowDefault) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var mainUrl = roomAvatarEvent ? roomAvatarEvent.getContent().url : null;
|
||||
const mainUrl = roomAvatarEvent ? roomAvatarEvent.getContent().url : null;
|
||||
if (mainUrl) {
|
||||
return ContentRepo.getHttpUriForMxc(
|
||||
baseUrl, mainUrl, width, height, resizeMethod
|
||||
baseUrl, mainUrl, width, height, resizeMethod,
|
||||
);
|
||||
}
|
||||
else if (allowDefault) {
|
||||
} else if (allowDefault) {
|
||||
return ContentRepo.getIdenticonUri(
|
||||
baseUrl, this.roomId, width, height
|
||||
baseUrl, this.roomId, width, height,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -336,15 +357,15 @@ Room.prototype.getAvatarUrl = function(baseUrl, width, height, resizeMethod,
|
||||
* @return {array} The room's alias as an array of strings
|
||||
*/
|
||||
Room.prototype.getAliases = function() {
|
||||
var alias_strings = [];
|
||||
const alias_strings = [];
|
||||
|
||||
var alias_events = this.currentState.getStateEvents("m.room.aliases");
|
||||
const alias_events = this.currentState.getStateEvents("m.room.aliases");
|
||||
if (alias_events) {
|
||||
for (var i = 0; i < alias_events.length; ++i) {
|
||||
var alias_event = alias_events[i];
|
||||
for (let i = 0; i < alias_events.length; ++i) {
|
||||
const alias_event = alias_events[i];
|
||||
if (utils.isArray(alias_event.getContent().aliases)) {
|
||||
Array.prototype.push.apply(
|
||||
alias_strings, alias_event.getContent().aliases
|
||||
alias_strings, alias_event.getContent().aliases,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -359,7 +380,7 @@ Room.prototype.getAliases = function() {
|
||||
* @return {?string} The room's canonical alias, or null if there is none
|
||||
*/
|
||||
Room.prototype.getCanonicalAlias = function() {
|
||||
var canonicalAlias = this.currentState.getStateEvents("m.room.canonical_alias", "");
|
||||
const canonicalAlias = this.currentState.getStateEvents("m.room.canonical_alias", "");
|
||||
if (canonicalAlias) {
|
||||
return canonicalAlias.getContent().alias;
|
||||
}
|
||||
@@ -389,7 +410,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
||||
timeline, paginationToken) {
|
||||
timeline.getTimelineSet().addEventsToTimeline(
|
||||
events, toStartOfTimeline,
|
||||
timeline, paginationToken
|
||||
timeline, paginationToken,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -399,7 +420,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
||||
* @return {RoomMember} The member or <code>null</code>.
|
||||
*/
|
||||
Room.prototype.getMember = function(userId) {
|
||||
var member = this.currentState.members[userId];
|
||||
const member = this.currentState.members[userId];
|
||||
if (!member) {
|
||||
return null;
|
||||
}
|
||||
@@ -444,7 +465,7 @@ Room.prototype.addEventsToTimeline = function(events, toStartOfTimeline,
|
||||
* @return {boolean} True if this user_id has the given membership state.
|
||||
*/
|
||||
Room.prototype.hasMembershipState = function(userId, membership) {
|
||||
var member = this.getMember(userId);
|
||||
const member = this.getMember(userId);
|
||||
if (!member) {
|
||||
return false;
|
||||
}
|
||||
@@ -460,8 +481,8 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) {
|
||||
if (this._filteredTimelineSets[filter.filterId]) {
|
||||
return this._filteredTimelineSets[filter.filterId];
|
||||
}
|
||||
var opts = Object.assign({ filter: filter }, this._opts);
|
||||
var timelineSet = new EventTimelineSet(this, opts);
|
||||
const opts = Object.assign({ filter: filter }, this._opts);
|
||||
const timelineSet = new EventTimelineSet(this, opts);
|
||||
reEmit(this, timelineSet, ["Room.timeline", "Room.timelineReset"]);
|
||||
this._filteredTimelineSets[filter.filterId] = timelineSet;
|
||||
this._timelineSets.push(timelineSet);
|
||||
@@ -473,21 +494,21 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) {
|
||||
// may have grown huge and so take a long time to filter.
|
||||
// see https://github.com/vector-im/vector-web/issues/2109
|
||||
|
||||
var unfilteredLiveTimeline = this.getLiveTimeline();
|
||||
const unfilteredLiveTimeline = this.getLiveTimeline();
|
||||
|
||||
unfilteredLiveTimeline.getEvents().forEach(function(event) {
|
||||
timelineSet.addLiveEvent(event);
|
||||
});
|
||||
|
||||
// find the earliest unfiltered timeline
|
||||
var timeline = unfilteredLiveTimeline;
|
||||
let timeline = unfilteredLiveTimeline;
|
||||
while (timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS)) {
|
||||
timeline = timeline.getNeighbouringTimeline(EventTimeline.BACKWARDS);
|
||||
}
|
||||
|
||||
timelineSet.getLiveTimeline().setPaginationToken(
|
||||
timeline.getPaginationToken(EventTimeline.BACKWARDS),
|
||||
EventTimeline.BACKWARDS
|
||||
EventTimeline.BACKWARDS,
|
||||
);
|
||||
|
||||
// alternatively, we could try to do something like this to try and re-paginate
|
||||
@@ -507,9 +528,9 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) {
|
||||
* @param {Filter} filter the filter whose timelineSet is to be forgotten
|
||||
*/
|
||||
Room.prototype.removeFilteredTimelineSet = function(filter) {
|
||||
var timelineSet = this._filteredTimelineSets[filter.filterId];
|
||||
const timelineSet = this._filteredTimelineSets[filter.filterId];
|
||||
delete this._filteredTimelineSets[filter.filterId];
|
||||
var i = this._timelineSets.indexOf(timelineSet);
|
||||
const i = this._timelineSets.indexOf(timelineSet);
|
||||
if (i > -1) {
|
||||
this._timelineSets.splice(i, 1);
|
||||
}
|
||||
@@ -525,12 +546,12 @@ Room.prototype.removeFilteredTimelineSet = function(filter) {
|
||||
* @private
|
||||
*/
|
||||
Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
||||
var i;
|
||||
let i;
|
||||
if (event.getType() === "m.room.redaction") {
|
||||
var redactId = event.event.redacts;
|
||||
const redactId = event.event.redacts;
|
||||
|
||||
// if we know about this event, redact its contents now.
|
||||
var redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
||||
const redactedEvent = this.getUnfilteredTimelineSet().findEventById(redactId);
|
||||
if (redactedEvent) {
|
||||
redactedEvent.makeRedacted(event);
|
||||
this.emit("Room.redaction", event, this);
|
||||
@@ -550,7 +571,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
||||
}
|
||||
|
||||
if (event.getUnsigned().transaction_id) {
|
||||
var existingEvent = this._txnToEvent[event.getUnsigned().transaction_id];
|
||||
const existingEvent = this._txnToEvent[event.getUnsigned().transaction_id];
|
||||
if (existingEvent) {
|
||||
// remote echo of an event we sent earlier
|
||||
this._handleRemoteEcho(event, existingEvent);
|
||||
@@ -568,7 +589,7 @@ Room.prototype._addLiveEvent = function(event, duplicateStrategy) {
|
||||
// pointing to an event that wasn't yet in the timeline
|
||||
if (event.sender) {
|
||||
this.addReceipt(synthesizeReceipt(
|
||||
event.sender.userId, event, "m.read"
|
||||
event.sender.userId, event, "m.read",
|
||||
), true);
|
||||
|
||||
// Any live events from a user could be taken as implicit
|
||||
@@ -615,7 +636,7 @@ Room.prototype.addPendingEvent = function(event, txnId) {
|
||||
EventTimeline.setEventMetadata(
|
||||
event,
|
||||
this.getLiveTimeline().getState(EventTimeline.FORWARDS),
|
||||
false
|
||||
false,
|
||||
);
|
||||
|
||||
this._txnToEvent[txnId] = event;
|
||||
@@ -623,15 +644,14 @@ Room.prototype.addPendingEvent = function(event, txnId) {
|
||||
if (this._opts.pendingEventOrdering == "detached") {
|
||||
this._pendingEventList.push(event);
|
||||
} else {
|
||||
for (var i = 0; i < this._timelineSets.length; i++) {
|
||||
var timelineSet = this._timelineSets[i];
|
||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||
const timelineSet = this._timelineSets[i];
|
||||
if (timelineSet.getFilter()) {
|
||||
if (this._filter.filterRoomTimeline([event]).length) {
|
||||
timelineSet.addEventToTimeline(event,
|
||||
timelineSet.getLiveTimeline(), false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
timelineSet.addEventToTimeline(event,
|
||||
timelineSet.getLiveTimeline(), false);
|
||||
}
|
||||
@@ -656,9 +676,9 @@ Room.prototype.addPendingEvent = function(event, txnId) {
|
||||
* @private
|
||||
*/
|
||||
Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) {
|
||||
var oldEventId = localEvent.getId();
|
||||
var newEventId = remoteEvent.getId();
|
||||
var oldStatus = localEvent.status;
|
||||
const oldEventId = localEvent.getId();
|
||||
const newEventId = remoteEvent.getId();
|
||||
const oldStatus = localEvent.status;
|
||||
|
||||
// no longer pending
|
||||
delete this._txnToEvent[remoteEvent.transaction_id];
|
||||
@@ -667,20 +687,18 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) {
|
||||
if (this._pendingEventList) {
|
||||
utils.removeElement(
|
||||
this._pendingEventList,
|
||||
function(ev) { return ev.getId() == oldEventId; },
|
||||
false
|
||||
function(ev) {
|
||||
return ev.getId() == oldEventId;
|
||||
}, false,
|
||||
);
|
||||
}
|
||||
|
||||
// replace the event source (this will preserve the plaintext payload if
|
||||
// any, which is good, because we don't want to try decoding it again).
|
||||
localEvent.event = remoteEvent.event;
|
||||
localEvent.handleRemoteEcho(remoteEvent.event);
|
||||
|
||||
// successfully sent.
|
||||
localEvent.status = null;
|
||||
|
||||
for (var i = 0; i < this._timelineSets.length; i++) {
|
||||
var timelineSet = this._timelineSets[i];
|
||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||
const timelineSet = this._timelineSets[i];
|
||||
|
||||
// if it's already in the timeline, update the timeline map. If it's not, add it.
|
||||
timelineSet.handleRemoteEcho(localEvent, oldEventId, newEventId);
|
||||
@@ -692,7 +710,7 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) {
|
||||
|
||||
/* a map from current event status to a list of allowed next statuses
|
||||
*/
|
||||
var ALLOWED_TRANSITIONS = {};
|
||||
const ALLOWED_TRANSITIONS = {};
|
||||
|
||||
ALLOWED_TRANSITIONS[EventStatus.ENCRYPTING] = [
|
||||
EventStatus.SENDING,
|
||||
@@ -739,7 +757,7 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
|
||||
|
||||
// SENT races against /sync, so we have to special-case it.
|
||||
if (newStatus == EventStatus.SENT) {
|
||||
var timeline = this.getUnfilteredTimelineSet().eventIdToTimeline(newEventId);
|
||||
const timeline = this.getUnfilteredTimelineSet().eventIdToTimeline(newEventId);
|
||||
if (timeline) {
|
||||
// we've already received the event via the event stream.
|
||||
// nothing more to do here.
|
||||
@@ -747,15 +765,15 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
|
||||
}
|
||||
}
|
||||
|
||||
var oldStatus = event.status;
|
||||
var oldEventId = event.getId();
|
||||
const oldStatus = event.status;
|
||||
const oldEventId = event.getId();
|
||||
|
||||
if (!oldStatus) {
|
||||
throw new Error("updatePendingEventStatus called on an event which is " +
|
||||
"not a local echo.");
|
||||
}
|
||||
|
||||
var allowed = ALLOWED_TRANSITIONS[oldStatus];
|
||||
const allowed = ALLOWED_TRANSITIONS[oldStatus];
|
||||
if (!allowed || allowed.indexOf(newStatus) < 0) {
|
||||
throw new Error("Invalid EventStatus transition " + oldStatus + "->" +
|
||||
newStatus);
|
||||
@@ -770,17 +788,17 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
|
||||
// if the event was already in the timeline (which will be the case if
|
||||
// opts.pendingEventOrdering==chronological), we need to update the
|
||||
// timeline map.
|
||||
for (var i = 0; i < this._timelineSets.length; i++) {
|
||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||
this._timelineSets[i].replaceEventId(oldEventId, newEventId);
|
||||
}
|
||||
}
|
||||
else if (newStatus == EventStatus.CANCELLED) {
|
||||
} else if (newStatus == EventStatus.CANCELLED) {
|
||||
// remove it from the pending event list, or the timeline.
|
||||
if (this._pendingEventList) {
|
||||
utils.removeElement(
|
||||
this._pendingEventList,
|
||||
function(ev) { return ev.getId() == oldEventId; },
|
||||
false
|
||||
function(ev) {
|
||||
return ev.getId() == oldEventId;
|
||||
}, false,
|
||||
);
|
||||
}
|
||||
this.removeEvent(oldEventId);
|
||||
@@ -807,24 +825,24 @@ Room.prototype.updatePendingEvent = function(event, newStatus, newEventId) {
|
||||
* @throws If <code>duplicateStrategy</code> is not falsey, 'replace' or 'ignore'.
|
||||
*/
|
||||
Room.prototype.addLiveEvents = function(events, duplicateStrategy) {
|
||||
var i;
|
||||
let i;
|
||||
if (duplicateStrategy && ["replace", "ignore"].indexOf(duplicateStrategy) === -1) {
|
||||
throw new Error("duplicateStrategy MUST be either 'replace' or 'ignore'");
|
||||
}
|
||||
|
||||
// sanity check that the live timeline is still live
|
||||
for (i = 0; i < this._timelineSets.length; i++) {
|
||||
var liveTimeline = this._timelineSets[i].getLiveTimeline();
|
||||
const liveTimeline = this._timelineSets[i].getLiveTimeline();
|
||||
if (liveTimeline.getPaginationToken(EventTimeline.FORWARDS)) {
|
||||
throw new Error(
|
||||
"live timeline " + i + " is no longer live - it has a pagination token " +
|
||||
"(" + liveTimeline.getPaginationToken(EventTimeline.FORWARDS) + ")"
|
||||
"(" + liveTimeline.getPaginationToken(EventTimeline.FORWARDS) + ")",
|
||||
);
|
||||
}
|
||||
if (liveTimeline.getNeighbouringTimeline(EventTimeline.FORWARDS)) {
|
||||
throw new Error(
|
||||
"live timeline " + i + " is no longer live - " +
|
||||
"it has a neighbouring timeline"
|
||||
"it has a neighbouring timeline",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -832,8 +850,7 @@ Room.prototype.addLiveEvents = function(events, duplicateStrategy) {
|
||||
for (i = 0; i < events.length; i++) {
|
||||
if (events[i].getType() === "m.typing") {
|
||||
this.currentState.setTypingEvent(events[i]);
|
||||
}
|
||||
else if (events[i].getType() === "m.receipt") {
|
||||
} else if (events[i].getType() === "m.receipt") {
|
||||
this.addReceipt(events[i]);
|
||||
}
|
||||
// N.B. account_data is added directly by /sync to avoid
|
||||
@@ -851,7 +868,7 @@ Room.prototype.addLiveEvents = function(events, duplicateStrategy) {
|
||||
* @param {String[]} event_ids A list of event_ids to remove.
|
||||
*/
|
||||
Room.prototype.removeEvents = function(event_ids) {
|
||||
for (var i = 0; i < event_ids.length; ++i) {
|
||||
for (let i = 0; i < event_ids.length; ++i) {
|
||||
this.removeEvent(event_ids[i]);
|
||||
}
|
||||
};
|
||||
@@ -864,9 +881,9 @@ Room.prototype.removeEvents = function(event_ids) {
|
||||
* @return {bool} true if the event was removed from any of the room's timeline sets
|
||||
*/
|
||||
Room.prototype.removeEvent = function(eventId) {
|
||||
var removedAny = false;
|
||||
for (var i = 0; i < this._timelineSets.length; i++) {
|
||||
var removed = this._timelineSets[i].removeEvent(eventId);
|
||||
let removedAny = false;
|
||||
for (let i = 0; i < this._timelineSets.length; i++) {
|
||||
const removed = this._timelineSets[i].removeEvent(eventId);
|
||||
if (removed) {
|
||||
removedAny = true;
|
||||
}
|
||||
@@ -885,15 +902,15 @@ Room.prototype.removeEvent = function(eventId) {
|
||||
Room.prototype.recalculate = function(userId) {
|
||||
// set fake stripped state events if this is an invite room so logic remains
|
||||
// consistent elsewhere.
|
||||
var self = this;
|
||||
var membershipEvent = this.currentState.getStateEvents(
|
||||
"m.room.member", userId
|
||||
const self = this;
|
||||
const membershipEvent = this.currentState.getStateEvents(
|
||||
"m.room.member", userId,
|
||||
);
|
||||
if (membershipEvent && membershipEvent.getContent().membership === "invite") {
|
||||
var strippedStateEvents = membershipEvent.event.invite_room_state || [];
|
||||
const strippedStateEvents = membershipEvent.event.invite_room_state || [];
|
||||
utils.forEach(strippedStateEvents, function(strippedEvent) {
|
||||
var existingEvent = self.currentState.getStateEvents(
|
||||
strippedEvent.type, strippedEvent.state_key
|
||||
const existingEvent = self.currentState.getStateEvents(
|
||||
strippedEvent.type, strippedEvent.state_key,
|
||||
);
|
||||
if (!existingEvent) {
|
||||
// set the fake stripped event instead
|
||||
@@ -903,18 +920,16 @@ Room.prototype.recalculate = function(userId) {
|
||||
content: strippedEvent.content,
|
||||
event_id: "$fake" + Date.now(),
|
||||
room_id: self.roomId,
|
||||
user_id: userId // technically a lie
|
||||
user_id: userId, // technically a lie
|
||||
})]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
var oldName = this.name;
|
||||
const oldName = this.name;
|
||||
this.name = calculateRoomName(this, userId);
|
||||
this.summary = new RoomSummary(this.roomId, {
|
||||
title: this.name
|
||||
title: this.name,
|
||||
});
|
||||
|
||||
if (oldName !== this.name) {
|
||||
@@ -946,7 +961,7 @@ Room.prototype.getUsersReadUpTo = function(event) {
|
||||
* @return {String} ID of the latest event that the given user has read, or null.
|
||||
*/
|
||||
Room.prototype.getEventReadUpTo = function(userId, ignoreSynthesized) {
|
||||
var receipts = this._receipts;
|
||||
let receipts = this._receipts;
|
||||
if (ignoreSynthesized) {
|
||||
receipts = this._realReceipts;
|
||||
}
|
||||
@@ -987,7 +1002,9 @@ Room.prototype.addReceipt = function(event, fake) {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if (fake === undefined) { fake = false; }
|
||||
if (fake === undefined) {
|
||||
fake = false;
|
||||
}
|
||||
if (!fake) {
|
||||
this._addReceiptsToStructure(event, this._realReceipts);
|
||||
// we don't bother caching real receipts by event ID
|
||||
@@ -1007,18 +1024,18 @@ Room.prototype.addReceipt = function(event, fake) {
|
||||
* @param {Object} receipts The object to add receipts to
|
||||
*/
|
||||
Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
utils.keys(event.getContent()).forEach(function(eventId) {
|
||||
utils.keys(event.getContent()[eventId]).forEach(function(receiptType) {
|
||||
utils.keys(event.getContent()[eventId][receiptType]).forEach(
|
||||
function(userId) {
|
||||
var receipt = event.getContent()[eventId][receiptType][userId];
|
||||
const receipt = event.getContent()[eventId][receiptType][userId];
|
||||
|
||||
if (!receipts[receiptType]) {
|
||||
receipts[receiptType] = {};
|
||||
}
|
||||
|
||||
var existingReceipt = receipts[receiptType][userId];
|
||||
const existingReceipt = receipts[receiptType][userId];
|
||||
|
||||
if (!existingReceipt) {
|
||||
receipts[receiptType][userId] = {};
|
||||
@@ -1027,7 +1044,7 @@ Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
||||
// than the one we already have. (This is managed
|
||||
// server-side, but because we synthesize RRs locally we
|
||||
// have to do it here too.)
|
||||
var ordering = self.getUnfilteredTimelineSet().compareEventOrdering(
|
||||
const ordering = self.getUnfilteredTimelineSet().compareEventOrdering(
|
||||
existingReceipt.eventId, eventId);
|
||||
if (ordering !== null && ordering >= 0) {
|
||||
return;
|
||||
@@ -1036,7 +1053,7 @@ Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
||||
|
||||
receipts[receiptType][userId] = {
|
||||
eventId: eventId,
|
||||
data: receipt
|
||||
data: receipt,
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -1049,17 +1066,17 @@ Room.prototype._addReceiptsToStructure = function(event, receipts) {
|
||||
* @return {Object} Map of receipts by event ID
|
||||
*/
|
||||
Room.prototype._buildReceiptCache = function(receipts) {
|
||||
var receiptCacheByEventId = {};
|
||||
const receiptCacheByEventId = {};
|
||||
utils.keys(receipts).forEach(function(receiptType) {
|
||||
utils.keys(receipts[receiptType]).forEach(function(userId) {
|
||||
var receipt = receipts[receiptType][userId];
|
||||
const receipt = receipts[receiptType][userId];
|
||||
if (!receiptCacheByEventId[receipt.eventId]) {
|
||||
receiptCacheByEventId[receipt.eventId] = [];
|
||||
}
|
||||
receiptCacheByEventId[receipt.eventId].push({
|
||||
userId: userId,
|
||||
type: receiptType,
|
||||
data: receipt.data
|
||||
data: receipt.data,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1104,8 +1121,8 @@ Room.prototype.addTags = function(event) {
|
||||
* @param {Array<MatrixEvent>} events an array of account_data events to add
|
||||
*/
|
||||
Room.prototype.addAccountData = function(events) {
|
||||
for (var i = 0; i < events.length; i++) {
|
||||
var event = events[i];
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const event = events[i];
|
||||
if (event.getType() === "m.tag") {
|
||||
this.addTags(event);
|
||||
}
|
||||
@@ -1137,16 +1154,16 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
|
||||
if (!ignoreRoomNameEvent) {
|
||||
// check for an alias, if any. for now, assume first alias is the
|
||||
// official one.
|
||||
var mRoomName = room.currentState.getStateEvents("m.room.name", "");
|
||||
const mRoomName = room.currentState.getStateEvents("m.room.name", "");
|
||||
if (mRoomName && mRoomName.getContent() && mRoomName.getContent().name) {
|
||||
return mRoomName.getContent().name;
|
||||
}
|
||||
}
|
||||
|
||||
var alias = room.getCanonicalAlias();
|
||||
let alias = room.getCanonicalAlias();
|
||||
|
||||
if (!alias) {
|
||||
var aliases = room.getAliases();
|
||||
const aliases = room.getAliases();
|
||||
|
||||
if (aliases.length) {
|
||||
alias = aliases[0];
|
||||
@@ -1157,16 +1174,16 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
|
||||
}
|
||||
|
||||
// get members that are NOT ourselves and are actually in the room.
|
||||
var otherMembers = utils.filter(room.currentState.getMembers(), function(m) {
|
||||
const otherMembers = utils.filter(room.currentState.getMembers(), function(m) {
|
||||
return (m.userId !== userId && m.membership !== "leave");
|
||||
});
|
||||
var allMembers = utils.filter(room.currentState.getMembers(), function(m) {
|
||||
const allMembers = utils.filter(room.currentState.getMembers(), function(m) {
|
||||
return (m.membership !== "leave");
|
||||
});
|
||||
var myMemberEventArray = utils.filter(room.currentState.getMembers(), function(m) {
|
||||
const myMemberEventArray = utils.filter(room.currentState.getMembers(), function(m) {
|
||||
return (m.userId == userId);
|
||||
});
|
||||
var myMemberEvent = (
|
||||
const myMemberEvent = (
|
||||
(myMemberEventArray.length && myMemberEventArray[0].events) ?
|
||||
myMemberEventArray[0].events.member.event : undefined
|
||||
);
|
||||
@@ -1176,7 +1193,7 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
|
||||
if (room.currentState.getMember(myMemberEvent.sender)) {
|
||||
// extract who invited us to the room
|
||||
return "Invite from " + room.currentState.getMember(
|
||||
myMemberEvent.sender
|
||||
myMemberEvent.sender,
|
||||
).name;
|
||||
} else if (allMembers[0].events.member) {
|
||||
// use the sender field from the invite event, although this only
|
||||
@@ -1193,45 +1210,38 @@ function calculateRoomName(room, userId, ignoreRoomNameEvent) {
|
||||
// self-chat, peeked room with 1 participant,
|
||||
// or inbound invite, or outbound 3PID invite.
|
||||
if (allMembers[0].userId === userId) {
|
||||
var thirdPartyInvites =
|
||||
const thirdPartyInvites =
|
||||
room.currentState.getStateEvents("m.room.third_party_invite");
|
||||
if (thirdPartyInvites && thirdPartyInvites.length > 0) {
|
||||
var name = "Inviting " +
|
||||
let name = "Inviting " +
|
||||
thirdPartyInvites[0].getContent().display_name;
|
||||
if (thirdPartyInvites.length > 1) {
|
||||
if (thirdPartyInvites.length == 2) {
|
||||
name += " and " +
|
||||
thirdPartyInvites[1].getContent().display_name;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
name += " and " +
|
||||
thirdPartyInvites.length + " others";
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return "Empty room";
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return allMembers[0].name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// there really isn't anyone in this room...
|
||||
return "Empty room";
|
||||
}
|
||||
}
|
||||
else if (otherMembers.length === 1) {
|
||||
} else if (otherMembers.length === 1) {
|
||||
return otherMembers[0].name;
|
||||
}
|
||||
else if (otherMembers.length === 2) {
|
||||
} else if (otherMembers.length === 2) {
|
||||
return (
|
||||
otherMembers[0].name + " and " + otherMembers[1].name
|
||||
);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return (
|
||||
otherMembers[0].name + " and " + (otherMembers.length - 1) + " others"
|
||||
);
|
||||
@@ -1248,11 +1258,11 @@ function reEmit(reEmitEntity, emittableEntity, eventNames) {
|
||||
// Transformation Example:
|
||||
// listener on "foo" => function(a,b) { ... }
|
||||
// Re-emit on "thing" => thing.emit("foo", a, b)
|
||||
var newArgs = [eventName];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
const newArgs = [eventName];
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
newArgs.push(arguments[i]);
|
||||
}
|
||||
reEmitEntity.emit.apply(reEmitEntity, newArgs);
|
||||
reEmitEntity.emit(...newArgs);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -19,8 +19,8 @@ limitations under the License.
|
||||
* @module models/search-result
|
||||
*/
|
||||
|
||||
var EventContext = require("./event-context");
|
||||
var utils = require("../utils");
|
||||
const EventContext = require("./event-context");
|
||||
const utils = require("../utils");
|
||||
|
||||
/**
|
||||
* Construct a new SearchResult
|
||||
@@ -45,11 +45,11 @@ function SearchResult(rank, eventContext) {
|
||||
*/
|
||||
|
||||
SearchResult.fromJson = function(jsonObj, eventMapper) {
|
||||
var jsonContext = jsonObj.context || {};
|
||||
var events_before = jsonContext.events_before || [];
|
||||
var events_after = jsonContext.events_after || [];
|
||||
const jsonContext = jsonObj.context || {};
|
||||
const events_before = jsonContext.events_before || [];
|
||||
const events_after = jsonContext.events_after || [];
|
||||
|
||||
var context = new EventContext(eventMapper(jsonObj.result));
|
||||
const context = new EventContext(eventMapper(jsonObj.result));
|
||||
|
||||
context.setPaginateToken(jsonContext.start, true);
|
||||
context.addEvents(utils.map(events_before, eventMapper), true);
|
||||
@@ -17,8 +17,8 @@ limitations under the License.
|
||||
/**
|
||||
* @module models/user
|
||||
*/
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
var utils = require("../utils");
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
const utils = require("../utils");
|
||||
|
||||
/**
|
||||
* Construct a new User. A User must have an ID and can optionally have extra
|
||||
@@ -54,7 +54,7 @@ function User(userId) {
|
||||
this.currentlyActive = false;
|
||||
this.events = {
|
||||
presence: null,
|
||||
profile: null
|
||||
profile: null,
|
||||
};
|
||||
this._updateModifiedTime();
|
||||
}
|
||||
@@ -73,26 +73,23 @@ User.prototype.setPresenceEvent = function(event) {
|
||||
if (event.getType() !== "m.presence") {
|
||||
return;
|
||||
}
|
||||
var firstFire = this.events.presence === null;
|
||||
const firstFire = this.events.presence === null;
|
||||
this.events.presence = event;
|
||||
|
||||
var eventsToFire = [];
|
||||
const eventsToFire = [];
|
||||
if (event.getContent().presence !== this.presence || firstFire) {
|
||||
eventsToFire.push("User.presence");
|
||||
}
|
||||
if (event.getContent().avatar_url &&
|
||||
event.getContent().avatar_url !== this.avatarUrl)
|
||||
{
|
||||
event.getContent().avatar_url !== this.avatarUrl) {
|
||||
eventsToFire.push("User.avatarUrl");
|
||||
}
|
||||
if (event.getContent().displayname &&
|
||||
event.getContent().displayname !== this.displayName)
|
||||
{
|
||||
event.getContent().displayname !== this.displayName) {
|
||||
eventsToFire.push("User.displayName");
|
||||
}
|
||||
if (event.getContent().currently_active !== undefined &&
|
||||
event.getContent().currently_active !== this.currentlyActive)
|
||||
{
|
||||
event.getContent().currently_active !== this.currentlyActive) {
|
||||
eventsToFire.push("User.currentlyActive");
|
||||
}
|
||||
|
||||
@@ -114,7 +111,7 @@ User.prototype.setPresenceEvent = function(event) {
|
||||
|
||||
this._updateModifiedTime();
|
||||
|
||||
for (var i = 0; i < eventsToFire.length; i++) {
|
||||
for (let i = 0; i < eventsToFire.length; i++) {
|
||||
this.emit(eventsToFire[i], event, this);
|
||||
}
|
||||
};
|
||||
@@ -125,7 +122,7 @@ User.prototype.setPresenceEvent = function(event) {
|
||||
* @param {string} name The new display name.
|
||||
*/
|
||||
User.prototype.setDisplayName = function(name) {
|
||||
var oldName = this.displayName;
|
||||
const oldName = this.displayName;
|
||||
this.displayName = name;
|
||||
if (name !== oldName) {
|
||||
this._updateModifiedTime();
|
||||
@@ -149,7 +146,7 @@ User.prototype.setRawDisplayName = function(name) {
|
||||
* @param {string} url The new avatar URL.
|
||||
*/
|
||||
User.prototype.setAvatarUrl = function(url) {
|
||||
var oldUrl = this.avatarUrl;
|
||||
const oldUrl = this.avatarUrl;
|
||||
this.avatarUrl = url;
|
||||
if (url !== oldUrl) {
|
||||
this._updateModifiedTime();
|
||||
@@ -23,24 +23,28 @@ limitations under the License.
|
||||
* @param {Object} client The Matrix client object to use
|
||||
*/
|
||||
function PushProcessor(client) {
|
||||
var escapeRegExp = function(string) {
|
||||
const escapeRegExp = function(string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
};
|
||||
|
||||
var matchingRuleFromKindSet = function(ev, kindset, device) {
|
||||
var rulekinds_in_order = ['override', 'content', 'room', 'sender', 'underride'];
|
||||
for (var ruleKindIndex = 0;
|
||||
const matchingRuleFromKindSet = function(ev, kindset, device) {
|
||||
const rulekinds_in_order = ['override', 'content', 'room', 'sender', 'underride'];
|
||||
for (let ruleKindIndex = 0;
|
||||
ruleKindIndex < rulekinds_in_order.length;
|
||||
++ruleKindIndex) {
|
||||
var kind = rulekinds_in_order[ruleKindIndex];
|
||||
var ruleset = kindset[kind];
|
||||
const kind = rulekinds_in_order[ruleKindIndex];
|
||||
const ruleset = kindset[kind];
|
||||
|
||||
for (var ruleIndex = 0; ruleIndex < ruleset.length; ++ruleIndex) {
|
||||
var rule = ruleset[ruleIndex];
|
||||
if (!rule.enabled) { continue; }
|
||||
for (let ruleIndex = 0; ruleIndex < ruleset.length; ++ruleIndex) {
|
||||
const rule = ruleset[ruleIndex];
|
||||
if (!rule.enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var rawrule = templateRuleToRaw(kind, rule, device);
|
||||
if (!rawrule) { continue; }
|
||||
const rawrule = templateRuleToRaw(kind, rule, device);
|
||||
if (!rawrule) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ruleMatchesEvent(rawrule, ev)) {
|
||||
rule.kind = kind;
|
||||
@@ -51,11 +55,11 @@ function PushProcessor(client) {
|
||||
return null;
|
||||
};
|
||||
|
||||
var templateRuleToRaw = function(kind, tprule, device) {
|
||||
var rawrule = {
|
||||
const templateRuleToRaw = function(kind, tprule, device) {
|
||||
const rawrule = {
|
||||
'rule_id': tprule.rule_id,
|
||||
'actions': tprule.actions,
|
||||
'conditions': []
|
||||
'conditions': [],
|
||||
};
|
||||
switch (kind) {
|
||||
case 'underride':
|
||||
@@ -63,55 +67,61 @@ function PushProcessor(client) {
|
||||
rawrule.conditions = tprule.conditions;
|
||||
break;
|
||||
case 'room':
|
||||
if (!tprule.rule_id) { return null; }
|
||||
if (!tprule.rule_id) {
|
||||
return null;
|
||||
}
|
||||
rawrule.conditions.push({
|
||||
'kind': 'event_match',
|
||||
'key': 'room_id',
|
||||
'pattern': tprule.rule_id
|
||||
'pattern': tprule.rule_id,
|
||||
});
|
||||
break;
|
||||
case 'sender':
|
||||
if (!tprule.rule_id) { return null; }
|
||||
if (!tprule.rule_id) {
|
||||
return null;
|
||||
}
|
||||
rawrule.conditions.push({
|
||||
'kind': 'event_match',
|
||||
'key': 'user_id',
|
||||
'pattern': tprule.rule_id
|
||||
'pattern': tprule.rule_id,
|
||||
});
|
||||
break;
|
||||
case 'content':
|
||||
if (!tprule.pattern) { return null; }
|
||||
if (!tprule.pattern) {
|
||||
return null;
|
||||
}
|
||||
rawrule.conditions.push({
|
||||
'kind': 'event_match',
|
||||
'key': 'content.body',
|
||||
'pattern': tprule.pattern
|
||||
'pattern': tprule.pattern,
|
||||
});
|
||||
break;
|
||||
}
|
||||
if (device) {
|
||||
rawrule.conditions.push({
|
||||
'kind': 'device',
|
||||
'profile_tag': device
|
||||
'profile_tag': device,
|
||||
});
|
||||
}
|
||||
return rawrule;
|
||||
};
|
||||
|
||||
var ruleMatchesEvent = function(rule, ev) {
|
||||
var ret = true;
|
||||
for (var i = 0; i < rule.conditions.length; ++i) {
|
||||
var cond = rule.conditions[i];
|
||||
const ruleMatchesEvent = function(rule, ev) {
|
||||
let ret = true;
|
||||
for (let i = 0; i < rule.conditions.length; ++i) {
|
||||
const cond = rule.conditions[i];
|
||||
ret &= eventFulfillsCondition(cond, ev);
|
||||
}
|
||||
//console.log("Rule "+rule.rule_id+(ret ? " matches" : " doesn't match"));
|
||||
return ret;
|
||||
};
|
||||
|
||||
var eventFulfillsCondition = function(cond, ev) {
|
||||
var condition_functions = {
|
||||
const eventFulfillsCondition = function(cond, ev) {
|
||||
const condition_functions = {
|
||||
"event_match": eventFulfillsEventMatchCondition,
|
||||
"device": eventFulfillsDeviceCondition,
|
||||
"contains_display_name": eventFulfillsDisplayNameCondition,
|
||||
"room_member_count": eventFulfillsRoomMemberCountCondition
|
||||
"room_member_count": eventFulfillsRoomMemberCountCondition,
|
||||
};
|
||||
if (condition_functions[cond.kind]) {
|
||||
return condition_functions[cond.kind](cond, ev);
|
||||
@@ -119,21 +129,29 @@ function PushProcessor(client) {
|
||||
return true;
|
||||
};
|
||||
|
||||
var eventFulfillsRoomMemberCountCondition = function(cond, ev) {
|
||||
if (!cond.is) { return false; }
|
||||
const eventFulfillsRoomMemberCountCondition = function(cond, ev) {
|
||||
if (!cond.is) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var room = client.getRoom(ev.getRoomId());
|
||||
if (!room || !room.currentState || !room.currentState.members) { return false; }
|
||||
const room = client.getRoom(ev.getRoomId());
|
||||
if (!room || !room.currentState || !room.currentState.members) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var memberCount = Object.keys(room.currentState.members).filter(function(m) {
|
||||
const memberCount = Object.keys(room.currentState.members).filter(function(m) {
|
||||
return room.currentState.members[m].membership == 'join';
|
||||
}).length;
|
||||
|
||||
var m = cond.is.match(/^([=<>]*)([0-9]*)$/);
|
||||
if (!m) { return false; }
|
||||
var ineq = m[1];
|
||||
var rhs = parseInt(m[2]);
|
||||
if (isNaN(rhs)) { return false; }
|
||||
const m = cond.is.match(/^([=<>]*)([0-9]*)$/);
|
||||
if (!m) {
|
||||
return false;
|
||||
}
|
||||
const ineq = m[1];
|
||||
const rhs = parseInt(m[2]);
|
||||
if (isNaN(rhs)) {
|
||||
return false;
|
||||
}
|
||||
switch (ineq) {
|
||||
case '':
|
||||
case '==':
|
||||
@@ -151,64 +169,68 @@ function PushProcessor(client) {
|
||||
}
|
||||
};
|
||||
|
||||
var eventFulfillsDisplayNameCondition = function(cond, ev) {
|
||||
var content = ev.getContent();
|
||||
const eventFulfillsDisplayNameCondition = function(cond, ev) {
|
||||
const content = ev.getContent();
|
||||
if (!content || !content.body || typeof content.body != 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var room = client.getRoom(ev.getRoomId());
|
||||
const room = client.getRoom(ev.getRoomId());
|
||||
if (!room || !room.currentState || !room.currentState.members ||
|
||||
!room.currentState.getMember(client.credentials.userId)) { return false; }
|
||||
!room.currentState.getMember(client.credentials.userId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var displayName = room.currentState.getMember(client.credentials.userId).name;
|
||||
const displayName = room.currentState.getMember(client.credentials.userId).name;
|
||||
|
||||
// N.B. we can't use \b as it chokes on unicode. however \W seems to be okay
|
||||
// as shorthand for [^0-9A-Za-z_].
|
||||
var pat = new RegExp("(^|\\W)" + escapeRegExp(displayName) + "(\\W|$)", 'i');
|
||||
const pat = new RegExp("(^|\\W)" + escapeRegExp(displayName) + "(\\W|$)", 'i');
|
||||
return content.body.search(pat) > -1;
|
||||
};
|
||||
|
||||
var eventFulfillsDeviceCondition = function(cond, ev) {
|
||||
const eventFulfillsDeviceCondition = function(cond, ev) {
|
||||
return false; // XXX: Allow a profile tag to be set for the web client instance
|
||||
};
|
||||
|
||||
var eventFulfillsEventMatchCondition = function(cond, ev) {
|
||||
var val = valueForDottedKey(cond.key, ev);
|
||||
if (!val || typeof val != 'string') { return false; }
|
||||
const eventFulfillsEventMatchCondition = function(cond, ev) {
|
||||
const val = valueForDottedKey(cond.key, ev);
|
||||
if (!val || typeof val != 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var pat;
|
||||
let pat;
|
||||
if (cond.key == 'content.body') {
|
||||
pat = '(^|\\W)' + globToRegexp(cond.pattern) + '(\\W|$)';
|
||||
} else {
|
||||
pat = '^' + globToRegexp(cond.pattern) + '$';
|
||||
}
|
||||
var regex = new RegExp(pat, 'i');
|
||||
const regex = new RegExp(pat, 'i');
|
||||
return !!val.match(regex);
|
||||
};
|
||||
|
||||
var globToRegexp = function(glob) {
|
||||
const globToRegexp = function(glob) {
|
||||
// From
|
||||
// https://github.com/matrix-org/synapse/blob/abbee6b29be80a77e05730707602f3bbfc3f38cb/synapse/push/__init__.py#L132
|
||||
// Because micromatch is about 130KB with dependencies,
|
||||
// and minimatch is not much better.
|
||||
var pat = escapeRegExp(glob);
|
||||
let pat = escapeRegExp(glob);
|
||||
pat = pat.replace(/\\\*/, '.*');
|
||||
pat = pat.replace(/\?/, '.');
|
||||
pat = pat.replace(/\\\[(!|)(.*)\\]/, function(match, p1, p2, offset, string) {
|
||||
var first = p1 && '^' || '';
|
||||
var second = p2.replace(/\\\-/, '-');
|
||||
const first = p1 && '^' || '';
|
||||
const second = p2.replace(/\\\-/, '-');
|
||||
return '[' + first + second + ']';
|
||||
});
|
||||
return pat;
|
||||
};
|
||||
|
||||
var valueForDottedKey = function(key, ev) {
|
||||
var parts = key.split('.');
|
||||
var val;
|
||||
const valueForDottedKey = function(key, ev) {
|
||||
const parts = key.split('.');
|
||||
let val;
|
||||
|
||||
// special-case the first component to deal with encrypted messages
|
||||
var firstPart = parts[0];
|
||||
const firstPart = parts[0];
|
||||
if (firstPart == 'content') {
|
||||
val = ev.getContent();
|
||||
parts.shift();
|
||||
@@ -221,33 +243,43 @@ function PushProcessor(client) {
|
||||
}
|
||||
|
||||
while (parts.length > 0) {
|
||||
var thispart = parts.shift();
|
||||
if (!val[thispart]) { return null; }
|
||||
const thispart = parts.shift();
|
||||
if (!val[thispart]) {
|
||||
return null;
|
||||
}
|
||||
val = val[thispart];
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
||||
var matchingRuleForEventWithRulesets = function(ev, rulesets) {
|
||||
if (!rulesets || !rulesets.device) { return null; }
|
||||
if (ev.getSender() == client.credentials.userId) { return null; }
|
||||
const matchingRuleForEventWithRulesets = function(ev, rulesets) {
|
||||
if (!rulesets || !rulesets.device) {
|
||||
return null;
|
||||
}
|
||||
if (ev.getSender() == client.credentials.userId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var allDevNames = Object.keys(rulesets.device);
|
||||
for (var i = 0; i < allDevNames.length; ++i) {
|
||||
var devname = allDevNames[i];
|
||||
var devrules = rulesets.device[devname];
|
||||
const allDevNames = Object.keys(rulesets.device);
|
||||
for (let i = 0; i < allDevNames.length; ++i) {
|
||||
const devname = allDevNames[i];
|
||||
const devrules = rulesets.device[devname];
|
||||
|
||||
var matchingRule = matchingRuleFromKindSet(devrules, devname);
|
||||
if (matchingRule) { return matchingRule; }
|
||||
const matchingRule = matchingRuleFromKindSet(devrules, devname);
|
||||
if (matchingRule) {
|
||||
return matchingRule;
|
||||
}
|
||||
}
|
||||
return matchingRuleFromKindSet(ev, rulesets.global);
|
||||
};
|
||||
|
||||
var pushActionsForEventAndRulesets = function(ev, rulesets) {
|
||||
var rule = matchingRuleForEventWithRulesets(ev, rulesets);
|
||||
if (!rule) { return {}; }
|
||||
const pushActionsForEventAndRulesets = function(ev, rulesets) {
|
||||
const rule = matchingRuleForEventWithRulesets(ev, rulesets);
|
||||
if (!rule) {
|
||||
return {};
|
||||
}
|
||||
|
||||
var actionObj = PushProcessor.actionListToActionsObject(rule.actions);
|
||||
const actionObj = PushProcessor.actionListToActionsObject(rule.actions);
|
||||
|
||||
// Some actions are implicit in some situations: we add those here
|
||||
if (actionObj.tweaks.highlight === undefined) {
|
||||
@@ -280,13 +312,15 @@ function PushProcessor(client) {
|
||||
* @return {object} A object with key 'notify' (true or false) and an object of actions
|
||||
*/
|
||||
PushProcessor.actionListToActionsObject = function(actionlist) {
|
||||
var actionobj = { 'notify': false, 'tweaks': {} };
|
||||
for (var i = 0; i < actionlist.length; ++i) {
|
||||
var action = actionlist[i];
|
||||
const actionobj = { 'notify': false, 'tweaks': {} };
|
||||
for (let i = 0; i < actionlist.length; ++i) {
|
||||
const action = actionlist[i];
|
||||
if (action === 'notify') {
|
||||
actionobj.notify = true;
|
||||
} else if (typeof action === 'object') {
|
||||
if (action.value === undefined) { action.value = true; }
|
||||
if (action.value === undefined) {
|
||||
action.value = true;
|
||||
}
|
||||
actionobj.tweaks[action.set_tweak] = action.value;
|
||||
}
|
||||
}
|
||||
@@ -27,20 +27,20 @@ limitations under the License.
|
||||
|
||||
// we schedule a callback at least this often, to check if we've missed out on
|
||||
// some wall-clock time due to being suspended.
|
||||
var TIMER_CHECK_PERIOD_MS = 1000;
|
||||
const TIMER_CHECK_PERIOD_MS = 1000;
|
||||
|
||||
// counter, for making up ids to return from setTimeout
|
||||
var _count = 0;
|
||||
let _count = 0;
|
||||
|
||||
// the key for our callback with the real global.setTimeout
|
||||
var _realCallbackKey;
|
||||
let _realCallbackKey;
|
||||
|
||||
// a sorted list of the callbacks to be run.
|
||||
// each is an object with keys [runAt, func, params, key].
|
||||
var _callbackList = [];
|
||||
const _callbackList = [];
|
||||
|
||||
// var debuglog = console.log.bind(console);
|
||||
var debuglog = function() {};
|
||||
const debuglog = function() {};
|
||||
|
||||
/**
|
||||
* Replace the function used by this module to get the current time.
|
||||
@@ -54,7 +54,7 @@ var debuglog = function() {};
|
||||
module.exports.setNow = function(f) {
|
||||
_now = f || Date.now;
|
||||
};
|
||||
var _now = Date.now;
|
||||
let _now = Date.now;
|
||||
|
||||
/**
|
||||
* reimplementation of window.setTimeout, which will call the callback if
|
||||
@@ -72,12 +72,12 @@ module.exports.setTimeout = function(func, delayMs) {
|
||||
delayMs = 0;
|
||||
}
|
||||
|
||||
var params = Array.prototype.slice.call(arguments, 2);
|
||||
var runAt = _now() + delayMs;
|
||||
var key = _count++;
|
||||
const params = Array.prototype.slice.call(arguments, 2);
|
||||
const runAt = _now() + delayMs;
|
||||
const key = _count++;
|
||||
debuglog("setTimeout: scheduling cb", key, "at", runAt,
|
||||
"(delay", delayMs, ")");
|
||||
var data = {
|
||||
const data = {
|
||||
runAt: runAt,
|
||||
func: func,
|
||||
params: params,
|
||||
@@ -85,10 +85,10 @@ module.exports.setTimeout = function(func, delayMs) {
|
||||
};
|
||||
|
||||
// figure out where it goes in the list
|
||||
var idx = binarySearch(
|
||||
const idx = binarySearch(
|
||||
_callbackList, function(el) {
|
||||
return el.runAt - runAt;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
_callbackList.splice(idx, 0, data);
|
||||
@@ -108,9 +108,9 @@ module.exports.clearTimeout = function(key) {
|
||||
}
|
||||
|
||||
// remove the element from the list
|
||||
var i;
|
||||
let i;
|
||||
for (i = 0; i < _callbackList.length; i++) {
|
||||
var cb = _callbackList[i];
|
||||
const cb = _callbackList[i];
|
||||
if (cb.key == key) {
|
||||
_callbackList.splice(i, 1);
|
||||
break;
|
||||
@@ -129,29 +129,29 @@ function _scheduleRealCallback() {
|
||||
global.clearTimeout(_realCallbackKey);
|
||||
}
|
||||
|
||||
var first = _callbackList[0];
|
||||
const first = _callbackList[0];
|
||||
|
||||
if (!first) {
|
||||
debuglog("_scheduleRealCallback: no more callbacks, not rescheduling");
|
||||
return;
|
||||
}
|
||||
|
||||
var now = _now();
|
||||
var delayMs = Math.min(first.runAt - now, TIMER_CHECK_PERIOD_MS);
|
||||
const now = _now();
|
||||
const delayMs = Math.min(first.runAt - now, TIMER_CHECK_PERIOD_MS);
|
||||
|
||||
debuglog("_scheduleRealCallback: now:", now, "delay:", delayMs);
|
||||
_realCallbackKey = global.setTimeout(_runCallbacks, delayMs);
|
||||
}
|
||||
|
||||
function _runCallbacks() {
|
||||
var cb;
|
||||
var now = _now();
|
||||
let cb;
|
||||
const now = _now();
|
||||
debuglog("_runCallbacks: now:", now);
|
||||
|
||||
// get the list of things to call
|
||||
var callbacksToRun = [];
|
||||
const callbacksToRun = [];
|
||||
while (true) {
|
||||
var first = _callbackList[0];
|
||||
const first = _callbackList[0];
|
||||
if (!first || first.runAt > now) {
|
||||
break;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ function _runCallbacks() {
|
||||
// register their own setTimeouts.
|
||||
_scheduleRealCallback();
|
||||
|
||||
for (var i = 0; i < callbacksToRun.length; i++) {
|
||||
for (let i = 0; i < callbacksToRun.length; i++) {
|
||||
cb = callbacksToRun[i];
|
||||
try {
|
||||
cb.func.apply(null, cb.params);
|
||||
@@ -184,12 +184,12 @@ function _runCallbacks() {
|
||||
*/
|
||||
function binarySearch(array, func) {
|
||||
// min is inclusive, max exclusive.
|
||||
var min = 0,
|
||||
let min = 0,
|
||||
max = array.length;
|
||||
|
||||
while (min < max) {
|
||||
var mid = (min + max) >> 1;
|
||||
var res = func(array[mid]);
|
||||
const mid = (min + max) >> 1;
|
||||
const res = func(array[mid]);
|
||||
if (res > 0) {
|
||||
// the element at 'mid' is too big; set it as the new max.
|
||||
max = mid;
|
||||
@@ -19,10 +19,10 @@ limitations under the License.
|
||||
* of requests.
|
||||
* @module scheduler
|
||||
*/
|
||||
var utils = require("./utils");
|
||||
var q = require("q");
|
||||
const utils = require("./utils");
|
||||
const q = require("q");
|
||||
|
||||
var DEBUG = false; // set true to enable console logging.
|
||||
const DEBUG = false; // set true to enable console logging.
|
||||
|
||||
/**
|
||||
* Construct a scheduler for Matrix. Requires
|
||||
@@ -60,7 +60,7 @@ function MatrixScheduler(retryAlgorithm, queueAlgorithm) {
|
||||
* @see MatrixScheduler.removeEventFromQueue To remove an event from the queue.
|
||||
*/
|
||||
MatrixScheduler.prototype.getQueueForEvent = function(event) {
|
||||
var name = this.queueAlgorithm(event);
|
||||
const name = this.queueAlgorithm(event);
|
||||
if (!name || !this._queues[name]) {
|
||||
return null;
|
||||
}
|
||||
@@ -76,11 +76,11 @@ MatrixScheduler.prototype.getQueueForEvent = function(event) {
|
||||
* @return {boolean} True if this event was removed.
|
||||
*/
|
||||
MatrixScheduler.prototype.removeEventFromQueue = function(event) {
|
||||
var name = this.queueAlgorithm(event);
|
||||
const name = this.queueAlgorithm(event);
|
||||
if (!name || !this._queues[name]) {
|
||||
return false;
|
||||
}
|
||||
var removed = false;
|
||||
let removed = false;
|
||||
utils.removeElement(this._queues[name], function(element) {
|
||||
if (element.event.getId() === event.getId()) {
|
||||
removed = true;
|
||||
@@ -110,7 +110,7 @@ MatrixScheduler.prototype.setProcessFunction = function(fn) {
|
||||
* resolved or rejected in due time, else null.
|
||||
*/
|
||||
MatrixScheduler.prototype.queueEvent = function(event) {
|
||||
var queueName = this.queueAlgorithm(event);
|
||||
const queueName = this.queueAlgorithm(event);
|
||||
if (!queueName) {
|
||||
return null;
|
||||
}
|
||||
@@ -118,15 +118,15 @@ MatrixScheduler.prototype.queueEvent = function(event) {
|
||||
if (!this._queues[queueName]) {
|
||||
this._queues[queueName] = [];
|
||||
}
|
||||
var defer = q.defer();
|
||||
const defer = q.defer();
|
||||
this._queues[queueName].push({
|
||||
event: event,
|
||||
defer: defer,
|
||||
attempts: 0
|
||||
attempts: 0,
|
||||
});
|
||||
debuglog(
|
||||
"Queue algorithm dumped event %s into queue '%s'",
|
||||
event.getId(), queueName
|
||||
event.getId(), queueName,
|
||||
);
|
||||
_startProcessingQueues(this);
|
||||
return defer.promise;
|
||||
@@ -155,7 +155,7 @@ MatrixScheduler.RETRY_BACKOFF_RATELIMIT = function(event, attempts, err) {
|
||||
}
|
||||
|
||||
if (err.name === "M_LIMIT_EXCEEDED") {
|
||||
var waitTime = err.data.retry_after_ms;
|
||||
const waitTime = err.data.retry_after_ms;
|
||||
if (waitTime) {
|
||||
return waitTime;
|
||||
}
|
||||
@@ -201,10 +201,10 @@ function _startProcessingQueues(scheduler) {
|
||||
|
||||
function _processQueue(scheduler, queueName) {
|
||||
// get head of queue
|
||||
var obj = _peekNextEvent(scheduler, queueName);
|
||||
const obj = _peekNextEvent(scheduler, queueName);
|
||||
if (!obj) {
|
||||
// queue is empty. Mark as inactive and stop recursing.
|
||||
var index = scheduler._activeQueues.indexOf(queueName);
|
||||
const index = scheduler._activeQueues.indexOf(queueName);
|
||||
if (index >= 0) {
|
||||
scheduler._activeQueues.splice(index, 1);
|
||||
}
|
||||
@@ -213,7 +213,7 @@ function _processQueue(scheduler, queueName) {
|
||||
}
|
||||
debuglog(
|
||||
"Queue '%s' has %s pending events",
|
||||
queueName, scheduler._queues[queueName].length
|
||||
queueName, scheduler._queues[queueName].length,
|
||||
);
|
||||
// fire the process function and if it resolves, resolve the deferred. Else
|
||||
// invoke the retry algorithm.
|
||||
@@ -227,22 +227,21 @@ function _processQueue(scheduler, queueName) {
|
||||
}, function(err) {
|
||||
obj.attempts += 1;
|
||||
// ask the retry algorithm when/if we should try again
|
||||
var waitTimeMs = scheduler.retryAlgorithm(obj.event, obj.attempts, err);
|
||||
const waitTimeMs = scheduler.retryAlgorithm(obj.event, obj.attempts, err);
|
||||
debuglog(
|
||||
"retry(%s) err=%s event_id=%s waitTime=%s",
|
||||
obj.attempts, err, obj.event.getId(), waitTimeMs
|
||||
obj.attempts, err, obj.event.getId(), waitTimeMs,
|
||||
);
|
||||
if (waitTimeMs === -1) { // give up (you quitter!)
|
||||
debuglog(
|
||||
"Queue '%s' giving up on event %s", queueName, obj.event.getId()
|
||||
"Queue '%s' giving up on event %s", queueName, obj.event.getId(),
|
||||
);
|
||||
// remove this from the queue
|
||||
_removeNextEvent(scheduler, queueName);
|
||||
obj.defer.reject(err);
|
||||
// process next event
|
||||
_processQueue(scheduler, queueName);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
setTimeout(function() {
|
||||
_processQueue(scheduler, queueName);
|
||||
}, waitTimeMs);
|
||||
@@ -251,7 +250,7 @@ function _processQueue(scheduler, queueName) {
|
||||
}
|
||||
|
||||
function _peekNextEvent(scheduler, queueName) {
|
||||
var queue = scheduler._queues[queueName];
|
||||
const queue = scheduler._queues[queueName];
|
||||
if (!utils.isArray(queue)) {
|
||||
return null;
|
||||
}
|
||||
@@ -259,7 +258,7 @@ function _peekNextEvent(scheduler, queueName) {
|
||||
}
|
||||
|
||||
function _removeNextEvent(scheduler, queueName) {
|
||||
var queue = scheduler._queues[queueName];
|
||||
const queue = scheduler._queues[queueName];
|
||||
if (!utils.isArray(queue)) {
|
||||
return null;
|
||||
}
|
||||
@@ -268,7 +267,7 @@ function _removeNextEvent(scheduler, queueName) {
|
||||
|
||||
function debuglog() {
|
||||
if (DEBUG) {
|
||||
console.log.apply(console, arguments);
|
||||
console.log(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,16 +18,15 @@ limitations under the License.
|
||||
* This is an internal module. See {@link MatrixInMemoryStore} for the public class.
|
||||
* @module store/memory
|
||||
*/
|
||||
var utils = require("../utils");
|
||||
var User = require("../models/user");
|
||||
const utils = require("../utils");
|
||||
const User = require("../models/user");
|
||||
|
||||
/**
|
||||
* Construct a new in-memory data store for the Matrix Client.
|
||||
* @constructor
|
||||
* @param {Object=} opts Config options
|
||||
* @param {LocalStorage} opts.localStorage The local storage instance to persist
|
||||
* some forms of data such as tokens. Rooms will NOT be stored. See
|
||||
* {@link WebStorageStore} to persist rooms.
|
||||
* some forms of data such as tokens. Rooms will NOT be stored.
|
||||
*/
|
||||
module.exports.MatrixInMemoryStore = function MatrixInMemoryStore(opts) {
|
||||
opts = opts || {};
|
||||
@@ -78,7 +77,7 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
// map up-to-date.
|
||||
room.currentState.on("RoomState.members", this._onRoomMember.bind(this));
|
||||
// add existing members
|
||||
var self = this;
|
||||
const self = this;
|
||||
room.currentState.getMembers().forEach(function(m) {
|
||||
self._onRoomMember(null, room.currentState, m);
|
||||
});
|
||||
@@ -98,12 +97,12 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
var user = this.users[member.userId] || new User(member.userId);
|
||||
const user = this.users[member.userId] || new User(member.userId);
|
||||
if (member.name) {
|
||||
user.setDisplayName(member.name);
|
||||
if (member.events.member) {
|
||||
user.setRawDisplayName(
|
||||
member.events.member.getDirectionalContent().displayname
|
||||
member.events.member.getDirectionalContent().displayname,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -203,7 +202,9 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
* @param {Filter} filter
|
||||
*/
|
||||
storeFilter: function(filter) {
|
||||
if (!filter) { return; }
|
||||
if (!filter) {
|
||||
return;
|
||||
}
|
||||
if (!this.filters[filter.userId]) {
|
||||
this.filters[filter.userId] = {};
|
||||
}
|
||||
@@ -234,8 +235,7 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
}
|
||||
try {
|
||||
return this.localStorage.getItem("mxjssdk_memory_filter_" + filterName);
|
||||
}
|
||||
catch (e) {}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -250,8 +250,7 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
}
|
||||
try {
|
||||
this.localStorage.setItem("mxjssdk_memory_filter_" + filterName, filterId);
|
||||
}
|
||||
catch (e) {}
|
||||
} catch (e) {}
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -261,7 +260,7 @@ module.exports.MatrixInMemoryStore.prototype = {
|
||||
* @param {Array<MatrixEvent>} events The events to store.
|
||||
*/
|
||||
storeAccountDataEvents: function(events) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
events.forEach(function(event) {
|
||||
self.accountData[event.getType()] = event;
|
||||
});
|
||||
@@ -19,10 +19,10 @@ limitations under the License.
|
||||
* @module store/session/webstorage
|
||||
*/
|
||||
|
||||
var utils = require("../../utils");
|
||||
const utils = require("../../utils");
|
||||
|
||||
var DEBUG = false; // set true to enable console logging.
|
||||
var E2E_PREFIX = "session.e2e.";
|
||||
const DEBUG = false; // set true to enable console logging.
|
||||
const E2E_PREFIX = "session.e2e.";
|
||||
|
||||
/**
|
||||
* Construct a web storage session store, capable of storing account keys,
|
||||
@@ -37,9 +37,12 @@ function WebStorageSessionStore(webStore) {
|
||||
this.store = webStore;
|
||||
if (!utils.isFunction(webStore.getItem) ||
|
||||
!utils.isFunction(webStore.setItem) ||
|
||||
!utils.isFunction(webStore.removeItem)) {
|
||||
!utils.isFunction(webStore.removeItem) ||
|
||||
!utils.isFunction(webStore.key) ||
|
||||
typeof(webStore.length) !== 'number'
|
||||
) {
|
||||
throw new Error(
|
||||
"Supplied webStore does not meet the WebStorage API interface"
|
||||
"Supplied webStore does not meet the WebStorage API interface",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -92,10 +95,31 @@ WebStorageSessionStore.prototype = {
|
||||
* @param {string} userId The user's ID.
|
||||
* @return {object} A map from device ID to keys for the device.
|
||||
*/
|
||||
getEndToEndDevicesForUser: function(userId) {
|
||||
getEndToEndDevicesForUser: function(userId) {
|
||||
return getJsonItem(this.store, keyEndToEndDevicesForUser(userId));
|
||||
},
|
||||
|
||||
/**
|
||||
* Store the sync token corresponding to the device list.
|
||||
*
|
||||
* This is used when starting the client, to get a list of the users who
|
||||
* have changed their device list since the list time we were running.
|
||||
*
|
||||
* @param {String?} token
|
||||
*/
|
||||
storeEndToEndDeviceSyncToken: function(token) {
|
||||
setJsonItem(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN, token);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the sync token corresponding to the device list.
|
||||
*
|
||||
* @return {String?} token
|
||||
*/
|
||||
getEndToEndDeviceSyncToken: function() {
|
||||
return getJsonItem(this.store, KEY_END_TO_END_DEVICE_SYNC_TOKEN);
|
||||
},
|
||||
|
||||
/**
|
||||
* Store a session between the logged-in user and another device
|
||||
* @param {string} deviceKey The public key of the other device.
|
||||
@@ -103,10 +127,10 @@ WebStorageSessionStore.prototype = {
|
||||
* @param {string} session Base64 encoded end-to-end session.
|
||||
*/
|
||||
storeEndToEndSession: function(deviceKey, sessionId, session) {
|
||||
var sessions = this.getEndToEndSessions(deviceKey) || {};
|
||||
const sessions = this.getEndToEndSessions(deviceKey) || {};
|
||||
sessions[sessionId] = session;
|
||||
setJsonItem(
|
||||
this.store, keyEndToEndSessions(deviceKey), sessions
|
||||
this.store, keyEndToEndSessions(deviceKey), sessions,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -120,13 +144,39 @@ WebStorageSessionStore.prototype = {
|
||||
return getJsonItem(this.store, keyEndToEndSessions(deviceKey));
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a list of all known inbound group sessions
|
||||
*
|
||||
* @return {{senderKey: string, sessionId: string}}
|
||||
*/
|
||||
getAllEndToEndInboundGroupSessionKeys: function() {
|
||||
const prefix = E2E_PREFIX + 'inboundgroupsessions/';
|
||||
const result = [];
|
||||
for (let i = 0; i < this.store.length; i++) {
|
||||
const key = this.store.key(i);
|
||||
if (!key.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
// we can't use split, as the components we are trying to split out
|
||||
// might themselves contain '/' characters. We rely on the
|
||||
// senderKey being a (32-byte) curve25519 key, base64-encoded
|
||||
// (hence 43 characters long).
|
||||
|
||||
result.push({
|
||||
senderKey: key.substr(prefix.length, 43),
|
||||
sessionId: key.substr(prefix.length + 44),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
getEndToEndInboundGroupSession: function(senderKey, sessionId) {
|
||||
var key = keyEndToEndInboundGroupSession(senderKey, sessionId);
|
||||
const key = keyEndToEndInboundGroupSession(senderKey, sessionId);
|
||||
return this.store.getItem(key);
|
||||
},
|
||||
|
||||
storeEndToEndInboundGroupSession: function(senderKey, sessionId, pickledSession) {
|
||||
var key = keyEndToEndInboundGroupSession(senderKey, sessionId);
|
||||
const key = keyEndToEndInboundGroupSession(senderKey, sessionId);
|
||||
return this.store.setItem(key, pickledSession);
|
||||
},
|
||||
|
||||
@@ -146,11 +196,12 @@ WebStorageSessionStore.prototype = {
|
||||
*/
|
||||
getEndToEndRoom: function(roomId) {
|
||||
return getJsonItem(this.store, keyEndToEndRoom(roomId));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
||||
var KEY_END_TO_END_ANNOUNCED = E2E_PREFIX + "announced";
|
||||
const KEY_END_TO_END_ACCOUNT = E2E_PREFIX + "account";
|
||||
const KEY_END_TO_END_ANNOUNCED = E2E_PREFIX + "announced";
|
||||
const KEY_END_TO_END_DEVICE_SYNC_TOKEN = E2E_PREFIX + "device_sync_token";
|
||||
|
||||
function keyEndToEndDevicesForUser(userId) {
|
||||
return E2E_PREFIX + "devices/" + userId;
|
||||
@@ -170,9 +221,10 @@ function keyEndToEndRoom(roomId) {
|
||||
|
||||
function getJsonItem(store, key) {
|
||||
try {
|
||||
// if the key is absent, store.getItem() returns null, and
|
||||
// JSON.parse(null) === null, so this returns null.
|
||||
return JSON.parse(store.getItem(key));
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
debuglog("Failed to get key %s: %s", key, e);
|
||||
debuglog(e.stack);
|
||||
}
|
||||
@@ -185,7 +237,7 @@ function setJsonItem(store, key, val) {
|
||||
|
||||
function debuglog() {
|
||||
if (DEBUG) {
|
||||
console.log.apply(console, arguments);
|
||||
console.log(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
+178
-146
@@ -23,20 +23,20 @@ limitations under the License.
|
||||
* an alternative syncing API, we may want to have a proper syncing interface
|
||||
* for HTTP and WS at some point.
|
||||
*/
|
||||
var q = require("q");
|
||||
var User = require("./models/user");
|
||||
var Room = require("./models/room");
|
||||
var utils = require("./utils");
|
||||
var Filter = require("./filter");
|
||||
var EventTimeline = require("./models/event-timeline");
|
||||
const q = require("q");
|
||||
const User = require("./models/user");
|
||||
const Room = require("./models/room");
|
||||
const utils = require("./utils");
|
||||
const Filter = require("./filter");
|
||||
const EventTimeline = require("./models/event-timeline");
|
||||
|
||||
var DEBUG = true;
|
||||
const DEBUG = true;
|
||||
|
||||
// /sync requests allow you to set a timeout= but the request may continue
|
||||
// beyond that and wedge forever, so we need to track how long we are willing
|
||||
// to keep open the connection. This constant is *ADDED* to the timeout= value
|
||||
// to determine the max time we're willing to wait.
|
||||
var BUFFER_PERIOD_MS = 80 * 1000;
|
||||
const BUFFER_PERIOD_MS = 80 * 1000;
|
||||
|
||||
function getFilterName(userId, suffix) {
|
||||
// scope this on the user ID because people may login on many accounts
|
||||
@@ -45,8 +45,10 @@ function getFilterName(userId, suffix) {
|
||||
}
|
||||
|
||||
function debuglog() {
|
||||
if (!DEBUG) { return; }
|
||||
console.log.apply(console, arguments);
|
||||
if (!DEBUG) {
|
||||
return;
|
||||
}
|
||||
console.log(...arguments);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,11 +58,14 @@ function debuglog() {
|
||||
* @constructor
|
||||
* @param {MatrixClient} client The matrix client instance to use.
|
||||
* @param {Object} opts Config options
|
||||
* @param {module:crypto=} opts.crypto Crypto manager
|
||||
*/
|
||||
function SyncApi(client, opts) {
|
||||
this.client = client;
|
||||
opts = opts || {};
|
||||
opts.initialSyncLimit = opts.initialSyncLimit || 8;
|
||||
opts.initialSyncLimit = (
|
||||
opts.initialSyncLimit === undefined ? 8 : opts.initialSyncLimit
|
||||
);
|
||||
opts.resolveInvitesToProfiles = opts.resolveInvitesToProfiles || false;
|
||||
opts.pollTimeout = opts.pollTimeout || (30 * 1000);
|
||||
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
|
||||
@@ -84,8 +89,8 @@ function SyncApi(client, opts) {
|
||||
* @return {Room}
|
||||
*/
|
||||
SyncApi.prototype.createRoom = function(roomId) {
|
||||
var client = this.client;
|
||||
var room = new Room(roomId, {
|
||||
const client = this.client;
|
||||
const room = new Room(roomId, {
|
||||
pendingEventOrdering: this.opts.pendingEventOrdering,
|
||||
timelineSupport: client.timelineSupport,
|
||||
});
|
||||
@@ -104,12 +109,12 @@ SyncApi.prototype.createRoom = function(roomId) {
|
||||
* @private
|
||||
*/
|
||||
SyncApi.prototype._registerStateListeners = function(room) {
|
||||
var client = this.client;
|
||||
const client = this.client;
|
||||
// we need to also re-emit room state and room member events, so hook it up
|
||||
// to the client now. We need to add a listener for RoomState.members in
|
||||
// order to hook them correctly. (TODO: find a better way?)
|
||||
reEmit(client, room.currentState, [
|
||||
"RoomState.events", "RoomState.members", "RoomState.newMember"
|
||||
"RoomState.events", "RoomState.members", "RoomState.newMember",
|
||||
]);
|
||||
room.currentState.on("RoomState.newMember", function(event, state, member) {
|
||||
member.user = client.getUser(member.userId);
|
||||
@@ -117,8 +122,8 @@ SyncApi.prototype._registerStateListeners = function(room) {
|
||||
client, member,
|
||||
[
|
||||
"RoomMember.name", "RoomMember.typing", "RoomMember.powerLevel",
|
||||
"RoomMember.membership"
|
||||
]
|
||||
"RoomMember.membership",
|
||||
],
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -140,34 +145,34 @@ SyncApi.prototype._deregisterStateListeners = function(room) {
|
||||
* @return {Promise} Resolved when they've been added to the store.
|
||||
*/
|
||||
SyncApi.prototype.syncLeftRooms = function() {
|
||||
var client = this.client;
|
||||
var self = this;
|
||||
const client = this.client;
|
||||
const self = this;
|
||||
|
||||
// grab a filter with limit=1 and include_leave=true
|
||||
var filter = new Filter(this.client.credentials.userId);
|
||||
const filter = new Filter(this.client.credentials.userId);
|
||||
filter.setTimelineLimit(1);
|
||||
filter.setIncludeLeaveRooms(true);
|
||||
|
||||
var localTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS;
|
||||
var qps = {
|
||||
timeout: 0 // don't want to block since this is a single isolated req
|
||||
const localTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS;
|
||||
const qps = {
|
||||
timeout: 0, // don't want to block since this is a single isolated req
|
||||
};
|
||||
|
||||
return client.getOrCreateFilter(
|
||||
getFilterName(client.credentials.userId, "LEFT_ROOMS"), filter
|
||||
getFilterName(client.credentials.userId, "LEFT_ROOMS"), filter,
|
||||
).then(function(filterId) {
|
||||
qps.filter = filterId;
|
||||
return client._http.authedRequest(
|
||||
undefined, "GET", "/sync", qps, undefined, localTimeoutMs
|
||||
undefined, "GET", "/sync", qps, undefined, localTimeoutMs,
|
||||
);
|
||||
}).then(function(data) {
|
||||
var leaveRooms = [];
|
||||
let leaveRooms = [];
|
||||
if (data.rooms && data.rooms.leave) {
|
||||
leaveRooms = self._mapSyncResponseToRoomArray(data.rooms.leave);
|
||||
}
|
||||
var rooms = [];
|
||||
const rooms = [];
|
||||
leaveRooms.forEach(function(leaveObj) {
|
||||
var room = leaveObj.room;
|
||||
const room = leaveObj.room;
|
||||
rooms.push(room);
|
||||
if (!leaveObj.isBrandNewRoom) {
|
||||
// the intention behind syncLeftRooms is to add in rooms which were
|
||||
@@ -181,9 +186,9 @@ SyncApi.prototype.syncLeftRooms = function() {
|
||||
return;
|
||||
}
|
||||
leaveObj.timeline = leaveObj.timeline || {};
|
||||
var timelineEvents =
|
||||
const timelineEvents =
|
||||
self._mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
var stateEvents = self._mapSyncEventsFormat(leaveObj.state, room);
|
||||
const stateEvents = self._mapSyncEventsFormat(leaveObj.state, room);
|
||||
|
||||
// set the back-pagination token. Do this *before* adding any
|
||||
// events so that clients can start back-paginating.
|
||||
@@ -208,8 +213,8 @@ SyncApi.prototype.syncLeftRooms = function() {
|
||||
* store.
|
||||
*/
|
||||
SyncApi.prototype.peek = function(roomId) {
|
||||
var self = this;
|
||||
var client = this.client;
|
||||
const self = this;
|
||||
const client = this.client;
|
||||
this._peekRoomId = roomId;
|
||||
return this.client.roomInitialSync(roomId, 20).then(function(response) {
|
||||
// make sure things are init'd
|
||||
@@ -217,18 +222,18 @@ SyncApi.prototype.peek = function(roomId) {
|
||||
response.messages.chunk = response.messages.chunk || [];
|
||||
response.state = response.state || [];
|
||||
|
||||
var peekRoom = self.createRoom(roomId);
|
||||
const peekRoom = self.createRoom(roomId);
|
||||
|
||||
// FIXME: Mostly duplicated from _processRoomEvents but not entirely
|
||||
// because "state" in this API is at the BEGINNING of the chunk
|
||||
var oldStateEvents = utils.map(
|
||||
utils.deepCopy(response.state), client.getEventMapper()
|
||||
const oldStateEvents = utils.map(
|
||||
utils.deepCopy(response.state), client.getEventMapper(),
|
||||
);
|
||||
var stateEvents = utils.map(
|
||||
response.state, client.getEventMapper()
|
||||
const stateEvents = utils.map(
|
||||
response.state, client.getEventMapper(),
|
||||
);
|
||||
var messages = utils.map(
|
||||
response.messages.chunk, client.getEventMapper()
|
||||
const messages = utils.map(
|
||||
response.messages.chunk, client.getEventMapper(),
|
||||
);
|
||||
|
||||
// XXX: copypasted from /sync until we kill off this
|
||||
@@ -237,11 +242,10 @@ SyncApi.prototype.peek = function(roomId) {
|
||||
if (response.presence && utils.isArray(response.presence)) {
|
||||
response.presence.map(client.getEventMapper()).forEach(
|
||||
function(presenceEvent) {
|
||||
var user = client.store.getUser(presenceEvent.getContent().user_id);
|
||||
let user = client.store.getUser(presenceEvent.getContent().user_id);
|
||||
if (user) {
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
user = createNewUser(client, presenceEvent.getContent().user_id);
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
client.store.storeUser(user);
|
||||
@@ -298,14 +302,13 @@ SyncApi.prototype._peekPoll = function(roomId, token) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
// FIXME: gut wrenching; hard-coded timeout values
|
||||
this.client._http.authedRequest(undefined, "GET", "/events", {
|
||||
room_id: roomId,
|
||||
timeout: 30 * 1000,
|
||||
from: token
|
||||
from: token,
|
||||
}, undefined, 50 * 1000).done(function(res) {
|
||||
|
||||
// We have a problem that we get presence both from /events and /sync
|
||||
// however, /sync only returns presence for users in rooms
|
||||
// you're actually joined to.
|
||||
@@ -318,11 +321,10 @@ SyncApi.prototype._peekPoll = function(roomId, token) {
|
||||
res.chunk.filter(function(e) {
|
||||
return e.type === "m.presence";
|
||||
}).map(self.client.getEventMapper()).forEach(function(presenceEvent) {
|
||||
var user = self.client.store.getUser(presenceEvent.getContent().user_id);
|
||||
let user = self.client.store.getUser(presenceEvent.getContent().user_id);
|
||||
if (user) {
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
user = createNewUser(self.client, presenceEvent.getContent().user_id);
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
self.client.store.storeUser(user);
|
||||
@@ -331,10 +333,10 @@ SyncApi.prototype._peekPoll = function(roomId, token) {
|
||||
});
|
||||
|
||||
// strip out events which aren't for the given room_id (e.g presence)
|
||||
var events = res.chunk.filter(function(e) {
|
||||
const events = res.chunk.filter(function(e) {
|
||||
return e.room_id === roomId;
|
||||
}).map(self.client.getEventMapper());
|
||||
var room = self.client.getRoom(roomId);
|
||||
const room = self.client.getRoom(roomId);
|
||||
room.addLiveEvents(events);
|
||||
self._peekPoll(roomId, res.end);
|
||||
}, function(err) {
|
||||
@@ -361,8 +363,8 @@ SyncApi.prototype.sync = function() {
|
||||
debuglog("SyncApi.sync: starting with sync token " +
|
||||
this.client.store.getSyncToken());
|
||||
|
||||
var client = this.client;
|
||||
var self = this;
|
||||
const client = this.client;
|
||||
const self = this;
|
||||
|
||||
this._running = true;
|
||||
|
||||
@@ -391,7 +393,7 @@ SyncApi.prototype.sync = function() {
|
||||
}
|
||||
|
||||
function getFilter() {
|
||||
var filter;
|
||||
let filter;
|
||||
if (self.opts.filter) {
|
||||
filter = self.opts.filter;
|
||||
} else {
|
||||
@@ -400,7 +402,7 @@ SyncApi.prototype.sync = function() {
|
||||
}
|
||||
|
||||
client.getOrCreateFilter(
|
||||
getFilterName(client.credentials.userId), filter
|
||||
getFilterName(client.credentials.userId), filter,
|
||||
).done(function(filterId) {
|
||||
// reset the notifications timeline to prepare it to paginate from
|
||||
// the current point in time.
|
||||
@@ -420,8 +422,7 @@ SyncApi.prototype.sync = function() {
|
||||
if (client.isGuest()) {
|
||||
// no push rules for guests, no access to POST filter for guests.
|
||||
self._sync({});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
getPushRules();
|
||||
}
|
||||
};
|
||||
@@ -436,7 +437,9 @@ SyncApi.prototype.stop = function() {
|
||||
this._onOnlineBound = undefined;
|
||||
}
|
||||
this._running = false;
|
||||
if (this._currentSyncRequest) { this._currentSyncRequest.abort(); }
|
||||
if (this._currentSyncRequest) {
|
||||
this._currentSyncRequest.abort();
|
||||
}
|
||||
if (this._keepAliveTimer) {
|
||||
clearTimeout(this._keepAliveTimer);
|
||||
this._keepAliveTimer = null;
|
||||
@@ -449,7 +452,9 @@ SyncApi.prototype.stop = function() {
|
||||
* @return {boolean} True if this resulted in a request being retried.
|
||||
*/
|
||||
SyncApi.prototype.retryImmediately = function() {
|
||||
if (!this._connectionReturnedDefer) { return false; }
|
||||
if (!this._connectionReturnedDefer) {
|
||||
return false;
|
||||
}
|
||||
this._startKeepAlives(0);
|
||||
return true;
|
||||
};
|
||||
@@ -461,8 +466,8 @@ SyncApi.prototype.retryImmediately = function() {
|
||||
* @param {boolean} syncOptions.hasSyncedBefore
|
||||
*/
|
||||
SyncApi.prototype._sync = function(syncOptions) {
|
||||
var client = this.client;
|
||||
var self = this;
|
||||
const client = this.client;
|
||||
const self = this;
|
||||
|
||||
if (!this._running) {
|
||||
debuglog("Sync no longer running: exiting.");
|
||||
@@ -474,14 +479,14 @@ SyncApi.prototype._sync = function(syncOptions) {
|
||||
return;
|
||||
}
|
||||
|
||||
var filterId = syncOptions.filterId;
|
||||
let filterId = syncOptions.filterId;
|
||||
if (client.isGuest() && !filterId) {
|
||||
filterId = this._getGuestFilter();
|
||||
}
|
||||
|
||||
var syncToken = client.store.getSyncToken();
|
||||
const syncToken = client.store.getSyncToken();
|
||||
|
||||
var qps = {
|
||||
const qps = {
|
||||
filter: filterId,
|
||||
timeout: this.opts.pollTimeout,
|
||||
};
|
||||
@@ -504,10 +509,10 @@ SyncApi.prototype._sync = function(syncOptions) {
|
||||
}
|
||||
|
||||
// normal timeout= plus buffer time
|
||||
var clientSideTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS;
|
||||
const clientSideTimeoutMs = this.opts.pollTimeout + BUFFER_PERIOD_MS;
|
||||
|
||||
this._currentSyncRequest = client._http.authedRequest(
|
||||
undefined, "GET", "/sync", qps, undefined, clientSideTimeoutMs
|
||||
undefined, "GET", "/sync", qps, undefined, clientSideTimeoutMs,
|
||||
);
|
||||
|
||||
this._currentSyncRequest.done(function(data) {
|
||||
@@ -518,21 +523,25 @@ SyncApi.prototype._sync = function(syncOptions) {
|
||||
|
||||
try {
|
||||
self._processSyncResponse(syncToken, data);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// log the exception with stack if we have it, else fall back
|
||||
// to the plain description
|
||||
console.error("Caught /sync error", e.stack || e);
|
||||
}
|
||||
|
||||
// emit synced events
|
||||
const syncEventData = {
|
||||
oldSyncToken: syncToken,
|
||||
nextSyncToken: data.next_batch,
|
||||
};
|
||||
|
||||
if (!syncOptions.hasSyncedBefore) {
|
||||
self._updateSyncState("PREPARED");
|
||||
self._updateSyncState("PREPARED", syncEventData);
|
||||
syncOptions.hasSyncedBefore = true;
|
||||
}
|
||||
|
||||
// keep emitting SYNCING -> SYNCING for clients who want to do bulk updates
|
||||
self._updateSyncState("SYNCING");
|
||||
self._updateSyncState("SYNCING", syncEventData);
|
||||
|
||||
self._sync(syncOptions);
|
||||
}, function(err) {
|
||||
@@ -542,6 +551,7 @@ SyncApi.prototype._sync = function(syncOptions) {
|
||||
self._connectionReturnedDefer.reject();
|
||||
self._connectionReturnedDefer = null;
|
||||
}
|
||||
self._updateSyncState("STOPPED");
|
||||
return;
|
||||
}
|
||||
console.error("/sync error %s", err);
|
||||
@@ -572,14 +582,15 @@ SyncApi.prototype._sync = function(syncOptions) {
|
||||
* @param {Object} data The response from /sync
|
||||
*/
|
||||
SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
var client = this.client;
|
||||
var self = this;
|
||||
const client = this.client;
|
||||
const self = this;
|
||||
|
||||
// data looks like:
|
||||
// {
|
||||
// next_batch: $token,
|
||||
// presence: { events: [] },
|
||||
// account_data: { events: [] },
|
||||
// device_lists: { changed: ["@user:server", ... ]},
|
||||
// to_device: { events: [] },
|
||||
// rooms: {
|
||||
// invite: {
|
||||
@@ -617,11 +628,10 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
if (data.presence && utils.isArray(data.presence.events)) {
|
||||
data.presence.events.map(client.getEventMapper()).forEach(
|
||||
function(presenceEvent) {
|
||||
var user = client.store.getUser(presenceEvent.getSender());
|
||||
let user = client.store.getUser(presenceEvent.getSender());
|
||||
if (user) {
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
user = createNewUser(client, presenceEvent.getSender());
|
||||
user.setPresenceEvent(presenceEvent);
|
||||
client.store.storeUser(user);
|
||||
@@ -632,7 +642,7 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
|
||||
// handle non-room account_data
|
||||
if (data.account_data && utils.isArray(data.account_data.events)) {
|
||||
var events = data.account_data.events.map(client.getEventMapper());
|
||||
const events = data.account_data.events.map(client.getEventMapper());
|
||||
client.store.storeAccountDataEvents(events);
|
||||
events.forEach(
|
||||
function(accountDataEvent) {
|
||||
@@ -641,7 +651,7 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
}
|
||||
client.emit("accountData", accountDataEvent);
|
||||
return accountDataEvent;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -651,28 +661,28 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
.map(client.getEventMapper())
|
||||
.forEach(
|
||||
function(toDeviceEvent) {
|
||||
var content = toDeviceEvent.getContent();
|
||||
const content = toDeviceEvent.getContent();
|
||||
if (
|
||||
toDeviceEvent.getType() == "m.room.message" &&
|
||||
content.msgtype == "m.bad.encrypted"
|
||||
) {
|
||||
console.warn(
|
||||
"Unable to decrypt to-device event: " + content.body
|
||||
"Unable to decrypt to-device event: " + content.body,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
client.emit("toDeviceEvent", toDeviceEvent);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// the returned json structure is a bit crap, so make it into a
|
||||
// nicer form (array) after applying sanity to make sure we don't fail
|
||||
// on missing keys (on the off chance)
|
||||
var inviteRooms = [];
|
||||
var joinRooms = [];
|
||||
var leaveRooms = [];
|
||||
let inviteRooms = [];
|
||||
let joinRooms = [];
|
||||
let leaveRooms = [];
|
||||
|
||||
if (data.rooms) {
|
||||
if (data.rooms.invite) {
|
||||
@@ -690,8 +700,8 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
|
||||
// Handle invites
|
||||
inviteRooms.forEach(function(inviteObj) {
|
||||
var room = inviteObj.room;
|
||||
var stateEvents =
|
||||
const room = inviteObj.room;
|
||||
const stateEvents =
|
||||
self._mapSyncEventsFormat(inviteObj.invite_state, room);
|
||||
self._processRoomEvents(room, stateEvents);
|
||||
if (inviteObj.isBrandNewRoom) {
|
||||
@@ -699,24 +709,26 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
client.store.storeRoom(room);
|
||||
client.emit("Room", room);
|
||||
}
|
||||
stateEvents.forEach(function(e) { client.emit("event", e); });
|
||||
stateEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle joins
|
||||
joinRooms.forEach(function(joinObj) {
|
||||
var room = joinObj.room;
|
||||
var stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
|
||||
var timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room);
|
||||
var ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
|
||||
var accountDataEvents = self._mapSyncEventsFormat(joinObj.account_data);
|
||||
const room = joinObj.room;
|
||||
const stateEvents = self._mapSyncEventsFormat(joinObj.state, room);
|
||||
const timelineEvents = self._mapSyncEventsFormat(joinObj.timeline, room);
|
||||
const ephemeralEvents = self._mapSyncEventsFormat(joinObj.ephemeral);
|
||||
const accountDataEvents = self._mapSyncEventsFormat(joinObj.account_data);
|
||||
|
||||
// we do this first so it's correct when any of the events fire
|
||||
if (joinObj.unread_notifications) {
|
||||
room.setUnreadNotificationCount(
|
||||
'total', joinObj.unread_notifications.notification_count
|
||||
'total', joinObj.unread_notifications.notification_count,
|
||||
);
|
||||
room.setUnreadNotificationCount(
|
||||
'highlight', joinObj.unread_notifications.highlight_count
|
||||
'highlight', joinObj.unread_notifications.highlight_count,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -727,9 +739,8 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
// events so that clients can start back-paginating.
|
||||
room.getLiveTimeline().setPaginationToken(
|
||||
joinObj.timeline.prev_batch, EventTimeline.BACKWARDS);
|
||||
}
|
||||
else if (joinObj.timeline.limited) {
|
||||
var limited = true;
|
||||
} else if (joinObj.timeline.limited) {
|
||||
let limited = true;
|
||||
|
||||
// we've got a limited sync, so we *probably* have a gap in the
|
||||
// timeline, so should reset. But we might have been peeking or
|
||||
@@ -743,8 +754,8 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
// which we'll try to paginate but not get any new events (which
|
||||
// will stop us linking the empty timeline into the chain).
|
||||
//
|
||||
for (var i = timelineEvents.length - 1; i >= 0; i--) {
|
||||
var eventId = timelineEvents[i].getId();
|
||||
for (let i = timelineEvents.length - 1; i >= 0; i--) {
|
||||
const eventId = timelineEvents[i].getId();
|
||||
if (room.getTimelineForEvent(eventId)) {
|
||||
debuglog("Already have event " + eventId + " in limited " +
|
||||
"sync - not resetting");
|
||||
@@ -798,20 +809,28 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
client.store.storeRoom(room);
|
||||
client.emit("Room", room);
|
||||
}
|
||||
stateEvents.forEach(function(e) { client.emit("event", e); });
|
||||
timelineEvents.forEach(function(e) { client.emit("event", e); });
|
||||
ephemeralEvents.forEach(function(e) { client.emit("event", e); });
|
||||
accountDataEvents.forEach(function(e) { client.emit("event", e); });
|
||||
stateEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
timelineEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
ephemeralEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
accountDataEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle leaves (e.g. kicked rooms)
|
||||
leaveRooms.forEach(function(leaveObj) {
|
||||
var room = leaveObj.room;
|
||||
var stateEvents =
|
||||
const room = leaveObj.room;
|
||||
const stateEvents =
|
||||
self._mapSyncEventsFormat(leaveObj.state, room);
|
||||
var timelineEvents =
|
||||
const timelineEvents =
|
||||
self._mapSyncEventsFormat(leaveObj.timeline, room);
|
||||
var accountDataEvents =
|
||||
const accountDataEvents =
|
||||
self._mapSyncEventsFormat(leaveObj.account_data);
|
||||
|
||||
self._processRoomEvents(room, stateEvents, timelineEvents);
|
||||
@@ -823,9 +842,15 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
client.emit("Room", room);
|
||||
}
|
||||
|
||||
stateEvents.forEach(function(e) { client.emit("event", e); });
|
||||
timelineEvents.forEach(function(e) { client.emit("event", e); });
|
||||
accountDataEvents.forEach(function(e) { client.emit("event", e); });
|
||||
stateEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
timelineEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
accountDataEvents.forEach(function(e) {
|
||||
client.emit("event", e);
|
||||
});
|
||||
});
|
||||
|
||||
// update the notification timeline, if appropriate.
|
||||
@@ -841,6 +866,13 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
|
||||
client.getNotifTimelineSet().addLiveEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle device list updates
|
||||
if (this.opts.crypto && data.device_lists && data.device_lists.changed) {
|
||||
data.device_lists.changed.forEach((u) => {
|
||||
this.opts.crypto.userDeviceListChanged(u);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -858,11 +890,11 @@ SyncApi.prototype._startKeepAlives = function(delay) {
|
||||
if (this._keepAliveTimer !== null) {
|
||||
clearTimeout(this._keepAliveTimer);
|
||||
}
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (delay > 0) {
|
||||
self._keepAliveTimer = setTimeout(
|
||||
self._pokeKeepAlive.bind(self),
|
||||
delay
|
||||
delay,
|
||||
);
|
||||
} else {
|
||||
self._pokeKeepAlive();
|
||||
@@ -877,7 +909,7 @@ SyncApi.prototype._startKeepAlives = function(delay) {
|
||||
*
|
||||
*/
|
||||
SyncApi.prototype._pokeKeepAlive = function() {
|
||||
var self = this;
|
||||
const self = this;
|
||||
function success() {
|
||||
clearTimeout(self._keepAliveTimer);
|
||||
if (self._connectionReturnedDefer) {
|
||||
@@ -894,7 +926,7 @@ SyncApi.prototype._pokeKeepAlive = function() {
|
||||
{
|
||||
prefix: '',
|
||||
localTimeoutMs: 15 * 1000,
|
||||
}
|
||||
},
|
||||
).done(function() {
|
||||
success();
|
||||
}, function(err) {
|
||||
@@ -908,7 +940,7 @@ SyncApi.prototype._pokeKeepAlive = function() {
|
||||
} else {
|
||||
self._keepAliveTimer = setTimeout(
|
||||
self._pokeKeepAlive.bind(self),
|
||||
5000 + Math.floor(Math.random() * 5000)
|
||||
5000 + Math.floor(Math.random() * 5000),
|
||||
);
|
||||
// A keepalive has failed, so we emit the
|
||||
// error state (whether or not this is the
|
||||
@@ -929,12 +961,12 @@ SyncApi.prototype._mapSyncResponseToRoomArray = function(obj) {
|
||||
// Maps { roomid: {stuff}, roomid: {stuff} }
|
||||
// to
|
||||
// [{stuff+Room+isBrandNewRoom}, {stuff+Room+isBrandNewRoom}]
|
||||
var client = this.client;
|
||||
var self = this;
|
||||
const client = this.client;
|
||||
const self = this;
|
||||
return utils.keys(obj).map(function(roomId) {
|
||||
var arrObj = obj[roomId];
|
||||
var room = client.store.getRoom(roomId);
|
||||
var isBrandNewRoom = false;
|
||||
const arrObj = obj[roomId];
|
||||
let room = client.store.getRoom(roomId);
|
||||
let isBrandNewRoom = false;
|
||||
if (!room) {
|
||||
room = self.createRoom(roomId);
|
||||
isBrandNewRoom = true;
|
||||
@@ -954,7 +986,7 @@ SyncApi.prototype._mapSyncEventsFormat = function(obj, room) {
|
||||
if (!obj || !utils.isArray(obj.events)) {
|
||||
return [];
|
||||
}
|
||||
var mapper = this.client.getEventMapper();
|
||||
const mapper = this.client.getEventMapper();
|
||||
return obj.events.map(function(e) {
|
||||
if (room) {
|
||||
e.room_id = room.roomId;
|
||||
@@ -970,7 +1002,7 @@ SyncApi.prototype._resolveInvites = function(room) {
|
||||
if (!room || !this.opts.resolveInvitesToProfiles) {
|
||||
return;
|
||||
}
|
||||
var client = this.client;
|
||||
const client = this.client;
|
||||
// For each invited room member we want to give them a displayname/avatar url
|
||||
// if they have one (the m.room.member invites don't contain this).
|
||||
room.getMembersWithMembership("invite").forEach(function(member) {
|
||||
@@ -979,22 +1011,21 @@ SyncApi.prototype._resolveInvites = function(room) {
|
||||
}
|
||||
member._requestedProfileInfo = true;
|
||||
// try to get a cached copy first.
|
||||
var user = client.getUser(member.userId);
|
||||
var promise;
|
||||
const user = client.getUser(member.userId);
|
||||
let promise;
|
||||
if (user) {
|
||||
promise = q({
|
||||
avatar_url: user.avatarUrl,
|
||||
displayname: user.displayName
|
||||
displayname: user.displayName,
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
promise = client.getProfileInfo(member.userId);
|
||||
}
|
||||
promise.done(function(info) {
|
||||
// slightly naughty by doctoring the invite event but this means all
|
||||
// the code paths remain the same between invite/join display name stuff
|
||||
// which is a worthy trade-off for some minor pollution.
|
||||
var inviteEvent = member.events.member;
|
||||
const inviteEvent = member.events.member;
|
||||
if (inviteEvent.getContent().membership !== "invite") {
|
||||
// between resolving and now they have since joined, so don't clobber
|
||||
return;
|
||||
@@ -1013,23 +1044,25 @@ SyncApi.prototype._resolveInvites = function(room) {
|
||||
* @param {Room} room
|
||||
* @param {MatrixEvent[]} stateEventList A list of state events. This is the state
|
||||
* at the *START* of the timeline list if it is supplied.
|
||||
* @param {?MatrixEvent[]} timelineEventList A list of timeline events. Lower index
|
||||
* @param {MatrixEvent[]} [timelineEventList] A list of timeline events. Lower index
|
||||
* is earlier in time. Higher index is later.
|
||||
*/
|
||||
SyncApi.prototype._processRoomEvents = function(room, stateEventList,
|
||||
timelineEventList) {
|
||||
timelineEventList = timelineEventList || [];
|
||||
var client = this.client;
|
||||
const client = this.client;
|
||||
// "old" and "current" state are the same initially; they
|
||||
// start diverging if the user paginates.
|
||||
// We must deep copy otherwise membership changes in old state
|
||||
// will leak through to current state!
|
||||
var oldStateEvents = utils.map(
|
||||
const oldStateEvents = utils.map(
|
||||
utils.deepCopy(
|
||||
stateEventList.map(function(mxEvent) { return mxEvent.event; })
|
||||
), client.getEventMapper()
|
||||
stateEventList.map(function(mxEvent) {
|
||||
return mxEvent.event;
|
||||
}),
|
||||
), client.getEventMapper(),
|
||||
);
|
||||
var stateEvents = stateEventList;
|
||||
const stateEvents = stateEventList;
|
||||
|
||||
// set the state of the room to as it was before the timeline executes
|
||||
//
|
||||
@@ -1048,11 +1081,10 @@ SyncApi.prototype._processRoomEvents = function(room, stateEventList,
|
||||
|
||||
// gather our notifications into this._notifEvents
|
||||
if (client.getNotifTimelineSet()) {
|
||||
for (var i = 0; i < timelineEventList.length; i++) {
|
||||
var pushActions = client.getPushActionsForEvent(timelineEventList[i]);
|
||||
for (let i = 0; i < timelineEventList.length; i++) {
|
||||
const pushActions = client.getPushActionsForEvent(timelineEventList[i]);
|
||||
if (pushActions && pushActions.notify &&
|
||||
pushActions.tweaks && pushActions.tweaks.highlight)
|
||||
{
|
||||
pushActions.tweaks && pushActions.tweaks.highlight) {
|
||||
this._notifEvents.push(timelineEventList[i]);
|
||||
}
|
||||
}
|
||||
@@ -1067,7 +1099,7 @@ SyncApi.prototype._processRoomEvents = function(room, stateEventList,
|
||||
* @return {string}
|
||||
*/
|
||||
SyncApi.prototype._getGuestFilter = function() {
|
||||
var guestRooms = this.client._guestRooms; // FIXME: horrible gut-wrenching
|
||||
const guestRooms = this.client._guestRooms; // FIXME: horrible gut-wrenching
|
||||
if (!guestRooms) {
|
||||
return "{}";
|
||||
}
|
||||
@@ -1076,9 +1108,9 @@ SyncApi.prototype._getGuestFilter = function() {
|
||||
return JSON.stringify({
|
||||
room: {
|
||||
timeline: {
|
||||
limit: 20
|
||||
}
|
||||
}
|
||||
limit: 20,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1088,7 +1120,7 @@ SyncApi.prototype._getGuestFilter = function() {
|
||||
* @param {Object} data Object of additional data to emit in the event
|
||||
*/
|
||||
SyncApi.prototype._updateSyncState = function(newState, data) {
|
||||
var old = this._syncState;
|
||||
const old = this._syncState;
|
||||
this._syncState = newState;
|
||||
this.client.emit("sync", this._syncState, old, data);
|
||||
};
|
||||
@@ -1105,10 +1137,10 @@ SyncApi.prototype._onOnline = function() {
|
||||
};
|
||||
|
||||
function createNewUser(client, userId) {
|
||||
var user = new User(userId);
|
||||
const user = new User(userId);
|
||||
reEmit(client, user, [
|
||||
"User.avatarUrl", "User.displayName", "User.presence",
|
||||
"User.currentlyActive", "User.lastPresenceTs"
|
||||
"User.currentlyActive", "User.lastPresenceTs",
|
||||
]);
|
||||
return user;
|
||||
}
|
||||
@@ -1122,11 +1154,11 @@ function reEmit(reEmitEntity, emittableEntity, eventNames) {
|
||||
// Transformation Example:
|
||||
// listener on "foo" => function(a,b) { ... }
|
||||
// Re-emit on "thing" => thing.emit("foo", a, b)
|
||||
var newArgs = [eventName];
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
const newArgs = [eventName];
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
newArgs.push(arguments[i]);
|
||||
}
|
||||
reEmitEntity.emit.apply(reEmitEntity, newArgs);
|
||||
reEmitEntity.emit(...newArgs);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -17,25 +17,25 @@ limitations under the License.
|
||||
|
||||
/** @module timeline-window */
|
||||
|
||||
var q = require("q");
|
||||
var EventTimeline = require("./models/event-timeline");
|
||||
const q = require("q");
|
||||
const EventTimeline = require("./models/event-timeline");
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var DEBUG = false;
|
||||
const DEBUG = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var debuglog = DEBUG ? console.log.bind(console) : function() {};
|
||||
const debuglog = DEBUG ? console.log.bind(console) : function() {};
|
||||
|
||||
/**
|
||||
* the number of times we ask the server for more events before giving up
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
var DEFAULT_PAGINATE_LOOP_LIMIT = 5;
|
||||
const DEFAULT_PAGINATE_LOOP_LIMIT = 5;
|
||||
|
||||
/**
|
||||
* Construct a TimelineWindow.
|
||||
@@ -92,15 +92,15 @@ function TimelineWindow(client, timelineSet, opts) {
|
||||
* @return {module:client.Promise}
|
||||
*/
|
||||
TimelineWindow.prototype.load = function(initialEventId, initialWindowSize) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
initialWindowSize = initialWindowSize || 20;
|
||||
|
||||
// given an EventTimeline, and an event index within it, initialise our
|
||||
// fields so that the event in question is in the middle of the window.
|
||||
var initFields = function(timeline, eventIndex) {
|
||||
var endIndex = Math.min(timeline.getEvents().length,
|
||||
const initFields = function(timeline, eventIndex) {
|
||||
const endIndex = Math.min(timeline.getEvents().length,
|
||||
eventIndex + Math.ceil(initialWindowSize / 2));
|
||||
var startIndex = Math.max(0, endIndex - initialWindowSize);
|
||||
const startIndex = Math.max(0, endIndex - initialWindowSize);
|
||||
self._start = new TimelineIndex(timeline, startIndex - timeline.getBaseIndex());
|
||||
self._end = new TimelineIndex(timeline, endIndex - timeline.getBaseIndex());
|
||||
self._eventCount = endIndex - startIndex;
|
||||
@@ -116,7 +116,7 @@ TimelineWindow.prototype.load = function(initialEventId, initialWindowSize) {
|
||||
return this._client.getEventTimeline(this._timelineSet, initialEventId)
|
||||
.then(function(tl) {
|
||||
// make sure that our window includes the event
|
||||
for (var i = 0; i < tl.getEvents().length; i++) {
|
||||
for (let i = 0; i < tl.getEvents().length; i++) {
|
||||
if (tl.getEvents()[i].getId() == initialEventId) {
|
||||
initFields(tl, i);
|
||||
return;
|
||||
@@ -126,7 +126,7 @@ TimelineWindow.prototype.load = function(initialEventId, initialWindowSize) {
|
||||
});
|
||||
} else {
|
||||
// start with the most recent events
|
||||
var tl = this._timelineSet.getLiveTimeline();
|
||||
const tl = this._timelineSet.getLiveTimeline();
|
||||
initFields(tl, tl.getEvents().length);
|
||||
return q();
|
||||
}
|
||||
@@ -146,7 +146,7 @@ TimelineWindow.prototype.load = function(initialEventId, initialWindowSize) {
|
||||
* @return {boolean} true if we can paginate in the given direction
|
||||
*/
|
||||
TimelineWindow.prototype.canPaginate = function(direction) {
|
||||
var tl;
|
||||
let tl;
|
||||
if (direction == EventTimeline.BACKWARDS) {
|
||||
tl = this._start;
|
||||
} else if (direction == EventTimeline.FORWARDS) {
|
||||
@@ -161,9 +161,13 @@ TimelineWindow.prototype.canPaginate = function(direction) {
|
||||
}
|
||||
|
||||
if (direction == EventTimeline.BACKWARDS) {
|
||||
if (tl.index > tl.minIndex()) { return true; }
|
||||
if (tl.index > tl.minIndex()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (tl.index < tl.maxIndex()) { return true; }
|
||||
if (tl.index < tl.maxIndex()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Boolean(tl.timeline.getNeighbouringTimeline(direction) ||
|
||||
@@ -205,7 +209,7 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
|
||||
requestLimit = DEFAULT_PAGINATE_LOOP_LIMIT;
|
||||
}
|
||||
|
||||
var tl;
|
||||
let tl;
|
||||
if (direction == EventTimeline.BACKWARDS) {
|
||||
tl = this._start;
|
||||
} else if (direction == EventTimeline.FORWARDS) {
|
||||
@@ -224,7 +228,7 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
|
||||
}
|
||||
|
||||
// try moving the cap
|
||||
var count = (direction == EventTimeline.BACKWARDS) ?
|
||||
const count = (direction == EventTimeline.BACKWARDS) ?
|
||||
tl.retreat(size) : tl.advance(size);
|
||||
|
||||
if (count) {
|
||||
@@ -232,9 +236,9 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
|
||||
debuglog("TimelineWindow: increased cap by " + count +
|
||||
" (now " + this._eventCount + ")");
|
||||
// remove some events from the other end, if necessary
|
||||
var excess = this._eventCount - this._windowLimit;
|
||||
const excess = this._eventCount - this._windowLimit;
|
||||
if (excess > 0) {
|
||||
this._unpaginate(excess, direction != EventTimeline.BACKWARDS);
|
||||
this.unpaginate(excess, direction != EventTimeline.BACKWARDS);
|
||||
}
|
||||
return q(true);
|
||||
}
|
||||
@@ -246,18 +250,18 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
|
||||
}
|
||||
|
||||
// try making a pagination request
|
||||
var token = tl.timeline.getPaginationToken(direction);
|
||||
const token = tl.timeline.getPaginationToken(direction);
|
||||
if (!token) {
|
||||
debuglog("TimelineWindow: no token");
|
||||
return q(false);
|
||||
}
|
||||
|
||||
debuglog("TimelineWindow: starting request");
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
var prom = this._client.paginateEventTimeline(tl.timeline, {
|
||||
const prom = this._client.paginateEventTimeline(tl.timeline, {
|
||||
backwards: direction == EventTimeline.BACKWARDS,
|
||||
limit: size
|
||||
limit: size,
|
||||
}).finally(function() {
|
||||
tl.pendingPaginate = null;
|
||||
}).then(function(r) {
|
||||
@@ -287,16 +291,14 @@ TimelineWindow.prototype.paginate = function(direction, size, makeRequest,
|
||||
|
||||
|
||||
/**
|
||||
* Trim the window to the windowlimit
|
||||
* Remove `delta` events from the start or end of the timeline.
|
||||
*
|
||||
* @param {number} delta number of events to remove from the timeline
|
||||
* @param {boolean} startOfTimeline if events should be removed from the start
|
||||
* of the timeline.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
TimelineWindow.prototype._unpaginate = function(delta, startOfTimeline) {
|
||||
var tl = startOfTimeline ? this._start : this._end;
|
||||
TimelineWindow.prototype.unpaginate = function(delta, startOfTimeline) {
|
||||
const tl = startOfTimeline ? this._start : this._end;
|
||||
|
||||
// sanity-check the delta
|
||||
if (delta > this._eventCount || delta < 0) {
|
||||
@@ -305,7 +307,7 @@ TimelineWindow.prototype._unpaginate = function(delta, startOfTimeline) {
|
||||
}
|
||||
|
||||
while (delta > 0) {
|
||||
var count = startOfTimeline ? tl.advance(delta) : tl.retreat(delta);
|
||||
const count = startOfTimeline ? tl.advance(delta) : tl.retreat(delta);
|
||||
if (count <= 0) {
|
||||
// sadness. This shouldn't be possible.
|
||||
throw new Error(
|
||||
@@ -332,13 +334,13 @@ TimelineWindow.prototype.getEvents = function() {
|
||||
return [];
|
||||
}
|
||||
|
||||
var result = [];
|
||||
const result = [];
|
||||
|
||||
// iterate through each timeline between this._start and this._end
|
||||
// (inclusive).
|
||||
var timeline = this._start.timeline;
|
||||
let timeline = this._start.timeline;
|
||||
while (true) {
|
||||
var events = timeline.getEvents();
|
||||
const events = timeline.getEvents();
|
||||
|
||||
// For the first timeline in the chain, we want to start at
|
||||
// this._start.index. For the last timeline in the chain, we want to
|
||||
@@ -348,7 +350,7 @@ TimelineWindow.prototype.getEvents = function() {
|
||||
// (Note that both this._start.index and this._end.index are relative
|
||||
// to their respective timelines' BaseIndex).
|
||||
//
|
||||
var startIndex = 0, endIndex = events.length;
|
||||
let startIndex = 0, endIndex = events.length;
|
||||
if (timeline === this._start.timeline) {
|
||||
startIndex = this._start.index + timeline.getBaseIndex();
|
||||
}
|
||||
@@ -356,7 +358,7 @@ TimelineWindow.prototype.getEvents = function() {
|
||||
endIndex = this._end.index + timeline.getBaseIndex();
|
||||
}
|
||||
|
||||
for (var i = startIndex; i < endIndex; i++) {
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
result.push(events[i]);
|
||||
}
|
||||
|
||||
@@ -417,7 +419,7 @@ TimelineIndex.prototype.advance = function(delta) {
|
||||
|
||||
// first try moving the index in the current timeline. See if there is room
|
||||
// to do so.
|
||||
var cappedDelta;
|
||||
let cappedDelta;
|
||||
if (delta < 0) {
|
||||
// we want to wind the index backwards.
|
||||
//
|
||||
@@ -445,7 +447,7 @@ TimelineIndex.prototype.advance = function(delta) {
|
||||
// the index is already at the start/end of the current timeline.
|
||||
//
|
||||
// next see if there is a neighbouring timeline to switch to.
|
||||
var neighbour = this.timeline.getNeighbouringTimeline(
|
||||
const neighbour = this.timeline.getNeighbouringTimeline(
|
||||
delta < 0 ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS);
|
||||
if (neighbour) {
|
||||
this.timeline = neighbour;
|
||||
+63
-67
@@ -26,9 +26,11 @@ limitations under the License.
|
||||
* @return {string} The encoded string e.g. foo=bar&baz=taz
|
||||
*/
|
||||
module.exports.encodeParams = function(params) {
|
||||
var qs = "";
|
||||
for (var key in params) {
|
||||
if (!params.hasOwnProperty(key)) { continue; }
|
||||
let qs = "";
|
||||
for (const key in params) {
|
||||
if (!params.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
qs += "&" + encodeURIComponent(key) + "=" +
|
||||
encodeURIComponent(params[key]);
|
||||
}
|
||||
@@ -44,10 +46,12 @@ module.exports.encodeParams = function(params) {
|
||||
* @return {string} The result of replacing all template variables e.g. '/foo/baz'.
|
||||
*/
|
||||
module.exports.encodeUri = function(pathTemplate, variables) {
|
||||
for (var key in variables) {
|
||||
if (!variables.hasOwnProperty(key)) { continue; }
|
||||
for (const key in variables) {
|
||||
if (!variables.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
pathTemplate = pathTemplate.replace(
|
||||
key, encodeURIComponent(variables[key])
|
||||
key, encodeURIComponent(variables[key]),
|
||||
);
|
||||
}
|
||||
return pathTemplate;
|
||||
@@ -61,8 +65,8 @@ module.exports.encodeUri = function(pathTemplate, variables) {
|
||||
* @return {Array} A new array with the results of the function.
|
||||
*/
|
||||
module.exports.map = function(array, fn) {
|
||||
var results = new Array(array.length);
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
const results = new Array(array.length);
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
results[i] = fn(array[i]);
|
||||
}
|
||||
return results;
|
||||
@@ -77,8 +81,8 @@ module.exports.map = function(array, fn) {
|
||||
* @return {Array} A new array with the results of the function.
|
||||
*/
|
||||
module.exports.filter = function(array, fn) {
|
||||
var results = [];
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
const results = [];
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
if (fn(array[i], i, array)) {
|
||||
results.push(array[i]);
|
||||
}
|
||||
@@ -92,9 +96,11 @@ module.exports.filter = function(array, fn) {
|
||||
* @return {string[]} The keys of the object.
|
||||
*/
|
||||
module.exports.keys = function(obj) {
|
||||
var keys = [];
|
||||
for (var key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) { continue; }
|
||||
const keys = [];
|
||||
for (const key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
keys.push(key);
|
||||
}
|
||||
return keys;
|
||||
@@ -106,9 +112,11 @@ module.exports.keys = function(obj) {
|
||||
* @return {Array<*>} The values of the object.
|
||||
*/
|
||||
module.exports.values = function(obj) {
|
||||
var values = [];
|
||||
for (var key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) { continue; }
|
||||
const values = [];
|
||||
for (const key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
values.push(obj[key]);
|
||||
}
|
||||
return values;
|
||||
@@ -121,7 +129,7 @@ module.exports.values = function(obj) {
|
||||
* function signature <code>fn(element, index)</code>.
|
||||
*/
|
||||
module.exports.forEach = function(array, fn) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
fn(array[i], i);
|
||||
}
|
||||
};
|
||||
@@ -138,15 +146,14 @@ module.exports.forEach = function(array, fn) {
|
||||
* the given function.
|
||||
*/
|
||||
module.exports.findElement = function(array, fn, reverse) {
|
||||
var i;
|
||||
let i;
|
||||
if (reverse) {
|
||||
for (i = array.length - 1; i >= 0; i--) {
|
||||
if (fn(array[i], i, array)) {
|
||||
return array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (i = 0; i < array.length; i++) {
|
||||
if (fn(array[i], i, array)) {
|
||||
return array[i];
|
||||
@@ -166,8 +173,8 @@ module.exports.findElement = function(array, fn, reverse) {
|
||||
* @return {boolean} True if an element was removed.
|
||||
*/
|
||||
module.exports.removeElement = function(array, fn, reverse) {
|
||||
var i;
|
||||
var removed;
|
||||
let i;
|
||||
let removed;
|
||||
if (reverse) {
|
||||
for (i = array.length - 1; i >= 0; i--) {
|
||||
if (fn(array[i], i, array)) {
|
||||
@@ -176,8 +183,7 @@ module.exports.removeElement = function(array, fn, reverse) {
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
for (i = 0; i < array.length; i++) {
|
||||
if (fn(array[i], i, array)) {
|
||||
removed = array[i];
|
||||
@@ -204,7 +210,8 @@ module.exports.isFunction = function(value) {
|
||||
* @return {boolean} True if it is an array.
|
||||
*/
|
||||
module.exports.isArray = function(value) {
|
||||
return Boolean(value && value.constructor === Array);
|
||||
return Array.isArray ? Array.isArray(value) :
|
||||
Boolean(value && value.constructor === Array);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -214,7 +221,7 @@ module.exports.isArray = function(value) {
|
||||
* @throws If the object is missing keys.
|
||||
*/
|
||||
module.exports.checkObjectHasKeys = function(obj, keys) {
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!obj.hasOwnProperty(keys[i])) {
|
||||
throw new Error("Missing required key: " + keys[i]);
|
||||
}
|
||||
@@ -228,8 +235,10 @@ module.exports.checkObjectHasKeys = function(obj, keys) {
|
||||
* @throws If there are extra keys.
|
||||
*/
|
||||
module.exports.checkObjectHasNoAdditionalKeys = function(obj, allowedKeys) {
|
||||
for (var key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) { continue; }
|
||||
for (const key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
if (allowedKeys.indexOf(key) === -1) {
|
||||
throw new Error("Unknown key: " + key);
|
||||
}
|
||||
@@ -254,7 +263,7 @@ module.exports.deepCopy = function(obj) {
|
||||
*
|
||||
* @return {boolean} true if the two objects are equal
|
||||
*/
|
||||
var deepCompare = module.exports.deepCompare = function(x, y) {
|
||||
const deepCompare = module.exports.deepCompare = function(x, y) {
|
||||
// Inspired by
|
||||
// http://stackoverflow.com/questions/1068834/object-comparison-in-javascript#1144249
|
||||
|
||||
@@ -300,7 +309,7 @@ var deepCompare = module.exports.deepCompare = function(x, y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < x.length; i++) {
|
||||
for (let i = 0; i < x.length; i++) {
|
||||
if (!deepCompare(x[i], y[i])) {
|
||||
return false;
|
||||
}
|
||||
@@ -311,7 +320,7 @@ var deepCompare = module.exports.deepCompare = function(x, y) {
|
||||
/* jshint -W089 */
|
||||
|
||||
// check that all of y's direct keys are in x
|
||||
var p;
|
||||
let p;
|
||||
for (p in y) {
|
||||
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
|
||||
return false;
|
||||
@@ -346,17 +355,13 @@ var deepCompare = module.exports.deepCompare = function(x, y) {
|
||||
* @return {Object} target
|
||||
*/
|
||||
module.exports.extend = function() {
|
||||
var target = arguments[0] || {};
|
||||
// disable jshint "The body of a for in should be wrapped in an if
|
||||
// statement"
|
||||
/* jshint -W089 */
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = arguments[i];
|
||||
for (var propName in source) {
|
||||
const target = arguments[0] || {};
|
||||
for (let i = 1; i < arguments.length; i++) {
|
||||
const source = arguments[i];
|
||||
for (const propName in source) { // eslint-disable-line guard-for-in
|
||||
target[propName] = source[propName];
|
||||
}
|
||||
}
|
||||
/* jshint +W089 */
|
||||
return target;
|
||||
};
|
||||
|
||||
@@ -370,22 +375,21 @@ module.exports.runPolyfills = function() {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
||||
if (!Array.prototype.filter) {
|
||||
Array.prototype.filter = function(fun/*, thisArg*/) {
|
||||
|
||||
if (this === void 0 || this === null) {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var t = Object(this);
|
||||
var len = t.length >>> 0;
|
||||
const t = Object(this);
|
||||
const len = t.length >>> 0;
|
||||
if (typeof fun !== 'function') {
|
||||
throw new TypeError();
|
||||
}
|
||||
|
||||
var res = [];
|
||||
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
|
||||
for (var i = 0; i < len; i++) {
|
||||
const res = [];
|
||||
const thisArg = arguments.length >= 2 ? arguments[1] : void 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (i in t) {
|
||||
var val = t[i];
|
||||
const val = t[i];
|
||||
|
||||
// NOTE: Technically this should Object.defineProperty at
|
||||
// the next index, as push can be affected by
|
||||
@@ -409,10 +413,8 @@ module.exports.runPolyfills = function() {
|
||||
// Production steps of ECMA-262, Edition 5, 15.4.4.19
|
||||
// Reference: http://es5.github.io/#x15.4.4.19
|
||||
if (!Array.prototype.map) {
|
||||
|
||||
Array.prototype.map = function(callback, thisArg) {
|
||||
|
||||
var T, A, k;
|
||||
let T, k;
|
||||
|
||||
if (this === null || this === undefined) {
|
||||
throw new TypeError(' this is null or not defined');
|
||||
@@ -420,12 +422,12 @@ module.exports.runPolyfills = function() {
|
||||
|
||||
// 1. Let O be the result of calling ToObject passing the |this|
|
||||
// value as the argument.
|
||||
var O = Object(this);
|
||||
const O = Object(this);
|
||||
|
||||
// 2. Let lenValue be the result of calling the Get internal
|
||||
// method of O with the argument "length".
|
||||
// 3. Let len be ToUint32(lenValue).
|
||||
var len = O.length >>> 0;
|
||||
const len = O.length >>> 0;
|
||||
|
||||
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
||||
// See: http://es5.github.com/#x9.11
|
||||
@@ -441,14 +443,13 @@ module.exports.runPolyfills = function() {
|
||||
// 6. Let A be a new array created as if by the expression new Array(len)
|
||||
// where Array is the standard built-in constructor with that name and
|
||||
// len is the value of len.
|
||||
A = new Array(len);
|
||||
const A = new Array(len);
|
||||
|
||||
// 7. Let k be 0
|
||||
k = 0;
|
||||
|
||||
// 8. Repeat, while k < len
|
||||
while (k < len) {
|
||||
|
||||
var kValue, mappedValue;
|
||||
|
||||
// a. Let Pk be ToString(k).
|
||||
@@ -458,7 +459,6 @@ module.exports.runPolyfills = function() {
|
||||
// This step can be combined with c
|
||||
// c. If kPresent is true, then
|
||||
if (k in O) {
|
||||
|
||||
// i. Let kValue be the result of calling the Get internal
|
||||
// method of O with argument Pk.
|
||||
kValue = O[k];
|
||||
@@ -503,10 +503,8 @@ module.exports.runPolyfills = function() {
|
||||
// Production steps of ECMA-262, Edition 5, 15.4.4.18
|
||||
// Reference: http://es5.github.io/#x15.4.4.18
|
||||
if (!Array.prototype.forEach) {
|
||||
|
||||
Array.prototype.forEach = function(callback, thisArg) {
|
||||
|
||||
var T, k;
|
||||
let T, k;
|
||||
|
||||
if (this === null || this === undefined) {
|
||||
throw new TypeError(' this is null or not defined');
|
||||
@@ -514,12 +512,12 @@ module.exports.runPolyfills = function() {
|
||||
|
||||
// 1. Let O be the result of calling ToObject passing the |this| value as the
|
||||
// argument.
|
||||
var O = Object(this);
|
||||
const O = Object(this);
|
||||
|
||||
// 2. Let lenValue be the result of calling the Get internal method of O with the
|
||||
// argument "length".
|
||||
// 3. Let len be ToUint32(lenValue).
|
||||
var len = O.length >>> 0;
|
||||
const len = O.length >>> 0;
|
||||
|
||||
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
||||
// See: http://es5.github.com/#x9.11
|
||||
@@ -537,7 +535,6 @@ module.exports.runPolyfills = function() {
|
||||
|
||||
// 7. Repeat, while k < len
|
||||
while (k < len) {
|
||||
|
||||
var kValue;
|
||||
|
||||
// a. Let Pk be ToString(k).
|
||||
@@ -548,7 +545,6 @@ module.exports.runPolyfills = function() {
|
||||
// This step can be combined with c
|
||||
// c. If kPresent is true, then
|
||||
if (k in O) {
|
||||
|
||||
// i. Let kValue be the result of calling the Get internal method of O with
|
||||
// argument Pk
|
||||
kValue = O[k];
|
||||
@@ -586,7 +582,7 @@ module.exports.inherits = function(ctor, superCtor) {
|
||||
function Temp() {}
|
||||
|
||||
// make a safe reference to Object.prototype.hasOwnProperty
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
return function(O) {
|
||||
// 1. If Type(O) is not Object or Null throw a TypeError exception.
|
||||
@@ -599,7 +595,7 @@ module.exports.inherits = function(ctor, superCtor) {
|
||||
// constructor with that name
|
||||
// 3. Set the [[Prototype]] internal property of obj to O.
|
||||
Temp.prototype = O;
|
||||
var obj = new Temp();
|
||||
const obj = new Temp();
|
||||
Temp.prototype = null; // Let's not keep a stray reference to O...
|
||||
|
||||
// 4. If the argument Properties is present and not undefined, add
|
||||
@@ -608,8 +604,8 @@ module.exports.inherits = function(ctor, superCtor) {
|
||||
// Properties.
|
||||
if (arguments.length > 1) {
|
||||
// Object.defineProperties does ToObject on its first argument.
|
||||
var Properties = Object(arguments[1]);
|
||||
for (var prop in Properties) {
|
||||
const Properties = Object(arguments[1]);
|
||||
for (const prop in Properties) {
|
||||
if (hasOwn.call(Properties, prop)) {
|
||||
obj[prop] = Properties[prop];
|
||||
}
|
||||
@@ -652,7 +648,7 @@ module.exports.inherits = function(ctor, superCtor) {
|
||||
value: ctor,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
configurable: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -18,9 +18,9 @@ limitations under the License.
|
||||
* This is an internal module. See {@link createNewMatrixCall} for the public API.
|
||||
* @module webrtc/call
|
||||
*/
|
||||
var utils = require("../utils");
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
var DEBUG = true; // set true to enable console logging.
|
||||
const utils = require("../utils");
|
||||
const EventEmitter = require("events").EventEmitter;
|
||||
const DEBUG = true; // set true to enable console logging.
|
||||
|
||||
// events: hangup, error(err), replaced(call), state(state, oldState)
|
||||
|
||||
@@ -43,7 +43,7 @@ function MatrixCall(opts) {
|
||||
this.turnServers = opts.turnServers || [];
|
||||
if (this.turnServers.length === 0) {
|
||||
this.turnServers.push({
|
||||
urls: [MatrixCall.FALLBACK_STUN_SERVER]
|
||||
urls: [MatrixCall.FALLBACK_STUN_SERVER],
|
||||
});
|
||||
}
|
||||
utils.forEach(this.turnServers, function(server) {
|
||||
@@ -122,28 +122,27 @@ MatrixCall.prototype.placeVideoCall = function(remoteVideoElement, localVideoEle
|
||||
* @throws If you have not specified a listener for 'error' events.
|
||||
*/
|
||||
MatrixCall.prototype.placeScreenSharingCall =
|
||||
function(remoteVideoElement, localVideoElement)
|
||||
{
|
||||
function(remoteVideoElement, localVideoElement) {
|
||||
debuglog("placeScreenSharingCall");
|
||||
checkForErrorListener(this);
|
||||
var screenConstraints = _getChromeScreenSharingConstraints(this);
|
||||
const screenConstraints = _getChromeScreenSharingConstraints(this);
|
||||
if (!screenConstraints) {
|
||||
return;
|
||||
}
|
||||
this.localVideoElement = localVideoElement;
|
||||
this.remoteVideoElement = remoteVideoElement;
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.webRtc.getUserMedia(screenConstraints, function(stream) {
|
||||
self.screenSharingStream = stream;
|
||||
debuglog("Got screen stream, requesting audio stream...");
|
||||
var audioConstraints = _getUserMediaVideoContraints('voice');
|
||||
const audioConstraints = _getUserMediaVideoContraints('voice');
|
||||
_placeCallWithConstraints(self, audioConstraints);
|
||||
}, function(err) {
|
||||
self.emit("error",
|
||||
callError(
|
||||
MatrixCall.ERR_NO_USER_MEDIA,
|
||||
"Failed to get screen-sharing stream: " + err
|
||||
)
|
||||
"Failed to get screen-sharing stream: " + err,
|
||||
),
|
||||
);
|
||||
});
|
||||
this.type = 'video';
|
||||
@@ -153,7 +152,7 @@ MatrixCall.prototype.placeScreenSharingCall =
|
||||
/**
|
||||
* Play the given HTMLMediaElement, serialising the operation into a chain
|
||||
* of promises to avoid racing access to the element
|
||||
* @param {Element} HTMLMediaElement element to play
|
||||
* @param {Element} element HTMLMediaElement element to play
|
||||
* @param {string} queueId Arbitrary ID to track the chain of promises to be used
|
||||
*/
|
||||
MatrixCall.prototype.playElement = function(element, queueId) {
|
||||
@@ -174,8 +173,7 @@ MatrixCall.prototype.playElement = function(element, queueId) {
|
||||
console.log("previous promise failed for " + queueId);
|
||||
return element.play();
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.mediaPromises[queueId] = element.play();
|
||||
}
|
||||
};
|
||||
@@ -183,7 +181,7 @@ MatrixCall.prototype.playElement = function(element, queueId) {
|
||||
/**
|
||||
* Pause the given HTMLMediaElement, serialising the operation into a chain
|
||||
* of promises to avoid racing access to the element
|
||||
* @param {Element} HTMLMediaElement element to pause
|
||||
* @param {Element} element HTMLMediaElement element to pause
|
||||
* @param {string} queueId Arbitrary ID to track the chain of promises to be used
|
||||
*/
|
||||
MatrixCall.prototype.pauseElement = function(element, queueId) {
|
||||
@@ -197,8 +195,7 @@ MatrixCall.prototype.pauseElement = function(element, queueId) {
|
||||
console.log("previous promise failed for " + queueId);
|
||||
return element.pause();
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// pause doesn't actually return a promise, but do this for symmetry
|
||||
// and just in case it does in future.
|
||||
this.mediaPromises[queueId] = element.pause();
|
||||
@@ -209,7 +206,7 @@ MatrixCall.prototype.pauseElement = function(element, queueId) {
|
||||
* Assign the given HTMLMediaElement by setting the .src attribute on it,
|
||||
* serialising the operation into a chain of promises to avoid racing access
|
||||
* to the element
|
||||
* @param {Element} HTMLMediaElement element to pause
|
||||
* @param {Element} element HTMLMediaElement element to pause
|
||||
* @param {string} src the src attribute value to assign to the element
|
||||
* @param {string} queueId Arbitrary ID to track the chain of promises to be used
|
||||
*/
|
||||
@@ -224,8 +221,7 @@ MatrixCall.prototype.assignElement = function(element, src, queueId) {
|
||||
console.log("previous promise failed for " + queueId);
|
||||
element.src = src;
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
element.src = src;
|
||||
}
|
||||
};
|
||||
@@ -270,9 +266,9 @@ MatrixCall.prototype.setLocalVideoElement = function(element) {
|
||||
this.URL.createObjectURL(this.localAVStream),
|
||||
"localVideo");
|
||||
element.muted = true;
|
||||
var self = this;
|
||||
const self = this;
|
||||
setTimeout(function() {
|
||||
var vel = self.getLocalVideoElement();
|
||||
const vel = self.getLocalVideoElement();
|
||||
if (vel.play) {
|
||||
self.playElement(vel, "localVideo");
|
||||
}
|
||||
@@ -310,12 +306,12 @@ MatrixCall.prototype.setRemoteAudioElement = function(element) {
|
||||
MatrixCall.prototype._initWithInvite = function(event) {
|
||||
this.msg = event.getContent();
|
||||
this.peerConn = _createPeerConnection(this);
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (this.peerConn) {
|
||||
this.peerConn.setRemoteDescription(
|
||||
new this.webRtc.RtcSessionDescription(this.msg.offer),
|
||||
hookCallback(self, self._onSetRemoteDescriptionSuccess),
|
||||
hookCallback(self, self._onSetRemoteDescriptionError)
|
||||
hookCallback(self, self._onSetRemoteDescriptionError),
|
||||
);
|
||||
}
|
||||
setState(this, 'ringing');
|
||||
@@ -330,8 +326,7 @@ MatrixCall.prototype._initWithInvite = function(event) {
|
||||
this.msg.offer.sdp.indexOf('m=video') > -1
|
||||
) {
|
||||
this.type = 'video';
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.type = 'voice';
|
||||
}
|
||||
|
||||
@@ -369,13 +364,13 @@ MatrixCall.prototype._initWithHangup = function(event) {
|
||||
*/
|
||||
MatrixCall.prototype.answer = function() {
|
||||
debuglog("Answering call %s of type %s", this.callId, this.type);
|
||||
var self = this;
|
||||
const self = this;
|
||||
|
||||
if (!this.localAVStream && !this.waitForLocalAVStream) {
|
||||
this.webRtc.getUserMedia(
|
||||
_getUserMediaVideoContraints(this.type),
|
||||
hookCallback(self, self._gotUserMediaForAnswer),
|
||||
hookCallback(self, self._getUserMediaFailed)
|
||||
hookCallback(self, self._getUserMediaFailed),
|
||||
);
|
||||
setState(this, 'wait_local_media');
|
||||
} else if (this.localAVStream) {
|
||||
@@ -421,10 +416,10 @@ MatrixCall.prototype._replacedBy = function(newCall) {
|
||||
MatrixCall.prototype.hangup = function(reason, suppressEvent) {
|
||||
debuglog("Ending call " + this.callId);
|
||||
terminate(this, "local", reason, !suppressEvent);
|
||||
var content = {
|
||||
const content = {
|
||||
version: 0,
|
||||
call_id: this.callId,
|
||||
reason: reason
|
||||
reason: reason,
|
||||
};
|
||||
sendEvent(this, 'm.call.hangup', content);
|
||||
};
|
||||
@@ -497,8 +492,8 @@ MatrixCall.prototype._gotUserMediaForInvite = function(stream) {
|
||||
return;
|
||||
}
|
||||
debuglog("_gotUserMediaForInvite -> " + this.type);
|
||||
var self = this;
|
||||
var videoEl = this.getLocalVideoElement();
|
||||
const self = this;
|
||||
const videoEl = this.getLocalVideoElement();
|
||||
|
||||
if (videoEl && this.type == 'video') {
|
||||
videoEl.autoplay = true;
|
||||
@@ -507,35 +502,33 @@ MatrixCall.prototype._gotUserMediaForInvite = function(stream) {
|
||||
this.assignElement(videoEl,
|
||||
this.URL.createObjectURL(this.screenSharingStream),
|
||||
"localVideo");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.assignElement(videoEl,
|
||||
this.URL.createObjectURL(stream),
|
||||
"localVideo");
|
||||
}
|
||||
videoEl.muted = true;
|
||||
setTimeout(function() {
|
||||
var vel = self.getLocalVideoElement();
|
||||
const vel = self.getLocalVideoElement();
|
||||
if (vel.play) {
|
||||
self.playElement(vel, "localVideo");
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (this.screenSharingStream) {
|
||||
this.screenSharingStream.addTrack(stream.getAudioTracks()[0]);
|
||||
stream = this.screenSharingStream;
|
||||
}
|
||||
|
||||
this.localAVStream = stream;
|
||||
// why do we enable audio (and only audio) tracks here? -- matthew
|
||||
setTracksEnabled(stream.getAudioTracks(), true);
|
||||
this.peerConn = _createPeerConnection(this);
|
||||
this.peerConn.addStream(stream);
|
||||
if (this.screenSharingStream) {
|
||||
console.log("Adding screen-sharing stream to peer connection");
|
||||
this.peerConn.addStream(this.screenSharingStream);
|
||||
// let's use this for the local preview...
|
||||
this.localAVStream = this.screenSharingStream;
|
||||
}
|
||||
this.peerConn.createOffer(
|
||||
hookCallback(self, self._gotLocalOffer),
|
||||
hookCallback(self, self._getLocalOfferFailed)
|
||||
hookCallback(self, self._getLocalOfferFailed),
|
||||
);
|
||||
setState(self, 'create_offer');
|
||||
};
|
||||
@@ -546,11 +539,11 @@ MatrixCall.prototype._gotUserMediaForInvite = function(stream) {
|
||||
* @param {Object} stream
|
||||
*/
|
||||
MatrixCall.prototype._gotUserMediaForAnswer = function(stream) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
if (self.state == 'ended') {
|
||||
return;
|
||||
}
|
||||
var localVidEl = self.getLocalVideoElement();
|
||||
const localVidEl = self.getLocalVideoElement();
|
||||
|
||||
if (localVidEl && self.type == 'video') {
|
||||
localVidEl.autoplay = true;
|
||||
@@ -559,7 +552,7 @@ MatrixCall.prototype._gotUserMediaForAnswer = function(stream) {
|
||||
"localVideo");
|
||||
localVidEl.muted = true;
|
||||
setTimeout(function() {
|
||||
var vel = self.getLocalVideoElement();
|
||||
const vel = self.getLocalVideoElement();
|
||||
if (vel.play) {
|
||||
self.playElement(vel, "localVideo");
|
||||
}
|
||||
@@ -570,22 +563,22 @@ MatrixCall.prototype._gotUserMediaForAnswer = function(stream) {
|
||||
setTracksEnabled(stream.getAudioTracks(), true);
|
||||
self.peerConn.addStream(stream);
|
||||
|
||||
var constraints = {
|
||||
const constraints = {
|
||||
'mandatory': {
|
||||
'OfferToReceiveAudio': true,
|
||||
'OfferToReceiveVideo': self.type == 'video'
|
||||
}
|
||||
'OfferToReceiveVideo': self.type == 'video',
|
||||
},
|
||||
};
|
||||
self.peerConn.createAnswer(function(description) {
|
||||
debuglog("Created answer: " + description);
|
||||
self.peerConn.setLocalDescription(description, function() {
|
||||
var content = {
|
||||
const content = {
|
||||
version: 0,
|
||||
call_id: self.callId,
|
||||
answer: {
|
||||
sdp: self.peerConn.localDescription.sdp,
|
||||
type: self.peerConn.localDescription.type
|
||||
}
|
||||
type: self.peerConn.localDescription.type,
|
||||
},
|
||||
};
|
||||
sendEvent(self, 'm.call.answer', content);
|
||||
setState(self, 'connecting');
|
||||
@@ -607,14 +600,14 @@ MatrixCall.prototype._gotLocalIceCandidate = function(event) {
|
||||
if (event.candidate) {
|
||||
debuglog(
|
||||
"Got local ICE " + event.candidate.sdpMid + " candidate: " +
|
||||
event.candidate.candidate
|
||||
event.candidate.candidate,
|
||||
);
|
||||
// As with the offer, note we need to make a copy of this object, not
|
||||
// pass the original: that broke in Chrome ~m43.
|
||||
var c = {
|
||||
const c = {
|
||||
candidate: event.candidate.candidate,
|
||||
sdpMid: event.candidate.sdpMid,
|
||||
sdpMLineIndex: event.candidate.sdpMLineIndex
|
||||
sdpMLineIndex: event.candidate.sdpMLineIndex,
|
||||
};
|
||||
sendCandidate(this, c);
|
||||
}
|
||||
@@ -634,7 +627,7 @@ MatrixCall.prototype._gotRemoteIceCandidate = function(cand) {
|
||||
this.peerConn.addIceCandidate(
|
||||
new this.webRtc.RtcIceCandidate(cand),
|
||||
function() {},
|
||||
function(e) {}
|
||||
function(e) {},
|
||||
);
|
||||
};
|
||||
|
||||
@@ -648,11 +641,11 @@ MatrixCall.prototype._receivedAnswer = function(msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
this.peerConn.setRemoteDescription(
|
||||
new this.webRtc.RtcSessionDescription(msg.answer),
|
||||
hookCallback(self, self._onSetRemoteDescriptionSuccess),
|
||||
hookCallback(self, self._onSetRemoteDescriptionError)
|
||||
hookCallback(self, self._onSetRemoteDescriptionError),
|
||||
);
|
||||
setState(self, 'connecting');
|
||||
};
|
||||
@@ -663,7 +656,7 @@ MatrixCall.prototype._receivedAnswer = function(msg) {
|
||||
* @param {Object} description
|
||||
*/
|
||||
MatrixCall.prototype._gotLocalOffer = function(description) {
|
||||
var self = this;
|
||||
const self = this;
|
||||
debuglog("Created offer: " + description);
|
||||
|
||||
if (self.state == 'ended') {
|
||||
@@ -673,7 +666,7 @@ MatrixCall.prototype._gotLocalOffer = function(description) {
|
||||
}
|
||||
|
||||
self.peerConn.setLocalDescription(description, function() {
|
||||
var content = {
|
||||
const content = {
|
||||
version: 0,
|
||||
call_id: self.callId,
|
||||
// OpenWebRTC appears to add extra stuff (like the DTLS fingerprint)
|
||||
@@ -687,9 +680,9 @@ MatrixCall.prototype._gotLocalOffer = function(description) {
|
||||
// Chrome (as of about m43).
|
||||
offer: {
|
||||
sdp: self.peerConn.localDescription.sdp,
|
||||
type: self.peerConn.localDescription.type
|
||||
type: self.peerConn.localDescription.type,
|
||||
},
|
||||
lifetime: MatrixCall.CALL_TIMEOUT_MS
|
||||
lifetime: MatrixCall.CALL_TIMEOUT_MS,
|
||||
};
|
||||
sendEvent(self, 'm.call.invite', content);
|
||||
|
||||
@@ -712,7 +705,7 @@ MatrixCall.prototype._gotLocalOffer = function(description) {
|
||||
MatrixCall.prototype._getLocalOfferFailed = function(error) {
|
||||
this.emit(
|
||||
"error",
|
||||
callError(MatrixCall.ERR_LOCAL_OFFER_FAILED, "Failed to start audio for call!")
|
||||
callError(MatrixCall.ERR_LOCAL_OFFER_FAILED, "Failed to start audio for call!"),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -727,8 +720,8 @@ MatrixCall.prototype._getUserMediaFailed = function(error) {
|
||||
callError(
|
||||
MatrixCall.ERR_NO_USER_MEDIA,
|
||||
"Couldn't start capturing media! Is your microphone set up and " +
|
||||
"does this app have permission?"
|
||||
)
|
||||
"does this app have permission?",
|
||||
),
|
||||
);
|
||||
this.hangup("user_media_failed");
|
||||
};
|
||||
@@ -742,7 +735,7 @@ MatrixCall.prototype._onIceConnectionStateChanged = function() {
|
||||
return; // because ICE can still complete as we're ending the call
|
||||
}
|
||||
debuglog(
|
||||
"Ice connection state changed to: " + this.peerConn.iceConnectionState
|
||||
"Ice connection state changed to: " + this.peerConn.iceConnectionState,
|
||||
);
|
||||
// ideally we'd consider the call to be connected when we get media but
|
||||
// chrome doesn't implement any of the 'onstarted' events yet
|
||||
@@ -762,7 +755,7 @@ MatrixCall.prototype._onIceConnectionStateChanged = function() {
|
||||
MatrixCall.prototype._onSignallingStateChanged = function() {
|
||||
debuglog(
|
||||
"call " + this.callId + ": Signalling state changed to: " +
|
||||
this.peerConn.signalingState
|
||||
this.peerConn.signalingState,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -791,7 +784,7 @@ MatrixCall.prototype._onSetRemoteDescriptionError = function(e) {
|
||||
MatrixCall.prototype._onAddStream = function(event) {
|
||||
debuglog("Stream id " + event.stream.id + " added");
|
||||
|
||||
var s = event.stream;
|
||||
const s = event.stream;
|
||||
|
||||
if (s.getVideoTracks().length > 0) {
|
||||
this.type = 'video';
|
||||
@@ -802,7 +795,7 @@ MatrixCall.prototype._onAddStream = function(event) {
|
||||
this.remoteAStream = s;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
const self = this;
|
||||
forAllTracksOnStream(s, function(t) {
|
||||
debuglog("Track id " + t.id + " added");
|
||||
// not currently implemented in chrome
|
||||
@@ -811,8 +804,7 @@ MatrixCall.prototype._onAddStream = function(event) {
|
||||
|
||||
if (event.stream.oninactive !== undefined) {
|
||||
event.stream.oninactive = hookCallback(self, self._onRemoteStreamEnded);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// onended is deprecated from Chrome 54
|
||||
event.stream.onended = hookCallback(self, self._onRemoteStreamEnded);
|
||||
}
|
||||
@@ -823,8 +815,7 @@ MatrixCall.prototype._onAddStream = function(event) {
|
||||
if (this.type === 'video') {
|
||||
_tryPlayRemoteStream(this);
|
||||
_tryPlayRemoteAudioStream(this);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
_tryPlayRemoteAudioStream(this);
|
||||
}
|
||||
};
|
||||
@@ -883,14 +874,14 @@ MatrixCall.prototype._onAnsweredElsewhere = function(msg) {
|
||||
terminate(this, "remote", "answered_elsewhere", true);
|
||||
};
|
||||
|
||||
var setTracksEnabled = function(tracks, enabled) {
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
const setTracksEnabled = function(tracks, enabled) {
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
tracks[i].enabled = enabled;
|
||||
}
|
||||
};
|
||||
|
||||
var isTracksEnabled = function(tracks) {
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
const isTracksEnabled = function(tracks) {
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
if (tracks[i].enabled) {
|
||||
return true; // at least one track is enabled
|
||||
}
|
||||
@@ -898,8 +889,8 @@ var isTracksEnabled = function(tracks) {
|
||||
return false;
|
||||
};
|
||||
|
||||
var setState = function(self, state) {
|
||||
var oldState = self.state;
|
||||
const setState = function(self, state) {
|
||||
const oldState = self.state;
|
||||
self.state = state;
|
||||
self.emit("state", state, oldState);
|
||||
};
|
||||
@@ -911,11 +902,11 @@ var setState = function(self, state) {
|
||||
* @param {Object} content
|
||||
* @return {Promise}
|
||||
*/
|
||||
var sendEvent = function(self, eventType, content) {
|
||||
const sendEvent = function(self, eventType, content) {
|
||||
return self.client.sendEvent(self.roomId, eventType, content);
|
||||
};
|
||||
|
||||
var sendCandidate = function(self, content) {
|
||||
const sendCandidate = function(self, content) {
|
||||
// Sends candidates with are sent in a special way because we try to amalgamate
|
||||
// them into one message
|
||||
self.candidateSendQueue.push(content);
|
||||
@@ -926,7 +917,7 @@ var sendCandidate = function(self, content) {
|
||||
}
|
||||
};
|
||||
|
||||
var terminate = function(self, hangupParty, hangupReason, shouldEmit) {
|
||||
const terminate = function(self, hangupParty, hangupReason, shouldEmit) {
|
||||
if (self.getRemoteVideoElement()) {
|
||||
if (self.getRemoteVideoElement().pause) {
|
||||
self.pauseElement(self.getRemoteVideoElement(), "remoteVideo");
|
||||
@@ -957,7 +948,7 @@ var terminate = function(self, hangupParty, hangupReason, shouldEmit) {
|
||||
}
|
||||
};
|
||||
|
||||
var stopAllMedia = function(self) {
|
||||
const stopAllMedia = function(self) {
|
||||
debuglog("stopAllMedia (stream=%s)", self.localAVStream);
|
||||
if (self.localAVStream) {
|
||||
forAllTracksOnStream(self.localAVStream, function(t) {
|
||||
@@ -997,15 +988,15 @@ var stopAllMedia = function(self) {
|
||||
}
|
||||
};
|
||||
|
||||
var _tryPlayRemoteStream = function(self) {
|
||||
const _tryPlayRemoteStream = function(self) {
|
||||
if (self.getRemoteVideoElement() && self.remoteAVStream) {
|
||||
var player = self.getRemoteVideoElement();
|
||||
const player = self.getRemoteVideoElement();
|
||||
player.autoplay = true;
|
||||
self.assignElement(player,
|
||||
self.URL.createObjectURL(self.remoteAVStream),
|
||||
"remoteVideo");
|
||||
setTimeout(function() {
|
||||
var vel = self.getRemoteVideoElement();
|
||||
const vel = self.getRemoteVideoElement();
|
||||
if (vel.play) {
|
||||
self.playElement(vel, "remoteVideo");
|
||||
}
|
||||
@@ -1017,15 +1008,15 @@ var _tryPlayRemoteStream = function(self) {
|
||||
}
|
||||
};
|
||||
|
||||
var _tryPlayRemoteAudioStream = function(self) {
|
||||
const _tryPlayRemoteAudioStream = function(self) {
|
||||
if (self.getRemoteAudioElement() && self.remoteAStream) {
|
||||
var player = self.getRemoteAudioElement();
|
||||
const player = self.getRemoteAudioElement();
|
||||
player.autoplay = true;
|
||||
self.assignElement(player,
|
||||
self.URL.createObjectURL(self.remoteAStream),
|
||||
"remoteAudio");
|
||||
setTimeout(function() {
|
||||
var ael = self.getRemoteAudioElement();
|
||||
const ael = self.getRemoteAudioElement();
|
||||
if (ael.play) {
|
||||
self.playElement(ael, "remoteAudio");
|
||||
}
|
||||
@@ -1037,58 +1028,58 @@ var _tryPlayRemoteAudioStream = function(self) {
|
||||
}
|
||||
};
|
||||
|
||||
var checkForErrorListener = function(self) {
|
||||
const checkForErrorListener = function(self) {
|
||||
if (self.listeners("error").length === 0) {
|
||||
throw new Error(
|
||||
"You MUST attach an error listener using call.on('error', function() {})"
|
||||
"You MUST attach an error listener using call.on('error', function() {})",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var callError = function(code, msg) {
|
||||
var e = new Error(msg);
|
||||
const callError = function(code, msg) {
|
||||
const e = new Error(msg);
|
||||
e.code = code;
|
||||
return e;
|
||||
};
|
||||
|
||||
var debuglog = function() {
|
||||
const debuglog = function() {
|
||||
if (DEBUG) {
|
||||
console.log.apply(console, arguments);
|
||||
console.log(...arguments);
|
||||
}
|
||||
};
|
||||
|
||||
var _sendCandidateQueue = function(self) {
|
||||
const _sendCandidateQueue = function(self) {
|
||||
if (self.candidateSendQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cands = self.candidateSendQueue;
|
||||
const cands = self.candidateSendQueue;
|
||||
self.candidateSendQueue = [];
|
||||
++self.candidateSendTries;
|
||||
var content = {
|
||||
const content = {
|
||||
version: 0,
|
||||
call_id: self.callId,
|
||||
candidates: cands
|
||||
candidates: cands,
|
||||
};
|
||||
debuglog("Attempting to send " + cands.length + " candidates");
|
||||
sendEvent(self, 'm.call.candidates', content).then(function() {
|
||||
self.candidateSendTries = 0;
|
||||
_sendCandidateQueue(self);
|
||||
}, function(error) {
|
||||
for (var i = 0; i < cands.length; i++) {
|
||||
for (let i = 0; i < cands.length; i++) {
|
||||
self.candidateSendQueue.push(cands[i]);
|
||||
}
|
||||
|
||||
if (self.candidateSendTries > 5) {
|
||||
debuglog(
|
||||
"Failed to send candidates on attempt %s. Giving up for now.",
|
||||
self.candidateSendTries
|
||||
self.candidateSendTries,
|
||||
);
|
||||
self.candidateSendTries = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var delayMs = 500 * Math.pow(2, self.candidateSendTries);
|
||||
const delayMs = 500 * Math.pow(2, self.candidateSendTries);
|
||||
++self.candidateSendTries;
|
||||
debuglog("Failed to send candidates. Retrying in " + delayMs + "ms");
|
||||
setTimeout(function() {
|
||||
@@ -1097,36 +1088,36 @@ var _sendCandidateQueue = function(self) {
|
||||
});
|
||||
};
|
||||
|
||||
var _placeCallWithConstraints = function(self, constraints) {
|
||||
const _placeCallWithConstraints = function(self, constraints) {
|
||||
self.client.callList[self.callId] = self;
|
||||
self.webRtc.getUserMedia(
|
||||
constraints,
|
||||
hookCallback(self, self._gotUserMediaForInvite),
|
||||
hookCallback(self, self._getUserMediaFailed)
|
||||
hookCallback(self, self._getUserMediaFailed),
|
||||
);
|
||||
setState(self, 'wait_local_media');
|
||||
self.direction = 'outbound';
|
||||
self.config = constraints;
|
||||
};
|
||||
|
||||
var _createPeerConnection = function(self) {
|
||||
var servers = self.turnServers;
|
||||
const _createPeerConnection = function(self) {
|
||||
let servers = self.turnServers;
|
||||
if (self.webRtc.vendor === "mozilla") {
|
||||
// modify turnServers struct to match what mozilla expects.
|
||||
servers = [];
|
||||
for (var i = 0; i < self.turnServers.length; i++) {
|
||||
for (var j = 0; j < self.turnServers[i].urls.length; j++) {
|
||||
for (let i = 0; i < self.turnServers.length; i++) {
|
||||
for (let j = 0; j < self.turnServers[i].urls.length; j++) {
|
||||
servers.push({
|
||||
url: self.turnServers[i].urls[j],
|
||||
username: self.turnServers[i].username,
|
||||
credential: self.turnServers[i].credential
|
||||
credential: self.turnServers[i].credential,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var pc = new self.webRtc.RtcPeerConnection({
|
||||
iceServers: servers
|
||||
const pc = new self.webRtc.RtcPeerConnection({
|
||||
iceServers: servers,
|
||||
});
|
||||
pc.oniceconnectionstatechange = hookCallback(self, self._onIceConnectionStateChanged);
|
||||
pc.onsignalingstatechange = hookCallback(self, self._onSignallingStateChanged);
|
||||
@@ -1135,20 +1126,12 @@ var _createPeerConnection = function(self) {
|
||||
return pc;
|
||||
};
|
||||
|
||||
var _getChromeScreenSharingConstraints = function(call) {
|
||||
var screen = global.screen;
|
||||
const _getChromeScreenSharingConstraints = function(call) {
|
||||
const screen = global.screen;
|
||||
if (!screen) {
|
||||
call.emit("error", callError(
|
||||
MatrixCall.ERR_NO_USER_MEDIA,
|
||||
"Couldn't determine screen sharing constaints."
|
||||
));
|
||||
return;
|
||||
}
|
||||
// it won't work at all if you're not on HTTPS so whine whine whine
|
||||
if (!global.window || global.window.location.protocol !== "https:") {
|
||||
call.emit("error", callError(
|
||||
MatrixCall.ERR_NO_USER_MEDIA,
|
||||
"You need to be using HTTPS to place a screen-sharing call."
|
||||
"Couldn't determine screen sharing constaints.",
|
||||
));
|
||||
return;
|
||||
}
|
||||
@@ -1161,13 +1144,13 @@ var _getChromeScreenSharingConstraints = function(call) {
|
||||
maxWidth: screen.width,
|
||||
maxHeight: screen.height,
|
||||
minFrameRate: 1,
|
||||
maxFrameRate: 10
|
||||
}
|
||||
}
|
||||
maxFrameRate: 10,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
var _getUserMediaVideoContraints = function(callType) {
|
||||
const _getUserMediaVideoContraints = function(callType) {
|
||||
switch (callType) {
|
||||
case 'voice':
|
||||
return ({audio: true, video: false});
|
||||
@@ -1177,33 +1160,33 @@ var _getUserMediaVideoContraints = function(callType) {
|
||||
minWidth: 640,
|
||||
maxWidth: 640,
|
||||
minHeight: 360,
|
||||
maxHeight: 360
|
||||
}
|
||||
maxHeight: 360,
|
||||
},
|
||||
}});
|
||||
}
|
||||
};
|
||||
|
||||
var hookCallback = function(call, fn) {
|
||||
const hookCallback = function(call, fn) {
|
||||
return function() {
|
||||
return fn.apply(call, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
var forAllVideoTracksOnStream = function(s, f) {
|
||||
var tracks = s.getVideoTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
const forAllVideoTracksOnStream = function(s, f) {
|
||||
const tracks = s.getVideoTracks();
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
f(tracks[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forAllAudioTracksOnStream = function(s, f) {
|
||||
var tracks = s.getAudioTracks();
|
||||
for (var i = 0; i < tracks.length; i++) {
|
||||
const forAllAudioTracksOnStream = function(s, f) {
|
||||
const tracks = s.getAudioTracks();
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
f(tracks[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var forAllTracksOnStream = function(s, f) {
|
||||
const forAllTracksOnStream = function(s, f) {
|
||||
forAllVideoTracksOnStream(s, f);
|
||||
forAllAudioTracksOnStream(s, f);
|
||||
};
|
||||
@@ -1219,25 +1202,25 @@ module.exports.MatrixCall = MatrixCall;
|
||||
* @return {MatrixCall} the call or null if the browser doesn't support calling.
|
||||
*/
|
||||
module.exports.createNewMatrixCall = function(client, roomId) {
|
||||
var w = global.window;
|
||||
var doc = global.document;
|
||||
const w = global.window;
|
||||
const doc = global.document;
|
||||
if (!w || !doc) {
|
||||
return null;
|
||||
}
|
||||
var webRtc = {};
|
||||
const webRtc = {};
|
||||
webRtc.isOpenWebRTC = function() {
|
||||
var scripts = doc.getElementById("script");
|
||||
const scripts = doc.getElementById("script");
|
||||
if (!scripts || !scripts.length) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < scripts.length; i++) {
|
||||
for (let i = 0; i < scripts.length; i++) {
|
||||
if (scripts[i].src.indexOf("owr.js") > -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
var getUserMedia = (
|
||||
const getUserMedia = (
|
||||
w.navigator.getUserMedia || w.navigator.webkitGetUserMedia ||
|
||||
w.navigator.mozGetUserMedia
|
||||
);
|
||||
@@ -1259,23 +1242,21 @@ module.exports.createNewMatrixCall = function(client, roomId) {
|
||||
webRtc.vendor = null;
|
||||
if (w.mozRTCPeerConnection) {
|
||||
webRtc.vendor = "mozilla";
|
||||
}
|
||||
else if (w.webkitRTCPeerConnection) {
|
||||
} else if (w.webkitRTCPeerConnection) {
|
||||
webRtc.vendor = "webkit";
|
||||
}
|
||||
else if (w.RTCPeerConnection) {
|
||||
} else if (w.RTCPeerConnection) {
|
||||
webRtc.vendor = "generic";
|
||||
}
|
||||
if (!webRtc.RtcIceCandidate || !webRtc.RtcSessionDescription ||
|
||||
!webRtc.RtcPeerConnection || !webRtc.getUserMedia) {
|
||||
return null; // WebRTC is not supported.
|
||||
}
|
||||
var opts = {
|
||||
const opts = {
|
||||
webRtc: webRtc,
|
||||
client: client,
|
||||
URL: w.URL,
|
||||
roomId: roomId,
|
||||
turnServers: client.getTurnServers()
|
||||
turnServers: client.getTurnServers(),
|
||||
};
|
||||
return new MatrixCall(opts);
|
||||
};
|
||||
Reference in New Issue
Block a user