Merge branch 'develop' into bwindels/verification-right-panel
This commit is contained in:
commit
d1fcef1211
515 changed files with 9064 additions and 7392 deletions
20
.babelrc
20
.babelrc
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"presets": [
|
|
||||||
"react",
|
|
||||||
"es2015",
|
|
||||||
"es2016"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"transform-builtin-extend",
|
|
||||||
{
|
|
||||||
"globals": ["Error"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"transform-class-properties",
|
|
||||||
"transform-object-rest-spread",
|
|
||||||
"transform-runtime",
|
|
||||||
"add-module-exports",
|
|
||||||
"syntax-dynamic-import"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,13 +1,60 @@
|
||||||
steps:
|
steps:
|
||||||
- label: ":eslint: Lint"
|
- label: ":eslint: JS Lint"
|
||||||
command:
|
command:
|
||||||
- "echo '--- Install js-sdk'"
|
- "echo '--- Install js-sdk'"
|
||||||
- "./scripts/ci/install-deps.sh"
|
- "./scripts/ci/install-deps.sh"
|
||||||
- "yarn lintwithexclusions"
|
- "yarn lint:js"
|
||||||
- "yarn stylelint"
|
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v3.0.1:
|
- docker#v3.0.1:
|
||||||
image: "node:10"
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: ":eslint: TS Lint"
|
||||||
|
command:
|
||||||
|
- "echo '--- Install js-sdk'"
|
||||||
|
- "./scripts/ci/install-deps.sh"
|
||||||
|
- "yarn lint:ts"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: ":eslint: Types Lint"
|
||||||
|
command:
|
||||||
|
- "echo '--- Install js-sdk'"
|
||||||
|
- "./scripts/ci/install-deps.sh"
|
||||||
|
- "yarn lint:types"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: ":jest: Tests"
|
||||||
|
agents:
|
||||||
|
# We use a medium sized instance instead of the normal small ones because
|
||||||
|
# webpack loves to gorge itself on resources.
|
||||||
|
queue: "medium"
|
||||||
|
command:
|
||||||
|
- "echo '--- Install js-sdk'"
|
||||||
|
# TODO: Remove hacky chmod for BuildKite
|
||||||
|
- "chmod +x ./scripts/ci/*.sh"
|
||||||
|
- "chmod +x ./scripts/*"
|
||||||
|
- "echo '--- Installing Dependencies'"
|
||||||
|
- "./scripts/ci/install-deps.sh"
|
||||||
|
- "echo '--- Running initial build steps'"
|
||||||
|
- "yarn build"
|
||||||
|
- "echo '+++ Running Tests'"
|
||||||
|
- "yarn test"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
|
- label: "🛠 Build"
|
||||||
|
command:
|
||||||
|
- "echo '--- Install js-sdk'"
|
||||||
|
- "./scripts/ci/install-deps.sh"
|
||||||
|
- "echo '+++ Building Project'"
|
||||||
|
- "yarn build"
|
||||||
|
plugins:
|
||||||
|
- docker#v3.0.1:
|
||||||
|
image: "node:12"
|
||||||
|
|
||||||
- label: ":chains: End-to-End Tests"
|
- label: ":chains: End-to-End Tests"
|
||||||
agents:
|
agents:
|
||||||
|
@ -21,39 +68,15 @@ steps:
|
||||||
- "chmod +x ./scripts/*"
|
- "chmod +x ./scripts/*"
|
||||||
- "echo '--- Install js-sdk'"
|
- "echo '--- Install js-sdk'"
|
||||||
- "./scripts/ci/install-deps.sh"
|
- "./scripts/ci/install-deps.sh"
|
||||||
|
- "echo '--- Running initial build steps'"
|
||||||
|
- "yarn build"
|
||||||
|
- "echo '+++ Running Tests'"
|
||||||
- "./scripts/ci/end-to-end-tests.sh"
|
- "./scripts/ci/end-to-end-tests.sh"
|
||||||
plugins:
|
plugins:
|
||||||
- docker#v3.0.1:
|
- docker#v3.0.1:
|
||||||
image: "matrixdotorg/riotweb-ci-e2etests-env:latest"
|
image: "matrixdotorg/riotweb-ci-e2etests-env:latest"
|
||||||
propagate-environment: true
|
propagate-environment: true
|
||||||
|
|
||||||
- label: ":karma: Tests"
|
|
||||||
agents:
|
|
||||||
# We use a medium sized instance instead of the normal small ones because
|
|
||||||
# webpack loves to gorge itself on resources.
|
|
||||||
queue: "medium"
|
|
||||||
command:
|
|
||||||
# Install chrome
|
|
||||||
- "echo '--- Installing Chrome'"
|
|
||||||
- "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -"
|
|
||||||
- "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
|
|
||||||
- "apt-get update"
|
|
||||||
- "apt-get install -y google-chrome-stable"
|
|
||||||
# Run tests
|
|
||||||
# TODO: Remove hacky chmod for BuildKite
|
|
||||||
- "chmod +x ./scripts/ci/*.sh"
|
|
||||||
- "chmod +x ./scripts/*"
|
|
||||||
- "echo '--- Installing Dependencies'"
|
|
||||||
- "./scripts/ci/install-deps.sh"
|
|
||||||
- "echo '+++ Running Tests'"
|
|
||||||
- "./scripts/ci/unit-tests.sh"
|
|
||||||
env:
|
|
||||||
CHROME_BIN: "/usr/bin/google-chrome-stable"
|
|
||||||
plugins:
|
|
||||||
- docker#v3.0.1:
|
|
||||||
image: "node:10"
|
|
||||||
propagate-environment: true
|
|
||||||
|
|
||||||
- label: "🔧 Riot Tests"
|
- label: "🔧 Riot Tests"
|
||||||
agents:
|
agents:
|
||||||
# We use a medium sized instance instead of the normal small ones because
|
# We use a medium sized instance instead of the normal small ones because
|
||||||
|
@ -66,12 +89,13 @@ steps:
|
||||||
- "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
|
- "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
|
||||||
- "apt-get update"
|
- "apt-get update"
|
||||||
- "apt-get install -y google-chrome-stable"
|
- "apt-get install -y google-chrome-stable"
|
||||||
# Run tests
|
|
||||||
# TODO: Remove hacky chmod for BuildKite
|
# TODO: Remove hacky chmod for BuildKite
|
||||||
- "chmod +x ./scripts/ci/*.sh"
|
- "chmod +x ./scripts/ci/*.sh"
|
||||||
- "chmod +x ./scripts/*"
|
- "chmod +x ./scripts/*"
|
||||||
- "echo '--- Installing Dependencies'"
|
- "echo '--- Installing Dependencies'"
|
||||||
- "./scripts/ci/install-deps.sh"
|
- "./scripts/ci/install-deps.sh"
|
||||||
|
- "echo '--- Running initial build steps'"
|
||||||
|
- "yarn build"
|
||||||
- "echo '+++ Running Tests'"
|
- "echo '+++ Running Tests'"
|
||||||
- "./scripts/ci/riot-unit-tests.sh"
|
- "./scripts/ci/riot-unit-tests.sh"
|
||||||
env:
|
env:
|
||||||
|
|
|
@ -33,7 +33,6 @@ src/components/views/rooms/RoomList.js
|
||||||
src/components/views/rooms/RoomPreviewBar.js
|
src/components/views/rooms/RoomPreviewBar.js
|
||||||
src/components/views/rooms/SearchBar.js
|
src/components/views/rooms/SearchBar.js
|
||||||
src/components/views/rooms/SearchResultTile.js
|
src/components/views/rooms/SearchResultTile.js
|
||||||
src/components/views/rooms/SlateMessageComposer.js
|
|
||||||
src/components/views/settings/ChangeAvatar.js
|
src/components/views/settings/ChangeAvatar.js
|
||||||
src/components/views/settings/ChangePassword.js
|
src/components/views/settings/ChangePassword.js
|
||||||
src/components/views/settings/DevicesPanel.js
|
src/components/views/settings/DevicesPanel.js
|
||||||
|
@ -58,7 +57,6 @@ src/utils/Receipt.js
|
||||||
src/Velociraptor.js
|
src/Velociraptor.js
|
||||||
test/components/structures/MessagePanel-test.js
|
test/components/structures/MessagePanel-test.js
|
||||||
test/components/views/dialogs/InteractiveAuthDialog-test.js
|
test/components/views/dialogs/InteractiveAuthDialog-test.js
|
||||||
test/components/views/rooms/MessageComposerInput-test.js
|
|
||||||
test/mock-clock.js
|
test/mock-clock.js
|
||||||
test/notifications/ContentRules-test.js
|
test/notifications/ContentRules-test.js
|
||||||
test/notifications/PushRuleVectorState-test.js
|
test/notifications/PushRuleVectorState-test.js
|
||||||
|
|
|
@ -5,7 +5,10 @@ const path = require('path');
|
||||||
// but only if they come from a module that starts with eslint-config-
|
// but only if they come from a module that starts with eslint-config-
|
||||||
// So we load the filename directly (and it could be in node_modules/
|
// So we load the filename directly (and it could be in node_modules/
|
||||||
// or or ../node_modules/ etc)
|
// or or ../node_modules/ etc)
|
||||||
const matrixJsSdkPath = path.dirname(require.resolve('matrix-js-sdk'));
|
//
|
||||||
|
// We add a `..` to the end because the js-sdk lives out of lib/, but the eslint
|
||||||
|
// config is at the project root.
|
||||||
|
const matrixJsSdkPath = path.join(path.dirname(require.resolve('matrix-js-sdk')), '..');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
parser: "babel-eslint",
|
parser: "babel-eslint",
|
||||||
|
@ -25,6 +28,7 @@ module.exports = {
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
jsx: true,
|
jsx: true,
|
||||||
|
legacyDecorators: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
|
190
CHANGELOG.md
190
CHANGELOG.md
|
@ -1,3 +1,193 @@
|
||||||
|
Changes in [1.7.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6) (2020-01-13)
|
||||||
|
===================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.6-rc.2...v1.7.6)
|
||||||
|
|
||||||
|
* Repair community member info panel
|
||||||
|
[\#3834](https://github.com/matrix-org/matrix-react-sdk/pull/3834)
|
||||||
|
* Add feature flag around the presence indicator in room list
|
||||||
|
[\#3833](https://github.com/matrix-org/matrix-react-sdk/pull/3833)
|
||||||
|
|
||||||
|
Changes in [1.7.6-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6-rc.2) (2020-01-08)
|
||||||
|
=============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.6-rc.1...v1.7.6-rc.2)
|
||||||
|
|
||||||
|
* Strip all variation selectors on emoji
|
||||||
|
[\#3818](https://github.com/matrix-org/matrix-react-sdk/pull/3818)
|
||||||
|
|
||||||
|
Changes in [1.7.6-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6-rc.1) (2020-01-06)
|
||||||
|
=============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5...v1.7.6-rc.1)
|
||||||
|
|
||||||
|
* Deduplicate recent emoji
|
||||||
|
[\#3806](https://github.com/matrix-org/matrix-react-sdk/pull/3806)
|
||||||
|
* Fix ability to remove avatars
|
||||||
|
[\#3803](https://github.com/matrix-org/matrix-react-sdk/pull/3803)
|
||||||
|
* Update from Weblate
|
||||||
|
[\#3810](https://github.com/matrix-org/matrix-react-sdk/pull/3810)
|
||||||
|
* User Info fetch latest RoomMember instead of showing historical data
|
||||||
|
[\#3788](https://github.com/matrix-org/matrix-react-sdk/pull/3788)
|
||||||
|
* Remove all usages of slate in favour of CIDER
|
||||||
|
[\#3808](https://github.com/matrix-org/matrix-react-sdk/pull/3808)
|
||||||
|
* Use display name when pinned messages are changed
|
||||||
|
[\#3809](https://github.com/matrix-org/matrix-react-sdk/pull/3809)
|
||||||
|
* Fix inverted diff line highlighting in dark theme
|
||||||
|
[\#3790](https://github.com/matrix-org/matrix-react-sdk/pull/3790)
|
||||||
|
* Bridge info settings tab
|
||||||
|
[\#3693](https://github.com/matrix-org/matrix-react-sdk/pull/3693)
|
||||||
|
* Send the labs flags the client is running with in rageshake
|
||||||
|
[\#3805](https://github.com/matrix-org/matrix-react-sdk/pull/3805)
|
||||||
|
* Initial implementation of FTUE user lists design
|
||||||
|
[\#3792](https://github.com/matrix-org/matrix-react-sdk/pull/3792)
|
||||||
|
* Update key backup creation and recovery paths for SSSS
|
||||||
|
[\#3800](https://github.com/matrix-org/matrix-react-sdk/pull/3800)
|
||||||
|
* Don't fail if logs exists and is an empty dir
|
||||||
|
[\#3798](https://github.com/matrix-org/matrix-react-sdk/pull/3798)
|
||||||
|
* Comment remaining non-cross-signing-compliant components
|
||||||
|
[\#3799](https://github.com/matrix-org/matrix-react-sdk/pull/3799)
|
||||||
|
* Remove 'unverify' from UserInfoPanel
|
||||||
|
[\#3797](https://github.com/matrix-org/matrix-react-sdk/pull/3797)
|
||||||
|
* Use deviceTrust when displaying key backup trust status
|
||||||
|
[\#3795](https://github.com/matrix-org/matrix-react-sdk/pull/3795)
|
||||||
|
* Don't crash if a keyshare request is removed
|
||||||
|
[\#3793](https://github.com/matrix-org/matrix-react-sdk/pull/3793)
|
||||||
|
* Convert /verify to checkDeviceTrust
|
||||||
|
[\#3794](https://github.com/matrix-org/matrix-react-sdk/pull/3794)
|
||||||
|
* Remove E2eIcon onClick
|
||||||
|
[\#3791](https://github.com/matrix-org/matrix-react-sdk/pull/3791)
|
||||||
|
* support channel names with slash in name/alias
|
||||||
|
[\#3778](https://github.com/matrix-org/matrix-react-sdk/pull/3778)
|
||||||
|
* Fix NPE when filtering the room list
|
||||||
|
[\#3787](https://github.com/matrix-org/matrix-react-sdk/pull/3787)
|
||||||
|
* Turn RoomAliasField into properly controlled and use in RoomSettings
|
||||||
|
[\#3782](https://github.com/matrix-org/matrix-react-sdk/pull/3782)
|
||||||
|
* fuzzy-sort MemberList
|
||||||
|
[\#3783](https://github.com/matrix-org/matrix-react-sdk/pull/3783)
|
||||||
|
* Serialize file uploads into room to match confirmation dialog order
|
||||||
|
[\#3786](https://github.com/matrix-org/matrix-react-sdk/pull/3786)
|
||||||
|
* Do not show Top Unread Messages Bar and Jump to bottom button if searching
|
||||||
|
[\#3785](https://github.com/matrix-org/matrix-react-sdk/pull/3785)
|
||||||
|
* Fix sticker picker chevron offset calculation
|
||||||
|
[\#3784](https://github.com/matrix-org/matrix-react-sdk/pull/3784)
|
||||||
|
* Fix not being able to promote others to the same power level as your own
|
||||||
|
[\#3781](https://github.com/matrix-org/matrix-react-sdk/pull/3781)
|
||||||
|
* Room Tile DMs online/active green dot
|
||||||
|
[\#3751](https://github.com/matrix-org/matrix-react-sdk/pull/3751)
|
||||||
|
* Fix spelling and grammar in README
|
||||||
|
[\#3780](https://github.com/matrix-org/matrix-react-sdk/pull/3780)
|
||||||
|
* Reintroduce working resizer code for right panel
|
||||||
|
[\#3776](https://github.com/matrix-org/matrix-react-sdk/pull/3776)
|
||||||
|
* Fix wrong scope binding on openHelp for TopLeftMenu
|
||||||
|
[\#3775](https://github.com/matrix-org/matrix-react-sdk/pull/3775)
|
||||||
|
* UserInfo hide kick/mute buttons if they make no sense
|
||||||
|
[\#3774](https://github.com/matrix-org/matrix-react-sdk/pull/3774)
|
||||||
|
* Fix duplicate Incoming Call prompt on Community Invite sublist
|
||||||
|
[\#3773](https://github.com/matrix-org/matrix-react-sdk/pull/3773)
|
||||||
|
* Apply new design to highlighted tags and add toggle mechanic
|
||||||
|
[\#3755](https://github.com/matrix-org/matrix-react-sdk/pull/3755)
|
||||||
|
* stop using ReactDOM.findDOMNode in componentWillUnmount, use refs
|
||||||
|
[\#3771](https://github.com/matrix-org/matrix-react-sdk/pull/3771)
|
||||||
|
* Add alt="" to presentational images
|
||||||
|
[\#3772](https://github.com/matrix-org/matrix-react-sdk/pull/3772)
|
||||||
|
* Fix room list filtering weird case sensitivity
|
||||||
|
[\#3759](https://github.com/matrix-org/matrix-react-sdk/pull/3759)
|
||||||
|
* Don't show the 'verify' button if the user is verified
|
||||||
|
[\#3758](https://github.com/matrix-org/matrix-react-sdk/pull/3758)
|
||||||
|
* Switch to using checkDeviceTrust
|
||||||
|
[\#3757](https://github.com/matrix-org/matrix-react-sdk/pull/3757)
|
||||||
|
* Migrate away from React Legacy contexts API
|
||||||
|
[\#3743](https://github.com/matrix-org/matrix-react-sdk/pull/3743)
|
||||||
|
* Migrate key backups to SSSS
|
||||||
|
[\#3749](https://github.com/matrix-org/matrix-react-sdk/pull/3749)
|
||||||
|
* Get rid of stripped-emoji.json in favour of an in-memory single source of
|
||||||
|
truth
|
||||||
|
[\#3745](https://github.com/matrix-org/matrix-react-sdk/pull/3745)
|
||||||
|
* Combine cross signing and verification over DM feature flags
|
||||||
|
[\#3753](https://github.com/matrix-org/matrix-react-sdk/pull/3753)
|
||||||
|
* apply unhomoglyph when filtering room list to fuzzify it
|
||||||
|
[\#3754](https://github.com/matrix-org/matrix-react-sdk/pull/3754)
|
||||||
|
* Make EmojiPicker an unmanaged Context Menu as it is too complex to be
|
||||||
|
managed
|
||||||
|
[\#3746](https://github.com/matrix-org/matrix-react-sdk/pull/3746)
|
||||||
|
* Internationalise M_TOO_LARGE error from Synapse
|
||||||
|
[\#3750](https://github.com/matrix-org/matrix-react-sdk/pull/3750)
|
||||||
|
* Replace UserInfo avatar with <MemberAvatar/> for fallback logic
|
||||||
|
[\#3748](https://github.com/matrix-org/matrix-react-sdk/pull/3748)
|
||||||
|
* Dropdown stop keyboard propagation if key handled
|
||||||
|
[\#3741](https://github.com/matrix-org/matrix-react-sdk/pull/3741)
|
||||||
|
* Fix right panel for multiple member info viewings
|
||||||
|
[\#3742](https://github.com/matrix-org/matrix-react-sdk/pull/3742)
|
||||||
|
* Fix Field validation tooltip sticking if blurred before async validation
|
||||||
|
resolved
|
||||||
|
[\#3740](https://github.com/matrix-org/matrix-react-sdk/pull/3740)
|
||||||
|
* Fix UserInfo exploding without a room being passed to it
|
||||||
|
[\#3738](https://github.com/matrix-org/matrix-react-sdk/pull/3738)
|
||||||
|
* Fix room directory maintaining and error state
|
||||||
|
[\#3737](https://github.com/matrix-org/matrix-react-sdk/pull/3737)
|
||||||
|
* Stop trapping tab in AddressPickerDialog
|
||||||
|
[\#3735](https://github.com/matrix-org/matrix-react-sdk/pull/3735)
|
||||||
|
* Stop using KeyboardEvent.keyCode as it is deprecated
|
||||||
|
[\#3736](https://github.com/matrix-org/matrix-react-sdk/pull/3736)
|
||||||
|
* Implement new design for uploading/removing avatars
|
||||||
|
[\#3733](https://github.com/matrix-org/matrix-react-sdk/pull/3733)
|
||||||
|
* Fix aspect ratio on room/profile avatar preview
|
||||||
|
[\#3731](https://github.com/matrix-org/matrix-react-sdk/pull/3731)
|
||||||
|
* Switch to react-focus-lock for it to comprehend Portals
|
||||||
|
[\#3732](https://github.com/matrix-org/matrix-react-sdk/pull/3732)
|
||||||
|
* Make combobox dropdown keyboard and screen reader accessible
|
||||||
|
[\#3729](https://github.com/matrix-org/matrix-react-sdk/pull/3729)
|
||||||
|
* Verify users when cross-signing enabled
|
||||||
|
[\#3728](https://github.com/matrix-org/matrix-react-sdk/pull/3728)
|
||||||
|
* Update from Weblate
|
||||||
|
[\#3730](https://github.com/matrix-org/matrix-react-sdk/pull/3730)
|
||||||
|
* Improve a11y of the unignore button in Settings
|
||||||
|
[\#3727](https://github.com/matrix-org/matrix-react-sdk/pull/3727)
|
||||||
|
* Fix ToggleSwitch A11Y (trapping tab and switch v. checkbox)
|
||||||
|
[\#3726](https://github.com/matrix-org/matrix-react-sdk/pull/3726)
|
||||||
|
* Make URL previews dismissable via keyboard and accessible to screen readers
|
||||||
|
[\#3725](https://github.com/matrix-org/matrix-react-sdk/pull/3725)
|
||||||
|
* Create new key backups using secret storage
|
||||||
|
[\#3720](https://github.com/matrix-org/matrix-react-sdk/pull/3720)
|
||||||
|
* Replace sign-ins with sessions
|
||||||
|
[\#3721](https://github.com/matrix-org/matrix-react-sdk/pull/3721)
|
||||||
|
* Refactor RightPanel to match expected behaviour
|
||||||
|
[\#3703](https://github.com/matrix-org/matrix-react-sdk/pull/3703)
|
||||||
|
* Render policy room event updates in the timeline
|
||||||
|
[\#3716](https://github.com/matrix-org/matrix-react-sdk/pull/3716)
|
||||||
|
* Wrap the await call for unknown device lookups
|
||||||
|
[\#3718](https://github.com/matrix-org/matrix-react-sdk/pull/3718)
|
||||||
|
* Add testing flow to bootstrap secret storage
|
||||||
|
[\#3640](https://github.com/matrix-org/matrix-react-sdk/pull/3640)
|
||||||
|
* Fix remaining context menu regressions
|
||||||
|
[\#3715](https://github.com/matrix-org/matrix-react-sdk/pull/3715)
|
||||||
|
* Migrate away from React Legacy string refs
|
||||||
|
[\#3712](https://github.com/matrix-org/matrix-react-sdk/pull/3712)
|
||||||
|
* Update copy for DM invites
|
||||||
|
[\#3706](https://github.com/matrix-org/matrix-react-sdk/pull/3706)
|
||||||
|
* Fix message action bar reaction picker regression
|
||||||
|
[\#3714](https://github.com/matrix-org/matrix-react-sdk/pull/3714)
|
||||||
|
* Add what-input to allow different scoping to focus-visible for MAB a11y
|
||||||
|
[\#3709](https://github.com/matrix-org/matrix-react-sdk/pull/3709)
|
||||||
|
* Mark the This/All Rooms scope buttons as radios for a11y
|
||||||
|
[\#3708](https://github.com/matrix-org/matrix-react-sdk/pull/3708)
|
||||||
|
* Switch ReactionsRowButton to an AccessibleButton for space/enter handling
|
||||||
|
[\#3707](https://github.com/matrix-org/matrix-react-sdk/pull/3707)
|
||||||
|
* Change the (edited) link to an AccessibleButton for a11y
|
||||||
|
[\#3710](https://github.com/matrix-org/matrix-react-sdk/pull/3710)
|
||||||
|
* Update from Weblate
|
||||||
|
[\#3713](https://github.com/matrix-org/matrix-react-sdk/pull/3713)
|
||||||
|
* Fix ?via= args in SpecPermalinkConstructor.js
|
||||||
|
[\#3694](https://github.com/matrix-org/matrix-react-sdk/pull/3694)
|
||||||
|
* Don't mark a room as unread when server ACLs are set
|
||||||
|
[\#3705](https://github.com/matrix-org/matrix-react-sdk/pull/3705)
|
||||||
|
* Make reaction buttons more accessible
|
||||||
|
[\#3704](https://github.com/matrix-org/matrix-react-sdk/pull/3704)
|
||||||
|
* yarn upgrade
|
||||||
|
[\#3701](https://github.com/matrix-org/matrix-react-sdk/pull/3701)
|
||||||
|
* Make CI scripts executable
|
||||||
|
[\#3698](https://github.com/matrix-org/matrix-react-sdk/pull/3698)
|
||||||
|
* ARIA compliant context menus
|
||||||
|
[\#3611](https://github.com/matrix-org/matrix-react-sdk/pull/3611)
|
||||||
|
|
||||||
Changes in [1.7.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.5) (2019-12-09)
|
Changes in [1.7.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.5) (2019-12-09)
|
||||||
===================================================================================================
|
===================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5-rc.1...v1.7.5)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5-rc.1...v1.7.5)
|
||||||
|
|
|
@ -67,6 +67,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold:
|
||||||
|
|
||||||
* After creating a new component you must run `yarn reskindex` to regenerate
|
* After creating a new component you must run `yarn reskindex` to regenerate
|
||||||
the `component-index.js` for the SDK (used in future for skinning)
|
the `component-index.js` for the SDK (used in future for skinning)
|
||||||
|
<!-- TODO: Remove this once this approach to skinning is replaced -->
|
||||||
|
|
||||||
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
|
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
|
||||||
CSS for matrix-react-sdk currently resides in
|
CSS for matrix-react-sdk currently resides in
|
||||||
|
|
17
__mocks__/browser-request.js
Normal file
17
__mocks__/browser-request.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const en = require("../src/i18n/strings/en_EN");
|
||||||
|
|
||||||
|
module.exports = jest.fn((opts, cb) => {
|
||||||
|
const url = opts.url || opts.uri;
|
||||||
|
if (url && url.endsWith("languages.json")) {
|
||||||
|
cb(undefined, {status: 200}, JSON.stringify({
|
||||||
|
"en": {
|
||||||
|
"fileName": "en_EN.json",
|
||||||
|
"label": "English",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
} else if (url && url.endsWith("en_EN.json")) {
|
||||||
|
cb(undefined, {status: 200}, JSON.stringify(en));
|
||||||
|
} else {
|
||||||
|
cb(true, {status: 404}, "");
|
||||||
|
}
|
||||||
|
});
|
1
__mocks__/imageMock.js
Normal file
1
__mocks__/imageMock.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "image-file-stub";
|
10
__mocks__/languages.json
Normal file
10
__mocks__/languages.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"fileName": "en_EN.json",
|
||||||
|
"label": "English"
|
||||||
|
},
|
||||||
|
"en-us": {
|
||||||
|
"fileName": "en_US.json",
|
||||||
|
"label": "English (US)"
|
||||||
|
}
|
||||||
|
}
|
26
babel.config.js
Normal file
26
babel.config.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module.exports = {
|
||||||
|
"sourceMaps": "inline",
|
||||||
|
"presets": [
|
||||||
|
["@babel/preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"browsers": [
|
||||||
|
"last 2 versions"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"modules": "commonjs"
|
||||||
|
}],
|
||||||
|
"@babel/preset-typescript",
|
||||||
|
"@babel/preset-flow",
|
||||||
|
"@babel/preset-react"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||||
|
"@babel/plugin-proposal-export-default-from",
|
||||||
|
"@babel/plugin-proposal-numeric-separator",
|
||||||
|
"@babel/plugin-proposal-class-properties",
|
||||||
|
"@babel/plugin-proposal-object-rest-spread",
|
||||||
|
"@babel/plugin-transform-flow-comments",
|
||||||
|
"@babel/plugin-syntax-dynamic-import",
|
||||||
|
"@babel/plugin-transform-runtime"
|
||||||
|
]
|
||||||
|
};
|
71
docs/skinning.md
Normal file
71
docs/skinning.md
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# Skinning
|
||||||
|
|
||||||
|
The react-sdk can be skinned to replace presentation components, CSS, or
|
||||||
|
other relevant parts of the SDK. Typically consumers will replace entire
|
||||||
|
components and get the ability for custom CSS as a result.
|
||||||
|
|
||||||
|
This doc isn't exhaustive on how skinning works, though it should cover
|
||||||
|
some of the more complicated parts such as component replacement.
|
||||||
|
|
||||||
|
## Loading a skin
|
||||||
|
|
||||||
|
1. Generate a `component-index.js` (preferably using the tools that the react-sdk
|
||||||
|
exposes). This can typically be done with a npm script like `"reskindex -h src/header"`.
|
||||||
|
2. In your app's entry point, add something like this code:
|
||||||
|
```javascript
|
||||||
|
import {loadSkin} from "matrix-react-sdk";
|
||||||
|
loadSkin(import("component-index").components);
|
||||||
|
// The rest of your imports go under this.
|
||||||
|
```
|
||||||
|
3. Import the remainder of the SDK and bootstrap your app.
|
||||||
|
|
||||||
|
It is extremely important that you **do not** import anything else from the
|
||||||
|
SDK prior to loading your skin as otherwise the skin might not work. Loading
|
||||||
|
the skin should be one of the first things your app does, if not the very
|
||||||
|
first thing.
|
||||||
|
|
||||||
|
Additionally, **do not** provide `loadSkin` with the react-sdk components
|
||||||
|
themselves otherwise the app might explode. The SDK is already aware of its
|
||||||
|
components and doesn't need to be told.
|
||||||
|
|
||||||
|
## Replacing components
|
||||||
|
|
||||||
|
Components that replace the react-sdk ones MUST have a `replaces` static
|
||||||
|
key on the component's class to describe which component it overrides. For
|
||||||
|
example, if your `VectorAuthPage` component is meant to replace the react-sdk
|
||||||
|
`AuthPage` component then you'd add `static replaces = 'views.auth.AuthPage';`
|
||||||
|
to the `VectorAuthPage` class.
|
||||||
|
|
||||||
|
Other than that, the skin just needs to be loaded normally as mentioned above.
|
||||||
|
Consumers of the SDK likely will not be interested in the rest of this section.
|
||||||
|
|
||||||
|
### SDK developer notes
|
||||||
|
|
||||||
|
Components in the react-sdk MUST be decorated with the `@replaceableComponent`
|
||||||
|
function. For components that can't use the decorator, they must use a
|
||||||
|
variation that provides similar functionality. The decorator gives consumers
|
||||||
|
an opportunity to load skinned components by abusing import ordering and
|
||||||
|
behaviour.
|
||||||
|
|
||||||
|
Decorators are executed at import time which is why we can abuse the import
|
||||||
|
ordering behaviour: importing `loadSkin` doesn't trigger any components to
|
||||||
|
be imported, allowing the consumer to specify a skin. When the consumer does
|
||||||
|
import a component (for example, `MatrixChat`), it starts to pull in all the
|
||||||
|
components via `import` statements. When the components get pulled in the
|
||||||
|
decorator checks with the skinned components to see if it should be replacing
|
||||||
|
the component being imported. The decorator then effectively replaces the
|
||||||
|
components when needed by specifying the skinned component as an override for
|
||||||
|
the SDK's component, which should in theory override critical functions like
|
||||||
|
`render()` and lifecycle event handlers.
|
||||||
|
|
||||||
|
The decorator also means that older usage of `getComponent()` is no longer
|
||||||
|
required because components should be replaced by the decorator. Eventually
|
||||||
|
the react-sdk should only have one usage of `getComponent()`: the decorator.
|
||||||
|
|
||||||
|
The decorator assumes that if `getComponent()` returns null that there is
|
||||||
|
no skinned version of the component and continues on using the SDK's component.
|
||||||
|
In previous versions of the SDK, the function would throw an error instead
|
||||||
|
because it also expected the skin to list the SDK's components as well, however
|
||||||
|
that is no longer possible due to the above.
|
||||||
|
|
||||||
|
In short, components should always be `import`ed.
|
39
jenkins.sh
39
jenkins.sh
|
@ -1,39 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
export NVM_DIR="$HOME/.nvm"
|
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
|
|
||||||
nvm use 10
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
scripts/fetchdep.sh matrix-org matrix-js-sdk
|
|
||||||
|
|
||||||
pushd matrix-js-sdk
|
|
||||||
yarn link
|
|
||||||
yarn install
|
|
||||||
popd
|
|
||||||
|
|
||||||
yarn link matrix-js-sdk
|
|
||||||
|
|
||||||
# install the other dependencies
|
|
||||||
yarn install
|
|
||||||
|
|
||||||
# run the mocha tests
|
|
||||||
yarn test --no-colors
|
|
||||||
|
|
||||||
# run eslint
|
|
||||||
yarn lintall -f checkstyle -o eslint.xml || true
|
|
||||||
|
|
||||||
# re-run the linter, excluding any files known to have errors or warnings.
|
|
||||||
yarn lintwithexclusions
|
|
||||||
|
|
||||||
# lint styles
|
|
||||||
yarn stylelint
|
|
||||||
|
|
||||||
# delete the old tarball, if it exists
|
|
||||||
rm -f matrix-react-sdk-*.tgz
|
|
||||||
|
|
||||||
# build our tarball
|
|
||||||
yarn pack
|
|
228
karma.conf.js
228
karma.conf.js
|
@ -1,228 +0,0 @@
|
||||||
// karma.conf.js - the config file for karma, which runs our tests.
|
|
||||||
|
|
||||||
var path = require('path');
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We use webpack to build our tests. It's a pain to have to wait for webpack
|
|
||||||
* to build everything; however it's the easiest way to load our dependencies
|
|
||||||
* from node_modules.
|
|
||||||
*
|
|
||||||
* If you run karma in multi-run mode (with `yarn test-multi`), it will watch
|
|
||||||
* the tests for changes, and webpack will rebuild using a cache. This is much quicker
|
|
||||||
* than a clean rebuild.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// the name of the test file. By default, a special file which runs all tests.
|
|
||||||
//
|
|
||||||
// TODO: this could be a pattern, and karma would run each file, with a
|
|
||||||
// separate webpack bundle for each file. But then we get a separate instance
|
|
||||||
// of the sdk, and each of the dependencies, for each test file, and everything
|
|
||||||
// gets very confused. Can we persuade webpack to put all of the dependencies
|
|
||||||
// in a 'common' bundle?
|
|
||||||
//
|
|
||||||
var testFile = process.env.KARMA_TEST_FILE || 'test/all-tests.js';
|
|
||||||
|
|
||||||
|
|
||||||
process.env.PHANTOMJS_BIN = 'node_modules/.bin/phantomjs';
|
|
||||||
|
|
||||||
function fileExists(name) {
|
|
||||||
try {
|
|
||||||
fs.statSync(name);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try find the gemini-scrollbar css in an version-agnostic way
|
|
||||||
var gsCss = 'node_modules/gemini-scrollbar/gemini-scrollbar.css';
|
|
||||||
if (!fileExists(gsCss)) {
|
|
||||||
gsCss = 'node_modules/react-gemini-scrollbar/'+gsCss;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
// frameworks to use
|
|
||||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
|
||||||
frameworks: ['mocha'],
|
|
||||||
|
|
||||||
// list of files / patterns to load in the browser
|
|
||||||
files: [
|
|
||||||
testFile,
|
|
||||||
gsCss,
|
|
||||||
|
|
||||||
// some images to reduce noise from the tests
|
|
||||||
{pattern: 'test/img/*', watched: false, included: false,
|
|
||||||
served: true, nocache: false},
|
|
||||||
// translation files
|
|
||||||
{pattern: 'src/i18n/strings/*', watcheed: false, included: false, served: true},
|
|
||||||
{pattern: 'test/i18n/*', watched: false, included: false, served: true},
|
|
||||||
],
|
|
||||||
|
|
||||||
proxies: {
|
|
||||||
// redirect img links to the karma server
|
|
||||||
"/img/": "/base/test/img/",
|
|
||||||
// special languages.json file for the tests
|
|
||||||
"/i18n/languages.json": "/base/test/i18n/languages.json",
|
|
||||||
// and redirect i18n requests
|
|
||||||
"/i18n/": "/base/src/i18n/strings/",
|
|
||||||
},
|
|
||||||
|
|
||||||
// list of files to exclude
|
|
||||||
//
|
|
||||||
// This doesn't work. It turns out that it's webpack which does the
|
|
||||||
// watching of the /test directory (karma only watches `testFile`
|
|
||||||
// itself). Webpack watches the directory so that it can spot
|
|
||||||
// new tests, which is fair enough; unfortunately it triggers a rebuild
|
|
||||||
// every time a lockfile is created in that directory, and there
|
|
||||||
// doesn't seem to be any way to tell webpack to ignore particular
|
|
||||||
// files in a watched directory.
|
|
||||||
//
|
|
||||||
// exclude: [
|
|
||||||
// '**/.#*'
|
|
||||||
// ],
|
|
||||||
|
|
||||||
// preprocess matching files before serving them to the browser
|
|
||||||
// available preprocessors:
|
|
||||||
// https://npmjs.org/browse/keyword/karma-preprocessor
|
|
||||||
preprocessors: {
|
|
||||||
'test/**/*.js': ['webpack', 'sourcemap']
|
|
||||||
},
|
|
||||||
|
|
||||||
// test results reporter to use
|
|
||||||
// possible values: 'dots', 'progress'
|
|
||||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
|
||||||
reporters: ['logcapture', 'spec', 'summary'],
|
|
||||||
|
|
||||||
specReporter: {
|
|
||||||
suppressErrorSummary: false, // do print error summary
|
|
||||||
suppressFailed: false, // do print information about failed tests
|
|
||||||
suppressPassed: false, // do print information about passed tests
|
|
||||||
showSpecTiming: true, // print the time elapsed for each spec
|
|
||||||
},
|
|
||||||
|
|
||||||
client: {
|
|
||||||
captureLogs: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// web server port
|
|
||||||
port: 9876,
|
|
||||||
|
|
||||||
// enable / disable colors in the output (reporters and logs)
|
|
||||||
colors: true,
|
|
||||||
|
|
||||||
// level of logging
|
|
||||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR ||
|
|
||||||
// config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
|
||||||
//
|
|
||||||
// This is strictly for logs that would be generated by the browser itself and we
|
|
||||||
// don't want to log about missing images, which are emitted on LOG_WARN.
|
|
||||||
logLevel: config.LOG_ERROR,
|
|
||||||
|
|
||||||
// enable / disable watching file and executing tests whenever any file
|
|
||||||
// changes
|
|
||||||
autoWatch: true,
|
|
||||||
|
|
||||||
// start these browsers
|
|
||||||
// available browser launchers:
|
|
||||||
// https://npmjs.org/browse/keyword/karma-launcher
|
|
||||||
browsers: [
|
|
||||||
'Chrome',
|
|
||||||
//'PhantomJS',
|
|
||||||
//'ChromeHeadless',
|
|
||||||
],
|
|
||||||
|
|
||||||
customLaunchers: {
|
|
||||||
'VectorChromeHeadless': {
|
|
||||||
base: 'Chrome',
|
|
||||||
flags: [
|
|
||||||
'--no-sandbox',
|
|
||||||
// See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md
|
|
||||||
'--headless',
|
|
||||||
'--disable-gpu',
|
|
||||||
// Without a remote debugging port, Google Chrome exits immediately.
|
|
||||||
'--remote-debugging-port=9222',
|
|
||||||
],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Continuous Integration mode
|
|
||||||
// if true, Karma captures browsers, runs the tests and exits
|
|
||||||
// singleRun: false,
|
|
||||||
|
|
||||||
// Concurrency level
|
|
||||||
// how many browser should be started simultaneous
|
|
||||||
concurrency: Infinity,
|
|
||||||
|
|
||||||
webpack: {
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js$/, loader: "babel-loader",
|
|
||||||
include: [path.resolve('./src'),
|
|
||||||
path.resolve('./test'),
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(gif|png|svg|ttf|woff2)$/,
|
|
||||||
loader: 'file-loader',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
noParse: [
|
|
||||||
// for cross platform compatibility use [\\\/] as the path separator
|
|
||||||
// this ensures that the regex trips on both Windows and *nix
|
|
||||||
|
|
||||||
// don't parse the languages within highlight.js. They
|
|
||||||
// cause stack overflows
|
|
||||||
// (https://github.com/webpack/webpack/issues/1721), and
|
|
||||||
// there is no need for webpack to parse them - they can
|
|
||||||
// just be included as-is.
|
|
||||||
/highlight\.js[\\\/]lib[\\\/]languages/,
|
|
||||||
|
|
||||||
// olm takes ages for webpack to process, and it's already heavily
|
|
||||||
// optimised, so there is little to gain by us uglifying it.
|
|
||||||
/olm[\\\/](javascript[\\\/])?olm\.js$/,
|
|
||||||
|
|
||||||
// also disable parsing for sinon, because it
|
|
||||||
// tries to do voodoo with 'require' which upsets
|
|
||||||
// webpack (https://github.com/webpack/webpack/issues/304)
|
|
||||||
/sinon[\\\/]pkg[\\\/]sinon\.js$/,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
alias: {
|
|
||||||
// alias any requires to the react module to the one in our
|
|
||||||
// path, otherwise we tend to get the react source included
|
|
||||||
// twice when using `npm link` / `yarn link`.
|
|
||||||
react: path.resolve('./node_modules/react'),
|
|
||||||
|
|
||||||
'matrix-react-sdk': path.resolve('test/skinned-sdk.js'),
|
|
||||||
'sinon': 'sinon/pkg/sinon.js',
|
|
||||||
},
|
|
||||||
modules: [
|
|
||||||
path.resolve('./test'),
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
},
|
|
||||||
devtool: 'inline-source-map',
|
|
||||||
externals: {
|
|
||||||
// Don't try to bundle electron: leave it as a commonjs dependency
|
|
||||||
// (the 'commonjs' here means it will output a 'require')
|
|
||||||
"electron": "commonjs electron",
|
|
||||||
},
|
|
||||||
// make sure we're flagged as development to avoid wasting time optimising
|
|
||||||
mode: 'development',
|
|
||||||
},
|
|
||||||
|
|
||||||
webpackMiddleware: {
|
|
||||||
stats: {
|
|
||||||
// don't fill the console up with a mahoosive list of modules
|
|
||||||
chunks: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
browserNoActivityTimeout: 15000,
|
|
||||||
});
|
|
||||||
};
|
|
134
package.json
134
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "1.7.5",
|
"version": "1.7.6",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -8,57 +8,51 @@
|
||||||
"url": "https://github.com/matrix-org/matrix-react-sdk"
|
"url": "https://github.com/matrix-org/matrix-react-sdk"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "lib/index.js",
|
|
||||||
"files": [
|
"files": [
|
||||||
".babelrc",
|
"lib",
|
||||||
".eslintrc.js",
|
"res",
|
||||||
|
"src",
|
||||||
|
"scripts",
|
||||||
|
"git-revision.txt",
|
||||||
|
"docs",
|
||||||
|
"header",
|
||||||
"CHANGELOG.md",
|
"CHANGELOG.md",
|
||||||
"CONTRIBUTING.rst",
|
"CONTRIBUTING.rst",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md",
|
"README.md",
|
||||||
"code_style.md",
|
"package.json"
|
||||||
"git-revision.txt",
|
|
||||||
"header",
|
|
||||||
"jenkins.sh",
|
|
||||||
"karma.conf.js",
|
|
||||||
"lib",
|
|
||||||
"package.json",
|
|
||||||
"release.sh",
|
|
||||||
"scripts",
|
|
||||||
"src",
|
|
||||||
"test",
|
|
||||||
"res"
|
|
||||||
],
|
],
|
||||||
"bin": {
|
"bin": {
|
||||||
"reskindex": "scripts/reskindex.js",
|
"reskindex": "scripts/reskindex.js",
|
||||||
"matrix-gen-i18n": "scripts/gen-i18n.js",
|
"matrix-gen-i18n": "scripts/gen-i18n.js",
|
||||||
"matrix-prune-i18n": "scripts/prune-i18n.js"
|
"matrix-prune-i18n": "scripts/prune-i18n.js"
|
||||||
},
|
},
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"typings": "./lib/index.d.ts",
|
||||||
|
"matrix_src_main": "./src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"reskindex": "node scripts/reskindex.js -h header",
|
|
||||||
"reskindex:watch": "node scripts/reskindex.js -h header -w",
|
|
||||||
"rethemendex": "res/css/rethemendex.sh",
|
|
||||||
"i18n": "matrix-gen-i18n",
|
"i18n": "matrix-gen-i18n",
|
||||||
"prunei18n": "matrix-prune-i18n",
|
"prunei18n": "matrix-prune-i18n",
|
||||||
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && ./scripts/gen-i18n.js && node scripts/compare-file.js src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && ./scripts/gen-i18n.js && node scripts/compare-file.js src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||||
"build": "yarn reskindex && yarn start:init",
|
"reskindex": "node scripts/reskindex.js -h header",
|
||||||
"build:watch": "babel src -w --skip-initial-build -d lib --source-maps --copy-files",
|
"reskindex:watch": "node scripts/reskindex.js -h header -w",
|
||||||
"start": "yarn start:init && yarn start:all",
|
"rethemendex": "res/css/rethemendex.sh",
|
||||||
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn build:watch\" \"yarn reskindex:watch\"",
|
|
||||||
"start:init": "babel src -d lib --source-maps --copy-files",
|
|
||||||
"lint": "eslint src/",
|
|
||||||
"lintall": "eslint src/ test/",
|
|
||||||
"lintwithexclusions": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
|
|
||||||
"stylelint": "stylelint 'res/css/**/*.scss'",
|
|
||||||
"clean": "rimraf lib",
|
"clean": "rimraf lib",
|
||||||
"prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt",
|
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
|
||||||
"test": "karma start --single-run=true --browsers VectorChromeHeadless",
|
"build:compile": "yarn reskindex && babel -d lib --verbose --extensions \".ts,.js\" src",
|
||||||
"test-multi": "karma start",
|
"build:types": "tsc --emitDeclarationOnly",
|
||||||
"e2etests": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080"
|
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:all",
|
||||||
|
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"",
|
||||||
|
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
||||||
|
"lint": "yarn lint:types && yarn lint:ts && yarn lint:js && yarn lint:style",
|
||||||
|
"lint:js": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
|
||||||
|
"lint:ts": "tslint --project ./tsconfig.json -t stylish",
|
||||||
|
"lint:types": "tsc --noEmit",
|
||||||
|
"lint:style": "stylelint 'res/css/**/*.scss'",
|
||||||
|
"test": "jest",
|
||||||
|
"test:e2e": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
|
||||||
"babel-runtime": "^6.26.0",
|
|
||||||
"blueimp-canvas-to-blob": "^3.5.0",
|
"blueimp-canvas-to-blob": "^3.5.0",
|
||||||
"browser-encrypt-attachment": "^0.3.0",
|
"browser-encrypt-attachment": "^0.3.0",
|
||||||
"browser-request": "^0.3.3",
|
"browser-request": "^0.3.3",
|
||||||
|
@ -74,7 +68,6 @@
|
||||||
"file-saver": "^1.3.3",
|
"file-saver": "^1.3.3",
|
||||||
"filesize": "3.5.6",
|
"filesize": "3.5.6",
|
||||||
"flux": "2.1.1",
|
"flux": "2.1.1",
|
||||||
"react-focus-lock": "^2.2.1",
|
|
||||||
"focus-visible": "^5.0.2",
|
"focus-visible": "^5.0.2",
|
||||||
"fuse.js": "^2.2.0",
|
"fuse.js": "^2.2.0",
|
||||||
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
||||||
|
@ -82,12 +75,13 @@
|
||||||
"glob": "^5.0.14",
|
"glob": "^5.0.14",
|
||||||
"glob-to-regexp": "^0.4.1",
|
"glob-to-regexp": "^0.4.1",
|
||||||
"highlight.js": "^9.15.8",
|
"highlight.js": "^9.15.8",
|
||||||
|
"html-entities": "^1.2.1",
|
||||||
"is-ip": "^2.0.0",
|
"is-ip": "^2.0.0",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"linkifyjs": "^2.1.6",
|
"linkifyjs": "^2.1.6",
|
||||||
"lodash": "^4.17.14",
|
"lodash": "^4.17.14",
|
||||||
"lolex": "4.2",
|
"lolex": "4.2",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
"matrix-js-sdk": "3.0.0",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
"pako": "^1.0.5",
|
"pako": "^1.0.5",
|
||||||
"png-chunks-extract": "^1.0.0",
|
"png-chunks-extract": "^1.0.0",
|
||||||
|
@ -99,13 +93,10 @@
|
||||||
"react-addons-css-transition-group": "15.6.2",
|
"react-addons-css-transition-group": "15.6.2",
|
||||||
"react-beautiful-dnd": "^4.0.1",
|
"react-beautiful-dnd": "^4.0.1",
|
||||||
"react-dom": "^16.9.0",
|
"react-dom": "^16.9.0",
|
||||||
|
"react-focus-lock": "^2.2.1",
|
||||||
"react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
|
"react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
|
||||||
"resize-observer-polyfill": "^1.5.0",
|
"resize-observer-polyfill": "^1.5.0",
|
||||||
"sanitize-html": "^1.18.4",
|
"sanitize-html": "^1.18.4",
|
||||||
"slate": "^0.41.2",
|
|
||||||
"slate-html-serializer": "^0.6.1",
|
|
||||||
"slate-md-serializer": "github:matrix-org/slate-md-serializer#f7c4ad3",
|
|
||||||
"slate-react": "^0.18.10",
|
|
||||||
"text-encoding-utf-8": "^1.0.1",
|
"text-encoding-utf-8": "^1.0.1",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"velocity-animate": "^1.5.2",
|
"velocity-animate": "^1.5.2",
|
||||||
|
@ -114,22 +105,28 @@
|
||||||
"zxcvbn": "^4.4.2"
|
"zxcvbn": "^4.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "^6.26.0",
|
"@babel/cli": "^7.7.5",
|
||||||
"babel-core": "^6.26.3",
|
"@babel/core": "^7.7.5",
|
||||||
"babel-eslint": "^10.0.1",
|
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||||
"babel-loader": "^7.1.5",
|
"@babel/plugin-proposal-decorators": "^7.7.4",
|
||||||
"babel-plugin-add-module-exports": "^0.2.1",
|
"@babel/plugin-proposal-export-default-from": "^7.7.4",
|
||||||
"babel-plugin-transform-builtin-extend": "^1.1.2",
|
"@babel/plugin-proposal-numeric-separator": "^7.7.4",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"@babel/plugin-proposal-object-rest-spread": "^7.7.4",
|
||||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
"@babel/plugin-transform-flow-comments": "^7.7.4",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||||
"babel-polyfill": "^6.26.0",
|
"@babel/preset-env": "^7.7.6",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"@babel/preset-flow": "^7.7.4",
|
||||||
"babel-preset-es2016": "^6.24.1",
|
"@babel/preset-react": "^7.7.4",
|
||||||
"babel-preset-es2017": "^6.24.1",
|
"@babel/preset-typescript": "^7.7.4",
|
||||||
"babel-preset-react": "^6.24.1",
|
"@babel/register": "^7.7.4",
|
||||||
|
"@babel/runtime": "^7.7.6",
|
||||||
|
"@peculiar/webcrypto": "^1.0.22",
|
||||||
|
"babel-eslint": "^10.0.3",
|
||||||
|
"babel-jest": "^24.9.0",
|
||||||
"chokidar": "^2.1.2",
|
"chokidar": "^2.1.2",
|
||||||
"concurrently": "^4.0.1",
|
"concurrently": "^4.0.1",
|
||||||
|
"enzyme": "^3.10.0",
|
||||||
|
"enzyme-adapter-react-16": "^1.15.1",
|
||||||
"eslint": "^5.12.0",
|
"eslint": "^5.12.0",
|
||||||
"eslint-config-google": "^0.7.1",
|
"eslint-config-google": "^0.7.1",
|
||||||
"eslint-plugin-babel": "^5.2.1",
|
"eslint-plugin-babel": "^5.2.1",
|
||||||
|
@ -138,32 +135,35 @@
|
||||||
"eslint-plugin-react": "^7.7.0",
|
"eslint-plugin-react": "^7.7.0",
|
||||||
"eslint-plugin-react-hooks": "^2.0.1",
|
"eslint-plugin-react-hooks": "^2.0.1",
|
||||||
"estree-walker": "^0.5.0",
|
"estree-walker": "^0.5.0",
|
||||||
"expect": "^24.1.0",
|
|
||||||
"file-loader": "^3.0.1",
|
"file-loader": "^3.0.1",
|
||||||
"flow-parser": "^0.57.3",
|
"flow-parser": "^0.57.3",
|
||||||
"jest-mock": "^23.2.0",
|
"jest": "^24.9.0",
|
||||||
"karma": "^4.0.1",
|
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
|
||||||
"karma-cli": "^1.0.1",
|
|
||||||
"karma-logcapture-reporter": "0.0.1",
|
|
||||||
"karma-mocha": "^1.3.0",
|
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
|
||||||
"karma-spec-reporter": "^0.0.31",
|
|
||||||
"karma-summary-reporter": "^1.5.1",
|
|
||||||
"karma-webpack": "^4.0.0-beta.0",
|
|
||||||
"matrix-mock-request": "^1.2.3",
|
"matrix-mock-request": "^1.2.3",
|
||||||
"matrix-react-test-utils": "^0.2.2",
|
"matrix-react-test-utils": "^0.2.2",
|
||||||
"mocha": "^5.0.5",
|
|
||||||
"react-test-renderer": "^16.9.0",
|
"react-test-renderer": "^16.9.0",
|
||||||
"require-json": "0.0.1",
|
"require-json": "0.0.1",
|
||||||
"rimraf": "^2.4.3",
|
"rimraf": "^2.4.3",
|
||||||
"sinon": "^5.0.7",
|
|
||||||
"source-map-loader": "^0.2.3",
|
"source-map-loader": "^0.2.3",
|
||||||
"stylelint": "^9.10.1",
|
"stylelint": "^9.10.1",
|
||||||
"stylelint-config-standard": "^18.2.0",
|
"stylelint-config-standard": "^18.2.0",
|
||||||
"stylelint-scss": "^3.9.0",
|
"stylelint-scss": "^3.9.0",
|
||||||
|
"tslint": "^5.20.1",
|
||||||
|
"typescript": "^3.7.3",
|
||||||
"walk": "^2.3.9",
|
"walk": "^2.3.9",
|
||||||
"webpack": "^4.20.2",
|
"webpack": "^4.20.2",
|
||||||
"webpack-cli": "^3.1.1"
|
"webpack-cli": "^3.1.1"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"testMatch": [
|
||||||
|
"<rootDir>/test/**/*-test.js"
|
||||||
|
],
|
||||||
|
"setupTestFrameworkScriptFile": "<rootDir>/test/setupTests.js",
|
||||||
|
"moduleNameMapper": {
|
||||||
|
"\\.(gif|png|svg|ttf|woff2)$": "<rootDir>/__mocks__/imageMock.js",
|
||||||
|
"\\$webapp/i18n/languages.json": "<rootDir>/__mocks__/languages.json"
|
||||||
|
},
|
||||||
|
"transformIgnorePatterns": [
|
||||||
|
"/node_modules/(?!matrix-js-sdk).+$"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
@import "./structures/_TopLeftMenuButton.scss";
|
@import "./structures/_TopLeftMenuButton.scss";
|
||||||
@import "./structures/_UploadBar.scss";
|
@import "./structures/_UploadBar.scss";
|
||||||
@import "./structures/_ViewSource.scss";
|
@import "./structures/_ViewSource.scss";
|
||||||
|
@import "./structures/auth/_CompleteSecurity.scss";
|
||||||
@import "./structures/auth/_Login.scss";
|
@import "./structures/auth/_Login.scss";
|
||||||
@import "./views/auth/_AuthBody.scss";
|
@import "./views/auth/_AuthBody.scss";
|
||||||
@import "./views/auth/_AuthButtons.scss";
|
@import "./views/auth/_AuthButtons.scss";
|
||||||
|
@ -56,6 +57,7 @@
|
||||||
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
|
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
|
||||||
@import "./views/dialogs/_CreateGroupDialog.scss";
|
@import "./views/dialogs/_CreateGroupDialog.scss";
|
||||||
@import "./views/dialogs/_CreateRoomDialog.scss";
|
@import "./views/dialogs/_CreateRoomDialog.scss";
|
||||||
|
@import "./views/dialogs/_DMInviteDialog.scss";
|
||||||
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
||||||
@import "./views/dialogs/_DeviceVerifyDialog.scss";
|
@import "./views/dialogs/_DeviceVerifyDialog.scss";
|
||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
|
@import "./views/dialogs/_DevtoolsDialog.scss";
|
||||||
|
|
51
res/css/structures/auth/_CompleteSecurity.scss
Normal file
51
res/css/structures/auth/_CompleteSecurity.scss
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_headerIcon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 0 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_heroIcon {
|
||||||
|
width: 128px;
|
||||||
|
height: 128px;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_body {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CompleteSecurity_actionRow {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
margin-inline-start: 18px;
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
color: $warning-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,5 +61,5 @@ input.mx_StatusMessageContextMenu_message {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_StatusMessageContextMenu_actionContainer .mx_Spinner {
|
.mx_StatusMessageContextMenu_actionContainer .mx_Spinner {
|
||||||
justify-content: start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ limitations under the License.
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: start;
|
align-items: flex-start;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
|
||||||
input[type=checkbox] {
|
input[type=checkbox] {
|
||||||
|
|
197
res/css/views/dialogs/_DMInviteDialog.scss
Normal file
197
res/css/views/dialogs/_DMInviteDialog.scss
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_addressBar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_editor {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%; // Needed to make the Field inside grow
|
||||||
|
background-color: $user-tile-hover-bg-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
min-height: 25px;
|
||||||
|
padding-left: 8px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_userTile {
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
position: relative;
|
||||||
|
top: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using a textarea for this element, to circumvent autofill
|
||||||
|
// Mostly copied from AddressPickerDialog
|
||||||
|
textarea,
|
||||||
|
textarea:focus {
|
||||||
|
height: 34px;
|
||||||
|
line-height: 34px;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-left: 12px;
|
||||||
|
margin: 0 !important;
|
||||||
|
border: 0 !important;
|
||||||
|
outline: 0 !important;
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
word-wrap: nowrap;
|
||||||
|
|
||||||
|
// Roughly fill about 2/5ths of the available space. This is to try and 'fill' the
|
||||||
|
// remaining space after a bunch of pills, but is a bit hacky. Ideally we'd have
|
||||||
|
// support for "fill remaining width", but traditional tricks don't work with what
|
||||||
|
// we're pushing into this "field". Flexbox just makes things worse. The theory is
|
||||||
|
// that users won't need more than about 2/5ths of the input to find the person
|
||||||
|
// they're looking for.
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_goButton {
|
||||||
|
width: 48px;
|
||||||
|
margin-left: 10px;
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_section {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $user-tile-hover-bg-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_avatarStack {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
|
||||||
|
& > * {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_selected {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 36px;
|
||||||
|
background-color: $username-variant1-color;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
mask-image: url('$(res)/img/feather-customised/check.svg');
|
||||||
|
mask-size: 100%;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
position: absolute;
|
||||||
|
top: 6px; // 50%
|
||||||
|
left: 6px; // 50%
|
||||||
|
background-color: #ffffff; // this is fine without a var because it's for both themes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_userId {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_time {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
float: right;
|
||||||
|
line-height: 36px; // Height of the avatar to keep the time vertically aligned
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_highlight {
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Many of these styles are stolen from mx_UserPill, but adjusted for the invite dialog.
|
||||||
|
.mx_DMInviteDialog_userTile {
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_userTile_pill {
|
||||||
|
background-color: $username-variant1-color;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
color: #ffffff; // this is fine without a var because it's for both themes
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_userTile_avatar {
|
||||||
|
border-radius: 20px;
|
||||||
|
position: relative;
|
||||||
|
left: -5px;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.mx_DMInviteDialog_userTile_avatar {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_userTile_name {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_userTile_threepidAvatar {
|
||||||
|
background-color: #ffffff; // this is fine without a var because it's for both themes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_userTile_remove {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,11 @@ limitations under the License.
|
||||||
mask-image: url('$(res)/img/feather-customised/users-sm.svg');
|
mask-image: url('$(res)/img/feather-customised/users-sm.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomSettingsDialog_bridgesIcon::before {
|
||||||
|
// This icon is pants, please improve :)
|
||||||
|
mask-image: url('$(res)/img/feather-customised/bridge.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomSettingsDialog_warningIcon::before {
|
.mx_RoomSettingsDialog_warningIcon::before {
|
||||||
mask-image: url('$(res)/img/feather-customised/warning-triangle.svg');
|
mask-image: url('$(res)/img/feather-customised/warning-triangle.svg');
|
||||||
}
|
}
|
||||||
|
@ -50,3 +55,17 @@ limitations under the License.
|
||||||
mask-size: 36px;
|
mask-size: 36px;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomSettingsDialog_BridgeList {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSettingsDialog_BridgeList li {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-width: 1px 0px;
|
||||||
|
border-color: #dee1f3;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseContainer {
|
.mx_CreateKeyBackupDialog_passPhraseContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateKeyBackupDialog_passPhraseHelp {
|
.mx_CreateKeyBackupDialog_passPhraseHelp {
|
||||||
|
|
|
@ -33,7 +33,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_passPhraseContainer {
|
.mx_CreateSecretStorageDialog_passPhraseContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateSecretStorageDialog_passPhraseHelp {
|
.mx_CreateSecretStorageDialog_passPhraseHelp {
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
.mx_MemberDeviceInfo {
|
.mx_MemberDeviceInfo {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
align-items: start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberDeviceInfo_icon {
|
.mx_MemberDeviceInfo_icon {
|
||||||
|
|
|
@ -101,7 +101,7 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
justify-content: start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
|
|
|
@ -263,3 +263,24 @@ limitations under the License.
|
||||||
.mx_RoomHeader_pinsIndicatorUnread {
|
.mx_RoomHeader_pinsIndicatorUnread {
|
||||||
background-color: $pinned-unread-color;
|
background-color: $pinned-unread-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomHeader_PrivateIcon.mx_RoomHeader_isPrivate {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
position: relative;
|
||||||
|
display: block !important;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: $roomtile-name-color;
|
||||||
|
mask-image: url('$(res)/img/feather-customised/lock-solid.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,4 +40,5 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomRecoveryReminder_secondary {
|
.mx_RoomRecoveryReminder_secondary {
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,3 +200,31 @@ limitations under the License.
|
||||||
.mx_GroupInviteTile .mx_RoomTile_name {
|
.mx_GroupInviteTile .mx_RoomTile_name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile.mx_RoomTile.mx_RoomTile_isPrivate .mx_RoomTile_name {
|
||||||
|
// Scoot the padding in a bit from 6px to make it look better
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomTile.mx_RoomTile_isPrivate .mx_RoomTile_PrivateIcon {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
position: relative;
|
||||||
|
display: block !important;
|
||||||
|
// Align the padlock with unencrypted room names
|
||||||
|
margin-left: 6px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
background-color: $roomtile-name-color;
|
||||||
|
mask-image: url('$(res)/img/feather-customised/lock-solid.svg');
|
||||||
|
mask-position: center;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
50
res/img/feather-customised/bridge.svg
Normal file
50
res/img/feather-customised/bridge.svg
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="svg8"
|
||||||
|
version="1.1"
|
||||||
|
viewBox="0 0 5.487504 5.7341776"
|
||||||
|
height="5.7341776mm"
|
||||||
|
width="5.487504mm">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
transform="translate(14.166523,-96.032669)"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
y="99.461258"
|
||||||
|
x="-10.861272"
|
||||||
|
height="2.0555882"
|
||||||
|
width="1.9322528"
|
||||||
|
id="rect831-6"
|
||||||
|
style="fill:none;stroke:#b8bec9;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
|
||||||
|
<path
|
||||||
|
id="path883"
|
||||||
|
d="m -11.98427,98.338257 1.122998,1.122998"
|
||||||
|
style="fill:#b8bec9;fill-opacity:1;stroke:#b8bec9;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
transform="scale(-1)"
|
||||||
|
y="-98.338257"
|
||||||
|
x="11.98427"
|
||||||
|
height="2.0555882"
|
||||||
|
width="1.9322529"
|
||||||
|
id="rect831-6-7"
|
||||||
|
style="fill:none;stroke:#b8bec9;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
4
res/img/feather-customised/lock-solid.svg
Normal file
4
res/img/feather-customised/lock-solid.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 13C3 11.8954 3.89543 11 5 11H19C20.1046 11 21 11.8954 21 13V20C21 21.1046 20.1046 22 19 22H5C3.89543 22 3 21.1046 3 20V13Z" fill="#2E2F32" stroke="#2E2F32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M7 11V7C7 4.23858 9.23858 2 12 2C14.7614 2 17 4.23858 17 7V11" stroke="#2E2F32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 532 B |
1
res/img/icon-email-pill-avatar.svg
Normal file
1
res/img/icon-email-pill-avatar.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="65.631" height="67.981"><defs><filter x="-.059" y="-.079" width="1.118" height="1.158" filterUnits="objectBoundingBox" id="a"><feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0.473684211 0 0 0 0 1 0 0 0 0.241258741 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" transform="matrix(3.40907 0 0 3.40907 -1493.716 -795.144)" fill="none" fill-rule="evenodd" stroke="#368bd6" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(441.5 237.5)"><circle r="2.286" cy="5.714" cx="6.286"/><path d="M8.571 3.429v2.857a1.714 1.714 0 103.429 0v-.572a5.714 5.714 0 10-2.24 4.537"/></g></g></svg>
|
After Width: | Height: | Size: 918 B |
1
res/img/icon-pill-remove.svg
Normal file
1
res/img/icon-pill-remove.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg width="58" height="60" viewBox="26 25 6 6" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-5.9%" y="-7.9%" width="111.8%" height="115.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0.473684211 0 0 0 0 1 0 0 0 0.241258741 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" transform="translate(-406 -215)" stroke="#61708B"><path d="M438 240l-6 6M432 240l6 6"/></g></svg>
|
After Width: | Height: | Size: 693 B |
|
@ -16,6 +16,7 @@ $room-highlight-color: #343a46;
|
||||||
// typical text (dark-on-white in light skin)
|
// typical text (dark-on-white in light skin)
|
||||||
$primary-fg-color: $text-primary-color;
|
$primary-fg-color: $text-primary-color;
|
||||||
$primary-bg-color: $bg-color;
|
$primary-bg-color: $bg-color;
|
||||||
|
$muted-fg-color: $header-panel-text-primary-color;
|
||||||
|
|
||||||
// used for dialog box text
|
// used for dialog box text
|
||||||
$light-fg-color: $header-panel-text-secondary-color;
|
$light-fg-color: $header-panel-text-secondary-color;
|
||||||
|
@ -172,6 +173,8 @@ $interactive-tooltip-fg-color: #ffffff;
|
||||||
|
|
||||||
$breadcrumb-placeholder-bg-color: #272c35;
|
$breadcrumb-placeholder-bg-color: #272c35;
|
||||||
|
|
||||||
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
// ***** Mixins! *****
|
// ***** Mixins! *****
|
||||||
|
|
||||||
@define-mixin mx_DialogButton {
|
@define-mixin mx_DialogButton {
|
||||||
|
@ -243,3 +246,13 @@ $breadcrumb-placeholder-bg-color: #272c35;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// diff highlight colors
|
||||||
|
// intentionally swapped to avoid inversion
|
||||||
|
.hljs-addition {
|
||||||
|
background: #fdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
background: #dfd;
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ $header-panel-bg-color: #f3f8fd;
|
||||||
// typical text (dark-on-white in light skin)
|
// typical text (dark-on-white in light skin)
|
||||||
$primary-fg-color: #2e2f32;
|
$primary-fg-color: #2e2f32;
|
||||||
$primary-bg-color: #ffffff;
|
$primary-bg-color: #ffffff;
|
||||||
|
$muted-fg-color: #61708b; // Commonly used in headings and relevant alt text
|
||||||
|
|
||||||
// used for dialog box text
|
// used for dialog box text
|
||||||
$light-fg-color: #747474;
|
$light-fg-color: #747474;
|
||||||
|
@ -293,6 +294,8 @@ $interactive-tooltip-fg-color: #ffffff;
|
||||||
|
|
||||||
$breadcrumb-placeholder-bg-color: #e8eef5;
|
$breadcrumb-placeholder-bg-color: #e8eef5;
|
||||||
|
|
||||||
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
// ***** Mixins! *****
|
// ***** Mixins! *****
|
||||||
|
|
||||||
@define-mixin mx_DialogButton {
|
@define-mixin mx_DialogButton {
|
||||||
|
@ -338,3 +341,12 @@ $breadcrumb-placeholder-bg-color: #e8eef5;
|
||||||
color: $accent-color;
|
color: $accent-color;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// diff highlight colors
|
||||||
|
.hljs-addition {
|
||||||
|
background: #dfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-deletion {
|
||||||
|
background: #fdd;
|
||||||
|
}
|
||||||
|
|
|
@ -36,7 +36,8 @@ echo "--- Install synapse & other dependencies"
|
||||||
./install.sh
|
./install.sh
|
||||||
# install static webserver to server symlinked local copy of riot
|
# install static webserver to server symlinked local copy of riot
|
||||||
./riot/install-webserver.sh
|
./riot/install-webserver.sh
|
||||||
mkdir logs || rm -r logs/*
|
rm -r logs || true
|
||||||
|
mkdir logs
|
||||||
echo "+++ Running end-to-end tests"
|
echo "+++ Running end-to-end tests"
|
||||||
TESTS_STARTED=1
|
TESTS_STARTED=1
|
||||||
./run.sh --no-sandbox --log-directory logs/
|
./run.sh --no-sandbox --log-directory logs/
|
||||||
|
|
|
@ -7,6 +7,7 @@ scripts/fetchdep.sh matrix-org matrix-js-sdk
|
||||||
pushd matrix-js-sdk
|
pushd matrix-js-sdk
|
||||||
yarn link
|
yarn link
|
||||||
yarn install
|
yarn install
|
||||||
|
yarn build
|
||||||
popd
|
popd
|
||||||
|
|
||||||
yarn link matrix-js-sdk
|
yarn link matrix-js-sdk
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# script which is run by the CI build (after `yarn test`).
|
|
||||||
#
|
|
||||||
# clones riot-web develop and runs the tests against our version of react-sdk.
|
|
||||||
|
|
||||||
set -ev
|
|
||||||
|
|
||||||
scripts/ci/build.sh
|
|
||||||
yarn test
|
|
|
@ -19,7 +19,6 @@ function reskindex() {
|
||||||
prevFiles = files;
|
prevFiles = files;
|
||||||
|
|
||||||
var header = args.h || args.header;
|
var header = args.h || args.header;
|
||||||
var packageJson = JSON.parse(fs.readFileSync('./package.json'));
|
|
||||||
|
|
||||||
var strm = fs.createWriteStream(componentIndexTmp);
|
var strm = fs.createWriteStream(componentIndexTmp);
|
||||||
|
|
||||||
|
@ -34,19 +33,7 @@ function reskindex() {
|
||||||
strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
|
strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
|
||||||
strm.write(" * You are not a salmon.\n");
|
strm.write(" * You are not a salmon.\n");
|
||||||
strm.write(" */\n\n");
|
strm.write(" */\n\n");
|
||||||
|
strm.write("let components = {};\n");
|
||||||
if (packageJson['matrix-react-parent']) {
|
|
||||||
const parentIndex = packageJson['matrix-react-parent'] +
|
|
||||||
'/lib/component-index';
|
|
||||||
strm.write(
|
|
||||||
`let components = require('${parentIndex}').components;
|
|
||||||
if (!components) {
|
|
||||||
throw new Error("'${parentIndex}' didn't export components");
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
} else {
|
|
||||||
strm.write("let components = {};\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < files.length; ++i) {
|
for (var i = 0; i < files.length; ++i) {
|
||||||
var file = files[i].replace('.js', '');
|
var file = files[i].replace('.js', '');
|
||||||
|
|
|
@ -16,8 +16,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import IdentityAuthClient from './IdentityAuthClient';
|
import IdentityAuthClient from './IdentityAuthClient';
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { getCurrentLanguage, _t, _td } from './languageHandler';
|
||||||
import PlatformPeg from './PlatformPeg';
|
import PlatformPeg from './PlatformPeg';
|
||||||
import SdkConfig from './SdkConfig';
|
import SdkConfig from './SdkConfig';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
|
|
||||||
const hashRegex = /#\/(groups?|room|user|settings|register|login|forgot_password|home|directory)/;
|
const hashRegex = /#\/(groups?|room|user|settings|register|login|forgot_password|home|directory)/;
|
||||||
const hashVarRegex = /#\/(group|room|user)\/.*$/;
|
const hashVarRegex = /#\/(group|room|user)\/.*$/;
|
||||||
|
@ -306,4 +306,4 @@ class Analytics {
|
||||||
if (!global.mxAnalytics) {
|
if (!global.mxAnalytics) {
|
||||||
global.mxAnalytics = new Analytics();
|
global.mxAnalytics = new Analytics();
|
||||||
}
|
}
|
||||||
module.exports = global.mxAnalytics;
|
export default global.mxAnalytics;
|
||||||
|
|
197
src/Avatar.js
197
src/Avatar.js
|
@ -15,13 +15,14 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
import {ContentRepo} from 'matrix-js-sdk';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
|
||||||
import DMRoomMap from './utils/DMRoomMap';
|
import DMRoomMap from './utils/DMRoomMap';
|
||||||
|
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
||||||
|
|
||||||
module.exports = {
|
export function avatarUrlForMember(member, width, height, resizeMethod) {
|
||||||
avatarUrlForMember: function(member, width, height, resizeMethod) {
|
let url;
|
||||||
let url = member.getAvatarUrl(
|
if (member && member.getAvatarUrl) {
|
||||||
|
url = member.getAvatarUrl(
|
||||||
MatrixClientPeg.get().getHomeserverUrl(),
|
MatrixClientPeg.get().getHomeserverUrl(),
|
||||||
Math.floor(width * window.devicePixelRatio),
|
Math.floor(width * window.devicePixelRatio),
|
||||||
Math.floor(height * window.devicePixelRatio),
|
Math.floor(height * window.devicePixelRatio),
|
||||||
|
@ -29,106 +30,106 @@ module.exports = {
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
if (!url) {
|
}
|
||||||
// member can be null here currently since on invites, the JS SDK
|
if (!url) {
|
||||||
// does not have enough info to build a RoomMember object for
|
// member can be null here currently since on invites, the JS SDK
|
||||||
// the inviter.
|
// does not have enough info to build a RoomMember object for
|
||||||
url = this.defaultAvatarUrlForString(member ? member.userId : '');
|
// the inviter.
|
||||||
|
url = defaultAvatarUrlForString(member ? member.userId : '');
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function avatarUrlForUser(user, width, height, resizeMethod) {
|
||||||
|
const url = getHttpUriForMxc(
|
||||||
|
MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl,
|
||||||
|
Math.floor(width * window.devicePixelRatio),
|
||||||
|
Math.floor(height * window.devicePixelRatio),
|
||||||
|
resizeMethod,
|
||||||
|
);
|
||||||
|
if (!url || url.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function defaultAvatarUrlForString(s) {
|
||||||
|
const images = ['03b381', '368bd6', 'ac3ba8'];
|
||||||
|
let total = 0;
|
||||||
|
for (let i = 0; i < s.length; ++i) {
|
||||||
|
total += s.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return require('../res/img/' + images[total % images.length] + '.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the first (non-sigil) character of 'name',
|
||||||
|
* converted to uppercase
|
||||||
|
* @param {string} name
|
||||||
|
* @return {string} the first letter
|
||||||
|
*/
|
||||||
|
export function getInitialLetter(name) {
|
||||||
|
if (!name) {
|
||||||
|
// XXX: We should find out what causes the name to sometimes be falsy.
|
||||||
|
console.trace("`name` argument to `getInitialLetter` not supplied");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (name.length < 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
let idx = 0;
|
||||||
|
const initial = name[0];
|
||||||
|
if ((initial === '@' || initial === '#' || initial === '+') && name[1]) {
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// string.codePointAt(0) would do this, but that isn't supported by
|
||||||
|
// some browsers (notably PhantomJS).
|
||||||
|
let chars = 1;
|
||||||
|
const first = name.charCodeAt(idx);
|
||||||
|
|
||||||
|
// check if it’s the start of a surrogate pair
|
||||||
|
if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) {
|
||||||
|
const second = name.charCodeAt(idx+1);
|
||||||
|
if (second >= 0xDC00 && second <= 0xDFFF) {
|
||||||
|
chars++;
|
||||||
}
|
}
|
||||||
return url;
|
}
|
||||||
},
|
|
||||||
|
|
||||||
avatarUrlForUser: function(user, width, height, resizeMethod) {
|
const firstChar = name.substring(idx, idx+chars);
|
||||||
const url = ContentRepo.getHttpUriForMxc(
|
return firstChar.toUpperCase();
|
||||||
MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl,
|
}
|
||||||
Math.floor(width * window.devicePixelRatio),
|
|
||||||
Math.floor(height * window.devicePixelRatio),
|
|
||||||
resizeMethod,
|
|
||||||
);
|
|
||||||
if (!url || url.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
|
|
||||||
defaultAvatarUrlForString: function(s) {
|
export function avatarUrlForRoom(room, width, height, resizeMethod) {
|
||||||
const images = ['03b381', '368bd6', 'ac3ba8'];
|
const explicitRoomAvatar = room.getAvatarUrl(
|
||||||
let total = 0;
|
MatrixClientPeg.get().getHomeserverUrl(),
|
||||||
for (let i = 0; i < s.length; ++i) {
|
width,
|
||||||
total += s.charCodeAt(i);
|
height,
|
||||||
}
|
resizeMethod,
|
||||||
return require('../res/img/' + images[total % images.length] + '.png');
|
false,
|
||||||
},
|
);
|
||||||
|
if (explicitRoomAvatar) {
|
||||||
|
return explicitRoomAvatar;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
let otherMember = null;
|
||||||
* returns the first (non-sigil) character of 'name',
|
const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
||||||
* converted to uppercase
|
if (otherUserId) {
|
||||||
* @param {string} name
|
otherMember = room.getMember(otherUserId);
|
||||||
* @return {string} the first letter
|
} else {
|
||||||
*/
|
// if the room is not marked as a 1:1, but only has max 2 members
|
||||||
getInitialLetter(name) {
|
// then still try to show any avatar (pref. other member)
|
||||||
if (!name) {
|
otherMember = room.getAvatarFallbackMember();
|
||||||
// XXX: We should find out what causes the name to sometimes be falsy.
|
}
|
||||||
console.trace("`name` argument to `getInitialLetter` not supplied");
|
if (otherMember) {
|
||||||
return undefined;
|
return otherMember.getAvatarUrl(
|
||||||
}
|
|
||||||
if (name.length < 1) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
let idx = 0;
|
|
||||||
const initial = name[0];
|
|
||||||
if ((initial === '@' || initial === '#' || initial === '+') && name[1]) {
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// string.codePointAt(0) would do this, but that isn't supported by
|
|
||||||
// some browsers (notably PhantomJS).
|
|
||||||
let chars = 1;
|
|
||||||
const first = name.charCodeAt(idx);
|
|
||||||
|
|
||||||
// check if it’s the start of a surrogate pair
|
|
||||||
if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) {
|
|
||||||
const second = name.charCodeAt(idx+1);
|
|
||||||
if (second >= 0xDC00 && second <= 0xDFFF) {
|
|
||||||
chars++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const firstChar = name.substring(idx, idx+chars);
|
|
||||||
return firstChar.toUpperCase();
|
|
||||||
},
|
|
||||||
|
|
||||||
avatarUrlForRoom(room, width, height, resizeMethod) {
|
|
||||||
const explicitRoomAvatar = room.getAvatarUrl(
|
|
||||||
MatrixClientPeg.get().getHomeserverUrl(),
|
MatrixClientPeg.get().getHomeserverUrl(),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
resizeMethod,
|
resizeMethod,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
if (explicitRoomAvatar) {
|
}
|
||||||
return explicitRoomAvatar;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let otherMember = null;
|
|
||||||
const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
|
||||||
if (otherUserId) {
|
|
||||||
otherMember = room.getMember(otherUserId);
|
|
||||||
} else {
|
|
||||||
// if the room is not marked as a 1:1, but only has max 2 members
|
|
||||||
// then still try to show any avatar (pref. other member)
|
|
||||||
otherMember = room.getAvatarFallbackMember();
|
|
||||||
}
|
|
||||||
if (otherMember) {
|
|
||||||
return otherMember.getAvatarUrl(
|
|
||||||
MatrixClientPeg.get().getHomeserverUrl(),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
resizeMethod,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -53,10 +53,10 @@ limitations under the License.
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import PlatformPeg from './PlatformPeg';
|
import PlatformPeg from './PlatformPeg';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
|
@ -302,7 +302,7 @@ function _onAction(payload) {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'place_call':
|
case 'place_call':
|
||||||
{
|
{
|
||||||
if (module.exports.getAnyActiveCall()) {
|
if (callHandler.getAnyActiveCall()) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Call Handler', 'Existing Call', ErrorDialog, {
|
Modal.createTrackedDialog('Call Handler', 'Existing Call', ErrorDialog, {
|
||||||
title: _t('Existing Call'),
|
title: _t('Existing Call'),
|
||||||
|
@ -355,7 +355,7 @@ function _onAction(payload) {
|
||||||
break;
|
break;
|
||||||
case 'incoming_call':
|
case 'incoming_call':
|
||||||
{
|
{
|
||||||
if (module.exports.getAnyActiveCall()) {
|
if (callHandler.getAnyActiveCall()) {
|
||||||
// ignore multiple incoming calls. in future, we may want a line-1/line-2 setup.
|
// ignore multiple incoming calls. in future, we may want a line-1/line-2 setup.
|
||||||
// we avoid rejecting with "busy" in case the user wants to answer it on a different device.
|
// we avoid rejecting with "busy" in case the user wants to answer it on a different device.
|
||||||
// in future we could signal a "local busy" as a warning to the caller.
|
// in future we could signal a "local busy" as a warning to the caller.
|
||||||
|
@ -523,7 +523,7 @@ if (!global.mxCallHandler) {
|
||||||
|
|
||||||
const callHandler = {
|
const callHandler = {
|
||||||
getCallForRoom: function(roomId) {
|
getCallForRoom: function(roomId) {
|
||||||
let call = module.exports.getCall(roomId);
|
let call = callHandler.getCall(roomId);
|
||||||
if (call) return call;
|
if (call) return call;
|
||||||
|
|
||||||
if (ConferenceHandler) {
|
if (ConferenceHandler) {
|
||||||
|
@ -583,4 +583,4 @@ if (global.mxCallHandler === undefined) {
|
||||||
global.mxCallHandler = callHandler;
|
global.mxCallHandler = callHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = global.mxCallHandler;
|
export default global.mxCallHandler;
|
||||||
|
|
|
@ -19,8 +19,8 @@ limitations under the License.
|
||||||
|
|
||||||
import extend from './extend';
|
import extend from './extend';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import RoomViewStore from './stores/RoomViewStore';
|
import RoomViewStore from './stores/RoomViewStore';
|
||||||
|
|
|
@ -15,10 +15,10 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import { deriveKey } from 'matrix-js-sdk/lib/crypto/key_passphrase';
|
import { deriveKey } from 'matrix-js-sdk/src/crypto/key_passphrase';
|
||||||
import { decodeRecoveryKey } from 'matrix-js-sdk/lib/crypto/recoverykey';
|
import { decodeRecoveryKey } from 'matrix-js-sdk/src/crypto/recoverykey';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
|
||||||
// This stores the secret storage private keys in memory for the JS SDK. This is
|
// This stores the secret storage private keys in memory for the JS SDK. This is
|
||||||
|
@ -97,7 +97,7 @@ export const crossSigningCallbacks = {
|
||||||
*
|
*
|
||||||
* Additionally, the secret storage keys are cached during the scope of this function
|
* Additionally, the secret storage keys are cached during the scope of this function
|
||||||
* to ensure the user is prompted only once for their secret storage
|
* to ensure the user is prompted only once for their secret storage
|
||||||
* passphrase. The cache is then
|
* passphrase. The cache is then cleared once the provided function completes.
|
||||||
*
|
*
|
||||||
* @param {Function} [func] An operation to perform once secret storage has been
|
* @param {Function} [func] An operation to perform once secret storage has been
|
||||||
* bootstrapped. Optional.
|
* bootstrapped. Optional.
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
|
|
||||||
function isMatch(query, name, uid) {
|
function isMatch(query, name, uid) {
|
||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
|
@ -105,36 +105,33 @@ class UserEntity extends Entity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function newEntity(jsx, matchFn) {
|
||||||
|
const entity = new Entity();
|
||||||
|
entity.getJsx = function() {
|
||||||
|
return jsx;
|
||||||
|
};
|
||||||
|
entity.matches = matchFn;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
/**
|
||||||
newEntity: function(jsx, matchFn) {
|
* @param {RoomMember[]} members
|
||||||
const entity = new Entity();
|
* @return {Entity[]}
|
||||||
entity.getJsx = function() {
|
*/
|
||||||
return jsx;
|
export function fromRoomMembers(members) {
|
||||||
};
|
return members.map(function(m) {
|
||||||
entity.matches = matchFn;
|
return new MemberEntity(m);
|
||||||
return entity;
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {RoomMember[]} members
|
* @param {User[]} users
|
||||||
* @return {Entity[]}
|
* @param {boolean} showInviteButton
|
||||||
*/
|
* @param {Function} inviteFn Called with the user ID.
|
||||||
fromRoomMembers: function(members) {
|
* @return {Entity[]}
|
||||||
return members.map(function(m) {
|
*/
|
||||||
return new MemberEntity(m);
|
export function fromUsers(users, showInviteButton, inviteFn) {
|
||||||
});
|
return users.map(function(u) {
|
||||||
},
|
return new UserEntity(u, showInviteButton, inviteFn);
|
||||||
|
});
|
||||||
/**
|
}
|
||||||
* @param {User[]} users
|
|
||||||
* @param {boolean} showInviteButton
|
|
||||||
* @param {Function} inviteFn Called with the user ID.
|
|
||||||
* @return {Entity[]}
|
|
||||||
*/
|
|
||||||
fromUsers: function(users, showInviteButton, inviteFn) {
|
|
||||||
return users.map(function(u) {
|
|
||||||
return new UserEntity(u, showInviteButton, inviteFn);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import URL from 'url';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import WidgetMessagingEndpoint from './WidgetMessagingEndpoint';
|
import WidgetMessagingEndpoint from './WidgetMessagingEndpoint';
|
||||||
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
import RoomViewStore from "./stores/RoomViewStore";
|
import RoomViewStore from "./stores/RoomViewStore";
|
||||||
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
import {IntegrationManagers} from "./integrations/IntegrationManagers";
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
|
|
|
@ -16,10 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import sdk from './';
|
import * as sdk from './';
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import MultiInviter from './utils/MultiInviter';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import GroupStore from './stores/GroupStore';
|
import GroupStore from './stores/GroupStore';
|
||||||
import {allSettled} from "./utils/promise";
|
import {allSettled} from "./utils/promise";
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import linkifyMatrix from './linkify-matrix';
|
||||||
import _linkifyElement from 'linkifyjs/element';
|
import _linkifyElement from 'linkifyjs/element';
|
||||||
import _linkifyString from 'linkifyjs/string';
|
import _linkifyString from 'linkifyjs/string';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
||||||
import EMOJIBASE_REGEX from 'emojibase-regex';
|
import EMOJIBASE_REGEX from 'emojibase-regex';
|
||||||
|
@ -377,6 +377,7 @@ class TextHighlighter extends BaseHighlighter {
|
||||||
* opts.stripReplyFallback: optional argument specifying the event is a reply and so fallback needs removing
|
* opts.stripReplyFallback: optional argument specifying the event is a reply and so fallback needs removing
|
||||||
* opts.returnString: return an HTML string rather than JSX elements
|
* opts.returnString: return an HTML string rather than JSX elements
|
||||||
* opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
|
* opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer
|
||||||
|
* opts.ref: React ref to attach to any React components returned (not compatible with opts.returnString)
|
||||||
*/
|
*/
|
||||||
export function bodyToHtml(content, highlights, opts={}) {
|
export function bodyToHtml(content, highlights, opts={}) {
|
||||||
const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body;
|
const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body;
|
||||||
|
@ -459,8 +460,8 @@ export function bodyToHtml(content, highlights, opts={}) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return isDisplayedWithHtml ?
|
return isDisplayedWithHtml ?
|
||||||
<span key="body" className={className} dangerouslySetInnerHTML={{ __html: safeBody }} dir="auto" /> :
|
<span key="body" ref={opts.ref} className={className} dangerouslySetInnerHTML={{ __html: safeBody }} dir="auto" /> :
|
||||||
<span key="body" className={className} dir="auto">{ strippedBody }</span>;
|
<span key="body" ref={opts.ref} className={className} dir="auto">{ strippedBody }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,9 +16,9 @@ limitations under the License.
|
||||||
|
|
||||||
import { createClient, SERVICE_TYPES } from 'matrix-js-sdk';
|
import { createClient, SERVICE_TYPES } from 'matrix-js-sdk';
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -16,41 +16,38 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports = {
|
/**
|
||||||
|
* Returns the actual height that an image of dimensions (fullWidth, fullHeight)
|
||||||
/**
|
* will occupy if resized to fit inside a thumbnail bounding box of size
|
||||||
* Returns the actual height that an image of dimensions (fullWidth, fullHeight)
|
* (thumbWidth, thumbHeight).
|
||||||
* will occupy if resized to fit inside a thumbnail bounding box of size
|
*
|
||||||
* (thumbWidth, thumbHeight).
|
* If the aspect ratio of the source image is taller than the aspect ratio of
|
||||||
*
|
* the thumbnail bounding box, then we return the thumbHeight parameter unchanged.
|
||||||
* If the aspect ratio of the source image is taller than the aspect ratio of
|
* Otherwise we return the thumbHeight parameter scaled down appropriately to
|
||||||
* the thumbnail bounding box, then we return the thumbHeight parameter unchanged.
|
* reflect the actual height the scaled thumbnail occupies.
|
||||||
* Otherwise we return the thumbHeight parameter scaled down appropriately to
|
*
|
||||||
* reflect the actual height the scaled thumbnail occupies.
|
* This is very useful for calculating how much height a thumbnail will actually
|
||||||
*
|
* consume in the timeline, when performing scroll offset calcuations
|
||||||
* This is very useful for calculating how much height a thumbnail will actually
|
* (e.g. scroll locking)
|
||||||
* consume in the timeline, when performing scroll offset calcuations
|
*/
|
||||||
* (e.g. scroll locking)
|
export function thumbHeight(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
||||||
*/
|
if (!fullWidth || !fullHeight) {
|
||||||
thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||||
if (!fullWidth || !fullHeight) {
|
// log this because it's spammy
|
||||||
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
return undefined;
|
||||||
// log this because it's spammy
|
}
|
||||||
return undefined;
|
if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
|
||||||
}
|
// no scaling needs to be applied
|
||||||
if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
|
return fullHeight;
|
||||||
// no scaling needs to be applied
|
}
|
||||||
return fullHeight;
|
const widthMulti = thumbWidth / fullWidth;
|
||||||
}
|
const heightMulti = thumbHeight / fullHeight;
|
||||||
const widthMulti = thumbWidth / fullWidth;
|
if (widthMulti < heightMulti) {
|
||||||
const heightMulti = thumbHeight / fullHeight;
|
// width is the dominant dimension so scaling will be fixed on that
|
||||||
if (widthMulti < heightMulti) {
|
return Math.floor(widthMulti * fullHeight);
|
||||||
// width is the dominant dimension so scaling will be fixed on that
|
} else {
|
||||||
return Math.floor(widthMulti * fullHeight);
|
// height is the dominant dimension so scaling will be fixed on that
|
||||||
} else {
|
return Math.floor(heightMulti * fullHeight);
|
||||||
// height is the dominant dimension so scaling will be fixed on that
|
}
|
||||||
return Math.floor(heightMulti * fullHeight);
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
|
|
||||||
export default class KeyRequestHandler {
|
export default class KeyRequestHandler {
|
||||||
|
@ -111,6 +111,12 @@ export default class KeyRequestHandler {
|
||||||
this._currentUser = null;
|
this._currentUser = null;
|
||||||
this._currentDevice = null;
|
this._currentDevice = null;
|
||||||
|
|
||||||
|
if (!this._pendingKeyRequests[userId] || !this._pendingKeyRequests[userId][deviceId]) {
|
||||||
|
// request was removed in the time the dialog was displayed
|
||||||
|
this._processNextRequest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r) {
|
if (r) {
|
||||||
for (const req of this._pendingKeyRequests[userId][deviceId]) {
|
for (const req of this._pendingKeyRequests[userId][deviceId]) {
|
||||||
req.share();
|
req.share();
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
|
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import EventIndexPeg from './indexing/EventIndexPeg';
|
import EventIndexPeg from './indexing/EventIndexPeg';
|
||||||
import createMatrixClient from './utils/createMatrixClient';
|
import createMatrixClient from './utils/createMatrixClient';
|
||||||
import Analytics from './Analytics';
|
import Analytics from './Analytics';
|
||||||
|
@ -28,7 +28,7 @@ import Presence from './Presence';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import DMRoomMap from './utils/DMRoomMap';
|
import DMRoomMap from './utils/DMRoomMap';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
||||||
import PlatformPeg from "./PlatformPeg";
|
import PlatformPeg from "./PlatformPeg";
|
||||||
import { sendLoginRequest } from "./Login";
|
import { sendLoginRequest } from "./Login";
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd.
|
Copyright 2017 Vector Creations Ltd.
|
||||||
Copyright 2017, 2018, 2019 New Vector Ltd
|
Copyright 2017, 2018, 2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -19,15 +19,15 @@ limitations under the License.
|
||||||
|
|
||||||
import {MatrixClient, MemoryStore} from 'matrix-js-sdk';
|
import {MatrixClient, MemoryStore} from 'matrix-js-sdk';
|
||||||
|
|
||||||
import utils from 'matrix-js-sdk/lib/utils';
|
import * as utils from 'matrix-js-sdk/src/utils';
|
||||||
import EventTimeline from 'matrix-js-sdk/lib/models/event-timeline';
|
import {EventTimeline} from 'matrix-js-sdk/src/models/event-timeline';
|
||||||
import EventTimelineSet from 'matrix-js-sdk/lib/models/event-timeline-set';
|
import {EventTimelineSet} from 'matrix-js-sdk/src/models/event-timeline-set';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import createMatrixClient from './utils/createMatrixClient';
|
import createMatrixClient from './utils/createMatrixClient';
|
||||||
import SettingsStore from './settings/SettingsStore';
|
import SettingsStore from './settings/SettingsStore';
|
||||||
import MatrixActionCreators from './actions/MatrixActionCreators';
|
import MatrixActionCreators from './actions/MatrixActionCreators';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import {verificationMethods} from 'matrix-js-sdk/lib/crypto';
|
import {verificationMethods} from 'matrix-js-sdk/src/crypto';
|
||||||
import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler";
|
import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler";
|
||||||
import * as StorageManager from './utils/StorageManager';
|
import * as StorageManager from './utils/StorageManager';
|
||||||
import IdentityAuthClient from './IdentityAuthClient';
|
import IdentityAuthClient from './IdentityAuthClient';
|
||||||
|
@ -48,7 +48,7 @@ interface MatrixClientCreds {
|
||||||
* This module provides a singleton instance of this class so the 'current'
|
* This module provides a singleton instance of this class so the 'current'
|
||||||
* Matrix Client object is available easily.
|
* Matrix Client object is available easily.
|
||||||
*/
|
*/
|
||||||
class MatrixClientPeg {
|
class _MatrixClientPeg {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.matrixClient = null;
|
this.matrixClient = null;
|
||||||
this._justRegisteredUserId = null;
|
this._justRegisteredUserId = null;
|
||||||
|
@ -223,9 +223,10 @@ class MatrixClientPeg {
|
||||||
};
|
};
|
||||||
|
|
||||||
opts.cryptoCallbacks = {};
|
opts.cryptoCallbacks = {};
|
||||||
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
|
// These are always installed regardless of the labs flag so that
|
||||||
Object.assign(opts.cryptoCallbacks, crossSigningCallbacks);
|
// cross-signing features can toggle on without reloading and also be
|
||||||
}
|
// accessed immediately after login.
|
||||||
|
Object.assign(opts.cryptoCallbacks, crossSigningCallbacks);
|
||||||
|
|
||||||
this.matrixClient = createMatrixClient(opts);
|
this.matrixClient = createMatrixClient(opts);
|
||||||
|
|
||||||
|
@ -245,6 +246,7 @@ class MatrixClientPeg {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!global.mxMatrixClientPeg) {
|
if (!global.mxMatrixClientPeg) {
|
||||||
global.mxMatrixClientPeg = new MatrixClientPeg();
|
global.mxMatrixClientPeg = new _MatrixClientPeg();
|
||||||
}
|
}
|
||||||
export default global.mxMatrixClientPeg;
|
|
||||||
|
export const MatrixClientPeg = global.mxMatrixClientPeg;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
import createReactClass from 'create-react-class';
|
||||||
import Analytics from './Analytics';
|
import Analytics from './Analytics';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import {defer} from "./utils/promise";
|
import {defer} from "./utils/promise";
|
||||||
|
|
|
@ -16,13 +16,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import PlatformPeg from './PlatformPeg';
|
import PlatformPeg from './PlatformPeg';
|
||||||
import TextForEvent from './TextForEvent';
|
import * as TextForEvent from './TextForEvent';
|
||||||
import Analytics from './Analytics';
|
import Analytics from './Analytics';
|
||||||
import Avatar from './Avatar';
|
import * as Avatar from './Avatar';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import SettingsStore, {SettingLevel} from "./settings/SettingsStore";
|
import SettingsStore, {SettingLevel} from "./settings/SettingsStore";
|
||||||
|
@ -364,4 +364,4 @@ if (!global.mxNotifier) {
|
||||||
global.mxNotifier = Notifier;
|
global.mxNotifier = Notifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = global.mxNotifier;
|
export default global.mxNotifier;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -22,7 +23,7 @@ limitations under the License.
|
||||||
* @return {Object[]} An array of objects with the form:
|
* @return {Object[]} An array of objects with the form:
|
||||||
* { key: $KEY, val: $VALUE, place: "add|del" }
|
* { key: $KEY, val: $VALUE, place: "add|del" }
|
||||||
*/
|
*/
|
||||||
module.exports.getKeyValueArrayDiffs = function(before, after) {
|
export function getKeyValueArrayDiffs(before, after) {
|
||||||
const results = [];
|
const results = [];
|
||||||
const delta = {};
|
const delta = {};
|
||||||
Object.keys(before).forEach(function(beforeKey) {
|
Object.keys(before).forEach(function(beforeKey) {
|
||||||
|
@ -76,7 +77,7 @@ module.exports.getKeyValueArrayDiffs = function(before, after) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shallow-compare two objects for equality: each key and value must be identical
|
* Shallow-compare two objects for equality: each key and value must be identical
|
||||||
|
@ -84,7 +85,7 @@ module.exports.getKeyValueArrayDiffs = function(before, after) {
|
||||||
* @param {Object} objB Second object to compare against the first
|
* @param {Object} objB Second object to compare against the first
|
||||||
* @return {boolean} whether the two objects have same key=values
|
* @return {boolean} whether the two objects have same key=values
|
||||||
*/
|
*/
|
||||||
module.exports.shallowEqual = function(objA, objB) {
|
export function shallowEqual(objA, objB) {
|
||||||
if (objA === objB) {
|
if (objA === objB) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -109,4 +110,4 @@ module.exports.shallowEqual = function(objA, objB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { _t } from './languageHandler';
|
||||||
* the client owns the given email address, which is then passed to the password
|
* the client owns the given email address, which is then passed to the password
|
||||||
* API on the homeserver in question with the new password.
|
* API on the homeserver in question with the new password.
|
||||||
*/
|
*/
|
||||||
class PasswordReset {
|
export default class PasswordReset {
|
||||||
/**
|
/**
|
||||||
* Configure the endpoints for password resetting.
|
* Configure the endpoints for password resetting.
|
||||||
* @param {string} homeserverUrl The URL to the HS which has the account to reset.
|
* @param {string} homeserverUrl The URL to the HS which has the account to reset.
|
||||||
|
@ -101,4 +101,3 @@ class PasswordReset {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = PasswordReset;
|
|
||||||
|
|
|
@ -47,4 +47,4 @@ class PlatformPeg {
|
||||||
if (!global.mxPlatformPeg) {
|
if (!global.mxPlatformPeg) {
|
||||||
global.mxPlatformPeg = new PlatformPeg();
|
global.mxPlatformPeg = new PlatformPeg();
|
||||||
}
|
}
|
||||||
module.exports = global.mxPlatformPeg;
|
export default global.mxPlatformPeg;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,7 +16,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
import dis from "./dispatcher";
|
import dis from "./dispatcher";
|
||||||
import Timer from './utils/Timer';
|
import Timer from './utils/Timer';
|
||||||
|
|
||||||
|
@ -104,4 +105,4 @@ class Presence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Presence();
|
export default new Presence();
|
||||||
|
|
|
@ -21,10 +21,10 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
// import MatrixClientPeg from './MatrixClientPeg';
|
// import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
|
|
||||||
// Regex for what a "safe" or "Matrix-looking" localpart would be.
|
// Regex for what a "safe" or "Matrix-looking" localpart would be.
|
||||||
// TODO: Update as needed for https://github.com/matrix-org/matrix-doc/issues/1514
|
// TODO: Update as needed for https://github.com/matrix-org/matrix-doc/issues/1514
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,26 +15,28 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import { EventStatus } from 'matrix-js-sdk';
|
import { EventStatus } from 'matrix-js-sdk';
|
||||||
|
|
||||||
module.exports = {
|
export default class Resend {
|
||||||
resendUnsentEvents: function(room) {
|
static resendUnsentEvents(room) {
|
||||||
room.getPendingEvents().filter(function(ev) {
|
room.getPendingEvents().filter(function(ev) {
|
||||||
return ev.status === EventStatus.NOT_SENT;
|
return ev.status === EventStatus.NOT_SENT;
|
||||||
}).forEach(function(event) {
|
}).forEach(function(event) {
|
||||||
module.exports.resend(event);
|
Resend.resend(event);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
cancelUnsentEvents: function(room) {
|
|
||||||
|
static cancelUnsentEvents(room) {
|
||||||
room.getPendingEvents().filter(function(ev) {
|
room.getPendingEvents().filter(function(ev) {
|
||||||
return ev.status === EventStatus.NOT_SENT;
|
return ev.status === EventStatus.NOT_SENT;
|
||||||
}).forEach(function(event) {
|
}).forEach(function(event) {
|
||||||
module.exports.removeFromQueue(event);
|
Resend.removeFromQueue(event);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
resend: function(event) {
|
|
||||||
|
static resend(event) {
|
||||||
const room = MatrixClientPeg.get().getRoom(event.getRoomId());
|
const room = MatrixClientPeg.get().getRoom(event.getRoomId());
|
||||||
MatrixClientPeg.get().resendEvent(event, room).then(function(res) {
|
MatrixClientPeg.get().resendEvent(event, room).then(function(res) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -43,15 +46,16 @@ module.exports = {
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
// XXX: temporary logging to try to diagnose
|
// XXX: temporary logging to try to diagnose
|
||||||
// https://github.com/vector-im/riot-web/issues/3148
|
// https://github.com/vector-im/riot-web/issues/3148
|
||||||
console.log('Resend got send failure: ' + err.name + '('+err+')');
|
console.log('Resend got send failure: ' + err.name + '(' + err + ')');
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'message_send_failed',
|
action: 'message_send_failed',
|
||||||
event: event,
|
event: event,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
removeFromQueue: function(event) {
|
|
||||||
|
static removeFromQueue(event) {
|
||||||
MatrixClientPeg.get().cancelPendingEvent(event);
|
MatrixClientPeg.get().cancelPendingEvent(event);
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -16,15 +16,16 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import MultiInviter from './utils/MultiInviter';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { getAddressType } from './UserAddress';
|
import { getAddressType } from './UserAddress';
|
||||||
import createRoom from './createRoom';
|
import createRoom from './createRoom';
|
||||||
import sdk from './';
|
import * as sdk from './';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import DMRoomMap from './utils/DMRoomMap';
|
import DMRoomMap from './utils/DMRoomMap';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room
|
* Invites multiple addresses to a room
|
||||||
|
@ -41,6 +42,18 @@ function inviteMultipleToRoom(roomId, addrs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showStartChatInviteDialog() {
|
export function showStartChatInviteDialog() {
|
||||||
|
if (SettingsStore.isFeatureEnabled("feature_ftue_dms")) {
|
||||||
|
const DMInviteDialog = sdk.getComponent("dialogs.DMInviteDialog");
|
||||||
|
Modal.createTrackedDialog('Start DM', '', DMInviteDialog, {
|
||||||
|
onFinished: (inviteIds) => {
|
||||||
|
// TODO: Replace _onStartDmFinished with less hacks
|
||||||
|
if (inviteIds.length > 0) _onStartDmFinished(true, inviteIds.map(i => ({address: i})));
|
||||||
|
// else ignore and just do nothing
|
||||||
|
},
|
||||||
|
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||||
|
|
||||||
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
|
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
|
||||||
|
@ -99,7 +112,7 @@ export function isValid3pidInvite(event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Immutable DMs replaces this
|
// TODO: Canonical DMs replaces this
|
||||||
function _onStartDmFinished(shouldInvite, addrs) {
|
function _onStartDmFinished(shouldInvite, addrs) {
|
||||||
if (!shouldInvite) return;
|
if (!shouldInvite) return;
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,8 @@ function tsOfNewestEvent(room) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mostRecentActivityFirst(roomList) {
|
export function mostRecentActivityFirst(roomList) {
|
||||||
return roomList.sort(function(a, b) {
|
return roomList.sort(function(a, b) {
|
||||||
return tsOfNewestEvent(b) - tsOfNewestEvent(a);
|
return tsOfNewestEvent(b) - tsOfNewestEvent(a);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
mostRecentActivityFirst,
|
|
||||||
};
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
|
import {PushProcessor} from 'matrix-js-sdk/src/pushprocessor';
|
||||||
|
|
||||||
export const ALL_MESSAGES_LOUD = 'all_messages_loud';
|
export const ALL_MESSAGES_LOUD = 'all_messages_loud';
|
||||||
export const ALL_MESSAGES = 'all_messages';
|
export const ALL_MESSAGES = 'all_messages';
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a room object, return the alias we should use for it,
|
* Given a room object, return the alias we should use for it,
|
||||||
|
|
|
@ -18,12 +18,11 @@ limitations under the License.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
||||||
const request = require('browser-request');
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
|
import request from "browser-request";
|
||||||
const SdkConfig = require('./SdkConfig');
|
|
||||||
const MatrixClientPeg = require('./MatrixClientPeg');
|
|
||||||
|
|
||||||
import * as Matrix from 'matrix-js-sdk';
|
import * as Matrix from 'matrix-js-sdk';
|
||||||
|
import SdkConfig from "./SdkConfig";
|
||||||
|
|
||||||
// The version of the integration manager API we're intending to work with
|
// The version of the integration manager API we're intending to work with
|
||||||
const imApiVersion = "1.1";
|
const imApiVersion = "1.1";
|
||||||
|
|
|
@ -232,7 +232,7 @@ Example:
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import { MatrixEvent } from 'matrix-js-sdk';
|
import { MatrixEvent } from 'matrix-js-sdk';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import WidgetUtils from './utils/WidgetUtils';
|
import WidgetUtils from './utils/WidgetUtils';
|
||||||
|
@ -658,30 +658,29 @@ const onMessage = function(event) {
|
||||||
|
|
||||||
let listenerCount = 0;
|
let listenerCount = 0;
|
||||||
let openManagerUrl = null;
|
let openManagerUrl = null;
|
||||||
module.exports = {
|
|
||||||
startListening: function() {
|
|
||||||
if (listenerCount === 0) {
|
|
||||||
window.addEventListener("message", onMessage, false);
|
|
||||||
}
|
|
||||||
listenerCount += 1;
|
|
||||||
},
|
|
||||||
|
|
||||||
stopListening: function() {
|
export function startListening() {
|
||||||
listenerCount -= 1;
|
if (listenerCount === 0) {
|
||||||
if (listenerCount === 0) {
|
window.addEventListener("message", onMessage, false);
|
||||||
window.removeEventListener("message", onMessage);
|
}
|
||||||
}
|
listenerCount += 1;
|
||||||
if (listenerCount < 0) {
|
}
|
||||||
// Make an error so we get a stack trace
|
|
||||||
const e = new Error(
|
|
||||||
"ScalarMessaging: mismatched startListening / stopListening detected." +
|
|
||||||
" Negative count",
|
|
||||||
);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setOpenManagerUrl: function(url) {
|
export function stopListening() {
|
||||||
openManagerUrl = url;
|
listenerCount -= 1;
|
||||||
},
|
if (listenerCount === 0) {
|
||||||
};
|
window.removeEventListener("message", onMessage);
|
||||||
|
}
|
||||||
|
if (listenerCount < 0) {
|
||||||
|
// Make an error so we get a stack trace
|
||||||
|
const e = new Error(
|
||||||
|
"ScalarMessaging: mismatched startListening / stopListening detected." +
|
||||||
|
" Negative count",
|
||||||
|
);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setOpenManagerUrl(url) {
|
||||||
|
openManagerUrl = url;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,7 +15,11 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const DEFAULTS = {
|
export interface ConfigOptions {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULTS: ConfigOptions = {
|
||||||
// URL to a page we show in an iframe to configure integrations
|
// URL to a page we show in an iframe to configure integrations
|
||||||
integrations_ui_url: "https://scalar.vector.im/",
|
integrations_ui_url: "https://scalar.vector.im/",
|
||||||
// Base URL to the REST interface of the integrations server
|
// Base URL to the REST interface of the integrations server
|
||||||
|
@ -23,30 +28,37 @@ export const DEFAULTS = {
|
||||||
bug_report_endpoint_url: null,
|
bug_report_endpoint_url: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SdkConfig {
|
export default class SdkConfig {
|
||||||
static get() {
|
private static instance: ConfigOptions;
|
||||||
return global.mxReactSdkConfig || {};
|
|
||||||
|
private static setInstance(i: ConfigOptions) {
|
||||||
|
SdkConfig.instance = i;
|
||||||
|
|
||||||
|
// For debugging purposes
|
||||||
|
(<any>window).mxReactSdkConfig = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static put(cfg) {
|
static get() {
|
||||||
|
return SdkConfig.instance || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
static put(cfg: ConfigOptions) {
|
||||||
const defaultKeys = Object.keys(DEFAULTS);
|
const defaultKeys = Object.keys(DEFAULTS);
|
||||||
for (let i = 0; i < defaultKeys.length; ++i) {
|
for (let i = 0; i < defaultKeys.length; ++i) {
|
||||||
if (cfg[defaultKeys[i]] === undefined) {
|
if (cfg[defaultKeys[i]] === undefined) {
|
||||||
cfg[defaultKeys[i]] = DEFAULTS[defaultKeys[i]];
|
cfg[defaultKeys[i]] = DEFAULTS[defaultKeys[i]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global.mxReactSdkConfig = cfg;
|
SdkConfig.setInstance(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unset() {
|
static unset() {
|
||||||
global.mxReactSdkConfig = undefined;
|
SdkConfig.setInstance({});
|
||||||
}
|
}
|
||||||
|
|
||||||
static add(cfg) {
|
static add(cfg: ConfigOptions) {
|
||||||
const liveConfig = SdkConfig.get();
|
const liveConfig = SdkConfig.get();
|
||||||
const newConfig = Object.assign({}, liveConfig, cfg);
|
const newConfig = Object.assign({}, liveConfig, cfg);
|
||||||
SdkConfig.put(newConfig);
|
SdkConfig.put(newConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SdkConfig;
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import EventIndexPeg from "./indexing/EventIndexPeg";
|
import EventIndexPeg from "./indexing/EventIndexPeg";
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
|
|
||||||
function serverSideSearch(term, roomId = undefined) {
|
function serverSideSearch(term, roomId = undefined) {
|
||||||
let filter;
|
let filter;
|
||||||
|
|
|
@ -28,21 +28,37 @@ class Skinner {
|
||||||
" b) A component has called getComponent at the root level",
|
" b) A component has called getComponent at the root level",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let comp = this.components[name];
|
|
||||||
// XXX: Temporarily also try 'views.' as we're currently
|
const doLookup = (components) => {
|
||||||
// leaving the 'views.' off views.
|
if (!components) return null;
|
||||||
|
let comp = components[name];
|
||||||
|
// XXX: Temporarily also try 'views.' as we're currently
|
||||||
|
// leaving the 'views.' off views.
|
||||||
|
if (!comp) {
|
||||||
|
comp = components['views.' + name];
|
||||||
|
}
|
||||||
|
return comp;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check the skin first
|
||||||
|
let comp = doLookup(this.components);
|
||||||
|
|
||||||
|
// If that failed, check against our own components
|
||||||
if (!comp) {
|
if (!comp) {
|
||||||
comp = this.components['views.'+name];
|
// Lazily load our own components because they might end up calling .getComponent()
|
||||||
|
comp = doLookup(require("./component-index").components);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just return nothing instead of erroring - the consumer should be smart enough to
|
||||||
|
// handle this at this point.
|
||||||
if (!comp) {
|
if (!comp) {
|
||||||
throw new Error("No such component: "+name);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// components have to be functions.
|
// components have to be functions.
|
||||||
const validType = typeof comp === 'function';
|
const validType = typeof comp === 'function';
|
||||||
if (!validType) {
|
if (!validType) {
|
||||||
throw new Error(`Not a valid component: ${name}.`);
|
throw new Error(`Not a valid component: ${name} (type = ${typeof(comp)}).`);
|
||||||
}
|
}
|
||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
|
@ -90,5 +106,5 @@ class Skinner {
|
||||||
if (global.mxSkinner === undefined) {
|
if (global.mxSkinner === undefined) {
|
||||||
global.mxSkinner = new Skinner();
|
global.mxSkinner = new Skinner();
|
||||||
}
|
}
|
||||||
module.exports = global.mxSkinner;
|
export default global.mxSkinner;
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,9 @@ limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import sdk from './index';
|
import * as sdk from './index';
|
||||||
import {_t, _td} from './languageHandler';
|
import {_t, _td} from './languageHandler';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import MultiInviter from './utils/MultiInviter';
|
||||||
|
@ -780,54 +780,52 @@ export const CommandMap = {
|
||||||
const deviceId = matches[2];
|
const deviceId = matches[2];
|
||||||
const fingerprint = matches[3];
|
const fingerprint = matches[3];
|
||||||
|
|
||||||
return success(
|
return success((async () => {
|
||||||
// Promise.resolve to handle transition from static result to promise; can be removed
|
const device = await cli.getStoredDevice(userId, deviceId);
|
||||||
// in future
|
if (!device) {
|
||||||
Promise.resolve(cli.getStoredDevice(userId, deviceId)).then((device) => {
|
throw new Error(_t('Unknown (user, device) pair:') + ` (${userId}, ${deviceId})`);
|
||||||
if (!device) {
|
}
|
||||||
throw new Error(_t('Unknown (user, device) pair:') + ` (${userId}, ${deviceId})`);
|
const deviceTrust = await cli.checkDeviceTrust(userId, deviceId);
|
||||||
}
|
|
||||||
|
|
||||||
if (device.isVerified()) {
|
if (deviceTrust.isVerified()) {
|
||||||
if (device.getFingerprint() === fingerprint) {
|
if (device.getFingerprint() === fingerprint) {
|
||||||
throw new Error(_t('Device already verified!'));
|
throw new Error(_t('Device already verified!'));
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_t('WARNING: Device already verified, but keys do NOT MATCH!'));
|
throw new Error(_t('WARNING: Device already verified, but keys do NOT MATCH!'));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (device.getFingerprint() !== fingerprint) {
|
if (device.getFingerprint() !== fingerprint) {
|
||||||
const fprint = device.getFingerprint();
|
const fprint = device.getFingerprint();
|
||||||
throw new Error(
|
throw new Error(
|
||||||
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
|
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
|
||||||
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
|
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
|
||||||
'"%(fingerprint)s". This could mean your communications are being intercepted!',
|
'"%(fingerprint)s". This could mean your communications are being intercepted!',
|
||||||
{
|
{
|
||||||
fprint,
|
fprint,
|
||||||
userId,
|
userId,
|
||||||
deviceId,
|
deviceId,
|
||||||
fingerprint,
|
fingerprint,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return cli.setDeviceVerified(userId, deviceId, true);
|
await cli.setDeviceVerified(userId, deviceId, true);
|
||||||
}).then(() => {
|
|
||||||
// Tell the user we verified everything
|
// Tell the user we verified everything
|
||||||
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||||
Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, {
|
Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, {
|
||||||
title: _t('Verified key'),
|
title: _t('Verified key'),
|
||||||
description: <div>
|
description: <div>
|
||||||
<p>
|
<p>
|
||||||
{
|
{
|
||||||
_t('The signing key you provided matches the signing key you received ' +
|
_t('The signing key you provided matches the signing key you received ' +
|
||||||
'from %(userId)s\'s device %(deviceId)s. Device marked as verified.',
|
'from %(userId)s\'s device %(deviceId)s. Device marked as verified.',
|
||||||
{userId, deviceId})
|
{userId, deviceId})
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
</div>,
|
</div>,
|
||||||
});
|
});
|
||||||
}),
|
})());
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return reject(this.getUsage());
|
return reject(this.getUsage());
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
//@flow
|
|
||||||
/*
|
|
||||||
Copyright 2017 Aviral Dasgupta
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {Value} from 'slate';
|
|
||||||
|
|
||||||
import _clamp from 'lodash/clamp';
|
|
||||||
|
|
||||||
type MessageFormat = 'rich' | 'markdown';
|
|
||||||
|
|
||||||
class HistoryItem {
|
|
||||||
// We store history items in their native format to ensure history is accurate
|
|
||||||
// and then convert them if our RTE has subsequently changed format.
|
|
||||||
value: Value;
|
|
||||||
format: MessageFormat = 'rich';
|
|
||||||
|
|
||||||
constructor(value: ?Value, format: ?MessageFormat) {
|
|
||||||
this.value = value;
|
|
||||||
this.format = format;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJSON(obj: Object): HistoryItem {
|
|
||||||
return new HistoryItem(
|
|
||||||
Value.fromJSON(obj.value),
|
|
||||||
obj.format,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): Object {
|
|
||||||
return {
|
|
||||||
value: this.value.toJSON(),
|
|
||||||
format: this.format,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class SlateComposerHistoryManager {
|
|
||||||
history: Array<HistoryItem> = [];
|
|
||||||
prefix: string;
|
|
||||||
lastIndex: number = 0; // used for indexing the storage
|
|
||||||
currentIndex: number = 0; // used for indexing the loaded validated history Array
|
|
||||||
|
|
||||||
constructor(roomId: string, prefix: string = 'mx_composer_history_') {
|
|
||||||
this.prefix = prefix + roomId;
|
|
||||||
|
|
||||||
// TODO: Performance issues?
|
|
||||||
let item;
|
|
||||||
for (; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) {
|
|
||||||
try {
|
|
||||||
this.history.push(
|
|
||||||
HistoryItem.fromJSON(JSON.parse(item)),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn("Throwing away unserialisable history", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.lastIndex = this.currentIndex;
|
|
||||||
// reset currentIndex to account for any unserialisable history
|
|
||||||
this.currentIndex = this.history.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
save(value: Value, format: MessageFormat) {
|
|
||||||
const item = new HistoryItem(value, format);
|
|
||||||
this.history.push(item);
|
|
||||||
this.currentIndex = this.history.length;
|
|
||||||
sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item.toJSON()));
|
|
||||||
}
|
|
||||||
|
|
||||||
getItem(offset: number): ?HistoryItem {
|
|
||||||
this.currentIndex = _clamp(this.currentIndex + offset, 0, this.history.length - 1);
|
|
||||||
return this.history[this.currentIndex];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,8 +16,8 @@ limitations under the License.
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import sdk from './';
|
import * as sdk from './';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
|
|
||||||
export class TermsNotSignedError extends Error {}
|
export class TermsNotSignedError extends Error {}
|
||||||
|
|
|
@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import CallHandler from './CallHandler';
|
import CallHandler from './CallHandler';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import * as Roles from './Roles';
|
import * as Roles from './Roles';
|
||||||
|
@ -473,7 +473,7 @@ function textForPowerEvent(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function textForPinnedEvent(event) {
|
function textForPinnedEvent(event) {
|
||||||
const senderName = event.getSender();
|
const senderName = event.sender ? event.sender.name : event.getSender();
|
||||||
return _t("%(senderName)s changed the pinned messages for the room.", {senderName});
|
return _t("%(senderName)s changed the pinned messages for the room.", {senderName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,10 +620,8 @@ for (const evType of ALL_RULE_TYPES) {
|
||||||
stateHandlers[evType] = textForMjolnirEvent;
|
stateHandlers[evType] = textForMjolnirEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
export function textForEvent(ev) {
|
||||||
textForEvent: function(ev) {
|
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
||||||
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
|
if (handler) return handler(ev);
|
||||||
if (handler) return handler(ev);
|
return '';
|
||||||
return '';
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -143,10 +143,14 @@ class Tinter {
|
||||||
* over time then the best bet is to register a single callback for the
|
* over time then the best bet is to register a single callback for the
|
||||||
* entire set.
|
* entire set.
|
||||||
*
|
*
|
||||||
|
* To ensure the tintable work happens at least once, it is also called as
|
||||||
|
* part of registration.
|
||||||
|
*
|
||||||
* @param {Function} tintable Function to call when the tint changes.
|
* @param {Function} tintable Function to call when the tint changes.
|
||||||
*/
|
*/
|
||||||
registerTintable(tintable) {
|
registerTintable(tintable) {
|
||||||
this.tintables.push(tintable);
|
this.tintables.push(tintable);
|
||||||
|
tintable();
|
||||||
}
|
}
|
||||||
|
|
||||||
getKeyRgb() {
|
getKeyRgb() {
|
||||||
|
|
144
src/Unread.js
144
src/Unread.js
|
@ -14,80 +14,78 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const MatrixClientPeg = require('./MatrixClientPeg');
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
import shouldHideEvent from './shouldHideEvent';
|
import shouldHideEvent from './shouldHideEvent';
|
||||||
const sdk = require('./index');
|
import * as sdk from "./index";
|
||||||
|
import {haveTileForEvent} from "./components/views/rooms/EventTile";
|
||||||
|
|
||||||
module.exports = {
|
/**
|
||||||
/**
|
* Returns true iff this event arriving in a room should affect the room's
|
||||||
* Returns true iff this event arriving in a room should affect the room's
|
* count of unread messages
|
||||||
* count of unread messages
|
*/
|
||||||
*/
|
export function eventTriggersUnreadCount(ev) {
|
||||||
eventTriggersUnreadCount: function(ev) {
|
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
|
||||||
if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) {
|
return false;
|
||||||
return false;
|
} else if (ev.getType() == 'm.room.member') {
|
||||||
} else if (ev.getType() == 'm.room.member') {
|
return false;
|
||||||
return false;
|
} else if (ev.getType() == 'm.room.third_party_invite') {
|
||||||
} else if (ev.getType() == 'm.room.third_party_invite') {
|
return false;
|
||||||
return false;
|
} else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
|
||||||
} else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') {
|
return false;
|
||||||
return false;
|
} else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
||||||
} else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') {
|
return false;
|
||||||
return false;
|
} else if (ev.getType() == 'm.room.aliases' || ev.getType() == 'm.room.canonical_alias') {
|
||||||
} else if (ev.getType() == 'm.room.aliases' || ev.getType() == 'm.room.canonical_alias') {
|
return false;
|
||||||
return false;
|
} else if (ev.getType() == 'm.room.server_acl') {
|
||||||
} else if (ev.getType() == 'm.room.server_acl') {
|
return false;
|
||||||
|
}
|
||||||
|
return haveTileForEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doesRoomHaveUnreadMessages(room) {
|
||||||
|
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
|
// get the most recent read receipt sent by our account.
|
||||||
|
// N.B. this is NOT a read marker (RM, aka "read up to marker"),
|
||||||
|
// despite the name of the method :((
|
||||||
|
const readUpToId = room.getEventReadUpTo(myUserId);
|
||||||
|
|
||||||
|
// as we don't send RRs for our own messages, make sure we special case that
|
||||||
|
// if *we* sent the last message into the room, we consider it not unread!
|
||||||
|
// Should fix: https://github.com/vector-im/riot-web/issues/3263
|
||||||
|
// https://github.com/vector-im/riot-web/issues/2427
|
||||||
|
// ...and possibly some of the others at
|
||||||
|
// https://github.com/vector-im/riot-web/issues/3363
|
||||||
|
if (room.timeline.length &&
|
||||||
|
room.timeline[room.timeline.length - 1].sender &&
|
||||||
|
room.timeline[room.timeline.length - 1].sender.userId === myUserId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this just looks at whatever history we have, which if we've only just started
|
||||||
|
// up probably won't be very much, so if the last couple of events are ones that
|
||||||
|
// don't count, we don't know if there are any events that do count between where
|
||||||
|
// we have and the read receipt. We could fetch more history to try & find out,
|
||||||
|
// but currently we just guess.
|
||||||
|
|
||||||
|
// Loop through messages, starting with the most recent...
|
||||||
|
for (let i = room.timeline.length - 1; i >= 0; --i) {
|
||||||
|
const ev = room.timeline[i];
|
||||||
|
|
||||||
|
if (ev.getId() == readUpToId) {
|
||||||
|
// If we've read up to this event, there's nothing more recent
|
||||||
|
// that counts and we can stop looking because the user's read
|
||||||
|
// this and everything before.
|
||||||
return false;
|
return false;
|
||||||
|
} else if (!shouldHideEvent(ev) && eventTriggersUnreadCount(ev)) {
|
||||||
|
// We've found a message that counts before we hit
|
||||||
|
// the user's read receipt, so this room is definitely unread.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
const EventTile = sdk.getComponent('rooms.EventTile');
|
}
|
||||||
return EventTile.haveTileForEvent(ev);
|
// If we got here, we didn't find a message that counted but didn't find
|
||||||
},
|
// the user's read receipt either, so we guess and say that the room is
|
||||||
|
// unread on the theory that false positives are better than false
|
||||||
doesRoomHaveUnreadMessages: function(room) {
|
// negatives here.
|
||||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
return true;
|
||||||
|
}
|
||||||
// get the most recent read receipt sent by our account.
|
|
||||||
// N.B. this is NOT a read marker (RM, aka "read up to marker"),
|
|
||||||
// despite the name of the method :((
|
|
||||||
const readUpToId = room.getEventReadUpTo(myUserId);
|
|
||||||
|
|
||||||
// as we don't send RRs for our own messages, make sure we special case that
|
|
||||||
// if *we* sent the last message into the room, we consider it not unread!
|
|
||||||
// Should fix: https://github.com/vector-im/riot-web/issues/3263
|
|
||||||
// https://github.com/vector-im/riot-web/issues/2427
|
|
||||||
// ...and possibly some of the others at
|
|
||||||
// https://github.com/vector-im/riot-web/issues/3363
|
|
||||||
if (room.timeline.length &&
|
|
||||||
room.timeline[room.timeline.length - 1].sender &&
|
|
||||||
room.timeline[room.timeline.length - 1].sender.userId === myUserId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this just looks at whatever history we have, which if we've only just started
|
|
||||||
// up probably won't be very much, so if the last couple of events are ones that
|
|
||||||
// don't count, we don't know if there are any events that do count between where
|
|
||||||
// we have and the read receipt. We could fetch more history to try & find out,
|
|
||||||
// but currently we just guess.
|
|
||||||
|
|
||||||
// Loop through messages, starting with the most recent...
|
|
||||||
for (let i = room.timeline.length - 1; i >= 0; --i) {
|
|
||||||
const ev = room.timeline[i];
|
|
||||||
|
|
||||||
if (ev.getId() == readUpToId) {
|
|
||||||
// If we've read up to this event, there's nothing more recent
|
|
||||||
// that counts and we can stop looking because the user's read
|
|
||||||
// this and everything before.
|
|
||||||
return false;
|
|
||||||
} else if (!shouldHideEvent(ev) && this.eventTriggersUnreadCount(ev)) {
|
|
||||||
// We've found a message that counts before we hit
|
|
||||||
// the user's read receipt, so this room is definitely unread.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If we got here, we didn't find a message that counted but didn't find
|
|
||||||
// the user's read receipt either, so we guess and say that the room is
|
|
||||||
// unread on the theory that false positives are better than false
|
|
||||||
// negatives here.
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,9 +15,9 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {createNewMatrixCall, Room} from "matrix-js-sdk";
|
import {createNewMatrixCall as jsCreateNewMatrixCall, Room} from "matrix-js-sdk";
|
||||||
import CallHandler from './CallHandler';
|
import CallHandler from './CallHandler';
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
|
|
||||||
// FIXME: this is Riot (Vector) specific code, but will be removed shortly when
|
// FIXME: this is Riot (Vector) specific code, but will be removed shortly when
|
||||||
// we switch over to jitsi entirely for video conferencing.
|
// we switch over to jitsi entirely for video conferencing.
|
||||||
|
@ -28,10 +29,10 @@ import MatrixClientPeg from "./MatrixClientPeg";
|
||||||
const USER_PREFIX = "fs_";
|
const USER_PREFIX = "fs_";
|
||||||
const DOMAIN = "matrix.org";
|
const DOMAIN = "matrix.org";
|
||||||
|
|
||||||
function ConferenceCall(matrixClient, groupChatRoomId) {
|
export function ConferenceCall(matrixClient, groupChatRoomId) {
|
||||||
this.client = matrixClient;
|
this.client = matrixClient;
|
||||||
this.groupRoomId = groupChatRoomId;
|
this.groupRoomId = groupChatRoomId;
|
||||||
this.confUserId = module.exports.getConferenceUserIdForRoom(this.groupRoomId);
|
this.confUserId = getConferenceUserIdForRoom(this.groupRoomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConferenceCall.prototype.setup = function() {
|
ConferenceCall.prototype.setup = function() {
|
||||||
|
@ -42,7 +43,7 @@ ConferenceCall.prototype.setup = function() {
|
||||||
// return a call for *this* room to be placed. We also tack on
|
// return a call for *this* room to be placed. We also tack on
|
||||||
// confUserId to speed up lookups (else we'd need to loop every room
|
// confUserId to speed up lookups (else we'd need to loop every room
|
||||||
// looking for a 1:1 room with this conf user ID!)
|
// looking for a 1:1 room with this conf user ID!)
|
||||||
const call = createNewMatrixCall(self.client, room.roomId);
|
const call = jsCreateNewMatrixCall(self.client, room.roomId);
|
||||||
call.confUserId = self.confUserId;
|
call.confUserId = self.confUserId;
|
||||||
call.groupRoomId = self.groupRoomId;
|
call.groupRoomId = self.groupRoomId;
|
||||||
return call;
|
return call;
|
||||||
|
@ -90,7 +91,7 @@ ConferenceCall.prototype._getConferenceUserRoom = function() {
|
||||||
* @param {string} userId The user ID to check.
|
* @param {string} userId The user ID to check.
|
||||||
* @return {boolean} True if it is a conference bot.
|
* @return {boolean} True if it is a conference bot.
|
||||||
*/
|
*/
|
||||||
module.exports.isConferenceUser = function(userId) {
|
export function isConferenceUser(userId) {
|
||||||
if (userId.indexOf("@" + USER_PREFIX) !== 0) {
|
if (userId.indexOf("@" + USER_PREFIX) !== 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -101,26 +102,26 @@ module.exports.isConferenceUser = function(userId) {
|
||||||
return /^!.+:.+/.test(decoded);
|
return /^!.+:.+/.test(decoded);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.getConferenceUserIdForRoom = function(roomId) {
|
export function getConferenceUserIdForRoom(roomId) {
|
||||||
// abuse browserify's core node Buffer support (strip padding ='s)
|
// abuse browserify's core node Buffer support (strip padding ='s)
|
||||||
const base64RoomId = new Buffer(roomId).toString("base64").replace(/=/g, "");
|
const base64RoomId = new Buffer(roomId).toString("base64").replace(/=/g, "");
|
||||||
return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
|
return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.createNewMatrixCall = function(client, roomId) {
|
export function createNewMatrixCall(client, roomId) {
|
||||||
const confCall = new ConferenceCall(
|
const confCall = new ConferenceCall(
|
||||||
client, roomId,
|
client, roomId,
|
||||||
);
|
);
|
||||||
return confCall.setup();
|
return confCall.setup();
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.getConferenceCallForRoom = function(roomId) {
|
export function getConferenceCallForRoom(roomId) {
|
||||||
// search for a conference 1:1 call for this group chat room ID
|
// search for a conference 1:1 call for this group chat room ID
|
||||||
const activeCall = CallHandler.getAnyActiveCall();
|
const activeCall = CallHandler.getAnyActiveCall();
|
||||||
if (activeCall && activeCall.confUserId) {
|
if (activeCall && activeCall.confUserId) {
|
||||||
const thisRoomConfUserId = module.exports.getConferenceUserIdForRoom(
|
const thisRoomConfUserId = getConferenceUserIdForRoom(
|
||||||
roomId,
|
roomId,
|
||||||
);
|
);
|
||||||
if (thisRoomConfUserId === activeCall.confUserId) {
|
if (thisRoomConfUserId === activeCall.confUserId) {
|
||||||
|
@ -128,8 +129,7 @@ module.exports.getConferenceCallForRoom = function(roomId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.ConferenceCall = ConferenceCall;
|
// TODO: Document this.
|
||||||
|
export const slot = 'conference';
|
||||||
module.exports.slot = 'conference';
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const React = require('react');
|
import React from "react";
|
||||||
const ReactDom = require('react-dom');
|
import ReactDom from "react-dom";
|
||||||
|
import Velocity from "velocity-animate";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
const Velocity = require('velocity-animate');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Velociraptor contains components and animates transitions with velocity.
|
* The Velociraptor contains components and animates transitions with velocity.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const Velocity = require('velocity-animate');
|
import Velocity from "velocity-animate";
|
||||||
|
|
||||||
// courtesy of https://github.com/julianshapiro/velocity/issues/283
|
// courtesy of https://github.com/julianshapiro/velocity/issues/283
|
||||||
// We only use easeOutBounce (easeInBounce is just sort of nonsensical)
|
// We only use easeOutBounce (easeInBounce is just sort of nonsensical)
|
||||||
|
|
|
@ -14,71 +14,69 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
|
||||||
module.exports = {
|
export function usersTypingApartFromMeAndIgnored(room) {
|
||||||
usersTypingApartFromMeAndIgnored: function(room) {
|
return usersTyping(
|
||||||
return this.usersTyping(
|
room, [MatrixClientPeg.get().credentials.userId].concat(MatrixClientPeg.get().getIgnoredUsers()),
|
||||||
room, [MatrixClientPeg.get().credentials.userId].concat(MatrixClientPeg.get().getIgnoredUsers()),
|
);
|
||||||
);
|
}
|
||||||
},
|
|
||||||
|
|
||||||
usersTypingApartFromMe: function(room) {
|
export function usersTypingApartFromMe(room) {
|
||||||
return this.usersTyping(
|
return usersTyping(
|
||||||
room, [MatrixClientPeg.get().credentials.userId],
|
room, [MatrixClientPeg.get().credentials.userId],
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a Room object and, optionally, a list of userID strings
|
* Given a Room object and, optionally, a list of userID strings
|
||||||
* to exclude, return a list of user objects who are typing.
|
* to exclude, return a list of user objects who are typing.
|
||||||
* @param {Room} room: room object to get users from.
|
* @param {Room} room: room object to get users from.
|
||||||
* @param {string[]} exclude: list of user mxids to exclude.
|
* @param {string[]} exclude: list of user mxids to exclude.
|
||||||
* @returns {string[]} list of user objects who are typing.
|
* @returns {string[]} list of user objects who are typing.
|
||||||
*/
|
*/
|
||||||
usersTyping: function(room, exclude) {
|
export function usersTyping(room, exclude) {
|
||||||
const whoIsTyping = [];
|
const whoIsTyping = [];
|
||||||
|
|
||||||
if (exclude === undefined) {
|
if (exclude === undefined) {
|
||||||
exclude = [];
|
exclude = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const memberKeys = Object.keys(room.currentState.members);
|
const memberKeys = Object.keys(room.currentState.members);
|
||||||
for (let i = 0; i < memberKeys.length; ++i) {
|
for (let i = 0; i < memberKeys.length; ++i) {
|
||||||
const userId = memberKeys[i];
|
const userId = memberKeys[i];
|
||||||
|
|
||||||
if (room.currentState.members[userId].typing) {
|
if (room.currentState.members[userId].typing) {
|
||||||
if (exclude.indexOf(userId) === -1) {
|
if (exclude.indexOf(userId) === -1) {
|
||||||
whoIsTyping.push(room.currentState.members[userId]);
|
whoIsTyping.push(room.currentState.members[userId]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return whoIsTyping;
|
return whoIsTyping;
|
||||||
},
|
}
|
||||||
|
|
||||||
whoIsTypingString: function(whoIsTyping, limit) {
|
export function whoIsTypingString(whoIsTyping, limit) {
|
||||||
let othersCount = 0;
|
let othersCount = 0;
|
||||||
if (whoIsTyping.length > limit) {
|
if (whoIsTyping.length > limit) {
|
||||||
othersCount = whoIsTyping.length - limit + 1;
|
othersCount = whoIsTyping.length - limit + 1;
|
||||||
}
|
}
|
||||||
if (whoIsTyping.length === 0) {
|
if (whoIsTyping.length === 0) {
|
||||||
return '';
|
return '';
|
||||||
} else if (whoIsTyping.length === 1) {
|
} else if (whoIsTyping.length === 1) {
|
||||||
return _t('%(displayName)s is typing …', {displayName: whoIsTyping[0].name});
|
return _t('%(displayName)s is typing …', {displayName: whoIsTyping[0].name});
|
||||||
}
|
}
|
||||||
const names = whoIsTyping.map(function(m) {
|
const names = whoIsTyping.map(function(m) {
|
||||||
return m.name;
|
return m.name;
|
||||||
|
});
|
||||||
|
if (othersCount>=1) {
|
||||||
|
return _t('%(names)s and %(count)s others are typing …', {
|
||||||
|
names: names.slice(0, limit - 1).join(', '),
|
||||||
|
count: othersCount,
|
||||||
});
|
});
|
||||||
if (othersCount>=1) {
|
} else {
|
||||||
return _t('%(names)s and %(count)s others are typing …', {
|
const lastPerson = names.pop();
|
||||||
names: names.slice(0, limit - 1).join(', '),
|
return _t('%(names)s and %(lastPerson)s are typing …', {names: names.join(', '), lastPerson: lastPerson});
|
||||||
count: othersCount,
|
}
|
||||||
});
|
}
|
||||||
} else {
|
|
||||||
const lastPerson = names.pop();
|
|
||||||
return _t('%(names)s and %(lastPerson)s are typing …', {names: names.join(', '), lastPerson: lastPerson});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ limitations under the License.
|
||||||
import FromWidgetPostMessageApi from './FromWidgetPostMessageApi';
|
import FromWidgetPostMessageApi from './FromWidgetPostMessageApi';
|
||||||
import ToWidgetPostMessageApi from './ToWidgetPostMessageApi';
|
import ToWidgetPostMessageApi from './ToWidgetPostMessageApi';
|
||||||
import Modal from "./Modal";
|
import Modal from "./Modal";
|
||||||
import MatrixClientPeg from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog";
|
import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog";
|
||||||
import WidgetUtils from "./utils/WidgetUtils";
|
import WidgetUtils from "./utils/WidgetUtils";
|
||||||
|
|
|
@ -16,11 +16,10 @@ limitations under the License.
|
||||||
|
|
||||||
import { asyncAction } from './actionCreators';
|
import { asyncAction } from './actionCreators';
|
||||||
import RoomListStore from '../stores/RoomListStore';
|
import RoomListStore from '../stores/RoomListStore';
|
||||||
|
|
||||||
import Modal from '../Modal';
|
import Modal from '../Modal';
|
||||||
import * as Rooms from '../Rooms';
|
import * as Rooms from '../Rooms';
|
||||||
import { _t } from '../languageHandler';
|
import { _t } from '../languageHandler';
|
||||||
import sdk from '../index';
|
import * as sdk from '../index';
|
||||||
|
|
||||||
const RoomListActions = {};
|
const RoomListActions = {};
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,18 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Key} from "../../../Keyboard";
|
import React from "react";
|
||||||
|
|
||||||
const React = require("react");
|
|
||||||
import createReactClass from 'create-react-class';
|
import createReactClass from 'create-react-class';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
const sdk = require('../../../index');
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
const MatrixClientPeg = require("../../../MatrixClientPeg");
|
import {Key} from "../../../Keyboard";
|
||||||
|
import * as sdk from "../../../index";
|
||||||
|
|
||||||
module.exports = createReactClass({
|
// XXX: This component is not cross-signing aware.
|
||||||
|
// https://github.com/vector-im/riot-web/issues/11752 tracks either updating this
|
||||||
|
// component or taking it out to pasture.
|
||||||
|
export default createReactClass({
|
||||||
displayName: 'EncryptedEventDialog',
|
displayName: 'EncryptedEventDialog',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
||||||
import sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
|
||||||
const PHASE_EDIT = 1;
|
const PHASE_EDIT = 1;
|
||||||
const PHASE_EXPORTING = 2;
|
const PHASE_EXPORTING = 2;
|
||||||
|
|
|
@ -20,7 +20,7 @@ import createReactClass from 'create-react-class';
|
||||||
|
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
||||||
import sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
function readFileAsArrayBuffer(file) {
|
function readFileAsArrayBuffer(file) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2018, 2019 New Vector Ltd
|
Copyright 2018, 2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -17,11 +17,13 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FileSaver from 'file-saver';
|
import FileSaver from 'file-saver';
|
||||||
|
import * as sdk from '../../../../index';
|
||||||
import sdk from '../../../../index';
|
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
import PropTypes from 'prop-types';
|
||||||
import { scorePassword } from '../../../../utils/PasswordScorer';
|
import { scorePassword } from '../../../../utils/PasswordScorer';
|
||||||
import { _t } from '../../../../languageHandler';
|
import { _t } from '../../../../languageHandler';
|
||||||
|
import { accessSecretStorage } from '../../../../CrossSigningManager';
|
||||||
|
import SettingsStore from '../../../../settings/SettingsStore';
|
||||||
|
|
||||||
const PHASE_PASSPHRASE = 0;
|
const PHASE_PASSPHRASE = 0;
|
||||||
const PHASE_PASSPHRASE_CONFIRM = 1;
|
const PHASE_PASSPHRASE_CONFIRM = 1;
|
||||||
|
@ -49,10 +51,20 @@ function selectText(target) {
|
||||||
* on the server.
|
* on the server.
|
||||||
*/
|
*/
|
||||||
export default class CreateKeyBackupDialog extends React.PureComponent {
|
export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
secureSecretStorage: PropTypes.bool,
|
||||||
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this._recoveryKeyNode = null;
|
||||||
|
this._keyBackupInfo = null;
|
||||||
|
this._setZxcvbnResultTimeout = null;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
secureSecretStorage: props.secureSecretStorage,
|
||||||
phase: PHASE_PASSPHRASE,
|
phase: PHASE_PASSPHRASE,
|
||||||
passPhrase: '',
|
passPhrase: '',
|
||||||
passPhraseConfirm: '',
|
passPhraseConfirm: '',
|
||||||
|
@ -61,12 +73,25 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
zxcvbnResult: null,
|
zxcvbnResult: null,
|
||||||
setPassPhrase: false,
|
setPassPhrase: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.state.secureSecretStorage === undefined) {
|
||||||
|
this.state.secureSecretStorage =
|
||||||
|
SettingsStore.isFeatureEnabled("feature_cross_signing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're using secret storage, skip ahead to the backing up step, as
|
||||||
|
// `accessSecretStorage` will handle passphrases as needed.
|
||||||
|
if (this.state.secureSecretStorage) {
|
||||||
|
this.state.phase = PHASE_BACKINGUP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
this._recoveryKeyNode = null;
|
// If we're using secret storage, skip ahead to the backing up step, as
|
||||||
this._keyBackupInfo = null;
|
// `accessSecretStorage` will handle passphrases as needed.
|
||||||
this._setZxcvbnResultTimeout = null;
|
if (this.state.secureSecretStorage) {
|
||||||
|
this._createBackup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -103,15 +128,26 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createBackup = async () => {
|
_createBackup = async () => {
|
||||||
|
const { secureSecretStorage } = this.state;
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_BACKINGUP,
|
phase: PHASE_BACKINGUP,
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
let info;
|
let info;
|
||||||
try {
|
try {
|
||||||
info = await MatrixClientPeg.get().createKeyBackupVersion(
|
if (secureSecretStorage) {
|
||||||
this._keyBackupInfo,
|
await accessSecretStorage(async () => {
|
||||||
);
|
info = await MatrixClientPeg.get().prepareKeyBackupVersion(
|
||||||
|
null /* random key */,
|
||||||
|
{ secureSecretStorage: true },
|
||||||
|
);
|
||||||
|
info = await MatrixClientPeg.get().createKeyBackupVersion(info);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
info = await MatrixClientPeg.get().createKeyBackupVersion(
|
||||||
|
this._keyBackupInfo,
|
||||||
|
);
|
||||||
|
}
|
||||||
await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup();
|
await MatrixClientPeg.get().scheduleAllGroupSessionsForBackup();
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_DONE,
|
phase: PHASE_DONE,
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import sdk from "../../../../index";
|
import * as sdk from "../../../../index";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
|
|
||||||
export default class IgnoreRecoveryReminderDialog extends React.PureComponent {
|
export default class IgnoreRecoveryReminderDialog extends React.PureComponent {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2018-2019 New Vector Ltd
|
Copyright 2018, 2019 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -16,8 +17,8 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import sdk from "../../../../index";
|
import * as sdk from "../../../../index";
|
||||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||||
import dis from "../../../../dispatcher";
|
import dis from "../../../../dispatcher";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import Modal from "../../../../Modal";
|
import Modal from "../../../../Modal";
|
||||||
|
@ -40,9 +41,11 @@ export default class NewRecoveryMethodDialog extends React.PureComponent {
|
||||||
|
|
||||||
onSetupClick = async () => {
|
onSetupClick = async () => {
|
||||||
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
|
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
|
||||||
Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, {
|
Modal.createTrackedDialog(
|
||||||
onFinished: this.props.onFinished,
|
'Restore Backup', '', RestoreKeyBackupDialog, {
|
||||||
});
|
onFinished: this.props.onFinished,
|
||||||
|
}, null, /* priority = */ false, /* static = */ true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -16,7 +17,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import sdk from "../../../../index";
|
import * as sdk from "../../../../index";
|
||||||
import dis from "../../../../dispatcher";
|
import dis from "../../../../dispatcher";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import Modal from "../../../../Modal";
|
import Modal from "../../../../Modal";
|
||||||
|
@ -35,6 +36,7 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
|
Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
|
||||||
import("./CreateKeyBackupDialog"),
|
import("./CreateKeyBackupDialog"),
|
||||||
|
null, null, /* priority = */ false, /* static = */ true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,22 +16,23 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import sdk from '../../../../index';
|
import * as sdk from '../../../../index';
|
||||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../../MatrixClientPeg';
|
||||||
import { scorePassword } from '../../../../utils/PasswordScorer';
|
import { scorePassword } from '../../../../utils/PasswordScorer';
|
||||||
import FileSaver from 'file-saver';
|
import FileSaver from 'file-saver';
|
||||||
import { _t } from '../../../../languageHandler';
|
import { _t } from '../../../../languageHandler';
|
||||||
import Modal from '../../../../Modal';
|
import Modal from '../../../../Modal';
|
||||||
|
|
||||||
const PHASE_LOADING = 0;
|
const PHASE_LOADING = 0;
|
||||||
const PHASE_MIGRATE = 1;
|
const PHASE_RESTORE_KEY_BACKUP = 1;
|
||||||
const PHASE_PASSPHRASE = 2;
|
const PHASE_MIGRATE = 2;
|
||||||
const PHASE_PASSPHRASE_CONFIRM = 3;
|
const PHASE_PASSPHRASE = 3;
|
||||||
const PHASE_SHOWKEY = 4;
|
const PHASE_PASSPHRASE_CONFIRM = 4;
|
||||||
const PHASE_KEEPITSAFE = 5;
|
const PHASE_SHOWKEY = 5;
|
||||||
const PHASE_STORING = 6;
|
const PHASE_KEEPITSAFE = 6;
|
||||||
const PHASE_DONE = 7;
|
const PHASE_STORING = 7;
|
||||||
const PHASE_OPTOUT_CONFIRM = 8;
|
const PHASE_DONE = 8;
|
||||||
|
const PHASE_OPTOUT_CONFIRM = 9;
|
||||||
|
|
||||||
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
|
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
|
||||||
const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms.
|
const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms.
|
||||||
|
@ -67,6 +68,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
downloaded: false,
|
downloaded: false,
|
||||||
zxcvbnResult: null,
|
zxcvbnResult: null,
|
||||||
setPassPhrase: false,
|
setPassPhrase: false,
|
||||||
|
backupInfo: null,
|
||||||
|
backupSigStatus: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
this._fetchBackupInfo();
|
this._fetchBackupInfo();
|
||||||
|
@ -80,10 +83,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
async _fetchBackupInfo() {
|
async _fetchBackupInfo() {
|
||||||
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||||
|
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
|
||||||
|
|
||||||
|
const phase = backupInfo ?
|
||||||
|
(backupSigStatus.usable ? PHASE_MIGRATE : PHASE_RESTORE_KEY_BACKUP) :
|
||||||
|
PHASE_PASSPHRASE;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: backupInfo ? PHASE_MIGRATE: PHASE_PASSPHRASE,
|
phase,
|
||||||
backupInfo,
|
backupInfo,
|
||||||
|
backupSigStatus,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,6 +170,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onRestoreKeyBackupClick = () => {
|
||||||
|
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
|
||||||
|
Modal.createTrackedDialog(
|
||||||
|
'Restore Backup', '', RestoreKeyBackupDialog, null, null,
|
||||||
|
/* priority = */ false, /* static = */ true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_onOptOutClick = () => {
|
_onOptOutClick = () => {
|
||||||
this.setState({phase: PHASE_OPTOUT_CONFIRM});
|
this.setState({phase: PHASE_OPTOUT_CONFIRM});
|
||||||
}
|
}
|
||||||
|
@ -268,6 +285,23 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE;
|
return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_renderPhaseRestoreKeyBackup() {
|
||||||
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
|
return <div>
|
||||||
|
<p>{_t(
|
||||||
|
"Key Backup is enabled on your account but has not been set " +
|
||||||
|
"up from this session. To set up secret storage, " +
|
||||||
|
"restore your key backup.",
|
||||||
|
)}</p>
|
||||||
|
<DialogButtons primaryButton={_t('Restore')}
|
||||||
|
onPrimaryButtonClick={this._onRestoreKeyBackupClick}
|
||||||
|
onCancel={this._onCancel}
|
||||||
|
hasCancel={true}
|
||||||
|
>
|
||||||
|
</DialogButtons>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
_renderPhaseMigrate() {
|
_renderPhaseMigrate() {
|
||||||
// TODO: This is a temporary screen so people who have the labs flag turned on and
|
// TODO: This is a temporary screen so people who have the labs flag turned on and
|
||||||
// click the button are aware they're making a change to their account.
|
// click the button are aware they're making a change to their account.
|
||||||
|
@ -277,9 +311,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return <div>
|
return <div>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"Secret Storage will be set up using your existing key backup details." +
|
"Secret Storage will be set up using your existing key backup details. " +
|
||||||
"Your secret storage passphrase and recovery key will be the same as " +
|
"Your secret storage passphrase and recovery key will be the same as " +
|
||||||
" they were for your key backup",
|
"they were for your key backup.",
|
||||||
)}</p>
|
)}</p>
|
||||||
<DialogButtons primaryButton={_t('Next')}
|
<DialogButtons primaryButton={_t('Next')}
|
||||||
onPrimaryButtonClick={this._onMigrateNextClick}
|
onPrimaryButtonClick={this._onMigrateNextClick}
|
||||||
|
@ -527,6 +561,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
|
|
||||||
_titleForPhase(phase) {
|
_titleForPhase(phase) {
|
||||||
switch (phase) {
|
switch (phase) {
|
||||||
|
case PHASE_RESTORE_KEY_BACKUP:
|
||||||
|
return _t('Restore your Key Backup');
|
||||||
case PHASE_MIGRATE:
|
case PHASE_MIGRATE:
|
||||||
return _t('Migrate from Key Backup');
|
return _t('Migrate from Key Backup');
|
||||||
case PHASE_PASSPHRASE:
|
case PHASE_PASSPHRASE:
|
||||||
|
@ -569,6 +605,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
||||||
case PHASE_LOADING:
|
case PHASE_LOADING:
|
||||||
content = this._renderBusyPhase();
|
content = this._renderBusyPhase();
|
||||||
break;
|
break;
|
||||||
|
case PHASE_RESTORE_KEY_BACKUP:
|
||||||
|
content = this._renderPhaseRestoreKeyBackup();
|
||||||
|
break;
|
||||||
case PHASE_MIGRATE:
|
case PHASE_MIGRATE:
|
||||||
content = this._renderPhaseMigrate();
|
content = this._renderPhaseMigrate();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -18,10 +18,10 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../languageHandler';
|
import { _t } from '../languageHandler';
|
||||||
import AutocompleteProvider from './AutocompleteProvider';
|
import AutocompleteProvider from './AutocompleteProvider';
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import {MatrixClientPeg} from '../MatrixClientPeg';
|
||||||
import QueryMatcher from './QueryMatcher';
|
import QueryMatcher from './QueryMatcher';
|
||||||
import {PillCompletion} from './Components';
|
import {PillCompletion} from './Components';
|
||||||
import sdk from '../index';
|
import * as sdk from '../index';
|
||||||
import _sortBy from 'lodash/sortBy';
|
import _sortBy from 'lodash/sortBy';
|
||||||
import {makeGroupPermalink} from "../utils/permalinks/Permalinks";
|
import {makeGroupPermalink} from "../utils/permalinks/Permalinks";
|
||||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
|
@ -46,7 +46,7 @@ export default class CommunityProvider extends AutocompleteProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletions(query: string, selection: SelectionRange, force?: boolean = false): Array<Completion> {
|
async getCompletions(query: string, selection: SelectionRange, force: boolean = false): Array<Completion> {
|
||||||
const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar');
|
||||||
|
|
||||||
// Disable autocompletions when composing commands because of various issues
|
// Disable autocompletions when composing commands because of various issues
|
||||||
|
|
|
@ -37,7 +37,7 @@ export default class DuckDuckGoProvider extends AutocompleteProvider {
|
||||||
+ `&format=json&no_redirect=1&no_html=1&t=${encodeURIComponent(REFERRER)}`;
|
+ `&format=json&no_redirect=1&no_html=1&t=${encodeURIComponent(REFERRER)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletions(query: string, selection: SelectionRange, force?: boolean = false) {
|
async getCompletions(query: string, selection: SelectionRange, force: boolean = false) {
|
||||||
const {command, range} = this.getCurrentCommand(query, selection);
|
const {command, range} = this.getCurrentCommand(query, selection);
|
||||||
if (!query || !command) {
|
if (!query || !command) {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -17,9 +17,9 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AutocompleteProvider from './AutocompleteProvider';
|
import AutocompleteProvider from './AutocompleteProvider';
|
||||||
import { _t } from '../languageHandler';
|
import { _t } from '../languageHandler';
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import {MatrixClientPeg} from '../MatrixClientPeg';
|
||||||
import {PillCompletion} from './Components';
|
import {PillCompletion} from './Components';
|
||||||
import sdk from '../index';
|
import * as sdk from '../index';
|
||||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
|
|
||||||
const AT_ROOM_REGEX = /@\S*/g;
|
const AT_ROOM_REGEX = /@\S*/g;
|
||||||
|
@ -30,7 +30,7 @@ export default class NotifProvider extends AutocompleteProvider {
|
||||||
this.room = room;
|
this.room = room;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletions(query: string, selection: SelectionRange, force?:boolean = false): Array<Completion> {
|
async getCompletions(query: string, selection: SelectionRange, force:boolean = false): Array<Completion> {
|
||||||
const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar');
|
const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar');
|
||||||
|
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018 New Vector 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Based originally on slate-plain-serializer
|
|
||||||
|
|
||||||
import { Block } from 'slate';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plain text serializer, which converts a Slate `value` to a plain text string,
|
|
||||||
* serializing pills into various different formats as required.
|
|
||||||
*
|
|
||||||
* @type {PlainWithPillsSerializer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
class PlainWithPillsSerializer {
|
|
||||||
/*
|
|
||||||
* @param {String} options.pillFormat - either 'md', 'plain', 'id'
|
|
||||||
*/
|
|
||||||
constructor(options = {}) {
|
|
||||||
const {
|
|
||||||
pillFormat = 'plain',
|
|
||||||
} = options;
|
|
||||||
this.pillFormat = pillFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a Slate `value` to a plain text string,
|
|
||||||
* serializing pills as either MD links, plain text representations or
|
|
||||||
* ID representations as required.
|
|
||||||
*
|
|
||||||
* @param {Value} value
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
serialize = value => {
|
|
||||||
return this._serializeNode(value.document);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize a `node` to plain text.
|
|
||||||
*
|
|
||||||
* @param {Node} node
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
_serializeNode = node => {
|
|
||||||
if (
|
|
||||||
node.object == 'document' ||
|
|
||||||
(node.object == 'block' && Block.isBlockList(node.nodes))
|
|
||||||
) {
|
|
||||||
return node.nodes.map(this._serializeNode).join('\n');
|
|
||||||
} else if (node.type == 'emoji') {
|
|
||||||
return node.data.get('emojiUnicode');
|
|
||||||
} else if (node.type == 'pill') {
|
|
||||||
const completion = node.data.get('completion');
|
|
||||||
// over the wire the @room pill is just plaintext
|
|
||||||
if (completion === '@room') return completion;
|
|
||||||
|
|
||||||
switch (this.pillFormat) {
|
|
||||||
case 'plain':
|
|
||||||
return completion;
|
|
||||||
case 'md':
|
|
||||||
return `[${ completion }](${ node.data.get('href') })`;
|
|
||||||
case 'id':
|
|
||||||
return node.data.get('completionId') || completion;
|
|
||||||
}
|
|
||||||
} else if (node.nodes) {
|
|
||||||
return node.nodes.map(this._serializeNode).join('');
|
|
||||||
} else {
|
|
||||||
return node.text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export.
|
|
||||||
*
|
|
||||||
* @type {PlainWithPillsSerializer}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default PlainWithPillsSerializer;
|
|
|
@ -20,11 +20,11 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../languageHandler';
|
import { _t } from '../languageHandler';
|
||||||
import AutocompleteProvider from './AutocompleteProvider';
|
import AutocompleteProvider from './AutocompleteProvider';
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import {MatrixClientPeg} from '../MatrixClientPeg';
|
||||||
import QueryMatcher from './QueryMatcher';
|
import QueryMatcher from './QueryMatcher';
|
||||||
import {PillCompletion} from './Components';
|
import {PillCompletion} from './Components';
|
||||||
import {getDisplayAliasForRoom} from '../Rooms';
|
import {getDisplayAliasForRoom} from '../Rooms';
|
||||||
import sdk from '../index';
|
import * as sdk from '../index';
|
||||||
import _sortBy from 'lodash/sortBy';
|
import _sortBy from 'lodash/sortBy';
|
||||||
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
|
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
|
||||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
|
@ -48,7 +48,7 @@ export default class RoomProvider extends AutocompleteProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletions(query: string, selection: SelectionRange, force?: boolean = false): Array<Completion> {
|
async getCompletions(query: string, selection: SelectionRange, force: boolean = false): Array<Completion> {
|
||||||
const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar');
|
const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar');
|
||||||
|
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
|
|
@ -22,10 +22,10 @@ import React from 'react';
|
||||||
import { _t } from '../languageHandler';
|
import { _t } from '../languageHandler';
|
||||||
import AutocompleteProvider from './AutocompleteProvider';
|
import AutocompleteProvider from './AutocompleteProvider';
|
||||||
import {PillCompletion} from './Components';
|
import {PillCompletion} from './Components';
|
||||||
import sdk from '../index';
|
import * as sdk from '../index';
|
||||||
import QueryMatcher from './QueryMatcher';
|
import QueryMatcher from './QueryMatcher';
|
||||||
import _sortBy from 'lodash/sortBy';
|
import _sortBy from 'lodash/sortBy';
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import {MatrixClientPeg} from '../MatrixClientPeg';
|
||||||
|
|
||||||
import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk';
|
import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk';
|
||||||
import {makeUserPermalink} from "../utils/permalinks/Permalinks";
|
import {makeUserPermalink} from "../utils/permalinks/Permalinks";
|
||||||
|
@ -91,7 +91,7 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
this.users = null;
|
this.users = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompletions(query: string, selection: SelectionRange, force?: boolean = false): Array<Completion> {
|
async getCompletions(query: string, selection: SelectionRange, force: boolean = false): Array<Completion> {
|
||||||
const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar');
|
||||||
|
|
||||||
// lazy-load user list into matcher
|
// lazy-load user list into matcher
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -20,7 +21,7 @@ import createReactClass from 'create-react-class';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
module.exports = createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'CompatibilityPage',
|
displayName: 'CompatibilityPage',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onAccept: PropTypes.func,
|
onAccept: PropTypes.func,
|
||||||
|
|
|
@ -21,7 +21,7 @@ import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {Key} from "../../Keyboard";
|
import {Key} from "../../Keyboard";
|
||||||
import sdk from "../../index";
|
import * as sdk from "../../index";
|
||||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||||
|
|
||||||
// Shamelessly ripped off Modal.js. There's probably a better way
|
// Shamelessly ripped off Modal.js. There's probably a better way
|
||||||
|
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import CustomRoomTagStore from '../../stores/CustomRoomTagStore';
|
import CustomRoomTagStore from '../../stores/CustomRoomTagStore';
|
||||||
import AutoHideScrollbar from './AutoHideScrollbar';
|
import AutoHideScrollbar from './AutoHideScrollbar';
|
||||||
import sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as FormattingUtils from '../../utils/FormattingUtils';
|
import * as FormattingUtils from '../../utils/FormattingUtils';
|
||||||
|
|
|
@ -23,9 +23,9 @@ import PropTypes from 'prop-types';
|
||||||
import request from 'browser-request';
|
import request from 'browser-request';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import sanitizeHtml from 'sanitize-html';
|
import sanitizeHtml from 'sanitize-html';
|
||||||
import sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -19,8 +20,8 @@ import createReactClass from 'create-react-class';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
import sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -126,4 +127,4 @@ const FilePanel = createReactClass({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = FilePanel;
|
export default FilePanel;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue