Merge branches 'develop' and 'travis/download-logs' of github.com:matrix-org/matrix-react-sdk into travis/download-logs

 Conflicts:
	yarn.lock
This commit is contained in:
Michael Telatynski 2020-03-30 15:52:36 +01:00
commit 2a45ccaef3
411 changed files with 24018 additions and 11578 deletions

View file

@ -1,126 +0,0 @@
steps:
- label: ":eslint: JS Lint"
command:
- "echo '--- Install js-sdk'"
- "./scripts/ci/install-deps.sh"
- "yarn lint:js"
plugins:
- docker#v3.0.1:
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"
agents:
# We use a xlarge sized instance instead of the normal small ones because
# e2e tests otherwise take +-8min
queue: "xlarge"
command:
# TODO: Remove hacky chmod for BuildKite
- "echo '--- Setup'"
- "chmod +x ./scripts/ci/*.sh"
- "chmod +x ./scripts/*"
- "echo '--- Install js-sdk'"
- "./scripts/ci/install-deps.sh"
- "echo '--- Running initial build steps'"
- "yarn build"
- "echo '+++ Running Tests'"
- "./scripts/ci/end-to-end-tests.sh"
plugins:
- docker#v3.0.1:
image: "matrixdotorg/riotweb-ci-e2etests-env:latest"
propagate-environment: true
- label: "🔧 Riot Tests"
agents:
# We use a medium sized instance instead of the normal small ones because
# webpack loves to gorge itself on resources.
queue: "medium"
command:
# Install chrome
- "echo '--- Installing Chrome'"
- "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -"
- "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'"
- "apt-get update"
- "apt-get install -y google-chrome-stable"
# 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'"
- "./scripts/ci/riot-unit-tests.sh"
env:
CHROME_BIN: "/usr/bin/google-chrome-stable"
plugins:
- docker#v3.0.1:
image: "node:10"
propagate-environment: true
- label: "🌐 i18n"
command:
- "echo '--- Fetching Dependencies'"
- "yarn install"
- "echo '+++ Testing i18n output'"
- "yarn diff-i18n"
plugins:
- docker#v3.0.1:
image: "node:10"
- wait
- label: "🐴 Trigger riot-web"
trigger: "riot-web"
branches: "develop"
build:
branch: "develop"
message: "[react-sdk] ${BUILDKITE_MESSAGE}"
async: true

View file

@ -1 +1,4 @@
src/component-index.js
test/end-to-end-tests/node_modules/
test/end-to-end-tests/riot/
test/end-to-end-tests/synapse/

View file

@ -1,6 +1,5 @@
# autogenerated file: run scripts/generate-eslint-error-ignore-file to update.
src/component-index.js
src/components/structures/RoomDirectory.js
src/components/structures/RoomStatusBar.js
src/components/structures/RoomView.js
@ -10,7 +9,6 @@ src/components/structures/UploadBar.js
src/components/views/avatars/BaseAvatar.js
src/components/views/avatars/MemberAvatar.js
src/components/views/create_room/RoomAlias.js
src/components/views/dialogs/DeactivateAccountDialog.js
src/components/views/dialogs/SetPasswordDialog.js
src/components/views/dialogs/UnknownDeviceDialog.js
src/components/views/elements/AddressSelector.js
@ -31,7 +29,6 @@ src/components/views/rooms/MemberInfo.js
src/components/views/rooms/MemberList.js
src/components/views/rooms/RoomList.js
src/components/views/rooms/RoomPreviewBar.js
src/components/views/rooms/SearchBar.js
src/components/views/rooms/SearchResultTile.js
src/components/views/settings/ChangeAvatar.js
src/components/views/settings/ChangePassword.js
@ -44,7 +41,6 @@ src/notifications/ContentRules.js
src/notifications/PushRuleVectorState.js
src/PlatformPeg.js
src/rageshake/rageshake.js
src/rageshake/submit-rageshake.js
src/ratelimitedfunc.js
src/Rooms.js
src/Unread.js
@ -60,4 +56,7 @@ test/components/views/dialogs/InteractiveAuthDialog-test.js
test/mock-clock.js
test/notifications/ContentRules-test.js
test/notifications/PushRuleVectorState-test.js
test/stores/RoomViewStore-test.js
src/component-index.js
test/end-to-end-tests/node_modules/
test/end-to-end-tests/riot/
test/end-to-end-tests/synapse/

View file

@ -1,3 +1,994 @@
Changes in [2.3.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0) (2020-03-30)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.3.0-rc.1...v2.3.0)
* Upgrade JS SDK to 5.2.0
Changes in [2.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0-rc.1) (2020-03-26)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3...v2.3.0-rc.1)
* Upgrade JS SDK to 5.2.0-rc.1
* Add a flag to control whether cross-signing signatures are trusted
[\#4277](https://github.com/matrix-org/matrix-react-sdk/pull/4277)
* Update from Weblate
[\#4282](https://github.com/matrix-org/matrix-react-sdk/pull/4282)
* Update copy on SSSS symmetric upgrade toast
[\#4281](https://github.com/matrix-org/matrix-react-sdk/pull/4281)
* Wait for SSSS upgrade to complete
[\#4270](https://github.com/matrix-org/matrix-react-sdk/pull/4270)
* Update cross-signing verification copy and fix i18n
[\#4278](https://github.com/matrix-org/matrix-react-sdk/pull/4278)
* Fix soft-crash on bad permalinks
[\#4280](https://github.com/matrix-org/matrix-react-sdk/pull/4280)
* Fix: make self-verification wait for incoming request
[\#4267](https://github.com/matrix-org/matrix-react-sdk/pull/4267)
* Fall back to non-standard persisted api for Safari
[\#4272](https://github.com/matrix-org/matrix-react-sdk/pull/4272)
* Respond to backup key sharing requests
[\#4275](https://github.com/matrix-org/matrix-react-sdk/pull/4275)
* Log and display secret sharing cache state
[\#4268](https://github.com/matrix-org/matrix-react-sdk/pull/4268)
* Support sending config and ready events to capable widgets (Jitsi)
[\#4266](https://github.com/matrix-org/matrix-react-sdk/pull/4266)
* If cached keys are present in the key backup dialog, use them
[\#4273](https://github.com/matrix-org/matrix-react-sdk/pull/4273)
* Fix formatbar not hidden on highlighted message sent
[\#4265](https://github.com/matrix-org/matrix-react-sdk/pull/4265)
* Support Jitsi conferences sent/received on Riot Mobile and older Riot Webs
[\#4252](https://github.com/matrix-org/matrix-react-sdk/pull/4252)
* Use unified function to check cross-signing is ready
[\#4263](https://github.com/matrix-org/matrix-react-sdk/pull/4263)
* Migrate SSSS to symmetric
[\#4224](https://github.com/matrix-org/matrix-react-sdk/pull/4224)
* Migration to symmetric SSSS
[\#4242](https://github.com/matrix-org/matrix-react-sdk/pull/4242)
* Always display verification request toasts on top
[\#4262](https://github.com/matrix-org/matrix-react-sdk/pull/4262)
* Fix: assume SAS is supported when starting request with .start
[\#4249](https://github.com/matrix-org/matrix-react-sdk/pull/4249)
* Fix logout when Olm failed to load.
[\#4261](https://github.com/matrix-org/matrix-react-sdk/pull/4261)
* Improve naming of Jitsi conferences
[\#4251](https://github.com/matrix-org/matrix-react-sdk/pull/4251)
* Handle matrix.to user permalink in-room rather than solo
[\#4245](https://github.com/matrix-org/matrix-react-sdk/pull/4245)
* Fix: filter room list (again) by canonical and alternative aliases
[\#4260](https://github.com/matrix-org/matrix-react-sdk/pull/4260)
* EventIndex: Add some logging to the file panel populating.
[\#4250](https://github.com/matrix-org/matrix-react-sdk/pull/4250)
* Update from Weblate
[\#4259](https://github.com/matrix-org/matrix-react-sdk/pull/4259)
* Migrate RoomView to React Contexts in the hope for better temporal stability
[\#4258](https://github.com/matrix-org/matrix-react-sdk/pull/4258)
* Update WidgetUtils.js fix Jitsi path
[\#4256](https://github.com/matrix-org/matrix-react-sdk/pull/4256)
* Fix local jitsi build url fail and missing argument
[\#4255](https://github.com/matrix-org/matrix-react-sdk/pull/4255)
* Add shortcut CmdOrCtrl+. to toggle right panel
[\#4244](https://github.com/matrix-org/matrix-react-sdk/pull/4244)
* Improve Keyboard Shortcuts. Add alt-arrows & alt-shift-arrows
[\#4241](https://github.com/matrix-org/matrix-react-sdk/pull/4241)
* Bring back legacy verification by comparing public device keys
[\#4240](https://github.com/matrix-org/matrix-react-sdk/pull/4240)
* Searching: Return an empty result if the search term is an empty string.
[\#4248](https://github.com/matrix-org/matrix-react-sdk/pull/4248)
* Break continuation on showHiddenEvents-rendered events
[\#4247](https://github.com/matrix-org/matrix-react-sdk/pull/4247)
* Watch for show-RR settings changes, use room-specific and fix margins
[\#4246](https://github.com/matrix-org/matrix-react-sdk/pull/4246)
* Register Mac electron specific Cmd+, shortcut to User Settings
[\#4243](https://github.com/matrix-org/matrix-react-sdk/pull/4243)
* Use a local wrapper for Jitsi calls
[\#4234](https://github.com/matrix-org/matrix-react-sdk/pull/4234)
* Invite Dialog fixes
[\#4233](https://github.com/matrix-org/matrix-react-sdk/pull/4233)
* RoomPreviewBar word-break the sender name too
[\#4239](https://github.com/matrix-org/matrix-react-sdk/pull/4239)
* Report to the user when a key signature upload fails
[\#4229](https://github.com/matrix-org/matrix-react-sdk/pull/4229)
* pre-send megolm keys when possible when a user starts typing
[\#4235](https://github.com/matrix-org/matrix-react-sdk/pull/4235)
* we don't do mx_fadable anymore so get rid of broken RightPanel disabling
[\#4238](https://github.com/matrix-org/matrix-react-sdk/pull/4238)
* Fix left left panel overflowing vertically
[\#4237](https://github.com/matrix-org/matrix-react-sdk/pull/4237)
* Fix custom tags causing left panel to over-expand
[\#4236](https://github.com/matrix-org/matrix-react-sdk/pull/4236)
* Add Keyboard shortcuts dialog
[\#4231](https://github.com/matrix-org/matrix-react-sdk/pull/4231)
* Don't use buildkite agent to upload logs
[\#4232](https://github.com/matrix-org/matrix-react-sdk/pull/4232)
* Remove Gemini Scrollbars
[\#4217](https://github.com/matrix-org/matrix-react-sdk/pull/4217)
* Room Directory Explore Servers redesign
[\#4209](https://github.com/matrix-org/matrix-react-sdk/pull/4209)
* Fix redo keyboard shortcut on macOS
[\#4110](https://github.com/matrix-org/matrix-react-sdk/pull/4110)
* Fix: ensure local state for aliases doesn't get garbled up
[\#4230](https://github.com/matrix-org/matrix-react-sdk/pull/4230)
* Rename 'jump to bottom' to avoid ublock block
[\#4208](https://github.com/matrix-org/matrix-react-sdk/pull/4208)
* Restore key backup in background after complete security
[\#4225](https://github.com/matrix-org/matrix-react-sdk/pull/4225)
* Fix key backup trust text for cross-signing
[\#4223](https://github.com/matrix-org/matrix-react-sdk/pull/4223)
* Add default on config setting to control call button in composer
[\#4227](https://github.com/matrix-org/matrix-react-sdk/pull/4227)
* Fix: make alternative addresses UX less confusing
[\#4221](https://github.com/matrix-org/matrix-react-sdk/pull/4221)
* Wait for verification request on login
[\#4222](https://github.com/matrix-org/matrix-react-sdk/pull/4222)
* EventIndex: Add support to delete events from the index.
[\#4204](https://github.com/matrix-org/matrix-react-sdk/pull/4204)
* EventIndex: Remove a checkpoint if the HTTP request returns a 403.
[\#4214](https://github.com/matrix-org/matrix-react-sdk/pull/4214)
* Move to composer when typing letters with Shift held
[\#4216](https://github.com/matrix-org/matrix-react-sdk/pull/4216)
* Wrap large room names when previewing them
[\#4213](https://github.com/matrix-org/matrix-react-sdk/pull/4213)
* Rename Review Devices to Review Sessions
[\#4219](https://github.com/matrix-org/matrix-react-sdk/pull/4219)
* Fix typo in tabIndex to make React happy
[\#4215](https://github.com/matrix-org/matrix-react-sdk/pull/4215)
* Proof of concept for custom theme adding
[\#4148](https://github.com/matrix-org/matrix-react-sdk/pull/4148)
* Remove stuff that yarn install doesn't think we need
[\#4205](https://github.com/matrix-org/matrix-react-sdk/pull/4205)
* Declare jsx in tsconfig for IDEs
[\#4207](https://github.com/matrix-org/matrix-react-sdk/pull/4207)
* Fix: best-effort to join room without canonical alias over federation from
room directory
[\#4210](https://github.com/matrix-org/matrix-react-sdk/pull/4210)
* Test for cross-signing homeserver support during login, toasts
[\#4206](https://github.com/matrix-org/matrix-react-sdk/pull/4206)
* Send verification request to a single device in a way compatible with non-
cross-signing
[\#4202](https://github.com/matrix-org/matrix-react-sdk/pull/4202)
* Fixes for removing local alias
[\#4199](https://github.com/matrix-org/matrix-react-sdk/pull/4199)
* yarn upgrade
[\#4201](https://github.com/matrix-org/matrix-react-sdk/pull/4201)
* Support TypeScript for React components
[\#4203](https://github.com/matrix-org/matrix-react-sdk/pull/4203)
* When room name is changed, show both the old and new name
[\#4183](https://github.com/matrix-org/matrix-react-sdk/pull/4183)
Changes in [2.2.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.3) (2020-03-17)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3-rc.1...v2.2.3)
* Upgrade JS SDK to 5.1.1
* Add default on config setting to control call button in composer
[\#4228](https://github.com/matrix-org/matrix-react-sdk/pull/4228)
* Fix: make alternative addresses UX less confusing
[\#4226](https://github.com/matrix-org/matrix-react-sdk/pull/4226)
* Fix: best-effort to join room without canonical alias over federation from
room directory
[\#4211](https://github.com/matrix-org/matrix-react-sdk/pull/4211)
Changes in [2.2.3-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.3-rc.1) (2020-03-11)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.1...v2.2.3-rc.1)
* Update from Weblate
[\#4200](https://github.com/matrix-org/matrix-react-sdk/pull/4200)
* Revert "enable 4s when accepting a verification request"
[\#4198](https://github.com/matrix-org/matrix-react-sdk/pull/4198)
* Don't remount main split children on rhs collapse
[\#4197](https://github.com/matrix-org/matrix-react-sdk/pull/4197)
* Add fallback label for canonical alias events that dont change anything
[\#4195](https://github.com/matrix-org/matrix-react-sdk/pull/4195)
* Immediately switch to verification dialog when clicking [Continue] from new
session dialog
[\#4196](https://github.com/matrix-org/matrix-react-sdk/pull/4196)
* Enable 4S if needed when trying to verify or accepting verification
[\#4194](https://github.com/matrix-org/matrix-react-sdk/pull/4194)
* Remove extraneous tab stop from room tree view.
[\#4193](https://github.com/matrix-org/matrix-react-sdk/pull/4193)
* Remove v1 identity server fallbacks
[\#4191](https://github.com/matrix-org/matrix-react-sdk/pull/4191)
* Allow editing of alt_aliases according to MSC2432
[\#4187](https://github.com/matrix-org/matrix-react-sdk/pull/4187)
* Update timeline rendering of aliases
[\#4189](https://github.com/matrix-org/matrix-react-sdk/pull/4189)
* Fix mark as read button for dark theme
[\#4190](https://github.com/matrix-org/matrix-react-sdk/pull/4190)
* Un-linkify version in settings
[\#4188](https://github.com/matrix-org/matrix-react-sdk/pull/4188)
* Make Mjolnir stop more robust
[\#4186](https://github.com/matrix-org/matrix-react-sdk/pull/4186)
* Fix secret sharing names to match spec
[\#4185](https://github.com/matrix-org/matrix-react-sdk/pull/4185)
* Share secrets with another device on request
[\#4172](https://github.com/matrix-org/matrix-react-sdk/pull/4172)
* Fall back to to_device verification if other user hasn't uploaded cross-
signing keys
[\#4181](https://github.com/matrix-org/matrix-react-sdk/pull/4181)
* Disable edits on redacted events
[\#4182](https://github.com/matrix-org/matrix-react-sdk/pull/4182)
* Use crypto.verification.request even when xsign is disabled
[\#4180](https://github.com/matrix-org/matrix-react-sdk/pull/4180)
* Reword the status for the currently indexing rooms.
[\#4084](https://github.com/matrix-org/matrix-react-sdk/pull/4084)
* Moved read receipts to the bottom of the message
[\#3892](https://github.com/matrix-org/matrix-react-sdk/pull/3892)
* Include a mark as read X under the scroll to unread button
[\#4159](https://github.com/matrix-org/matrix-react-sdk/pull/4159)
* Show the room presence indicator, even when cross-singing is enabled
[\#4178](https://github.com/matrix-org/matrix-react-sdk/pull/4178)
* Add local echo when clicking "Manually Verify" in unverified session dialog
[\#4179](https://github.com/matrix-org/matrix-react-sdk/pull/4179)
* link to matrix.org/security-disclosure-policy in help screen
[\#4129](https://github.com/matrix-org/matrix-react-sdk/pull/4129)
* only show verify button if user has uploaded cross-signing keys
[\#4174](https://github.com/matrix-org/matrix-react-sdk/pull/4174)
* Fix room alias references in topics
[\#4176](https://github.com/matrix-org/matrix-react-sdk/pull/4176)
* Fix not being able to start chats when you have no rooms
[\#4177](https://github.com/matrix-org/matrix-react-sdk/pull/4177)
* Disable registration flows on SSO servers
[\#4170](https://github.com/matrix-org/matrix-react-sdk/pull/4170)
* Don't group blank membership changes
[\#4160](https://github.com/matrix-org/matrix-react-sdk/pull/4160)
* Ensure the room list always triggers updates on itself
[\#4175](https://github.com/matrix-org/matrix-react-sdk/pull/4175)
* Fix composer touch bar flickering on keypress in Chrome
[\#4173](https://github.com/matrix-org/matrix-react-sdk/pull/4173)
* Document scrollpanel and BACAT scrolling
[\#4167](https://github.com/matrix-org/matrix-react-sdk/pull/4167)
* riot-desktop open SSO in browser so user doesn't have to auth twice
[\#4158](https://github.com/matrix-org/matrix-react-sdk/pull/4158)
* Lock login and registration buttons after submit
[\#4165](https://github.com/matrix-org/matrix-react-sdk/pull/4165)
* Suggest the server's results as lower quality in the invite dialog
[\#4149](https://github.com/matrix-org/matrix-react-sdk/pull/4149)
* Adjust scroll offset with relative scrolling
[\#4166](https://github.com/matrix-org/matrix-react-sdk/pull/4166)
* only automatically download in usercontent if user requested it
[\#4163](https://github.com/matrix-org/matrix-react-sdk/pull/4163)
* Fix having to decrypt & download in two steps
[\#4162](https://github.com/matrix-org/matrix-react-sdk/pull/4162)
* Use bash for release script
[\#4161](https://github.com/matrix-org/matrix-react-sdk/pull/4161)
* Revert to manual sorting for custom tag rooms
[\#4157](https://github.com/matrix-org/matrix-react-sdk/pull/4157)
* Fix the last char of people's names being cut off in the invite dialog
[\#4150](https://github.com/matrix-org/matrix-react-sdk/pull/4150)
* Add /whois SlashCommand to open UserInfo
[\#4154](https://github.com/matrix-org/matrix-react-sdk/pull/4154)
* word-break in pills and wrap the background correctly
[\#4155](https://github.com/matrix-org/matrix-react-sdk/pull/4155)
* don't show "This alias is available to use" if the alias is invalid
[\#4153](https://github.com/matrix-org/matrix-react-sdk/pull/4153)
* Don't ask to enable analytics when Do Not Track is enabled
[\#4098](https://github.com/matrix-org/matrix-react-sdk/pull/4098)
* Fix MELS not breaking on day boundaries regression
[\#4152](https://github.com/matrix-org/matrix-react-sdk/pull/4152)
* Fix Quote on search results page
[\#4151](https://github.com/matrix-org/matrix-react-sdk/pull/4151)
* Ensure errors when creating a DM are raised to the user
[\#4144](https://github.com/matrix-org/matrix-react-sdk/pull/4144)
* Add a Login button to startAnyRegistrationFlow
[\#3829](https://github.com/matrix-org/matrix-react-sdk/pull/3829)
* Use latest backup status directly rather than via state
[\#4147](https://github.com/matrix-org/matrix-react-sdk/pull/4147)
* Prefer account password variation of upgrading
[\#4146](https://github.com/matrix-org/matrix-react-sdk/pull/4146)
* Hide user avatars from screen readers in group and room user lists.
[\#4145](https://github.com/matrix-org/matrix-react-sdk/pull/4145)
* Room List sorting algorithms
[\#4085](https://github.com/matrix-org/matrix-react-sdk/pull/4085)
* Clear selected tags when disabling tag panel
[\#4143](https://github.com/matrix-org/matrix-react-sdk/pull/4143)
* Ignore cursor jumping shortcuts with shift
[\#4142](https://github.com/matrix-org/matrix-react-sdk/pull/4142)
* add local echo for clicking 'start verification' button
[\#4138](https://github.com/matrix-org/matrix-react-sdk/pull/4138)
* Fix formatting buttons not marking the composer as modified
[\#4141](https://github.com/matrix-org/matrix-react-sdk/pull/4141)
* Upgrade deps
[\#4136](https://github.com/matrix-org/matrix-react-sdk/pull/4136)
* Remove debug line from Analytics
[\#4137](https://github.com/matrix-org/matrix-react-sdk/pull/4137)
* Use the right function for creating binary verification QR codes
[\#4140](https://github.com/matrix-org/matrix-react-sdk/pull/4140)
* Ensure verification QR codes use the right buffer size
[\#4139](https://github.com/matrix-org/matrix-react-sdk/pull/4139)
* Don't prefix QR codes with the length of the static marker string
[\#4128](https://github.com/matrix-org/matrix-react-sdk/pull/4128)
* Solve fixed-width digit display in flowed text
[\#4127](https://github.com/matrix-org/matrix-react-sdk/pull/4127)
* Limit UserInfo Displayname to 3 lines to get rid of scrollbars
[\#4135](https://github.com/matrix-org/matrix-react-sdk/pull/4135)
Changes in [2.2.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.1) (2020-03-04)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.0...v2.2.1)
* Adjust scroll offset with relative scrolling
[\#4171](https://github.com/matrix-org/matrix-react-sdk/pull/4171)
* Disable registration flows on SSO servers
[\#4169](https://github.com/matrix-org/matrix-react-sdk/pull/4169)
Changes in [2.2.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.0) (2020-03-02)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.0-rc.1...v2.2.0)
* Upgrade JS SDK to 5.1.0
* Ignore cursor jumping shortcuts with shift
[\#4142](https://github.com/matrix-org/matrix-react-sdk/pull/4142)
Changes in [2.2.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.0-rc.1) (2020-02-26)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.1.1...v2.2.0-rc.1)
* Upgrade JS SDK to 5.1.0-rc.1
* Fix message context menu breaking on invalid m.room.pinned_events event
[\#4133](https://github.com/matrix-org/matrix-react-sdk/pull/4133)
* Update from Weblate
[\#4134](https://github.com/matrix-org/matrix-react-sdk/pull/4134)
* Notify platform of language changes
[\#4121](https://github.com/matrix-org/matrix-react-sdk/pull/4121)
* Handle errors when previewing rooms more safely
[\#4132](https://github.com/matrix-org/matrix-react-sdk/pull/4132)
* Don't try to collapse zero events with a group
[\#4131](https://github.com/matrix-org/matrix-react-sdk/pull/4131)
* Don't print errors when the tab is used with no autocomplete present
[\#4130](https://github.com/matrix-org/matrix-react-sdk/pull/4130)
* Improve UI feedback while waiting for network
[\#4126](https://github.com/matrix-org/matrix-react-sdk/pull/4126)
* Ensure DMs tagged outside of account data work in the invite dialog
[\#4123](https://github.com/matrix-org/matrix-react-sdk/pull/4123)
* Show a warning dialog when user indicates a new session wasn't them
[\#4125](https://github.com/matrix-org/matrix-react-sdk/pull/4125)
* Show cancel events as hidden events if we wouldn't usually render them
[\#4120](https://github.com/matrix-org/matrix-react-sdk/pull/4120)
* Collapsed room list has unaligned room tiles #4030 version 2
[\#4033](https://github.com/matrix-org/matrix-react-sdk/pull/4033)
* Check for cross-signing homeserver support
[\#4118](https://github.com/matrix-org/matrix-react-sdk/pull/4118)
* Don't leak if show_sas never comes (or already came)
[\#4119](https://github.com/matrix-org/matrix-react-sdk/pull/4119)
* Add verification request viewer in devtools
[\#4106](https://github.com/matrix-org/matrix-react-sdk/pull/4106)
* update phase when request prop changes
[\#4117](https://github.com/matrix-org/matrix-react-sdk/pull/4117)
* Handle file downloading locally in electron rather than sending to browser
[\#4113](https://github.com/matrix-org/matrix-react-sdk/pull/4113)
* Remove unused CIDER setting watcher
[\#4116](https://github.com/matrix-org/matrix-react-sdk/pull/4116)
* Use alt_aliases for pills and autocomplete
[\#4102](https://github.com/matrix-org/matrix-react-sdk/pull/4102)
* Add shortcuts for beginning / end of composer
[\#4108](https://github.com/matrix-org/matrix-react-sdk/pull/4108)
* Update from Weblate
[\#4115](https://github.com/matrix-org/matrix-react-sdk/pull/4115)
* Revert "Fix escaped markdown passing backslashes through"
[\#4114](https://github.com/matrix-org/matrix-react-sdk/pull/4114)
* Fix a couple of React warnings/errors
[\#4112](https://github.com/matrix-org/matrix-react-sdk/pull/4112)
* Fix two big DOM leaks which were locking Chrome solid.
[\#4111](https://github.com/matrix-org/matrix-react-sdk/pull/4111)
* Filter out empty strings when pasting IDs into the invite dialog
[\#4109](https://github.com/matrix-org/matrix-react-sdk/pull/4109)
* Remove buildkite pipeline
[\#4107](https://github.com/matrix-org/matrix-react-sdk/pull/4107)
* Use binary packing for verification QR codes
[\#4091](https://github.com/matrix-org/matrix-react-sdk/pull/4091)
* Fix several small bugs with the invite/DM dialog
[\#4099](https://github.com/matrix-org/matrix-react-sdk/pull/4099)
* ignore e2e tests node_modules during linting
[\#4103](https://github.com/matrix-org/matrix-react-sdk/pull/4103)
* Apply null-guard to room pills for when we can't fetch the room
[\#4104](https://github.com/matrix-org/matrix-react-sdk/pull/4104)
* Fix theme being overridden to light even after login is completed
[\#4105](https://github.com/matrix-org/matrix-react-sdk/pull/4105)
* Fix bug where SSSS could be overwritten if user never cross-signs
[\#4100](https://github.com/matrix-org/matrix-react-sdk/pull/4100)
* Accept canonical alias for pills
[\#4096](https://github.com/matrix-org/matrix-react-sdk/pull/4096)
* Fix: don't advertise ability to scan a QR code for verification
[\#4094](https://github.com/matrix-org/matrix-react-sdk/pull/4094)
* Fixes for printing event indexing stats.
[\#4082](https://github.com/matrix-org/matrix-react-sdk/pull/4082)
* Remove exec so release script continues
[\#4095](https://github.com/matrix-org/matrix-react-sdk/pull/4095)
* Use Persistent Storage where possible
[\#4092](https://github.com/matrix-org/matrix-react-sdk/pull/4092)
* Fix user page (missing null check)
[\#4088](https://github.com/matrix-org/matrix-react-sdk/pull/4088)
* Cancel verification request on dialog close
[\#4081](https://github.com/matrix-org/matrix-react-sdk/pull/4081)
* Fix various memory leaks due to method re-binding
[\#4093](https://github.com/matrix-org/matrix-react-sdk/pull/4093)
* Fix share message context menu option keyboard a11y
[\#4073](https://github.com/matrix-org/matrix-react-sdk/pull/4073)
Changes in [2.1.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.1.1) (2020-02-19)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.1.0...v2.1.1)
* show spinner while loading local aliases
[\#4090](https://github.com/matrix-org/matrix-react-sdk/pull/4090)
* Don't index key verification events.
[\#4083](https://github.com/matrix-org/matrix-react-sdk/pull/4083)
* Get rid of dependence on usercontent.riot.im
[\#4046](https://github.com/matrix-org/matrix-react-sdk/pull/4046)
* also detect aliases using new /aliases endpoint for room access settings
[\#4089](https://github.com/matrix-org/matrix-react-sdk/pull/4089)
* get local aliases from /aliases in room settings
[\#4086](https://github.com/matrix-org/matrix-react-sdk/pull/4086)
* Start verification sessions in an E2E DM where possible
[\#4080](https://github.com/matrix-org/matrix-react-sdk/pull/4080)
* Only show supported verification methods
[\#4077](https://github.com/matrix-org/matrix-react-sdk/pull/4077)
* Use local echo in VerificationRequest for accepting/declining a verification
request
[\#4072](https://github.com/matrix-org/matrix-react-sdk/pull/4072)
* Report installed PWA, touch input status in rageshakes, analytics
[\#4078](https://github.com/matrix-org/matrix-react-sdk/pull/4078)
* refactor event grouping into separate helper classes
[\#4059](https://github.com/matrix-org/matrix-react-sdk/pull/4059)
* Find existing requests when starting a new verification request
[\#4070](https://github.com/matrix-org/matrix-react-sdk/pull/4070)
* Always speak the full text of the typing indicator when it updates.
[\#4074](https://github.com/matrix-org/matrix-react-sdk/pull/4074)
* Fix escaped markdown passing backslashes through
[\#4008](https://github.com/matrix-org/matrix-react-sdk/pull/4008)
* Move the sidebar to below the sidebar tab buttons for screen readers.
[\#4071](https://github.com/matrix-org/matrix-react-sdk/pull/4071)
Changes in [2.1.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.1.0) (2020-02-17)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.1.0-rc.2...v2.1.0)
* Automate SDK dep upgrades for release
[\#4076](https://github.com/matrix-org/matrix-react-sdk/pull/4076)
Changes in [2.1.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.1.0-rc.2) (2020-02-13)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.1.0-rc.1...v2.1.0-rc.2)
* Fix error in previous attempt to upgrade JS SDK
Changes in [2.1.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.1.0-rc.1) (2020-02-13)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.0.0...v2.1.0-rc.1)
* Upgrade JS SDK to 5.0.0-rc.1
* don't show tooltips on big icons
[\#4067](https://github.com/matrix-org/matrix-react-sdk/pull/4067)
* Update from Weblate
[\#4069](https://github.com/matrix-org/matrix-react-sdk/pull/4069)
* Fix sending of visit variables to Matomo
[\#4068](https://github.com/matrix-org/matrix-react-sdk/pull/4068)
* Use embedded piwik script rather than piwik.js to respect CSP
[\#4066](https://github.com/matrix-org/matrix-react-sdk/pull/4066)
* remove methods arg to requestVerification(DM)
[\#4058](https://github.com/matrix-org/matrix-react-sdk/pull/4058)
* Check for null config settings a bit safer
[\#4061](https://github.com/matrix-org/matrix-react-sdk/pull/4061)
* Score user ID searches higher when they match nearly exactly
[\#4060](https://github.com/matrix-org/matrix-react-sdk/pull/4060)
* Fix uncentered letter inside avatar for currently typing users
[\#4051](https://github.com/matrix-org/matrix-react-sdk/pull/4051)
* Disable 'start' button after clicking in VerificationPanel
[\#4065](https://github.com/matrix-org/matrix-react-sdk/pull/4065)
* Fixed bug where key reset didn't always return the right key
[\#4057](https://github.com/matrix-org/matrix-react-sdk/pull/4057)
* Don't render avatars in pills for screen readers.
[\#4062](https://github.com/matrix-org/matrix-react-sdk/pull/4062)
* Make QR self-verification compatible with RiotX
[\#4044](https://github.com/matrix-org/matrix-react-sdk/pull/4044)
* Verify single device from other user in right panel & Not Trusted dialog
[\#4043](https://github.com/matrix-org/matrix-react-sdk/pull/4043)
* Disable verification buttons after clicking to avoid double submission
[\#4049](https://github.com/matrix-org/matrix-react-sdk/pull/4049)
* Verification toast fixes
[\#4048](https://github.com/matrix-org/matrix-react-sdk/pull/4048)
* Use EncryptionPanel everywhere, part I
[\#4042](https://github.com/matrix-org/matrix-react-sdk/pull/4042)
* quick fix for cross-signing reset bug
[\#4056](https://github.com/matrix-org/matrix-react-sdk/pull/4056)
* Fix error message rendering for key entry
[\#4055](https://github.com/matrix-org/matrix-react-sdk/pull/4055)
* Fix recaptcha blocked by CSP for non-SSL origins
[\#4052](https://github.com/matrix-org/matrix-react-sdk/pull/4052)
* Fix watcher for showTypingNotifications setting
[\#4054](https://github.com/matrix-org/matrix-react-sdk/pull/4054)
* Allow custom hs url submission on enter
[\#4053](https://github.com/matrix-org/matrix-react-sdk/pull/4053)
* Support keepSecretStoragePassphraseForSession at the config level too
[\#4045](https://github.com/matrix-org/matrix-react-sdk/pull/4045)
* Add setting to allow hiding of typing indicator
[\#4047](https://github.com/matrix-org/matrix-react-sdk/pull/4047)
* Button to reset cross-signing and SSSS keys
[\#4041](https://github.com/matrix-org/matrix-react-sdk/pull/4041)
* Use forms to wrap password fields so Chrome doesn't go wild
[\#3974](https://github.com/matrix-org/matrix-react-sdk/pull/3974)
* Update QR code rendering to support VerificationRequests
[\#4001](https://github.com/matrix-org/matrix-react-sdk/pull/4001)
* Differentiate AccessSecretStorageDialog dismiss dialog based on which key we
want to read
[\#4038](https://github.com/matrix-org/matrix-react-sdk/pull/4038)
* Only emit in RoomViewStore when state actually changes
[\#4039](https://github.com/matrix-org/matrix-react-sdk/pull/4039)
* Mark AccessSecretStorageDialog to not be closed by clicking background
[\#4029](https://github.com/matrix-org/matrix-react-sdk/pull/4029)
* Let pointer events fall through to scroll button
[\#4037](https://github.com/matrix-org/matrix-react-sdk/pull/4037)
* Improve event indexing status strings for translation
[\#4035](https://github.com/matrix-org/matrix-react-sdk/pull/4035)
* Button size reviewed for word consuming languages & Settings showing devices
are a bit too tight
[\#4024](https://github.com/matrix-org/matrix-react-sdk/pull/4024)
* Only enumerate settings handlers which are supported
[\#4034](https://github.com/matrix-org/matrix-react-sdk/pull/4034)
* Fix listener removal in verification tile
[\#4036](https://github.com/matrix-org/matrix-react-sdk/pull/4036)
* Do not show alarming red shields on large encrypted rooms for your own
device
[\#4028](https://github.com/matrix-org/matrix-react-sdk/pull/4028)
* Add a class for styling room directory permissions
[\#4007](https://github.com/matrix-org/matrix-react-sdk/pull/4007)
* double-check user verification
[\#4010](https://github.com/matrix-org/matrix-react-sdk/pull/4010)
* Use minimist instead of optimist as it is deprecated
[\#4031](https://github.com/matrix-org/matrix-react-sdk/pull/4031)
* SettingsStore, use a counter instead of wall clock for watcher ids
[\#4032](https://github.com/matrix-org/matrix-react-sdk/pull/4032)
* Don't crash immediately if the room directory chunk is null/empty
[\#4027](https://github.com/matrix-org/matrix-react-sdk/pull/4027)
* Fix verification toast to close at 0s
[\#3998](https://github.com/matrix-org/matrix-react-sdk/pull/3998)
* Fix listener leak in TagPanel
[\#4026](https://github.com/matrix-org/matrix-react-sdk/pull/4026)
* Update from Weblate
[\#4025](https://github.com/matrix-org/matrix-react-sdk/pull/4025)
* Honour the isLogin flag in theme.js
[\#4023](https://github.com/matrix-org/matrix-react-sdk/pull/4023)
* ManageEventIndexDialog: Show how many rooms are being currently crawled.
[\#4022](https://github.com/matrix-org/matrix-react-sdk/pull/4022)
* Advertise that we can scan QR codes even though we can't
[\#4021](https://github.com/matrix-org/matrix-react-sdk/pull/4021)
* Checkpoint addition fixes and return of the crawler sleep time setting.
[\#4020](https://github.com/matrix-org/matrix-react-sdk/pull/4020)
* Truncate SAS emoji labels to fit
[\#4018](https://github.com/matrix-org/matrix-react-sdk/pull/4018)
* Apply copy edits to security setup flow
[\#4017](https://github.com/matrix-org/matrix-react-sdk/pull/4017)
* Fix user trust text to match what was checked
[\#4016](https://github.com/matrix-org/matrix-react-sdk/pull/4016)
* Fix size of invite only icon
[\#4015](https://github.com/matrix-org/matrix-react-sdk/pull/4015)
* Add temporary feature flag to control padlocks
[\#4013](https://github.com/matrix-org/matrix-react-sdk/pull/4013)
* Add an override for the theme
[\#4014](https://github.com/matrix-org/matrix-react-sdk/pull/4014)
* Add title to complete security loading
[\#4011](https://github.com/matrix-org/matrix-react-sdk/pull/4011)
* Only display the first zxcvbn warning/suggestion
[\#4012](https://github.com/matrix-org/matrix-react-sdk/pull/4012)
* Log exceptions from accessSecretStorage
[\#4009](https://github.com/matrix-org/matrix-react-sdk/pull/4009)
* Add advanced option to keep secret storage in memory for session
[\#3995](https://github.com/matrix-org/matrix-react-sdk/pull/3995)
* Add shields to member list, move power label to text
[\#4006](https://github.com/matrix-org/matrix-react-sdk/pull/4006)
* Make encryption events into bubble-style tiles
[\#4005](https://github.com/matrix-org/matrix-react-sdk/pull/4005)
* Update copy when the user verifies their own devices
[\#4000](https://github.com/matrix-org/matrix-react-sdk/pull/4000)
* Use Sets instead of array scans and simplify hiding of invalid users when
inviting
[\#4004](https://github.com/matrix-org/matrix-react-sdk/pull/4004)
* Fix room completion for invited rooms and upgraded rooms
[\#4003](https://github.com/matrix-org/matrix-react-sdk/pull/4003)
* Make shields in UserInfo black if user isn't verified
[\#3999](https://github.com/matrix-org/matrix-react-sdk/pull/3999)
* Change verify user text
[\#3994](https://github.com/matrix-org/matrix-react-sdk/pull/3994)
* Disable all inputs in login form while busy, not just the submit button
[\#3996](https://github.com/matrix-org/matrix-react-sdk/pull/3996)
* fix SAS dialog width
[\#3993](https://github.com/matrix-org/matrix-react-sdk/pull/3993)
* Update placeholder in the composer when it gets changed
[\#3990](https://github.com/matrix-org/matrix-react-sdk/pull/3990)
* Send initial device display name on register
[\#3992](https://github.com/matrix-org/matrix-react-sdk/pull/3992)
* Update QR code handling for new spec
[\#3959](https://github.com/matrix-org/matrix-react-sdk/pull/3959)
* Apply the Olympic effect to SAS Emoji Verification
[\#3989](https://github.com/matrix-org/matrix-react-sdk/pull/3989)
* Pass an ID to the <Field/> as needed and fix div inside p nesting
[\#3988](https://github.com/matrix-org/matrix-react-sdk/pull/3988)
* Update user info for device and trust changes
[\#3987](https://github.com/matrix-org/matrix-react-sdk/pull/3987)
* Relax secret storage account data check
[\#3985](https://github.com/matrix-org/matrix-react-sdk/pull/3985)
* Fix various races that prevented the right panel being in the right state
for verifications
[\#3984](https://github.com/matrix-org/matrix-react-sdk/pull/3984)
* Fix verifying individual devices
[\#3986](https://github.com/matrix-org/matrix-react-sdk/pull/3986)
* Update from Weblate
[\#3982](https://github.com/matrix-org/matrix-react-sdk/pull/3982)
* Replace device with session in UI text
[\#3980](https://github.com/matrix-org/matrix-react-sdk/pull/3980)
* Add missing await causing promises to be leaked as room IDs
[\#3981](https://github.com/matrix-org/matrix-react-sdk/pull/3981)
* Change new session toast to unverified
[\#3978](https://github.com/matrix-org/matrix-react-sdk/pull/3978)
* Replace Verify button in UserInfo verification with "Learn more"
[\#3975](https://github.com/matrix-org/matrix-react-sdk/pull/3975)
* Don't peek until the matrix client is ready
[\#3979](https://github.com/matrix-org/matrix-react-sdk/pull/3979)
* Verification: don't block UI update on verification finishing
[\#3976](https://github.com/matrix-org/matrix-react-sdk/pull/3976)
* Adjust icons with in person with design
[\#3977](https://github.com/matrix-org/matrix-react-sdk/pull/3977)
* Update copy for right panel verification
[\#3973](https://github.com/matrix-org/matrix-react-sdk/pull/3973)
* Check for timeline in pre-join UISI path
[\#3972](https://github.com/matrix-org/matrix-react-sdk/pull/3972)
* Let users paste text if they've already started filtering invite targets
[\#3970](https://github.com/matrix-org/matrix-react-sdk/pull/3970)
* Filter event types when deciding on activity metrics for DM suggestions
[\#3969](https://github.com/matrix-org/matrix-react-sdk/pull/3969)
* Revert a change causing a login loop
[\#3971](https://github.com/matrix-org/matrix-react-sdk/pull/3971)
* Improve the docs for the event index and fix some type hints.
[\#3960](https://github.com/matrix-org/matrix-react-sdk/pull/3960)
* Automatically focus on the invite dialog input
[\#3968](https://github.com/matrix-org/matrix-react-sdk/pull/3968)
* Restore key backup in Complete Security dialog
[\#3966](https://github.com/matrix-org/matrix-react-sdk/pull/3966)
* Right Panel Verification improvements
[\#3967](https://github.com/matrix-org/matrix-react-sdk/pull/3967)
* Cross Signing Right Panel Verification Decoration
[\#3950](https://github.com/matrix-org/matrix-react-sdk/pull/3950)
* Passing refireParams actually prevented this from working
[\#3965](https://github.com/matrix-org/matrix-react-sdk/pull/3965)
* Start new key backup in security setup flow
[\#3964](https://github.com/matrix-org/matrix-react-sdk/pull/3964)
* Tweak styling of the unread indicator circle.
[\#3958](https://github.com/matrix-org/matrix-react-sdk/pull/3958)
* Add device IDs in user info tooltips
[\#3963](https://github.com/matrix-org/matrix-react-sdk/pull/3963)
* Improve encryption upgrade on login flow
[\#3962](https://github.com/matrix-org/matrix-react-sdk/pull/3962)
* Switch back to legacy decorators
[\#3961](https://github.com/matrix-org/matrix-react-sdk/pull/3961)
* Style bridge settings tab according to design
[\#3894](https://github.com/matrix-org/matrix-react-sdk/pull/3894)
* Fix skinning and babel targets
[\#3957](https://github.com/matrix-org/matrix-react-sdk/pull/3957)
* Enable cross-signing lab when key in storage
[\#3956](https://github.com/matrix-org/matrix-react-sdk/pull/3956)
* Add new session verification details dialog
[\#3953](https://github.com/matrix-org/matrix-react-sdk/pull/3953)
* Fix issue where we don't notice if our own devices shouldn't be trusted
[\#3949](https://github.com/matrix-org/matrix-react-sdk/pull/3949)
* Add separate component for post-auth security flows
[\#3951](https://github.com/matrix-org/matrix-react-sdk/pull/3951)
* Add more logging to settings watchers
[\#3952](https://github.com/matrix-org/matrix-react-sdk/pull/3952)
* Use https for recaptcha for all non-http protocols
[\#3944](https://github.com/matrix-org/matrix-react-sdk/pull/3944)
* Add status and management UI for the event indexer
[\#3672](https://github.com/matrix-org/matrix-react-sdk/pull/3672)
* Remove DM icons if `feature_cross_signing` is enabled; hide padlocks in DM
room headers
[\#3948](https://github.com/matrix-org/matrix-react-sdk/pull/3948)
* Stop rogue verification toast if you verify during login
[\#3943](https://github.com/matrix-org/matrix-react-sdk/pull/3943)
* Show incoming verification requests in the 'complete security' phase
[\#3942](https://github.com/matrix-org/matrix-react-sdk/pull/3942)
* Dismiss logged out device toasts
[\#3941](https://github.com/matrix-org/matrix-react-sdk/pull/3941)
* Verification nag toasts
[\#3940](https://github.com/matrix-org/matrix-react-sdk/pull/3940)
* Update from Weblate
[\#3947](https://github.com/matrix-org/matrix-react-sdk/pull/3947)
* Remember password for e2e bootstrapping
[\#3939](https://github.com/matrix-org/matrix-react-sdk/pull/3939)
* fix compound emoji
[\#3946](https://github.com/matrix-org/matrix-react-sdk/pull/3946)
* Setup flow for cross-signing on login / registration
[\#3937](https://github.com/matrix-org/matrix-react-sdk/pull/3937)
* Update profile avatar letter size
[\#3935](https://github.com/matrix-org/matrix-react-sdk/pull/3935)
* Hide default encryption algorithm
[\#3936](https://github.com/matrix-org/matrix-react-sdk/pull/3936)
* Resolve default export warnings from Webpack
[\#3938](https://github.com/matrix-org/matrix-react-sdk/pull/3938)
* Add null check for cross-signing info in verification panel
[\#3934](https://github.com/matrix-org/matrix-react-sdk/pull/3934)
* Add trace logging to figure out which component is causing weird events
[\#3926](https://github.com/matrix-org/matrix-react-sdk/pull/3926)
* Remove user lists feature flag, making it the default
[\#3906](https://github.com/matrix-org/matrix-react-sdk/pull/3906)
* Last bit of polish for user lists
[\#3925](https://github.com/matrix-org/matrix-react-sdk/pull/3925)
* QR code verification
[\#3871](https://github.com/matrix-org/matrix-react-sdk/pull/3871)
* Do less unnecessary work on CI
[\#3933](https://github.com/matrix-org/matrix-react-sdk/pull/3933)
* Re-enable stylelint on CI
[\#3932](https://github.com/matrix-org/matrix-react-sdk/pull/3932)
* Design pass for room icons
[\#3931](https://github.com/matrix-org/matrix-react-sdk/pull/3931)
* Populate the file panel using the event index if available.
[\#3858](https://github.com/matrix-org/matrix-react-sdk/pull/3858)
* Split AsyncWrapper out from Modal
[\#3928](https://github.com/matrix-org/matrix-react-sdk/pull/3928)
* Fix error in verification code on develop
[\#3930](https://github.com/matrix-org/matrix-react-sdk/pull/3930)
* Seperates out the padlock icon, and adds a tooltip
[\#3929](https://github.com/matrix-org/matrix-react-sdk/pull/3929)
* Cross Signing redesign for composer
[\#3910](https://github.com/matrix-org/matrix-react-sdk/pull/3910)
* Fix verifying your own devices with to_device messages
[\#3927](https://github.com/matrix-org/matrix-react-sdk/pull/3927)
* Room list reflects encryption state
[\#3908](https://github.com/matrix-org/matrix-react-sdk/pull/3908)
* Make the entire User Info scrollable, sticky close button
[\#3914](https://github.com/matrix-org/matrix-react-sdk/pull/3914)
* Remove riot logo from the security setup screens
[\#3916](https://github.com/matrix-org/matrix-react-sdk/pull/3916)
* Only say the session is verified if it is now verified
[\#3917](https://github.com/matrix-org/matrix-react-sdk/pull/3917)
* Hide password section if you can't change your password
[\#3924](https://github.com/matrix-org/matrix-react-sdk/pull/3924)
* Ensure a plaintext version of the composer ends up on the clipboard
[\#3922](https://github.com/matrix-org/matrix-react-sdk/pull/3922)
* Move & upgrade babel runtime into dependencies (like it wants)
[\#3920](https://github.com/matrix-org/matrix-react-sdk/pull/3920)
* Don't list every single alias when there's many
[\#3918](https://github.com/matrix-org/matrix-react-sdk/pull/3918)
* Try to populate user IDs even when the server's directory fails us
[\#3907](https://github.com/matrix-org/matrix-react-sdk/pull/3907)
* Remove .event property on verification request
[\#3912](https://github.com/matrix-org/matrix-react-sdk/pull/3912)
* Attempt to fix Safari + VoiceOver misunderstanding the timeline list
[\#3911](https://github.com/matrix-org/matrix-react-sdk/pull/3911)
* Enable encryption in DMs with device keys
[\#3913](https://github.com/matrix-org/matrix-react-sdk/pull/3913)
* Fix scrollable area and padding in user lists dialog
[\#3905](https://github.com/matrix-org/matrix-react-sdk/pull/3905)
* Add Reject & Ignore user button to invites view
[\#3909](https://github.com/matrix-org/matrix-react-sdk/pull/3909)
* Fix paragraph-awareness of the composer formatting features
[\#3891](https://github.com/matrix-org/matrix-react-sdk/pull/3891)
* Updated visuals for cross-signing bootstrap
[\#3903](https://github.com/matrix-org/matrix-react-sdk/pull/3903)
* Implement some parts of new cross signing bootstrap UI
[\#3897](https://github.com/matrix-org/matrix-react-sdk/pull/3897)
* Treat links as external in report content admin message
[\#3904](https://github.com/matrix-org/matrix-react-sdk/pull/3904)
* Be consistent about our settings svg, free the other one
[\#3902](https://github.com/matrix-org/matrix-react-sdk/pull/3902)
* Change prepublish script to prepare
[\#3899](https://github.com/matrix-org/matrix-react-sdk/pull/3899)
* Remove the react-sdk version
[\#3901](https://github.com/matrix-org/matrix-react-sdk/pull/3901)
* BuildKite: Retry end-to-end tests automatically once if they fail
[\#3900](https://github.com/matrix-org/matrix-react-sdk/pull/3900)
* Slash Command improvements around sending messages with leading slash
[\#3893](https://github.com/matrix-org/matrix-react-sdk/pull/3893)
* Support admin configurable message when reporting content
[\#3898](https://github.com/matrix-org/matrix-react-sdk/pull/3898)
* Don't warn on unverified users; ensured behavior stays the same with flags
off
[\#3896](https://github.com/matrix-org/matrix-react-sdk/pull/3896)
* Fix roving room list for resizer and ff tabstop a11y
[\#3895](https://github.com/matrix-org/matrix-react-sdk/pull/3895)
* Verify individual messages via cross-signing
[\#3875](https://github.com/matrix-org/matrix-react-sdk/pull/3875)
* Fix layering of dependencies in riot-web and e2e tests
[\#3882](https://github.com/matrix-org/matrix-react-sdk/pull/3882)
* Implement Roving Tab Index and Room List as TreeView
[\#3844](https://github.com/matrix-org/matrix-react-sdk/pull/3844)
* Move room header shields over the avatar for the room
[\#3888](https://github.com/matrix-org/matrix-react-sdk/pull/3888)
* Fix toast icon to prevent clipping
[\#3890](https://github.com/matrix-org/matrix-react-sdk/pull/3890)
* Only show devices and verify actions in E2EE rooms
[\#3889](https://github.com/matrix-org/matrix-react-sdk/pull/3889)
* Change user info verification checks to use cross-signing
[\#3887](https://github.com/matrix-org/matrix-react-sdk/pull/3887)
* Fix click-to-ping not inserting colon if composer non-empty
[\#3886](https://github.com/matrix-org/matrix-react-sdk/pull/3886)
* Fix emoticon space completion for upper case emoticons like :D xD
[\#3884](https://github.com/matrix-org/matrix-react-sdk/pull/3884)
* Repair cross-signing panel with async status
[\#3880](https://github.com/matrix-org/matrix-react-sdk/pull/3880)
* Remove temporary key backup button
[\#3878](https://github.com/matrix-org/matrix-react-sdk/pull/3878)
* Score users who have recently spoken higher in invite suggestions
[\#3866](https://github.com/matrix-org/matrix-react-sdk/pull/3866)
* Initial support for verification in right panel
[\#3796](https://github.com/matrix-org/matrix-react-sdk/pull/3796)
* Prevent the invite dialog from jumping around when elements change
[\#3868](https://github.com/matrix-org/matrix-react-sdk/pull/3868)
* Add prepublish script
[\#3876](https://github.com/matrix-org/matrix-react-sdk/pull/3876)
Changes in [2.0.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.0.0) (2020-01-27)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.0.0-rc.2...v2.0.0)
* Ensure a plaintext version of the composer ends up on the clipboard
[\#3923](https://github.com/matrix-org/matrix-react-sdk/pull/3923)
* Move & upgrade babel runtime into dependencies (like it wants)
[\#3921](https://github.com/matrix-org/matrix-react-sdk/pull/3921)
* Don't list every single alias when there's many
[\#3919](https://github.com/matrix-org/matrix-react-sdk/pull/3919)
Changes in [2.0.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.0.0-rc.2) (2020-01-20)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.0.0-rc.1...v2.0.0-rc.2)
* Add prepublish script
[\#3877](https://github.com/matrix-org/matrix-react-sdk/pull/3877)
Changes in [2.0.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.0.0-rc.1) (2020-01-20)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.6...v2.0.0-rc.1)
BREAKING CHANGES
================
* The react-sdk node module now exports ES6 rather than ES5. If you
wish to supports target that aren't compatible with ES6, you
will need to transpile the react-sdk to a suitable dialect.
All Changes
===========
* Fix arrows keys moving through edit history
[\#3874](https://github.com/matrix-org/matrix-react-sdk/pull/3874)
* Fix error about MessagePanel not being available for read markers
[\#3867](https://github.com/matrix-org/matrix-react-sdk/pull/3867)
* Adjust secret storage to work before sync
[\#3864](https://github.com/matrix-org/matrix-react-sdk/pull/3864)
* Update from Weblate
[\#3872](https://github.com/matrix-org/matrix-react-sdk/pull/3872)
* Remove unused deps and dev-deps
[\#3870](https://github.com/matrix-org/matrix-react-sdk/pull/3870)
* Tidy Jest test stuff and dependencies
[\#3869](https://github.com/matrix-org/matrix-react-sdk/pull/3869)
* Move feature flag check for new session toast
[\#3865](https://github.com/matrix-org/matrix-react-sdk/pull/3865)
* Catch exception in checkTerms if no ID server
[\#3863](https://github.com/matrix-org/matrix-react-sdk/pull/3863)
* Catch exception if passphrase dialog cancelled
[\#3862](https://github.com/matrix-org/matrix-react-sdk/pull/3862)
* Disable key request dialogs with cross-signing
[\#3860](https://github.com/matrix-org/matrix-react-sdk/pull/3860)
* Toasts for new, unverified sessions
[\#3859](https://github.com/matrix-org/matrix-react-sdk/pull/3859)
* Check for a matrixclient before trying to use it
[\#3861](https://github.com/matrix-org/matrix-react-sdk/pull/3861)
* Room header & message box shields now reflect cross-signing state
[\#3850](https://github.com/matrix-org/matrix-react-sdk/pull/3850)
* Fix Array.concat undefined
[\#3857](https://github.com/matrix-org/matrix-react-sdk/pull/3857)
* Update chokidar to fix reskindex not working
[\#3856](https://github.com/matrix-org/matrix-react-sdk/pull/3856)
* Make the new DM invite dialog work for regular invites too
[\#3854](https://github.com/matrix-org/matrix-react-sdk/pull/3854)
* Fix event handler leak in MemberStatusMessageAvatar
[\#3855](https://github.com/matrix-org/matrix-react-sdk/pull/3855)
* Move DM creation logic into DMInviteDialog
[\#3843](https://github.com/matrix-org/matrix-react-sdk/pull/3843)
* Remove all text when cutting in the composer
[\#3848](https://github.com/matrix-org/matrix-react-sdk/pull/3848)
* Add a ToastStore
[\#3853](https://github.com/matrix-org/matrix-react-sdk/pull/3853)
* 'Members' button always toggle the right panel
[\#3804](https://github.com/matrix-org/matrix-react-sdk/pull/3804)
* Fix timing of when Composer considers itself to be modified
[\#3842](https://github.com/matrix-org/matrix-react-sdk/pull/3842)
* Compute download file icon immediately
[\#3851](https://github.com/matrix-org/matrix-react-sdk/pull/3851)
* Fix not being able to open profiles from the timeline
[\#3852](https://github.com/matrix-org/matrix-react-sdk/pull/3852)
* Add post-login complete security flow
[\#3847](https://github.com/matrix-org/matrix-react-sdk/pull/3847)
* Added cut/copy and pasting user pills from editor.
[\#3828](https://github.com/matrix-org/matrix-react-sdk/pull/3828)
* Fix imports for help & support tab
[\#3846](https://github.com/matrix-org/matrix-react-sdk/pull/3846)
* Humanize the recent DM rooms ourselves for translations
[\#3841](https://github.com/matrix-org/matrix-react-sdk/pull/3841)
* Improve the quality of invite suggestions by filtering out DMs
[\#3840](https://github.com/matrix-org/matrix-react-sdk/pull/3840)
* Fix linter and tests on develop
[\#3845](https://github.com/matrix-org/matrix-react-sdk/pull/3845)
* Fix sourcemaps by refactoring the build system
[\#3839](https://github.com/matrix-org/matrix-react-sdk/pull/3839)
* Don't error on unverified/unknown devices.
[\#3837](https://github.com/matrix-org/matrix-react-sdk/pull/3837)
* Padlock icons in room header
[\#3835](https://github.com/matrix-org/matrix-react-sdk/pull/3835)
* Don't allow upgrade from untrusted key backup.
[\#3822](https://github.com/matrix-org/matrix-react-sdk/pull/3822)
* Emoji verification: Change name of 🔒 to lock
[\#3825](https://github.com/matrix-org/matrix-react-sdk/pull/3825)
* Room padlock decorations only if cross-signing is enabled
[\#3838](https://github.com/matrix-org/matrix-react-sdk/pull/3838)
* Enable end-to-end tests for sourcemaps (+Windows instructions)
[\#3827](https://github.com/matrix-org/matrix-react-sdk/pull/3827)
* Repair community member info panel
[\#3832](https://github.com/matrix-org/matrix-react-sdk/pull/3832)
* Add feature flag around the presence indicator in room list
[\#3831](https://github.com/matrix-org/matrix-react-sdk/pull/3831)
* Display a padlock icon beside invite-only rooms in the room list
[\#3821](https://github.com/matrix-org/matrix-react-sdk/pull/3821)
* Update from Weblate
[\#3830](https://github.com/matrix-org/matrix-react-sdk/pull/3830)
* Fix listener leak on RoomView
[\#3826](https://github.com/matrix-org/matrix-react-sdk/pull/3826)
* Regenerate i18n for sourcemaps branch
[\#3824](https://github.com/matrix-org/matrix-react-sdk/pull/3824)
* Fix tests for sourcemaps branch
[\#3823](https://github.com/matrix-org/matrix-react-sdk/pull/3823)
* Jest
[\#3724](https://github.com/matrix-org/matrix-react-sdk/pull/3724)
* Sourcemaps: develop -> feature branch
[\#3817](https://github.com/matrix-org/matrix-react-sdk/pull/3817)
* Support pasting a bunch of identifiers into the invite dialog
[\#3820](https://github.com/matrix-org/matrix-react-sdk/pull/3820)
* Support 3PIDs (email addresses) in the invite dialog
[\#3819](https://github.com/matrix-org/matrix-react-sdk/pull/3819)
* Placeholder PR for cleaner diffs: ES6
[\#3765](https://github.com/matrix-org/matrix-react-sdk/pull/3765)
* Misc fixes for ES6 imports/exports
[\#3766](https://github.com/matrix-org/matrix-react-sdk/pull/3766)
* Wire up the invite targets dialog to a real composer and show selections
[\#3815](https://github.com/matrix-org/matrix-react-sdk/pull/3815)
* Change ref handling in TextualBody to prevent it parsing generated nodes
[\#3711](https://github.com/matrix-org/matrix-react-sdk/pull/3711)
* Render encoded html entities in og:description
[\#3789](https://github.com/matrix-org/matrix-react-sdk/pull/3789)
* Update package.json for new build process + cosmetics
[\#3767](https://github.com/matrix-org/matrix-react-sdk/pull/3767)
* Convert CommonJS exports to ES6 exports
[\#3761](https://github.com/matrix-org/matrix-react-sdk/pull/3761)
* Round 2 of CommonJS to ES6 imports
[\#3764](https://github.com/matrix-org/matrix-react-sdk/pull/3764)
* Strip all variation selectors on emoji
[\#3814](https://github.com/matrix-org/matrix-react-sdk/pull/3814)
* Use the new js-sdk imports and import from src
[\#3763](https://github.com/matrix-org/matrix-react-sdk/pull/3763)
* Convert many imports to handle ES6 exports
[\#3762](https://github.com/matrix-org/matrix-react-sdk/pull/3762)
* Fix userinfo for users not in the room
[\#3812](https://github.com/matrix-org/matrix-react-sdk/pull/3812)
* Attempt to fix e2e tests
[\#3811](https://github.com/matrix-org/matrix-react-sdk/pull/3811)
* Add bunch of null-guards and similar to fix React Errors/complaints
[\#3752](https://github.com/matrix-org/matrix-react-sdk/pull/3752)
* Delegate all room alias validation to the RoomAliasField validator
[\#3807](https://github.com/matrix-org/matrix-react-sdk/pull/3807)
* Support filtering and searching for users to invite in DMs
[\#3802](https://github.com/matrix-org/matrix-react-sdk/pull/3802)
* Add suggestions for which users to invite to chat
[\#3801](https://github.com/matrix-org/matrix-react-sdk/pull/3801)
* Use `flex-start` instead of `start` for postcss
[\#3760](https://github.com/matrix-org/matrix-react-sdk/pull/3760)
* Define getLanguageFromBrowser() for LanguageDropdown
[\#3769](https://github.com/matrix-org/matrix-react-sdk/pull/3769)
* Introduce babel's export-default-from plugin to fix build errors
[\#3768](https://github.com/matrix-org/matrix-react-sdk/pull/3768)
* Add a bit of debugging to incorrect components in the Skinner
[\#3770](https://github.com/matrix-org/matrix-react-sdk/pull/3770)
* [BREAKING] Refactor the entire build process for babel@7 and TypeScript
(chunk 1 of many)
[\#3722](https://github.com/matrix-org/matrix-react-sdk/pull/3722)
* Implementation of new potential skinning mechanism
[\#3723](https://github.com/matrix-org/matrix-react-sdk/pull/3723)
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)

View file

@ -34,7 +34,7 @@ All code lands on the `develop` branch - `master` is only used for stable releas
**Please file PRs against `develop`!!**
Please follow the standard Matrix contributor's guide:
https://github.com/matrix-org/synapse/tree/master/CONTRIBUTING.rst
https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.rst
Please follow the Matrix JS/React code style as per:
https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md

View file

@ -1,16 +1,17 @@
const en = require("../src/i18n/strings/en_EN");
module.exports = jest.fn((opts, cb) => {
if (opts.url.endsWith("languages.json")) {
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 (opts.url.endsWith("en_EN.json")) {
} else if (url && url.endsWith("en_EN.json")) {
cb(undefined, {status: 200}, JSON.stringify(en));
} else {
cb(undefined, {status: 404}, "");
cb(true, {status: 404}, "");
}
});

View file

@ -2,19 +2,16 @@ module.exports = {
"sourceMaps": "inline",
"presets": [
["@babel/preset-env", {
"targets": {
"browsers": [
"last 2 versions"
]
},
"modules": "commonjs"
"targets": [
"last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions"
],
}],
"@babel/preset-typescript",
"@babel/preset-flow",
"@babel/preset-react"
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-decorators", {legacy: true}],
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-class-properties",

31
docs/jitsi.md Normal file
View file

@ -0,0 +1,31 @@
# Jitsi Wrapper
**Note**: These are developer docs. Please consult your client's documentation for
instructions on setting up Jitsi.
The react-sdk wraps all Jitsi call widgets in a local wrapper called `jitsi.html`
which takes several parameters:
*Query string*:
* `widgetId`: The ID of the widget. This is needed for communication back to the
react-sdk.
* `parentUrl`: The URL of the parent window. This is also needed for
communication back to the react-sdk.
*Hash/fragment (formatted as a query string)*:
* `conferenceDomain`: The domain to connect Jitsi Meet to.
* `conferenceId`: The room or conference ID to connect Jitsi Meet to.
* `isAudioOnly`: Boolean for whether this is a voice-only conference. May not
be present, should default to `false`.
* `displayName`: The display name of the user viewing the widget. May not
be present or could be null.
* `avatarUrl`: The HTTP(S) URL for the avatar of the user viewing the widget. May
not be present or could be null.
* `userId`: The MXID of the user viewing the widget. May not be present or could
be null.
The react-sdk will assume that `jitsi.html` is at the path of wherever it is currently
being served. For example, `https://riot.im/develop/jitsi.html` or `vector://webapp/jitsi.html`.
The `jitsi.html` wrapper can use the react-sdk's `WidgetApi` to communicate, making
it easier to actually implement the feature.

28
docs/scrolling.md Normal file
View file

@ -0,0 +1,28 @@
# ScrollPanel
## Updates
During an onscroll event, we check whether we're getting close to the top or bottom edge of the loaded content. If close enough, we fire a request to load more through the callback passed in the `onFillRequest` prop. This returns a promise is passed down from `TimelinePanel`, where it will call paginate on the `TimelineWindow` and once the events are received back, update its state with the new events. This update trickles down to the `MessagePanel`, which rerenders all tiles and passed that to `ScrollPanel`. ScrollPanels `componentDidUpdate` method gets called, and we do the scroll housekeeping there (read below). Once the rerender has completed, the `setState` callback is called and we resolve the promise returned by `onFillRequest`. Now we check the DOM to see if we need more fill requests.
## Prevent Shrinking
ScrollPanel supports a mode to prevent it shrinking. This is used to prevent a jump when at the bottom of the timeline and people start and stop typing. It gets cleared automatically when 200px above the bottom of the timeline.
## BACAT (Bottom-Aligned, Clipped-At-Top) scrolling
BACAT scrolling implements a different way of restoring the scroll position in the timeline while tiles out of view are changing height or tiles are being added or removed. It was added in https://github.com/matrix-org/matrix-react-sdk/pull/2842.
The motivation for the changes is having noticed that setting scrollTop while scrolling tends to not work well, with it interrupting ongoing scrolling and also querying scrollTop reporting outdated values and consecutive scroll adjustments cancelling each out previous ones. This seems to be worse on macOS than other platforms, presumably because of a higher resolution in scroll events there. Also see https://github.com/vector-im/riot-web/issues/528. The BACAT approach allows to only have to change the scroll offset when adding or removing tiles.
The approach taken instead is to vertically align the timeline tiles to the bottom of the scroll container (using flexbox) and give the timeline inside the scroll container an explicit height, initially set to a multiple of the PAGE_SIZE (400px at time of writing) as needed by the content. When scrolled up, we can compensate for anything that grew below the viewport by changing the height of the timeline to maintain what's currently visible in the viewport without adjusting the scrollTop and hence without jumping.
For anything above the viewport growing or shrinking, we don't need to do anything as the timeline is bottom-aligned. We do need to update the height manually to keep all content visible as more is loaded. To maintain scroll position after the portion above the viewport changes height, we need to set the scrollTop, as we cannot balance it out with more height changes. We do this 100ms after the user has stopped scrolling, so setting scrollTop has not nasty side-effects.
As of https://github.com/matrix-org/matrix-react-sdk/pull/4166, we are scrolling to compensate for height changes by calling `scrollBy(0, x)` rather than reading and than setting `scrollTop`, as reading `scrollTop` can (again, especially on macOS) easily return values that are out of sync with what is on the screen, probably because scrolling can be done [off the main thread](https://wiki.mozilla.org/Platform/GFX/APZ) in some circumstances. This seems to further prevent jumps.
### How does it work?
`componentDidUpdate` is called when a tile in the timeline is updated (as we rerender the whole timeline) or tiles are added or removed (see Updates section before). From here, `checkScroll` is called, which calls `_restoreSavedScrollState`. Now, we increase the timeline height if something below the viewport grew by adjusting `this._bottomGrowth`. `bottomGrowth` is the height added to the timeline (on top of the height from the number of pages calculated at the last `_updateHeight` run) to compensate for growth below the viewport. This is cleared during the next run of `_updateHeight`. Remember that the tiles in the timeline are aligned to the bottom.
From `_restoreSavedScrollState` we also call `_updateHeight` which waits until the user stops scrolling for 100ms and then recalculates the amount of pages of 400px the timeline should be sized to, to be able to show all of its (newly added) content. We have to adjust the scroll offset (which is why we wait until scrolling has stopped) now because the space above the viewport has likely changed.

27
docs/usercontent.md Normal file
View file

@ -0,0 +1,27 @@
# Usercontent
While decryption itself is safe to be done without a sandbox,
letting the browser and user interact with the resulting data may be dangerous,
previously `usercontent.riot.im` was used to act as a sandbox on a different origin to close the attack surface,
it is now possible to do by using a combination of a sandboxed iframe and some code written into the app which consumes this SDK.
Usercontent is an iframe sandbox target for allowing a user to safely download a decrypted attachment from a sandboxed origin where it cannot be used to XSS your riot session out from under you.
Its function is to create an Object URL for the user/browser to use but bound to an origin different to that of the riot instance to protect against XSS.
It exposes a function over a postMessage API, when sent an object with the matching fields to render a download link with the Object URL:
```json5
{
"imgSrc": "", // the src of the image to display in the download link
"imgStyle": "", // the style to apply to the image
"style": "", // the style to apply to the download link
"download": "", // download attribute to pass to the <a/> tag
"textContent": "", // the text to put inside the download link
"blob": "", // the data blob to wrap in an object url and allow the user to download
}
```
If only imgSrc, imgStyle and style are passed then just update the existing link without overwriting other things about it.
It is expected that this target be available at `usercontent/` relative to the root of the app, this can be seen in riot-web's webpack config.

View file

@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
"version": "1.7.6",
"version": "2.3.0",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@ -31,6 +31,7 @@
"typings": "./lib/index.d.ts",
"matrix_src_main": "./src/index.js",
"scripts": {
"prepare": "yarn build",
"i18n": "matrix-gen-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",
@ -39,20 +40,21 @@
"rethemendex": "res/css/rethemendex.sh",
"clean": "rimraf lib",
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
"build:compile": "yarn reskindex && babel -d lib --verbose --extensions \".ts,.js\" src",
"build:types": "tsc --emitDeclarationOnly",
"build:compile": "yarn reskindex && babel -d lib --verbose --extensions \".ts,.js,.tsx\" src",
"build:types": "tsc --emitDeclarationOnly --jsx react",
"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:types": "tsc --noEmit --jsx react",
"lint:style": "stylelint 'res/css/**/*.scss'",
"test": "jest",
"test:e2e": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080"
},
"dependencies": {
"@babel/runtime": "^7.8.3",
"blueimp-canvas-to-blob": "^3.5.0",
"browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3",
@ -70,31 +72,27 @@
"flux": "2.1.1",
"focus-visible": "^5.0.2",
"fuse.js": "^2.2.0",
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
"gfm.css": "^1.1.1",
"glob": "^5.0.14",
"glob-to-regexp": "^0.4.1",
"highlight.js": "^9.15.8",
"html-entities": "^1.2.1",
"is-ip": "^2.0.0",
"isomorphic-fetch": "^2.2.1",
"linkifyjs": "^2.1.6",
"lodash": "^4.17.14",
"lolex": "4.2",
"matrix-js-sdk": "3.0.0",
"optimist": "^0.6.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"minimist": "^1.2.0",
"pako": "^1.0.5",
"png-chunks-extract": "^1.0.0",
"project-name-generator": "^2.1.7",
"prop-types": "^15.5.8",
"qrcode": "^1.4.4",
"qrcode-react": "^0.1.16",
"qs": "^6.6.0",
"querystring": "^0.2.0",
"react": "^16.9.0",
"react-addons-css-transition-group": "15.6.2",
"react-beautiful-dnd": "^4.0.1",
"react-dom": "^16.9.0",
"react-focus-lock": "^2.2.1",
"react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
"resize-observer-polyfill": "^1.5.0",
"sanitize-html": "^1.18.4",
"tar-js": "^0.3.0",
@ -102,7 +100,6 @@
"url": "^0.11.0",
"velocity-animate": "^1.5.2",
"what-input": "^5.2.6",
"whatwg-fetch": "^1.1.1",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
@ -114,17 +111,18 @@
"@babel/plugin-proposal-numeric-separator": "^7.7.4",
"@babel/plugin-proposal-object-rest-spread": "^7.7.4",
"@babel/plugin-transform-flow-comments": "^7.7.4",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.7.6",
"@babel/preset-flow": "^7.7.4",
"@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.7.4",
"@babel/register": "^7.7.4",
"@babel/runtime": "^7.7.6",
"@peculiar/webcrypto": "^1.0.22",
"@types/classnames": "^2.2.10",
"@types/react": "16.9",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"chokidar": "^2.1.2",
"chokidar": "^3.3.1",
"concurrently": "^4.0.1",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.1",
@ -138,11 +136,12 @@
"estree-walker": "^0.5.0",
"file-loader": "^3.0.1",
"flow-parser": "^0.57.3",
"glob": "^5.0.14",
"jest": "^24.9.0",
"lolex": "^5.1.2",
"matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2",
"react-test-renderer": "^16.9.0",
"require-json": "0.0.1",
"rimraf": "^2.4.3",
"source-map-loader": "^0.2.3",
"stylelint": "^9.10.1",
@ -158,7 +157,9 @@
"testMatch": [
"<rootDir>/test/**/*-test.js"
],
"setupTestFrameworkScriptFile": "<rootDir>/test/setupTests.js",
"setupFilesAfterEnv": [
"<rootDir>/test/setupTests.js"
],
"moduleNameMapper": {
"\\.(gif|png|svg|ttf|woff2)$": "<rootDir>/__mocks__/imageMock.js",
"\\$webapp/i18n/languages.json": "<rootDir>/__mocks__/languages.json"

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
#
# Script to perform a release of matrix-react-sdk.
#
@ -9,4 +9,52 @@ set -e
cd `dirname $0`
exec ./node_modules/matrix-js-sdk/release.sh -z "$@"
for i in matrix-js-sdk
do
echo "Checking version of $i..."
depver=`cat package.json | jq -r .dependencies[\"$i\"]`
latestver=`yarn info -s $i dist-tags.next`
if [ "$depver" != "$latestver" ]
then
echo "The latest version of $i is $latestver but package.json depends on $depver."
echo -n "Type 'u' to auto-upgrade, 'c' to continue anyway, or 'a' to abort:"
read resp
if [ "$resp" != "u" ] && [ "$resp" != "c" ]
then
echo "Aborting."
exit 1
fi
if [ "$resp" == "u" ]
then
echo "Upgrading $i to $latestver..."
yarn add -E $i@$latestver
git add -u
# The `-e` flag opens the editor and gives you a chance to check
# the upgrade for correctness.
git commit -m "Upgrade $i to $latestver" -e
fi
fi
done
./node_modules/matrix-js-sdk/release.sh -z "$@"
release="${1#v}"
prerelease=0
# We check if this build is a prerelease by looking to
# see if the version has a hyphen in it. Crude,
# but semver doesn't support postreleases so anything
# with a hyphen is a prerelease.
echo $release | grep -q '-' && prerelease=1
if [ $prerelease -eq 0 ]
then
# For a release, reset SDK deps back to the `develop` branch.
for i in matrix-js-sdk
do
echo "Resetting $i to develop branch..."
yarn add github:matrix-org/$i#develop
git add -u
git commit -m "Reset $i back to develop branch"
done
git push origin develop
fi

View file

@ -42,10 +42,15 @@ pre, code {
font-size: 100% !important;
}
.error, .warning {
.error, .warning,
.text-error, .text-warning {
color: $warning-color;
}
.text-success {
color: $accent-color;
}
b {
// On Firefox, the default weight for `<b>` is `bolder` which results in no bold
// effect since we only have specific weights of our fonts available.
@ -202,37 +207,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
transition: opacity 0.2s ease-in-out;
}
/* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48.
Stop the scrollbar view from pushing out the container's overall sizing, which causes
flexbox to adapt to the new size and cause the view to keep growing.
*/
.gm-scrollbar-container .gm-scroll-view {
position: absolute;
}
/* Expand thumbs on hoverover */
.gm-scrollbar {
border-radius: 5px !important;
}
.gm-scrollbar.-vertical {
width: 6px;
transition: width 120ms ease-out !important;
}
.gm-scrollbar.-vertical:hover,
.gm-scrollbar.-vertical:active {
width: 8px;
transition: width 120ms ease-out !important;
}
.gm-scrollbar.-horizontal {
height: 6px;
transition: height 120ms ease-out !important;
}
.gm-scrollbar.-horizontal:hover,
.gm-scrollbar.-horizontal:active {
height: 8px;
transition: height 120ms ease-out !important;
}
// These are magic constants which are excluded from tinting, to let themes
// (which only have CSS, unlike skins) tell the app what their non-tinted
// colourscheme is by inspecting the stylesheet DOM.
@ -338,6 +312,14 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
margin-bottom: 10px;
}
.mx_Dialog_titleImage {
vertical-align: middle;
width: 25px;
height: 25px;
margin-left: -2px;
margin-right: 4px;
}
.mx_Dialog_title {
font-size: 22px;
line-height: 36px;
@ -378,7 +360,13 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
text-align: right;
}
.mx_Dialog button, .mx_Dialog input[type="submit"] {
/* XXX: Our button style are a mess: buttons that happen to appear in dialogs get special styles applied
* to them that no button anywhere else in the app gets by default. In practice, buttons in other places
* in the app look the same by being AccessibleButtons, or possibly by having explict button classes.
* We should go through and have one consistent set of styles for buttons throughout the app.
* For now, I am duplicating the selectors here for mx_Dialog and mx_DialogButtons.
*/
.mx_Dialog button, .mx_Dialog input[type="submit"], .mx_Dialog_buttons button, .mx_Dialog_buttons input[type="submit"] {
@mixin mx_DialogButton;
margin-left: 0px;
margin-right: 8px;
@ -394,27 +382,32 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
margin-right: 0px;
}
.mx_Dialog button:hover, .mx_Dialog input[type="submit"]:hover {
.mx_Dialog button:hover, .mx_Dialog input[type="submit"]:hover, .mx_Dialog_buttons button:hover, .mx_Dialog_buttons input[type="submit"]:hover {
@mixin mx_DialogButton_hover;
}
.mx_Dialog button:focus, .mx_Dialog input[type="submit"]:focus {
.mx_Dialog button:focus, .mx_Dialog input[type="submit"]:focus, .mx_Dialog_buttons button:focus, .mx_Dialog_buttons input[type="submit"]:focus {
filter: brightness($focus-brightness);
}
.mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary {
.mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary, .mx_Dialog_buttons button.mx_Dialog_primary, .mx_Dialog_buttons input[type="submit"].mx_Dialog_primary {
color: $accent-fg-color;
background-color: $accent-color;
min-width: 156px;
}
.mx_Dialog button.danger, .mx_Dialog input[type="submit"].danger {
.mx_Dialog button.danger, .mx_Dialog input[type="submit"].danger, .mx_Dialog_buttons button.danger, .mx_Dialog_buttons input[type="submit"].danger {
background-color: $warning-color;
border: solid 1px $warning-color;
color: $accent-fg-color;
}
.mx_Dialog button:disabled, .mx_Dialog input[type="submit"]:disabled {
.mx_Dialog button.warning, .mx_Dialog input[type="submit"].warning {
border: solid 1px $warning-color;
color: $warning-color;
}
.mx_Dialog button:disabled, .mx_Dialog input[type="submit"]:disabled, .mx_Dialog_buttons button:disabled, .mx_Dialog_buttons input[type="submit"]:disabled {
background-color: $light-fg-color;
border: solid 1px $light-fg-color;
opacity: 0.7;

View file

@ -28,6 +28,7 @@
@import "./structures/_TopLeftMenuButton.scss";
@import "./structures/_UploadBar.scss";
@import "./structures/_ViewSource.scss";
@import "./structures/auth/_CompleteSecurity.scss";
@import "./structures/auth/_Login.scss";
@import "./views/auth/_AuthBody.scss";
@import "./views/auth/_AuthButtons.scss";
@ -35,6 +36,7 @@
@import "./views/auth/_AuthHeader.scss";
@import "./views/auth/_AuthHeaderLogo.scss";
@import "./views/auth/_AuthPage.scss";
@import "./views/auth/_CompleteSecurityBody.scss";
@import "./views/auth/_CountryDropdown.scss";
@import "./views/auth/_InteractiveAuthEntryComponents.scss";
@import "./views/auth/_LanguageSelector.scss";
@ -56,15 +58,18 @@
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
@import "./views/dialogs/_CreateGroupDialog.scss";
@import "./views/dialogs/_CreateRoomDialog.scss";
@import "./views/dialogs/_DMInviteDialog.scss";
@import "./views/dialogs/_DeactivateAccountDialog.scss";
@import "./views/dialogs/_DeviceVerifyDialog.scss";
@import "./views/dialogs/_DevtoolsDialog.scss";
@import "./views/dialogs/_EncryptedEventDialog.scss";
@import "./views/dialogs/_GroupAddressPicker.scss";
@import "./views/dialogs/_IncomingSasDialog.scss";
@import "./views/dialogs/_InviteDialog.scss";
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
@import "./views/dialogs/_MessageEditHistoryDialog.scss";
@import "./views/dialogs/_NewSessionReviewDialog.scss";
@import "./views/dialogs/_RoomSettingsDialog.scss";
@import "./views/dialogs/_RoomSettingsDialogBridges.scss";
@import "./views/dialogs/_RoomUpgradeDialog.scss";
@import "./views/dialogs/_RoomUpgradeWarningDialog.scss";
@import "./views/dialogs/_SetEmailDialog.scss";
@ -124,7 +129,6 @@
@import "./views/messages/_MEmoteBody.scss";
@import "./views/messages/_MFileBody.scss";
@import "./views/messages/_MImageBody.scss";
@import "./views/messages/_MKeyVerificationRequest.scss";
@import "./views/messages/_MNoticeBody.scss";
@import "./views/messages/_MStickerBody.scss";
@import "./views/messages/_MTextBody.scss";
@ -139,7 +143,10 @@
@import "./views/messages/_TextualEvent.scss";
@import "./views/messages/_UnknownBody.scss";
@import "./views/messages/_ViewSourceEvent.scss";
@import "./views/messages/_common_CryptoEvent.scss";
@import "./views/right_panel/_EncryptionInfo.scss";
@import "./views/right_panel/_UserInfo.scss";
@import "./views/right_panel/_VerificationPanel.scss";
@import "./views/room_settings/_AliasSettings.scss";
@import "./views/room_settings/_ColorSettings.scss";
@import "./views/rooms/_AppsDrawer.scss";
@ -150,6 +157,7 @@
@import "./views/rooms/_EditMessageComposer.scss";
@import "./views/rooms/_EntityTile.scss";
@import "./views/rooms/_EventTile.scss";
@import "./views/rooms/_InviteOnlyIcon.scss";
@import "./views/rooms/_JumpToBottomButton.scss";
@import "./views/rooms/_LinkPreviewWidget.scss";
@import "./views/rooms/_MemberDeviceInfo.scss";
@ -170,7 +178,6 @@
@import "./views/rooms/_RoomTile.scss";
@import "./views/rooms/_RoomUpgradeWarningBar.scss";
@import "./views/rooms/_SearchBar.scss";
@import "./views/rooms/_SearchableEntityList.scss";
@import "./views/rooms/_SendMessageComposer.scss";
@import "./views/rooms/_Stickers.scss";
@import "./views/rooms/_TopUnreadMessagesBar.scss";
@ -179,6 +186,7 @@
@import "./views/settings/_AvatarSetting.scss";
@import "./views/settings/_CrossSigningPanel.scss";
@import "./views/settings/_DevicesPanel.scss";
@import "./views/settings/_E2eAdvancedPanel.scss";
@import "./views/settings/_EmailAddresses.scss";
@import "./views/settings/_IntegrationManager.scss";
@import "./views/settings/_KeyBackupPanel.scss";

View file

@ -14,69 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
/* This file has CSS for both native and non-native scrollbars in an order
* that's fairly logical to read but duplicates a selector to separate the
* hiding/showing from the sizing.
*/
/* stylelint-disable no-duplicate-selectors */
/*
1. for browsers that support native overlay auto-hiding scrollbars
*/
.mx_AutoHideScrollbar {
overflow-x: hidden;
overflow-y: auto;
-ms-overflow-style: -ms-autohiding-scrollbar;
}
/*
2. webkit also supports overflow:overlay where the scrollbars don't take any space
in the layout but they don't autohide, so do that only on hover
*/
body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar {
overflow-y: hidden;
}
body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar:hover {
overflow-y: overlay;
}
/*
3. as a last fallback, compensate for the scrollbar taking up space in the layout
by having giving the child element (.mx_AutoHideScrollbar_offset) a
negative right margin of the width of the scrollbar when the container
is overflowing. This is what Firefox ends up using. Overflow is detected
in javascript, and adds the mx_AutoHideScrollbar_overflow class to the container.
This only works in Firefox, which should be fine as this fallback is only needed there.
*/
body.mx_scrollbar_nooverlay {
.mx_AutoHideScrollbar {
overflow-y: hidden;
}
.mx_AutoHideScrollbar:hover {
overflow-y: auto;
}
/*
offset scrollbar width with negative margin-right
include before and after psuedo-elements here so they can
be used to do something interesting like scroll-indicating
gradients (see IndicatorScrollBar)
*/
.mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow > .mx_AutoHideScrollbar_offset,
.mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::before,
.mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::after {
margin-right: calc(-1 * var(--scrollbar-width));
}
}
// style the native scrollbars ...
// ... standard css scrollbars (firefox at time of writing)
.mx_AutoHideScrollbar {
// make any scrollbar grey and thin
html {
scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color;
}
// scrollbar-width is not inherited (but -color is, why?!),
// so declare it on every element
* {
scrollbar-width: thin;
}
// or fallback for webkit browsers
::-webkit-scrollbar {
width: 6px;
height: 6px;
@ -84,6 +31,37 @@ body.mx_scrollbar_nooverlay {
}
::-webkit-scrollbar-thumb {
background-color: $scrollbar-thumb-color;
border-radius: 3px;
background-color: $scrollbar-thumb-color;
}
// make auto-hide scrollbars not transparent again on hover
.mx_AutoHideScrollbar:hover {
scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color;
&::-webkit-scrollbar {
background-color: $scrollbar-track-color;
}
&::-webkit-scrollbar-thumb {
background-color: $scrollbar-thumb-color;
}
}
// make scrollbars transparent for autohide scrollbars
.mx_AutoHideScrollbar {
overflow-x: hidden;
overflow-y: auto;
overflow-y: overlay; // where supported
-ms-overflow-style: -ms-autohiding-scrollbar;
&::-webkit-scrollbar {
background-color: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: transparent;
}
scrollbar-color: transparent transparent;
}

View file

@ -63,7 +63,7 @@ limitations under the License.
}
.mx_GroupHeader_editButton::before {
mask-image: url('$(res)/img/icons-settings-room.svg');
mask-image: url('$(res)/img/feather-customised/settings.svg');
}
.mx_GroupHeader_shareButton::before {
@ -180,10 +180,6 @@ limitations under the License.
line-height: 2em;
}
.mx_GroupView > .mx_MainSplit {
flex: 1;
}
.mx_GroupView_body {
flex-grow: 1;
}
@ -341,8 +337,8 @@ limitations under the License.
display: none;
}
.mx_GroupView_body .gm-scroll-view > * {
margin: 11px 50px 0px 68px;
.mx_GroupView_body .mx_AutoHideScrollbar > * {
margin: 11px 50px 50px 68px;
}
.mx_GroupView_groupDesc textarea {
@ -370,7 +366,7 @@ limitations under the License.
padding: 40px 20px;
}
.mx_GroupView .mx_MemberInfo .gm-scroll-view > :not(.mx_MemberInfo_avatar) {
.mx_GroupView .mx_MemberInfo .mx_AutoHideScrollbar > :not(.mx_MemberInfo_avatar) {
padding-left: 16px;
padding-right: 16px;
}

View file

@ -18,6 +18,7 @@ limitations under the License.
display: flex;
flex-direction: row;
min-width: 0;
height: 100%;
}
// move hit area 5px to the right so it doesn't overlap with the timeline scrollbar

View file

@ -76,13 +76,6 @@ limitations under the License.
flex: 1 1 0;
min-width: 0;
/* Experimental fix for https://github.com/vector-im/vector-web/issues/947
and https://github.com/vector-im/vector-web/issues/946.
Empirically this stops the MessagePanel's width exploding outwards when
gemini is in 'prevented' mode
*/
overflow-x: auto;
/* To fix https://github.com/vector-im/riot-web/issues/3298 where Safari
needed height 100% all the way down to the HomePage. Height does not
have to be auto, empirically.

View file

@ -67,9 +67,6 @@ limitations under the License.
}
}
.mx_MyGroups_headerCard_header {
font-weight: bold;
margin-bottom: 10px;
@ -98,6 +95,11 @@ limitations under the License.
display: flex;
flex-direction: column;
overflow-y: auto;
}
.mx_MyGroups_scrollable {
overflow-y: inherit;
}
.mx_MyGroups_placeholder {

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@ -18,7 +19,7 @@ limitations under the License.
overflow-x: hidden;
flex: 0 0 auto;
position: relative;
min-width: 250px;
min-width: 264px;
display: flex;
flex-direction: column;
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@ -45,9 +46,8 @@ limitations under the License.
}
.mx_RoomDirectory_listheader {
display: flex;
margin-top: 12px;
margin-bottom: 12px;
display: block;
margin-top: 13px;
}
.mx_RoomDirectory_searchbox {
@ -64,7 +64,7 @@ limitations under the License.
}
.mx_RoomDirectory_table {
font-size: 14px;
font-size: 12px;
color: $primary-fg-color;
width: 100%;
text-align: left;
@ -112,6 +112,7 @@ limitations under the License.
.mx_RoomDirectory_name {
display: inline-block;
font-size: 18px;
font-weight: 600;
}
@ -119,6 +120,16 @@ limitations under the License.
display: inline-block;
}
.mx_RoomDirectory_perm {
border-radius: 10px;
display: inline-block;
height: 20px;
line-height: 20px;
padding: 0 5px;
color: $accent-fg-color;
background-color: $rte-room-pill-color;
}
.mx_RoomDirectory_topic {
cursor: initial;
color: $light-fg-color;
@ -138,8 +149,8 @@ limitations under the License.
padding: 0;
}
.mx_RoomDirectory p {
font-size: 14px;
.mx_RoomDirectory > span {
font-size: 15px;
margin-top: 0;
.mx_AccessibleButton {

View file

@ -166,41 +166,22 @@ limitations under the License.
// overflow indicators
.mx_RoomSubList:not(.resized-all) > .mx_RoomSubList_scroll {
&.mx_IndicatorScrollbar_topOverflow::before,
&.mx_IndicatorScrollbar_bottomOverflow::after {
&.mx_IndicatorScrollbar_topOverflow::before {
position: sticky;
content: "";
top: 0;
left: 0;
right: 0;
height: 8px;
content: "";
display: block;
z-index: 100;
display: block;
pointer-events: none;
}
&.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset {
margin-top: -8px;
}
&.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset {
margin-bottom: -8px;
}
&.mx_IndicatorScrollbar_topOverflow::before {
top: 0;
transition: background-image 0.1s ease-in;
background: linear-gradient(to top, $panel-gradient);
}
/*
// for now, we remove the bottomOverflow entirely as we don't want to
// lose the screen real-estate due to a bg-colored gradient, but we also
// don't want to use drop shadows and risk a confusing hierarchy of cards.
// so, instead, we hard-clip at the bottom but soft-clip at the top.
&.mx_IndicatorScrollbar_bottomOverflow::after {
bottom: 0;
transition: background-image 0.1s ease-in;
margin: 0px -8px;
background: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.0));
&.mx_IndicatorScrollbar_topOverflow {
margin-top: -8px;
}
*/
}

View file

@ -23,6 +23,7 @@ limitations under the License.
flex-direction: column;
align-items: center;
justify-content: space-between;
min-height: 0;
}
.mx_TagPanel_items_selected {
@ -57,6 +58,7 @@ limitations under the License.
.mx_TagPanel .mx_TagPanel_scroller {
flex-grow: 1;
width: 100%;
}
.mx_TagPanel .mx_TagPanel_tagTileContainer {

View file

@ -51,8 +51,8 @@ limitations under the License.
&.mx_Toast_hasIcon {
&::after {
content: "";
width: 20px;
height: 20px;
width: 22px;
height: 22px;
grid-column: 1;
grid-row: 1;
mask-size: 100%;
@ -64,6 +64,10 @@ limitations under the License.
background-color: $primary-fg-color;
}
&.mx_Toast_icon_verification_warning::after {
background-image: url("$(res)/img/e2e/warning.svg");
}
h2, .mx_Toast_body {
grid-column: 2;
}
@ -94,5 +98,9 @@ limitations under the License.
margin: 4px 0 11px 0;
font-size: 12px;
}
.mx_Toast_deviceID {
font-size: 10px;
}
}
}

View file

@ -0,0 +1,55 @@
/*
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-right: 4px;
position: relative;
}
.mx_CompleteSecurity_heroIcon {
width: 128px;
height: 128px;
position: relative;
margin: 0 auto;
}
.mx_CompleteSecurity_body {
font-size: 15px;
}
.mx_CompleteSecurity_waiting {
color: $notice-secondary-color;
}
.mx_CompleteSecurity_actionRow {
display: flex;
justify-content: flex-end;
.mx_AccessibleButton {
margin-inline-start: 18px;
&.warning {
color: $warning-color;
}
}
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2019 New Vector Ltd
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.
@ -16,12 +17,12 @@ limitations under the License.
.mx_AuthBody {
width: 500px;
font-size: 12px;
color: $authpage-secondary-color;
background-color: $authpage-body-bg-color;
border-radius: 0 4px 4px 0;
padding: 25px 60px;
box-sizing: border-box;
font-size: 12px;
color: $authpage-secondary-color;
h2 {
font-size: 24px;

View file

@ -0,0 +1,42 @@
/*
Copyright 2019 New Vector Ltd
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_CompleteSecurityBody {
width: 600px;
color: $authpage-primary-color;
background-color: $authpage-body-bg-color;
border-radius: 4px;
padding: 20px;
box-sizing: border-box;
h2 {
font-size: 24px;
font-weight: 600;
margin-top: 0;
}
h3 {
font-size: 14px;
font-weight: 600;
}
a:link,
a:hover,
a:visited {
@mixin mx_Dialog_link;
}
}

View file

@ -189,3 +189,37 @@ limitations under the License.
}
}
}
.mx_DevTools_VerificationRequest {
border: 1px solid #cccccc;
border-radius: 3px;
padding: 1px 5px;
margin-bottom: 6px;
font-family: $monospace-font-family;
dl {
display: grid;
grid-template-columns: max-content auto;
margin: 0;
}
dd {
grid-column-start: 2;
}
dd:empty {
color: #666666;
&::after {
content: "(empty)";
}
}
dt {
font-weight: bold;
grid-column-start: 1;
}
dt::after {
content: ":";
}
}

View file

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_DMInviteDialog_addressBar {
.mx_InviteDialog_addressBar {
display: flex;
flex-direction: row;
.mx_DMInviteDialog_editor {
.mx_InviteDialog_editor {
flex: 1;
width: 100%; // Needed to make the Field inside grow
background-color: $user-tile-hover-bg-color;
@ -28,7 +28,7 @@ limitations under the License.
overflow-x: hidden;
overflow-y: auto;
.mx_DMInviteDialog_userTile {
.mx_InviteDialog_userTile {
display: inline-block;
float: left;
position: relative;
@ -61,15 +61,26 @@ limitations under the License.
}
}
.mx_DMInviteDialog_goButton {
width: 48px;
.mx_InviteDialog_goButton {
min-width: 48px;
margin-left: 10px;
height: 25px;
line-height: 25px;
}
.mx_InviteDialog_buttonAndSpinner {
.mx_Spinner {
// Width and height are required to trick the layout engine.
width: 20px;
height: 20px;
margin-left: 5px;
display: inline-block;
vertical-align: middle;
}
}
}
.mx_DMInviteDialog_section {
.mx_InviteDialog_section {
padding-bottom: 10px;
h3 {
@ -80,7 +91,7 @@ limitations under the License.
}
}
.mx_DMInviteDialog_roomTile {
.mx_InviteDialog_roomTile {
cursor: pointer;
padding: 5px 10px;
@ -93,7 +104,7 @@ limitations under the License.
vertical-align: middle;
}
.mx_DMInviteDialog_roomTile_avatarStack {
.mx_InviteDialog_roomTile_avatarStack {
display: inline-block;
position: relative;
width: 36px;
@ -106,7 +117,7 @@ limitations under the License.
}
}
.mx_DMInviteDialog_roomTile_selected {
.mx_InviteDialog_roomTile_selected {
width: 36px;
height: 36px;
border-radius: 36px;
@ -120,7 +131,7 @@ limitations under the License.
height: 24px;
grid-column: 1;
grid-row: 1;
mask-image: url('$(res)/img/feather-customised/check.svg');
mask-image: url("$(res)/img/feather-customised/check.svg");
mask-size: 100%;
mask-repeat: no-repeat;
position: absolute;
@ -130,20 +141,20 @@ limitations under the License.
}
}
.mx_DMInviteDialog_roomTile_name {
.mx_InviteDialog_roomTile_name {
font-weight: 600;
font-size: 14px;
color: $primary-fg-color;
margin-left: 7px;
}
.mx_DMInviteDialog_roomTile_userId {
.mx_InviteDialog_roomTile_userId {
font-size: 12px;
color: $muted-fg-color;
margin-left: 7px;
}
.mx_DMInviteDialog_roomTile_time {
.mx_InviteDialog_roomTile_time {
text-align: right;
font-size: 12px;
color: $muted-fg-color;
@ -151,16 +162,16 @@ limitations under the License.
line-height: 36px; // Height of the avatar to keep the time vertically aligned
}
.mx_DMInviteDialog_roomTile_highlight {
.mx_InviteDialog_roomTile_highlight {
font-weight: 900;
}
}
// Many of these styles are stolen from mx_UserPill, but adjusted for the invite dialog.
.mx_DMInviteDialog_userTile {
.mx_InviteDialog_userTile {
margin-right: 8px;
.mx_DMInviteDialog_userTile_pill {
.mx_InviteDialog_userTile_pill {
background-color: $username-variant1-color;
border-radius: 12px;
display: inline-block;
@ -170,28 +181,48 @@ limitations under the License.
padding-right: 8px;
color: #ffffff; // this is fine without a var because it's for both themes
.mx_DMInviteDialog_userTile_avatar {
.mx_InviteDialog_userTile_avatar {
border-radius: 20px;
position: relative;
left: -5px;
top: 2px;
}
img.mx_DMInviteDialog_userTile_avatar {
img.mx_InviteDialog_userTile_avatar {
vertical-align: top;
}
.mx_DMInviteDialog_userTile_name {
.mx_InviteDialog_userTile_name {
vertical-align: top;
}
.mx_DMInviteDialog_userTile_threepidAvatar {
.mx_InviteDialog_userTile_threepidAvatar {
background-color: #ffffff; // this is fine without a var because it's for both themes
}
}
.mx_DMInviteDialog_userTile_remove {
.mx_InviteDialog_userTile_remove {
display: inline-block;
margin-left: 4px;
}
}
.mx_InviteDialog {
// Prevent the dialog from jumping around randomly when elements change.
height: 590px;
padding-left: 20px; // the design wants some padding on the left
}
.mx_InviteDialog_userSections {
margin-top: 10px;
overflow-y: auto;
padding-right: 45px;
height: 455px; // mx_InviteDialog's height minus some for the upper elements
}
// Right margin for the design. We could apply this to the whole dialog, but then the scrollbar
// for the user section gets weird.
.mx_InviteDialog_helpText,
.mx_InviteDialog_addressBar {
margin-right: 45px;
}

View file

@ -0,0 +1,65 @@
/*
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_KeyboardShortcutsDialog {
display: flex;
flex-wrap: wrap;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
margin-bottom: -50px;
max-height: 1100px; // XXX: this may need adjusting when adding new shortcuts
.mx_KeyboardShortcutsDialog_category {
width: 33.3333%; // 3 columns
margin: 0 0 40px;
& > div {
padding-left: 5px;
}
}
h3 {
margin: 0 0 10px;
}
h5 {
margin: 15px 0 5px;
font-weight: normal;
}
kbd {
padding: 5px;
border-radius: 4px;
background-color: $reaction-row-button-bg-color;
margin-right: 5px;
min-width: 20px;
text-align: center;
display: inline-block;
border: 1px solid $kbd-border-color;
box-shadow: 0 2px $kbd-border-color;
margin-bottom: 4px;
text-transform: capitalize;
& + kbd {
margin-left: 5px;
}
}
.mx_KeyboardShortcutsDialog_inline div {
display: inline;
}
}

View file

@ -0,0 +1,37 @@
/*
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_NewSessionReviewDialog_header {
display: flex;
align-items: center;
margin-top: 0;
}
.mx_NewSessionReviewDialog_headerIcon {
width: 24px;
height: 24px;
margin-right: 4px;
position: relative;
}
.mx_NewSessionReviewDialog_deviceName {
font-weight: 600;
}
.mx_NewSessionReviewDialog_deviceID {
font-size: 12px;
color: $notice-secondary-color;
}

View file

@ -56,16 +56,3 @@ limitations under the License.
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;
}

View file

@ -0,0 +1,112 @@
/*
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_RoomSettingsDialog_BridgeList {
padding: 0;
.mx_AccessibleButton {
display: inline;
margin: 0;
padding: 0;
}
}
.mx_RoomSettingsDialog_BridgeList li {
list-style-type: none;
padding: 5px;
margin-bottom: 8px;
border-width: 1px 1px;
border-color: $primary-hairline-color;
border-style: solid;
border-radius: 5px;
.column-icon {
float: left;
padding-right: 10px;
* {
border-radius: 5px;
border: 1px solid $input-darker-bg-color;
}
.noProtocolIcon {
width: 48px;
height: 48px;
background: $input-darker-bg-color;
border-radius: 5px;
}
.protocol-icon {
float: left;
margin-right: 5px;
img {
border-radius: 5px;
border-width: 1px 1px;
border-color: $primary-hairline-color;
}
span {
/* Correct letter placement */
left: auto;
}
}
}
.column-data {
display: inline-block;
width: 85%;
> h3 {
margin-top: 0px;
margin-bottom: 0px;
font-size: 16pt;
color: $primary-fg-color;
}
> * {
margin-top: 4px;
margin-bottom: 0;
}
.workspace-channel-details {
color: $primary-fg-color;
font-weight: 600;
.channel {
margin-left: 5px;
}
}
.mx_showMore {
display: block;
text-align: left;
margin-top: 10px;
}
.metadata {
color: $muted-fg-color;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 0;
}
.metadata.visible {
overflow-y: visible;
text-overflow: ellipsis;
white-space: normal;
}
}
}

View file

@ -14,14 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// CSS voodoo to support a gemini-scrollbar for the contents of the dialog
.mx_Dialog_unknownDevice .mx_Dialog {
// ideally we'd shrink the height to fit when needed, but in practice this
// is a pain in the ass. plus might as well make the dialog big given how
// important it is.
height: 100%;
}
.mx_UnknownDeviceDialog {
height: 100%;
display: flex;
@ -44,6 +36,7 @@ limitations under the License.
.mx_UnknownDeviceDialog .mx_Dialog_content {
margin-bottom: 24px;
overflow-y: scroll;
}
.mx_UnknownDeviceDialog_deviceList > li {

View file

@ -85,3 +85,9 @@ limitations under the License.
flex: 1;
white-space: nowrap;
}
.mx_CreateKeyBackupDialog {
details .mx_AccessibleButton {
margin: 1em 0; // emulate paragraph spacing because we can't put this button in a paragraph due to HTML rules
}
}

View file

@ -1,6 +1,6 @@
/*
Copyright 2018 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");
you may not use this file except in compliance with the License.
@ -15,6 +15,34 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_CreateSecretStorageDialog {
// Why you ask? Because CompleteSecurityBody is 600px so this is the width
// we end up when in there, but when in our own dialog we set our own width
// so need to fix it to something sensible as otherwise we'd end up either
// really wide or really narrow depending on the phase. I bet you wish you
// never asked.
width: 560px;
.mx_SettingsFlag {
display: flex;
}
.mx_SettingsFlag_label {
flex: 1 1 0;
min-width: 0;
font-weight: 600;
}
.mx_ToggleSwitch {
flex: 0 0 auto;
margin-left: 30px;
}
details .mx_AccessibleButton {
margin: 1em 0; // emulate paragraph spacing because we can't put this button in a paragraph due to HTML rules
}
}
.mx_CreateSecretStorageDialog .mx_Dialog_title {
/* TODO: Consider setting this for all dialog titles. */
margin-bottom: 1em;
@ -22,7 +50,7 @@ limitations under the License.
.mx_CreateSecretStorageDialog_primaryContainer {
/* FIXME: plinth colour in new theme(s). background-color: $accent-color; */
padding: 20px;
padding-top: 20px;
}
.mx_CreateSecretStorageDialog_primaryContainer::after {
@ -36,9 +64,13 @@ limitations under the License.
align-items: flex-start;
}
.mx_Field.mx_CreateSecretStorageDialog_passPhraseField {
margin-top: 0px;
}
.mx_CreateSecretStorageDialog_passPhraseHelp {
flex: 1;
height: 85px;
height: 64px;
margin-left: 20px;
font-size: 80%;
}
@ -47,16 +79,8 @@ limitations under the License.
width: 100%;
}
.mx_CreateSecretStorageDialog_passPhraseInput {
flex: none;
width: 250px;
border: 1px solid $accent-color;
border-radius: 5px;
padding: 10px;
margin-bottom: 1em;
}
.mx_CreateSecretStorageDialog_passPhraseMatch {
width: 200px;
margin-left: 20px;
}
@ -82,6 +106,10 @@ limitations under the License.
align-items: center;
}
.mx_CreateSecretStorageDialog_recoveryKeyButtons .mx_AccessibleButton {
margin-right: 10px;
}
.mx_CreateSecretStorageDialog_recoveryKeyButtons button {
flex: 1;
white-space: nowrap;

View file

@ -1,5 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@ -15,70 +15,149 @@ limitations under the License.
*/
.mx_NetworkDropdown {
height: 32px;
position: relative;
}
width: max-content;
padding-right: 32px;
margin-left: auto;
margin-right: 9px;
margin-top: 12px;
.mx_NetworkDropdown_input {
position: relative;
border-radius: 3px;
border: 1px solid $strong-input-border-color;
font-weight: 300;
font-size: 13px;
user-select: none;
}
.mx_NetworkDropdown_arrow {
border-color: $primary-fg-color transparent transparent;
border-style: solid;
border-width: 5px 5px 0;
display: block;
height: 0;
position: absolute;
right: 10px;
top: 16px;
width: 0;
}
.mx_NetworkDropdown_networkoption {
height: 37px;
line-height: 37px;
padding-left: 8px;
padding-right: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.mx_NetworkDropdown_networkoption img {
margin: 5px;
width: 25px;
vertical-align: middle;
}
input.mx_NetworkDropdown_networkoption, input.mx_NetworkDropdown_networkoption:focus {
border: 0;
padding-top: 0;
padding-bottom: 0;
.mx_AccessibleButton {
width: max-content;
}
}
.mx_NetworkDropdown_menu {
position: absolute;
left: -1px;
right: -1px;
top: 100%;
z-index: 2;
min-width: 204px;
margin: 0;
padding: 0px;
border-radius: 3px;
border: 1px solid $accent-color;
box-sizing: border-box;
border-radius: 4px;
border: 1px solid $dialog-close-fg-color;
background-color: $primary-bg-color;
}
.mx_NetworkDropdown_menu .mx_NetworkDropdown_networkoption:hover {
background-color: $focus-bg-color;
}
.mx_NetworkDropdown_menu_network {
font-weight: bold;
}
.mx_NetworkDropdown_server {
padding: 12px 0;
border-bottom: 1px solid $input-darker-fg-color;
.mx_NetworkDropdown_server_title {
padding: 0 10px;
font-size: 15px;
font-weight: 600;
line-height: 20px;
margin-bottom: 4px;
// remove server button
.mx_AccessibleButton {
position: absolute;
display: inline;
right: 12px;
height: 16px;
width: 16px;
margin-top: 4px;
&::after {
content: "";
position: absolute;
width: 16px;
height: 16px;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url('$(res)/img/feather-customised/x.svg');
background-color: $notice-primary-color;
}
}
}
.mx_NetworkDropdown_server_subtitle {
padding: 0 10px;
font-size: 10px;
line-height: 14px;
margin-top: -4px;
margin-bottom: 4px;
color: $muted-fg-color;
}
.mx_NetworkDropdown_server_network {
font-size: 12px;
line-height: 16px;
padding: 4px 10px;
cursor: pointer;
position: relative;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
&[aria-checked=true]::after {
content: "";
position: absolute;
width: 16px;
height: 16px;
right: 10px;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url('$(res)/img/feather-customised/check.svg');
background-color: $input-valid-border-color;
}
}
}
.mx_NetworkDropdown_server_add,
.mx_NetworkDropdown_server_network {
&:hover {
background-color: $header-panel-bg-color;
}
}
.mx_NetworkDropdown_server_add {
padding: 16px 10px 16px 32px;
position: relative;
border-radius: 0 0 4px 4px;
&::before {
content: "";
position: absolute;
width: 16px;
height: 16px;
left: 7px;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url('$(res)/img/feather-customised/plus.svg');
background-color: $muted-fg-color;
}
}
.mx_NetworkDropdown_handle {
position: relative;
&::after {
content: "";
position: absolute;
width: 24px;
height: 24px;
right: -28px; // - (24 + 4)
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url('$(res)/img/feather-customised/chevron-down.svg');
background-color: $primary-fg-color;
}
.mx_NetworkDropdown_handle_server {
color: $muted-fg-color;
font-size: 12px;
}
}
.mx_NetworkDropdown_dialog .mx_Dialog {
width: 45vw;
}

View file

@ -18,7 +18,6 @@ limitations under the License.
display: flex;
padding-left: 9px;
padding-right: 9px;
margin: 0 5px 0 0 !important;
}
.mx_DirectorySearchBox_joinButton {

View file

@ -20,14 +20,21 @@ limitations under the License.
}
.mx_EditableItem {
display: flex;
margin-bottom: 5px;
margin-left: 15px;
}
.mx_EditableItem_delete {
order: 3;
margin-right: 5px;
cursor: pointer;
vertical-align: middle;
width: 14px;
height: 14px;
mask-image: url('$(res)/img/feather-customised/cancel.svg');
mask-repeat: no-repeat;
background-color: $warning-color;
mask-size: 100%;
}
.mx_EditableItem_email {
@ -36,12 +43,19 @@ limitations under the License.
.mx_EditableItem_promptText {
margin-right: 10px;
order: 2;
}
.mx_EditableItem_confirmBtn {
margin-right: 5px;
}
.mx_EditableItem_item {
flex: auto 1 0;
order: 1;
}
.mx_EditableItemList_label {
margin-bottom: 5px;
}

View file

@ -13,6 +13,11 @@
padding-left: 5px;
}
a.mx_Pill {
word-break: break-all;
display: inline;
}
/* More specific to override `.markdown-body a` text-decoration */
.mx_EventTile_content .markdown-body a.mx_Pill {
text-decoration: none;

View file

@ -1,5 +1,5 @@
/*
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");
you may not use this file except in compliance with the License.
@ -14,60 +14,62 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_KeyVerification {
.mx_cryptoEvent {
display: grid;
grid-template-columns: 24px minmax(0, 1fr) min-content;
&.mx_KeyVerification_icon::after {
&.mx_cryptoEvent_icon::after {
grid-column: 1;
grid-row: 1 / 3;
width: 12px;
width: 16px;
height: 16px;
content: "";
mask-image: url("$(res)/img/e2e/normal.svg");
mask-repeat: no-repeat;
mask-size: 100%;
background-image: url("$(res)/img/e2e/normal.svg");
background-repeat: no-repeat;
background-size: 100%;
margin-top: 4px;
background-color: $primary-fg-color;
}
&.mx_KeyVerification_icon_verified::after {
mask-image: url("$(res)/img/e2e/verified.svg");
background-color: $accent-color;
&.mx_cryptoEvent_icon_verified::after {
background-image: url("$(res)/img/e2e/verified.svg");
}
.mx_KeyVerification_title, .mx_KeyVerification_subtitle, .mx_KeyVerification_state {
&.mx_cryptoEvent_icon_warning::after {
background-image: url("$(res)/img/e2e/warning.svg");
}
.mx_cryptoEvent_title, .mx_cryptoEvent_subtitle, .mx_cryptoEvent_state {
overflow-wrap: break-word;
}
.mx_KeyVerification_title {
.mx_cryptoEvent_title {
font-weight: 600;
font-size: 15px;
grid-column: 2;
grid-row: 1;
}
.mx_KeyVerification_subtitle {
.mx_cryptoEvent_subtitle {
grid-column: 2;
grid-row: 2;
}
.mx_KeyVerification_state, .mx_KeyVerification_subtitle {
.mx_cryptoEvent_state, .mx_cryptoEvent_subtitle {
font-size: 12px;
}
.mx_KeyVerification_state, .mx_KeyVerification_buttons {
.mx_cryptoEvent_state, .mx_cryptoEvent_buttons {
grid-column: 3;
grid-row: 1 / 3;
}
.mx_KeyVerification_buttons {
.mx_cryptoEvent_buttons {
align-items: center;
display: flex;
}
.mx_KeyVerification_state {
.mx_cryptoEvent_state {
width: 130px;
padding: 10px 20px;
margin: auto 0;

View file

@ -0,0 +1,26 @@
/*
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_UserInfo {
.mx_EncryptionInfo_spinner {
.mx_Spinner {
margin-top: 25px;
margin-bottom: 15px;
}
text-align: center;
}
}

View file

@ -23,15 +23,23 @@ limitations under the License.
font-size: 12px;
.mx_UserInfo_cancel {
height: 16px;
width: 16px;
padding: 10px 0 10px 10px;
cursor: pointer;
mask-image: url('$(res)/img/minimise.svg');
mask-repeat: no-repeat;
mask-position: 16px center;
background-color: $rightpanel-button-color;
position: absolute;
top: 0;
border-radius: 4px;
background-color: $dark-panel-bg-color;
margin: 9px;
z-index: 1; // render on top of the right panel
div {
height: 16px;
width: 16px;
padding: 4px;
mask-image: url('$(res)/img/minimise.svg');
mask-repeat: no-repeat;
mask-position: 7px center;
background-color: $rightpanel-button-color;
}
}
h2 {
@ -41,12 +49,17 @@ limitations under the License.
}
.mx_UserInfo_container {
padding: 0 16px 16px 16px;
padding: 8px 16px;
}
.mx_UserInfo_separator {
border-bottom: 1px solid lightgray;
}
.mx_UserInfo_memberDetailsContainer {
padding-top: 0;
padding-bottom: 0;
margin-bottom: 8px;
}
.mx_RoomTile_nameContainer {
@ -68,6 +81,7 @@ limitations under the License.
.mx_UserInfo_avatar > div {
max-width: 30vh;
margin: 0 auto;
transition: 0.5s;
}
.mx_UserInfo_avatar > div > div {
@ -95,8 +109,9 @@ limitations under the License.
justify-content: center;
// override the calculated sizes so that the letter isn't HUGE
font-size: 26px !important;
font-size: 56px !important;
width: 100% !important;
transition: font-size 0.5s;
}
.mx_UserInfo_avatar .mx_BaseAvatar.mx_BaseAvatar_image {
@ -122,12 +137,19 @@ limitations under the License.
font-size: 18px;
line-height: 25px;
flex: 1;
overflow-x: auto;
max-height: 50px;
display: flex;
justify-content: center;
align-items: center;
// limit to 2 lines, show an ellipsis if it overflows
// this looks webkit specific but is supported by Firefox 68+
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
text-overflow: ellipsis;
.mx_E2EIcon {
margin: 5px;
}
@ -196,10 +218,9 @@ limitations under the License.
padding-bottom: 16px;
}
.mx_UserInfo_scrollContainer .mx_UserInfo_container {
.mx_UserInfo_container:not(.mx_UserInfo_separator) {
padding-top: 16px;
padding-bottom: 0;
border-bottom: none;
> :not(h3) {
margin-left: 8px;
@ -242,17 +263,25 @@ limitations under the License.
.mx_UserInfo_expand {
display: flex;
margin-top: 11px;
color: $accent-color;
}
}
.mx_UserInfo_verify {
.mx_UserInfo_wideButton {
display: block;
background-color: $accent-color;
color: $accent-fg-color;
border-radius: 4px;
padding: 7px 1.5em;
text-align: center;
margin: 16px 0;
}
button.mx_UserInfo_wideButton {
width: 100%; // FIXME get rid of this once we get rid of DialogButtons here
}
}
.mx_UserInfo.mx_UserInfo_smallAvatar {
.mx_UserInfo_avatar > div {
max-width: 72px;
margin: 0 auto;
}
.mx_UserInfo_avatar .mx_BaseAvatar_initial {
font-size: 40px !important; // override the other override because here the avatar is smaller
}
}

View file

@ -0,0 +1,121 @@
/*
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_UserInfo {
.mx_EncryptionPanel_cancel {
mask: url('$(res)/img/feather-customised/cancel.svg');
mask-repeat: no-repeat;
mask-position: center;
mask-size: cover;
width: 14px;
height: 14px;
background-color: $settings-subsection-fg-color;
cursor: pointer;
position: absolute;
z-index: 100;
top: 14px;
right: 14px;
}
.mx_VerificationPanel_verified_section .mx_E2EIcon {
// Override general user info margin
margin: 0 auto !important;
}
.mx_VerificationPanel_qrCode {
padding: 4px 4px 0 4px;
background: white;
border-radius: 4px;
width: max-content;
max-width: 100%;
// Override general user info margin
margin: 0 auto !important;
canvas {
// override height and width which are set on the element directly
height: auto !important;
width: 100% !important;
max-width: 240px;
}
}
}
// Special case styling for EncryptionPanel in a Modal dialog
.mx_Dialog, .mx_CompleteSecurity_body {
.mx_VerificationPanel_QRPhase_startOptions {
display: flex;
margin-top: 10px;
margin-bottom: 10px;
align-items: stretch;
> .mx_VerificationPanel_QRPhase_betweenText {
width: 50px;
vertical-align: middle;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
}
.mx_VerificationPanel_QRPhase_startOption {
background-color: $user-tile-hover-bg-color;
border-radius: 10px;
flex: 1;
display: flex;
padding: 10px;
align-items: center;
flex-direction: column;
position: relative;
canvas, .mx_VerificationPanel_QRPhase_noQR {
width: 220px !important;
height: 220px !important;
background-color: #fff;
border-radius: 4px;
vertical-align: middle;
text-align: center;
padding: 10px;
}
> p {
font-weight: 700;
}
.mx_VerificationPanel_QRPhase_helpText {
font-size: 14px;
margin-top: 71px;
text-align: center;
}
.mx_AccessibleButton {
position: absolute;
bottom: 30px;
}
}
}
// EncryptionPanel when verification is done
.mx_VerificationPanel_verified_section {
// center the big shield icon
.mx_E2EIcon {
margin: 0 auto;
}
// right align the "Got it" button
.mx_AccessibleButton {
float: right;
}
}
}

View file

@ -26,3 +26,21 @@ limitations under the License.
outline: none;
box-shadow: none;
}
.mx_AliasSettings {
summary {
cursor: pointer;
color: $accent-color;
font-weight: 600;
list-style: none;
// list-style doesn't do it for webkit
&::-webkit-details-marker {
display: none;
}
}
.mx_AliasSettings_localAliasHeader {
margin-top: 35px;
}
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
@ -19,6 +20,15 @@ limitations under the License.
align-items: center;
color: $primary-fg-color;
cursor: pointer;
.mx_E2EIcon {
margin: 0;
position: absolute;
bottom: 2px;
right: 7px;
height: 15px;
width: 15px;
}
}
.mx_EntityTile:hover {
@ -30,7 +40,7 @@ limitations under the License.
content: "";
position: absolute;
top: calc(50% - 8px); // center
right: 10px;
right: -8px;
mask: url('$(res)/img/member_chevron.png');
mask-repeat: no-repeat;
width: 16px;
@ -64,14 +74,6 @@ limitations under the License.
position: relative;
}
.mx_EntityTile_power {
position: absolute;
width: 16px;
height: 17px;
top: 0px;
right: 6px;
}
.mx_EntityTile_name,
.mx_GroupRoomTile_name {
flex: 1 1 0;
@ -83,6 +85,7 @@ limitations under the License.
.mx_EntityTile_details {
overflow: hidden;
flex: 1;
}
.mx_EntityTile_ellipsis .mx_EntityTile_name {
@ -112,10 +115,6 @@ limitations under the License.
opacity: 0.25;
}
.mx_EntityTile:not(.mx_EntityTile_noHover):hover .mx_EntityTile_name {
font-size: 13px;
}
.mx_EntityTile_subtext {
font-size: 11px;
opacity: 0.5;
@ -123,3 +122,17 @@ limitations under the License.
white-space: nowrap;
text-overflow: clip;
}
.mx_EntityTile_power {
padding-inline-start: 6px;
font-size: 10px;
color: $notice-secondary-color;
max-width: 6em;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_EntityTile:hover .mx_EntityTile_power {
display: none;
}

View file

@ -112,8 +112,6 @@ limitations under the License.
.mx_EventTile_line, .mx_EventTile_reply {
position: relative;
/* ideally should be 100px, but 95px gives us a max thumbnail size of 800x600, which is nice */
margin-right: 110px;
padding-left: 65px; /* left gutter */
padding-top: 4px;
padding-bottom: 2px;
@ -122,6 +120,13 @@ limitations under the License.
line-height: 22px;
}
.mx_RoomView_timeline_rr_enabled {
.mx_EventTile_line, .mx_EventTile_reply {
/* ideally should be 100px, but 95px gives us a max thumbnail size of 800x600, which is nice */
margin-right: 110px;
}
}
.mx_EventTile_bubbleContainer {
display: grid;
grid-template-columns: 1fr 100px;
@ -367,6 +372,11 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
opacity: 1;
}
.mx_EventTile_e2eIcon_unknown {
background-image: url('$(res)/img/e2e/warning.svg');
opacity: 1;
}
.mx_EventTile_e2eIcon_unencrypted {
background-image: url('$(res)/img/e2e/warning.svg');
opacity: 1;
@ -415,7 +425,8 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
}
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line {
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
padding-left: 60px;
}
@ -427,8 +438,13 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
border-left: $e2e-unverified-color 5px solid;
}
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
border-left: $e2e-unknown-color 5px solid;
}
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line {
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unknown.mx_EventTile_info .mx_EventTile_line {
padding-left: 78px;
}
@ -439,14 +455,16 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp {
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp {
left: 3px;
width: auto;
}
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > .mx_EventTile_e2eIcon,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > .mx_EventTile_e2eIcon {
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > .mx_EventTile_e2eIcon,
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > .mx_EventTile_e2eIcon {
display: block;
left: 41px;
}

View file

@ -0,0 +1,58 @@
/*
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.
*/
@define-mixin mx_InviteOnlyIcon {
width: 12px;
height: 12px;
position: relative;
display: block !important;
}
@define-mixin mx_InviteOnlyIcon_padlock {
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;
}
.mx_InviteOnlyIcon_large {
@mixin mx_InviteOnlyIcon;
margin: 0 4px;
&::before {
@mixin mx_InviteOnlyIcon_padlock;
width: 12px;
height: 12px;
}
}
.mx_InviteOnlyIcon_small {
@mixin mx_InviteOnlyIcon;
left: -2px;
&::before {
@mixin mx_InviteOnlyIcon_padlock;
width: 10px;
height: 10px;
}
}

View file

@ -76,6 +76,8 @@ limitations under the License.
left: 60px;
margin-right: 0; // Counteract the E2EIcon class
margin-left: 3px; // Counteract the E2EIcon class
width: 15px;
height: 15px;
}
.mx_MessageComposer_noperm_error {

View file

@ -41,7 +41,7 @@ limitations under the License.
overflow-x: visible;
}
.mx_AutoHideScrollbar_offset {
.mx_AutoHideScrollbar {
display: flex;
flex-direction: row;
height: 100%;

View file

@ -19,7 +19,12 @@ limitations under the License.
border-bottom: 1px solid $primary-hairline-color;
.mx_E2EIcon {
margin: 0 5px;
margin: 0;
position: absolute;
bottom: -2px;
right: -6px;
height: 15px;
width: 15px;
}
}
@ -171,6 +176,7 @@ limitations under the License.
width: 28px;
height: 28px;
margin: 0 7px;
position: relative;
}
.mx_RoomHeader_avatar .mx_BaseAvatar_image {
@ -263,24 +269,3 @@ limitations under the License.
.mx_RoomHeader_pinsIndicatorUnread {
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;
}
}

View file

@ -33,6 +33,13 @@ limitations under the License.
}
}
h3,
.mx_RoomPreviewBar_message p {
// break-word, with fallback to break-all, which is wider supported
word-break: break-all;
word-break: break-word;
}
.mx_Spinner {
width: auto;
height: auto;
@ -117,12 +124,17 @@ limitations under the License.
.mx_RoomPreviewBar_actions {
flex-direction: column-reverse;
.mx_AccessibleButton {
padding: 7px 50px;//extra wide
padding: 7px 50px; //extra wide
}
& > * {
margin-top: 12px;
}
.mx_AccessibleButton.mx_AccessibleButton_kind_primary {
// to account for the padding of the primary button which causes inconsistent look between
// subsequent secondary (text) buttons
margin-bottom: 7px;
}
}
}

View file

@ -98,9 +98,22 @@ limitations under the License.
z-index: 2;
}
// Note we match .mx_E2EIcon to make sure this matches more tightly than just
// .mx_E2EIcon on its own
.mx_RoomTile_e2eIcon.mx_E2EIcon {
height: 14px;
width: 14px;
display: block;
position: absolute;
bottom: -2px;
right: -5px;
z-index: 1;
margin: 0;
}
.mx_RoomTile_name {
font-size: 14px;
padding: 0 6px;
padding: 0 4px;
color: $roomtile-name-color;
white-space: nowrap;
overflow-x: hidden;
@ -142,10 +155,11 @@ limitations under the License.
}
}
// toggle menuButton and badge on hover/menu displayed
// toggle menuButton and badge on menu displayed
.mx_RoomTile_menuDisplayed,
// or on keyboard focus of room tile
.mx_RoomTile.focus-visible:focus-within,
.mx_LeftPanel_container:not(.collapsed) .mx_RoomTile:focus-within,
// or on pointer hover
.mx_LeftPanel_container:not(.collapsed) .mx_RoomTile:hover {
.mx_RoomTile_menuButton {
display: block;
@ -200,31 +214,3 @@ limitations under the License.
.mx_GroupInviteTile .mx_RoomTile_name {
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;
}
}

View file

@ -1,77 +0,0 @@
/*
Copyright 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SearchableEntityList {
display: flex;
flex-direction: column;
}
.mx_SearchableEntityList_query {
font-family: $font-family;
border-radius: 3px;
border: 1px solid $input-border-color;
padding: 9px;
color: $primary-fg-color;
background-color: $primary-bg-color;
margin-left: 3px;
font-size: 15px;
margin-bottom: 8px;
width: 189px;
}
.mx_SearchableEntityList_query::-moz-placeholder {
color: $primary-fg-color;
opacity: 0.5;
font-size: 12px;
}
.mx_SearchableEntityList_query::-webkit-input-placeholder {
color: $primary-fg-color;
opacity: 0.5;
font-size: 12px;
}
.mx_SearchableEntityList_listWrapper {
flex: 1;
overflow-y: auto;
}
.mx_SearchableEntityList_list {
display: table;
table-layout: fixed;
width: 100%;
}
.mx_SearchableEntityList_list .mx_EntityTile_chevron {
display: none;
}
.mx_SearchableEntityList_hrWrapper {
width: 100%;
flex: 0 0 auto;
}
.mx_SearchableEntityList hr {
height: 1px;
border: 0px;
color: $primary-fg-color;
background-color: $primary-fg-color;
margin-right: 15px;
margin-top: 11px;
margin-bottom: 11px;
}

View file

@ -25,19 +25,16 @@ limitations under the License.
}
.mx_TopUnreadMessagesBar::after {
content: "·";
content: "";
position: absolute;
top: -8px;
left: 11px;
width: 16px;
height: 16px;
width: 4px;
height: 4px;
border-radius: 16px;
font-weight: 600;
font-size: 30px;
line-height: 14px;
text-align: center;
color: $secondary-accent-color;
background-color: $accent-color;
background-color: $secondary-accent-color;
border: 6px solid $accent-color;
pointer-events: none;
}
.mx_TopUnreadMessagesBar_scrollUp {
@ -54,8 +51,30 @@ limitations under the License.
position: absolute;
width: 38px;
height: 38px;
mask: url('$(res)/img/icon-jump-to-first-unread.svg');
mask-image: url('$(res)/img/icon-jump-to-first-unread.svg');
mask-repeat: no-repeat;
mask-position: 9px 13px;
background: $roomtile-name-color;
}
.mx_TopUnreadMessagesBar_markAsRead {
display: block;
width: 18px;
height: 18px;
background: $primary-bg-color;
border: 1.3px solid $roomtile-name-color;
border-radius: 10px;
margin: 5px auto;
}
.mx_TopUnreadMessagesBar_markAsRead::before {
content: "";
position: absolute;
width: 18px;
height: 18px;
mask-image: url('$(res)/img/cancel.svg');
mask-repeat: no-repeat;
mask-size: 10px;
mask-position: 4px 4px;
background: $roomtile-name-color;
}

View file

@ -31,14 +31,15 @@ limitations under the License.
margin-left: -12px;
}
.mx_WhoIsTypingTile_avatars .mx_BaseAvatar_image {
border: 1px solid $primary-bg-color;
}
.mx_WhoIsTypingTile_avatars .mx_BaseAvatar_initial {
padding-top: 1px;
}
.mx_WhoIsTypingTile_avatars .mx_BaseAvatar {
border: 1px solid $primary-bg-color;
border-radius: 40px;
}
.mx_WhoIsTypingTile_remainingAvatarPlaceholder {
position: relative;
display: inline-block;

View file

@ -18,7 +18,7 @@ limitations under the License.
display: table;
table-layout: fixed;
width: 880px;
border-spacing: 2px;
border-spacing: 10px;
}
.mx_DevicesPanel_header {
@ -32,7 +32,11 @@ limitations under the License.
.mx_DevicesPanel_header > div {
display: table-cell;
vertical-align: bottom;
vertical-align: middle;
}
.mx_DevicesPanel_header .mx_DevicesPanel_deviceName {
width: 50%;
}
.mx_DevicesPanel_header .mx_DevicesPanel_deviceLastSeen {

View file

@ -0,0 +1,20 @@
/*
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_E2eAdvancedPanel_settingLongDescription {
margin-right: 150px;
}

View file

@ -45,9 +45,17 @@ limitations under the License.
margin: 10px 100px 10px 0; // Align with the rest of the view
}
.mx_SettingsTab_section .mx_SettingsFlag {
margin-right: 100px;
margin-bottom: 10px;
.mx_SettingsTab_section {
margin-bottom: 24px;
.mx_SettingsFlag {
margin-right: 100px;
margin-bottom: 10px;
}
&.mx_SettingsTab_subsectionText .mx_SettingsFlag {
margin-right: 0px !important;
}
}
.mx_SettingsTab_section .mx_SettingsFlag .mx_SettingsFlag_label {

View file

@ -14,6 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_PreferencesUserSettingsTab .mx_Field {
@mixin mx_Settings_fullWidthField;
.mx_PreferencesUserSettingsTab {
.mx_Field {
@mixin mx_Settings_fullWidthField;
}
.mx_SettingsTab_section {
margin-bottom: 30px;
}
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2019 New Vector Ltd.
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.
@ -28,21 +29,35 @@ limitations under the License.
.mx_VerificationShowSas_emojiSas {
text-align: center;
display: flex;
flex-wrap: wrap;
justify-content: center;
margin: 25px 0;
}
.mx_VerificationShowSas_emojiSas_block {
display: inline-block;
margin-left: 15px;
margin-right: 15px;
text-align: center;
margin-bottom: 20px;
margin-bottom: 16px;
position: relative;
width: 52px;
}
.mx_Dialog .mx_VerificationShowSas_emojiSas_block,
.mx_AuthPage_modal .mx_VerificationShowSas_emojiSas_block {
width: 60px;
}
.mx_VerificationShowSas_emojiSas_emoji {
font-size: 48px;
font-size: 32px;
}
.mx_VerificationShowSas_emojiSas_label {
text-align: center;
font-weight: bold;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 12px;
}
.mx_VerificationShowSas_emojiSas_break {
flex-basis: 100%;
}

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="17px" viewBox="-1 -1 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: sketchtool 3.4.4 (395) - http://www.bohemiancoding.com/sketch -->
<title>icons_owner</title>
<desc>Created with sketchtool.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="02_19-Room-contextual-menu-hover" sketch:type="MSArtboardGroup" transform="translate(-1000.000000, -128.000000)">
<g id="people_open" sketch:type="MSLayerGroup" transform="translate(966.000000, 59.000000)">
<g id="icons_owner" transform="translate(35.000000, 70.000000)" sketch:type="MSShapeGroup">
<path d="M0.441894529,1.80537109 C2.59277353,3.03442388 4.25305977,2.17675781 5.9832796,0.805371094 C8.01666135,2.17675787 9.50756797,3.12670903 11.6293941,1.80537109 C11.6293941,7.01538067 11.9379879,12.2253912 5.9832796,12.2253906 C0.0285712975,12.2253901 0.441894531,7.01538067 0.441894529,1.80537109 Z" id="Path-2-Copy" stroke="#FFFFFF" fill="#F6A623"></path>
<polygon id="Star-1" fill="#FFFFFF" points="6 8.8 3.88397309 9.91246118 4.28809827 7.55623059 2.57619654 5.88753882 4.94198655 5.54376941 6 3.4 7.05801345 5.54376941 9.42380346 5.88753882 7.71190173 7.55623059 8.11602691 9.91246118 "></polygon>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 9L12 15L18 9" stroke="#2E2F32" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 195 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

View file

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="16px" height="17px" viewBox="-1 -1 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<!-- Generator: sketchtool 3.4.4 (395) - http://www.bohemiancoding.com/sketch -->
<title>icons_admin</title>
<desc>Created with sketchtool.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="02_19-Room-contextual-menu-hover" sketch:type="MSArtboardGroup" transform="translate(-1000.000000, -172.000000)" stroke="#FFFFFF" fill="#C2C5AF">
<g id="people_open" sketch:type="MSLayerGroup" transform="translate(966.000000, 59.000000)">
<g id="icons_admin" transform="translate(35.000000, 114.000000)" sketch:type="MSShapeGroup">
<path d="M0.441894529,1.80537109 C2.59277353,3.03442388 4.25305977,2.17675781 5.9832796,0.805371094 C8.01666135,2.17675787 9.50756797,3.12670903 11.6293941,1.80537109 C11.6293941,7.01538067 11.9379879,12.2253912 5.9832796,12.2253906 C0.0285712975,12.2253901 0.441894531,7.01538067 0.441894529,1.80537109 Z" id="Path-2-Copy-2"></path>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -165,6 +165,8 @@ $reaction-row-button-hover-border-color: $header-panel-text-primary-color;
$reaction-row-button-selected-bg-color: #1f6954;
$reaction-row-button-selected-border-color: $accent-color;
$kbd-border-color: #000000;
$tooltip-timeline-bg-color: $tagpanel-bg-color;
$tooltip-timeline-fg-color: #ffffff;
@ -219,10 +221,6 @@ $user-tile-hover-bg-color: $header-panel-bg-color;
filter: invert(1);
}
.gm-scrollbar .thumb {
filter: invert(1);
}
// markdown overrides:
.mx_EventTile_content .markdown-body pre:hover {
border-color: #808080 !important; // inverted due to rules below

View file

@ -5,9 +5,12 @@
Arial empirically gets it right, hence prioritising Arial here. */
/* We fall through to Twemoji for emoji rather than falling through
to native Emoji fonts (if any) to ensure cross-browser consistency */
$font-family: Nunito, Twemoji, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', Arial, Helvetica, Sans-Serif;
/* Noto Color Emoji contains digits, in fixed-width, therefore causing
digits in flowed text to stand out.
TODO: Consider putting all emoji fonts to the end rather than the front. */
$font-family: Nunito, Twemoji, 'Apple Color Emoji', 'Segoe UI Emoji', Arial, Helvetica, Sans-Serif, 'Noto Color Emoji';
$monospace-font-family: Inconsolata, Twemoji, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', Courier, monospace;
$monospace-font-family: Inconsolata, Twemoji, 'Apple Color Emoji', 'Segoe UI Emoji', Courier, monospace, 'Noto Color Emoji';
// unified palette
// try to use these colors when possible
@ -224,6 +227,7 @@ $copy-button-url: "$(res)/img/icon_copy_message.svg";
// e2e
$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
$e2e-unknown-color: #e8bf37;
$e2e-unverified-color: #e8bf37;
$e2e-warning-color: #ba6363;
@ -286,6 +290,8 @@ $reaction-row-button-hover-border-color: $focus-bg-color;
$reaction-row-button-selected-bg-color: #e9fff9;
$reaction-row-button-selected-border-color: $accent-color;
$kbd-border-color: $reaction-row-button-border-color;
$tooltip-timeline-bg-color: $tagpanel-bg-color;
$tooltip-timeline-fg-color: #ffffff;

View file

@ -1,25 +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
RIOT_WEB_DIR=riot-web
REACT_SDK_DIR=`pwd`
yarn link
scripts/fetchdep.sh vector-im riot-web
pushd "$RIOT_WEB_DIR"
yarn link matrix-js-sdk
yarn link matrix-react-sdk
yarn install
yarn build
popd

View file

@ -6,30 +6,23 @@
set -ev
upload_logs() {
echo "--- Uploading logs"
buildkite-agent artifact upload "logs/**/*;synapse/installations/consent/homeserver.log"
}
handle_error() {
EXIT_CODE=$?
if [ $TESTS_STARTED -eq 1 ]; then
upload_logs
fi
exit $EXIT_CODE
}
trap 'handle_error' ERR
RIOT_WEB_DIR=riot-web
REACT_SDK_DIR=`pwd`
echo "--- Building Riot"
scripts/ci/build.sh
scripts/ci/layered-riot-web.sh
cd ../riot-web
riot_web_dir=`pwd`
CI_PACKAGE=true yarn build
cd ../matrix-react-sdk
# run end to end tests
pushd test/end-to-end-tests
ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web
ln -s $riot_web_dir riot/riot-web
# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh
# CHROME_PATH=$(which google-chrome-stable) ./run.sh
echo "--- Install synapse & other dependencies"

View file

@ -6,9 +6,9 @@ scripts/fetchdep.sh matrix-org matrix-js-sdk
pushd matrix-js-sdk
yarn link
yarn install
yarn install $@
yarn build
popd
yarn link matrix-js-sdk
yarn install
yarn install $@

31
scripts/ci/layered-riot-web.sh Executable file
View file

@ -0,0 +1,31 @@
#!/bin/bash
# Creates an environment similar to one that riot-web would expect for
# development. This means going one directory up (and assuming we're in
# a directory like /workdir/matrix-react-sdk) and putting riot-web and
# the js-sdk there.
cd ../ # Assume we're at something like /workdir/matrix-react-sdk
# Set up the js-sdk first
matrix-react-sdk/scripts/fetchdep.sh matrix-org matrix-js-sdk
pushd matrix-js-sdk
yarn link
yarn install
popd
# Now set up the react-sdk
pushd matrix-react-sdk
yarn link matrix-js-sdk
yarn link
yarn install
popd
# Finally, set up riot-web
matrix-react-sdk/scripts/fetchdep.sh vector-im riot-web
pushd riot-web
yarn link matrix-js-sdk
yarn link matrix-react-sdk
yarn install
yarn build:res
popd

View file

@ -6,9 +6,7 @@
set -ev
RIOT_WEB_DIR=riot-web
scripts/ci/build.sh
pushd "$RIOT_WEB_DIR"
scripts/ci/layered-riot-web.sh
cd ../riot-web
yarn build:genfiles # so the tests can run. Faster version of `build`
yarn test
popd

View file

@ -17,7 +17,7 @@ clone() {
if [ -n "$branch" ]
then
echo "Trying to use $org/$repo#$branch"
git clone git://github.com/$org/$repo.git $repo --branch "$branch" && exit 0
git clone git://github.com/$org/$repo.git $repo --branch "$branch" --depth 1 && exit 0
fi
}

View file

@ -237,7 +237,7 @@ const walkOpts = {
const fullPath = path.join(root, fileStats.name);
let trs;
if (fileStats.name.endsWith('.js')) {
if (fileStats.name.endsWith('.js') || fileStats.name.endsWith('.tsx')) {
trs = getTranslationsJs(fullPath);
} else if (fileStats.name.endsWith('.html')) {
trs = getTranslationsOther(fullPath);

View file

@ -14,8 +14,10 @@ echo "generating $out"
# autogenerated file: run scripts/generate-eslint-error-ignore-file to update.
EOF
./node_modules/.bin/eslint --no-ignore -f json src test |
./node_modules/.bin/eslint -f json src test |
jq -r '.[] | select((.errorCount + .warningCount) > 0) | .filePath' |
sed -e 's/.*matrix-react-sdk\///';
} > "$out"
# also append rules from eslintignore file
cat .eslintignore >> $out

View file

@ -2,17 +2,20 @@
var fs = require('fs');
var path = require('path');
var glob = require('glob');
var args = require('optimist').argv;
var args = require('minimist')(process.argv);
var chokidar = require('chokidar');
var componentIndex = path.join('src', 'component-index.js');
var componentIndexTmp = componentIndex+".tmp";
var componentsDir = path.join('src', 'components');
var componentGlob = '**/*.js';
var componentJsGlob = '**/*.js';
var componentTsGlob = '**/*.tsx';
var prevFiles = [];
function reskindex() {
var files = glob.sync(componentGlob, {cwd: componentsDir}).sort();
var jsFiles = glob.sync(componentJsGlob, {cwd: componentsDir}).sort();
var tsFiles = glob.sync(componentTsGlob, {cwd: componentsDir}).sort();
var files = [...tsFiles, ...jsFiles];
if (!filesHaveChanged(files, prevFiles)) {
return;
}
@ -36,7 +39,7 @@ function reskindex() {
strm.write("let components = {};\n");
for (var i = 0; i < files.length; ++i) {
var file = files[i].replace('.js', '');
var file = files[i].replace('.js', '').replace('.tsx', '');
var moduleName = (file.replace(/\//g, '.'));
var importName = moduleName.replace(/\./g, "$");
@ -79,7 +82,7 @@ if (!args.w) {
}
var watchDebouncer = null;
chokidar.watch(path.join(componentsDir, componentGlob)).on('all', (event, path) => {
chokidar.watch(path.join(componentsDir, componentJsGlob)).on('all', (event, path) => {
if (path === componentIndex) return;
if (watchDebouncer) clearTimeout(watchDebouncer);
watchDebouncer = setTimeout(reskindex, 1000);

View file

@ -1,18 +1,21 @@
/*
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
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
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
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.
*/
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { getCurrentLanguage, _t, _td } from './languageHandler';
import PlatformPeg from './PlatformPeg';
@ -54,6 +57,8 @@ function getRedactedUrl() {
}
const customVariables = {
// The Matomo installation at https://matomo.riot.im is currently configured
// with a limit of 10 custom variables.
'App Platform': {
id: 1,
expl: _td('The platform you\'re on'),
@ -61,7 +66,7 @@ const customVariables = {
},
'App Version': {
id: 2,
expl: _td('The version of Riot.im'),
expl: _td('The version of Riot'),
example: '15.0.0',
},
'User Type': {
@ -84,20 +89,25 @@ const customVariables = {
expl: _td('Whether or not you\'re using the Richtext mode of the Rich Text Editor'),
example: 'off',
},
'Breadcrumbs': {
id: 9,
expl: _td("Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)"),
example: 'disabled',
},
'Homeserver URL': {
id: 7,
expl: _td('Your homeserver\'s URL'),
example: 'https://matrix.org',
},
'Identity Server URL': {
'Touch Input': {
id: 8,
expl: _td('Your identity server\'s URL'),
example: 'https://vector.im',
expl: _td("Whether you're using Riot on a device where touch is the primary input mechanism"),
example: 'false',
},
'Breadcrumbs': {
id: 9,
expl: _td("Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)"),
example: 'disabled',
},
'Installed PWA': {
id: 10,
expl: _td("Whether you're using Riot as an installed Progressive Web App"),
example: 'false',
},
};
@ -106,61 +116,80 @@ function whitelistRedact(whitelist, str) {
return '<redacted>';
}
const UID_KEY = "mx_Riot_Analytics_uid";
const CREATION_TS_KEY = "mx_Riot_Analytics_cts";
const VISIT_COUNT_KEY = "mx_Riot_Analytics_vc";
const LAST_VISIT_TS_KEY = "mx_Riot_Analytics_lvts";
function getUid() {
try {
let data = localStorage.getItem(UID_KEY);
if (!data) {
localStorage.setItem(UID_KEY, data = [...Array(16)].map(() => Math.random().toString(16)[2]).join(''));
}
return data;
} catch (e) {
console.error("Analytics error: ", e);
return "";
}
}
const HEARTBEAT_INTERVAL = 30 * 1000; // seconds
class Analytics {
constructor() {
this._paq = null;
this.disabled = true;
this.baseUrl = null;
this.siteId = null;
this.visitVariables = {};
this.firstPage = true;
this._heartbeatIntervalID = null;
this.creationTs = localStorage.getItem(CREATION_TS_KEY);
if (!this.creationTs) {
localStorage.setItem(CREATION_TS_KEY, this.creationTs = new Date().getTime());
}
this.lastVisitTs = localStorage.getItem(LAST_VISIT_TS_KEY);
this.visitCount = localStorage.getItem(VISIT_COUNT_KEY) || 0;
localStorage.setItem(VISIT_COUNT_KEY, parseInt(this.visitCount, 10) + 1);
}
get disabled() {
return !this.baseUrl;
}
/**
* Enable Analytics if initialized but disabled
* otherwise try and initalize, no-op if piwik config missing
*/
enable() {
if (this._paq || this._init()) {
this.disabled = false;
}
}
async enable() {
if (!this.disabled) return;
/**
* Disable Analytics calls, will not fully unload Piwik until a refresh,
* but this is second best, Piwik should not pull anything implicitly.
*/
disable() {
this.trackEvent('Analytics', 'opt-out');
// disableHeartBeatTimer is undocumented but exists in the piwik code
// the _paq.push method will result in an error being printed in the console
// if an unknown method signature is passed
this._paq.push(['disableHeartBeatTimer']);
this.disabled = true;
}
_init() {
const config = SdkConfig.get();
if (!config || !config.piwik || !config.piwik.url || !config.piwik.siteId) return;
const url = config.piwik.url;
const siteId = config.piwik.siteId;
const self = this;
window._paq = this._paq = window._paq || [];
this._paq.push(['setTrackerUrl', url+'piwik.php']);
this._paq.push(['setSiteId', siteId]);
this._paq.push(['trackAllContentImpressions']);
this._paq.push(['discardHashTag', false]);
this._paq.push(['enableHeartBeatTimer']);
// this._paq.push(['enableLinkTracking', true]);
this.baseUrl = new URL("piwik.php", config.piwik.url);
// set constants
this.baseUrl.searchParams.set("rec", 1); // rec is required for tracking
this.baseUrl.searchParams.set("idsite", config.piwik.siteId); // rec is required for tracking
this.baseUrl.searchParams.set("apiv", 1); // API version to use
this.baseUrl.searchParams.set("send_image", 0); // we want a 204, not a tiny GIF
// set user parameters
this.baseUrl.searchParams.set("_id", getUid()); // uuid
this.baseUrl.searchParams.set("_idts", this.creationTs); // first ts
this.baseUrl.searchParams.set("_idvc", parseInt(this.visitCount, 10)+ 1); // visit count
if (this.lastVisitTs) {
this.baseUrl.searchParams.set("_viewts", this.lastVisitTs); // last visit ts
}
const platform = PlatformPeg.get();
this._setVisitVariable('App Platform', platform.getHumanReadableName());
platform.getAppVersion().then((version) => {
this._setVisitVariable('App Version', version);
}).catch(() => {
try {
this._setVisitVariable('App Version', await platform.getAppVersion());
} catch (e) {
this._setVisitVariable('App Version', 'unknown');
});
}
this._setVisitVariable('Chosen Language', getCurrentLanguage());
@ -168,20 +197,77 @@ class Analytics {
this._setVisitVariable('Instance', window.location.pathname);
}
(function() {
const g = document.createElement('script');
const s = document.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=url+'piwik.js';
let installedPWA = "unknown";
try {
// Known to work at least for desktop Chrome
installedPWA = window.matchMedia('(display-mode: standalone)').matches;
} catch (e) { }
this._setVisitVariable('Installed PWA', installedPWA);
g.onload = function() {
console.log('Initialised anonymous analytics');
self._paq = window._paq;
};
let touchInput = "unknown";
try {
// MDN claims broad support across browsers
touchInput = window.matchMedia('(pointer: coarse)').matches;
} catch (e) { }
this._setVisitVariable('Touch Input', touchInput);
s.parentNode.insertBefore(g, s);
})();
// start heartbeat
this._heartbeatIntervalID = window.setInterval(this.ping.bind(this), HEARTBEAT_INTERVAL);
}
return true;
/**
* Disable Analytics, stop the heartbeat and clear identifiers from localStorage
*/
disable() {
if (this.disabled) return;
this.trackEvent('Analytics', 'opt-out');
window.clearInterval(this._heartbeatIntervalID);
this.baseUrl = null;
this.visitVariables = {};
localStorage.removeItem(UID_KEY);
localStorage.removeItem(CREATION_TS_KEY);
localStorage.removeItem(VISIT_COUNT_KEY);
localStorage.removeItem(LAST_VISIT_TS_KEY);
}
async _track(data) {
if (this.disabled) return;
const now = new Date();
const params = {
...data,
url: getRedactedUrl(),
_cvar: JSON.stringify(this.visitVariables), // user custom vars
res: `${window.screen.width}x${window.screen.height}`, // resolution as WWWWxHHHH
rand: String(Math.random()).slice(2, 8), // random nonce to cache-bust
h: now.getHours(),
m: now.getMinutes(),
s: now.getSeconds(),
};
const url = new URL(this.baseUrl);
for (const key in params) {
url.searchParams.set(key, params[key]);
}
try {
await window.fetch(url, {
method: "GET",
mode: "no-cors",
cache: "no-cache",
redirect: "follow",
});
} catch (e) {
console.error("Analytics error: ", e);
}
}
ping() {
this._track({
ping: 1,
});
localStorage.setItem(LAST_VISIT_TS_KEY, new Date().getTime()); // update last visit ts
}
trackPageChange(generationTimeMs) {
@ -193,31 +279,29 @@ class Analytics {
return;
}
if (typeof generationTimeMs === 'number') {
this._paq.push(['setGenerationTimeMs', generationTimeMs]);
} else {
if (typeof generationTimeMs !== 'number') {
console.warn('Analytics.trackPageChange: expected generationTimeMs to be a number');
// But continue anyway because we still want to track the change
}
this._paq.push(['setCustomUrl', getRedactedUrl()]);
this._paq.push(['trackPageView']);
this._track({
gt_ms: generationTimeMs,
});
}
trackEvent(category, action, name, value) {
if (this.disabled) return;
this._paq.push(['setCustomUrl', getRedactedUrl()]);
this._paq.push(['trackEvent', category, action, name, value]);
}
logout() {
if (this.disabled) return;
this._paq.push(['deleteCookies']);
this._track({
e_c: category,
e_a: action,
e_n: name,
e_v: value,
});
}
_setVisitVariable(key, value) {
if (this.disabled) return;
this._paq.push(['setCustomVariable', customVariables[key].id, key, value, 'visit']);
this.visitVariables[customVariables[key].id] = [key, value];
}
setLoggedIn(isGuest, homeserverUrl, identityServerUrl) {
@ -227,16 +311,9 @@ class Analytics {
if (!config.piwik) return;
const whitelistedHSUrls = config.piwik.whitelistedHSUrls || [];
const whitelistedISUrls = config.piwik.whitelistedISUrls || [];
this._setVisitVariable('User Type', isGuest ? 'Guest' : 'Logged In');
this._setVisitVariable('Homeserver URL', whitelistRedact(whitelistedHSUrls, homeserverUrl));
this._setVisitVariable('Identity Server URL', whitelistRedact(whitelistedISUrls, identityServerUrl));
}
setRichtextMode(state) {
if (this.disabled) return;
this._setVisitVariable('RTE: Uses Richtext Mode', state ? 'on' : 'off');
}
setBreadcrumbs(state) {
@ -244,13 +321,11 @@ class Analytics {
this._setVisitVariable('Breadcrumbs', state ? 'enabled' : 'disabled');
}
showDetailsModal() {
showDetailsModal = () => {
let rows = [];
if (window.Piwik) {
const Tracker = window.Piwik.getAsyncTracker();
rows = Object.values(customVariables).map((v) => Tracker.getCustomVariable(v.id)).filter(Boolean);
if (!this.disabled) {
rows = Object.values(this.visitVariables);
} else {
// Piwik may not have been enabled, so show example values
rows = Object.keys(customVariables).map(
(k) => [
k,
@ -271,7 +346,7 @@ class Analytics {
},
),
},
{ expl: _td('Your User Agent'), value: navigator.userAgent },
{ expl: _td('Your user agent'), value: navigator.userAgent },
{ expl: _td('Your device resolution'), value: resolution },
];
@ -280,7 +355,7 @@ class Analytics {
title: _t('Analytics'),
description: <div className="mx_AnalyticsModal">
<div>
{ _t('The information being sent to us to help make Riot.im better includes:') }
{ _t('The information being sent to us to help make Riot better includes:') }
</div>
<table>
{ rows.map((row) => <tr key={row[0]}>
@ -300,7 +375,7 @@ class Analytics {
</div>
</div>,
});
}
};
}
if (!global.mxAnalytics) {

92
src/AsyncWrapper.js Normal file
View file

@ -0,0 +1,92 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
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.
*/
import createReactClass from 'create-react-class';
import * as sdk from './index';
import PropTypes from 'prop-types';
import { _t } from './languageHandler';
/**
* Wrap an asynchronous loader function with a react component which shows a
* spinner until the real component loads.
*/
export default createReactClass({
propTypes: {
/** A promise which resolves with the real component
*/
prom: PropTypes.object.isRequired,
},
getInitialState: function() {
return {
component: null,
error: null,
};
},
componentWillMount: function() {
this._unmounted = false;
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
console.log('Starting load of AsyncWrapper for modal');
this.props.prom.then((result) => {
if (this._unmounted) {
return;
}
// Take the 'default' member if it's there, then we support
// passing in just an import()ed module, since ES6 async import
// always returns a module *namespace*.
const component = result.default ? result.default : result;
this.setState({component});
}).catch((e) => {
console.warn('AsyncWrapper promise failed', e);
this.setState({error: e});
});
},
componentWillUnmount: function() {
this._unmounted = true;
},
_onWrapperCancelClick: function() {
this.props.onFinished(false);
},
render: function() {
if (this.state.component) {
const Component = this.state.component;
return <Component {...this.props} />;
} else if (this.state.error) {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <BaseDialog onFinished={this.props.onFinished}
title={_t("Error")}
>
{_t("Unable to load! Check your network connectivity and try again.")}
<DialogButtons primaryButton={_t("Dismiss")}
onPrimaryButtonClick={this._onWrapperCancelClick}
hasCancel={false}
/>
</BaseDialog>;
} else {
// show a spinner until the component is loaded.
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
},
});

View file

@ -102,6 +102,8 @@ export function getInitialLetter(name) {
}
export function avatarUrlForRoom(room, width, height, resizeMethod) {
if (!room) return null; // null-guard
const explicitRoomAvatar = room.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
width,

View file

@ -4,6 +4,7 @@
Copyright 2016 Aviral Dasgupta
Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
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.
@ -18,6 +19,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {MatrixClient} from "matrix-js-sdk";
import dis from './dispatcher';
import BaseEventIndexManager from './indexing/BaseEventIndexManager';
@ -162,4 +164,28 @@ export default class BasePlatform {
getEventIndexingManager(): BaseEventIndexManager | null {
return null;
}
setLanguage(preferredLangs: string[]) {}
getSSOCallbackUrl(hsUrl: string, isUrl: string): URL {
const url = new URL(window.location.href);
// XXX: at this point, the fragment will always be #/login, which is no
// use to anyone. Ideally, we would get the intended fragment from
// MatrixChat.screenAfterLogin so that you could follow #/room links etc
// through an SSO login.
url.hash = "";
url.searchParams.set("homeserver", hsUrl);
url.searchParams.set("identityServer", isUrl);
return url;
}
/**
* Begin Single Sign On flows.
* @param {MatrixClient} mxClient the matrix client using which we should start the flow
* @param {"sso"|"cas"} loginType the type of SSO it is, CAS/SSO.
*/
startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") {
const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl());
window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO
}
}

View file

@ -64,8 +64,8 @@ import SdkConfig from './SdkConfig';
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
import WidgetUtils from './utils/WidgetUtils';
import WidgetEchoStore from './stores/WidgetEchoStore';
import {IntegrationManagers} from "./integrations/IntegrationManagers";
import SettingsStore, { SettingLevel } from './settings/SettingsStore';
import {generateHumanReadableId} from "./utils/NamingUtils";
global.mxCalls = {
//room_id: MatrixCall
@ -139,11 +139,11 @@ function _setCallListeners(call) {
Modal.createTrackedDialog('Call Failed', '', QuestionDialog, {
title: _t('Call Failed'),
description: _t(
"There are unknown devices in this room: "+
"There are unknown sessions in this room: "+
"if you proceed without verifying them, it will be "+
"possible for someone to eavesdrop on your call.",
),
button: _t('Review Devices'),
button: _t('Review Sessions'),
onFinished: function(confirmed) {
if (confirmed) {
const room = MatrixClientPeg.get().getRoom(call.roomId);
@ -395,32 +395,6 @@ function _onAction(payload) {
}
async function _startCallApp(roomId, type) {
// check for a working integration manager. Technically we could put
// the state event in anyway, but the resulting widget would then not
// work for us. Better that the user knows before everyone else in the
// room sees it.
const managers = IntegrationManagers.sharedInstance();
let haveScalar = false;
if (managers.hasManager()) {
try {
const scalarClient = managers.getPrimaryManager().getScalarClient();
await scalarClient.connect();
haveScalar = scalarClient.hasCredentials();
} catch (e) {
// ignore
}
}
if (!haveScalar) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Could not connect to the integration server', '', ErrorDialog, {
title: _t('Could not connect to the integration server'),
description: _t('A conference call could not be started because the integrations server is not available'),
});
return;
}
dis.dispatch({
action: 'appsDrawer',
show: true,
@ -456,31 +430,22 @@ async function _startCallApp(roomId, type) {
return;
}
// This inherits its poor naming from the field of the same name that goes into
// the event. It's just a random string to make the Jitsi URLs unique.
const widgetSessionId = Math.random().toString(36).substring(2);
const confId = room.roomId.replace(/[^A-Za-z0-9]/g, '') + widgetSessionId;
// NB. we can't just encodeURICompoent all of these because the $ signs need to be there
// (but currently the only thing that needs encoding is the confId)
const queryString = [
'confId='+encodeURIComponent(confId),
'isAudioConf='+(type === 'voice' ? 'true' : 'false'),
'displayName=$matrix_display_name',
'avatarUrl=$matrix_avatar_url',
'email=$matrix_user_id',
].join('&');
const confId = `JitsiConference_${generateHumanReadableId()}`;
const jitsiDomain = SdkConfig.get()['jitsi']['preferredDomain'];
let widgetUrl;
if (SdkConfig.get().integrations_jitsi_widget_url) {
// Try this config key. This probably isn't ideal as a way of discovering this
// URL, but this will at least allow the integration manager to not be hardcoded.
widgetUrl = SdkConfig.get().integrations_jitsi_widget_url + '?' + queryString;
} else {
const apiUrl = IntegrationManagers.sharedInstance().getPrimaryManager().apiUrl;
widgetUrl = apiUrl + '/widgets/jitsi.html?' + queryString;
}
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl();
const widgetData = { widgetSessionId };
// TODO: Remove URL hacks when the mobile clients eventually support v2 widgets
const parsedUrl = new URL(widgetUrl);
parsedUrl.search = ''; // set to empty string to make the URL class use searchParams instead
parsedUrl.searchParams.set('confId', confId);
widgetUrl = parsedUrl.toString();
const widgetData = {
conferenceId: confId,
isAudioOnly: type === 'voice',
domain: jitsiDomain,
};
const widgetId = (
'jitsi_' +

View file

@ -20,6 +20,8 @@ import {MatrixClientPeg} from './MatrixClientPeg';
import { deriveKey } from 'matrix-js-sdk/src/crypto/key_passphrase';
import { decodeRecoveryKey } from 'matrix-js-sdk/src/crypto/recoverykey';
import { _t } from './languageHandler';
import SettingsStore from './settings/SettingsStore';
import {encodeBase64} from "matrix-js-sdk/src/crypto/olmlib";
// This stores the secret storage private keys in memory for the JS SDK. This is
// only meant to act as a cache to avoid prompting the user multiple times
@ -27,9 +29,43 @@ import { _t } from './languageHandler';
// single secret storage operation, as it will clear the cached keys once the
// operation ends.
let secretStorageKeys = {};
let cachingAllowed = false;
let secretStorageBeingAccessed = false;
async function getSecretStorageKey({ keys: keyInfos }) {
function isCachingAllowed() {
return (
secretStorageBeingAccessed ||
SettingsStore.getValue("keepSecretStoragePassphraseForSession")
);
}
export class AccessCancelledError extends Error {
constructor() {
super("Secret storage access canceled");
}
}
async function confirmToDismiss(name) {
let description;
if (name === "m.cross_signing.user_signing") {
description = _t("If you cancel now, you won't complete verifying the other user.");
} else if (name === "m.cross_signing.self_signing") {
description = _t("If you cancel now, you won't complete verifying your other session.");
} else {
description = _t("If you cancel now, you won't complete your secret storage operation.");
}
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const [sure] = await Modal.createDialog(QuestionDialog, {
title: _t("Cancel entering passphrase?"),
description,
danger: true,
cancelButton: _t("Enter passphrase"),
button: _t("Cancel"),
}).finished;
return sure;
}
async function getSecretStorageKey({ keys: keyInfos }, ssssItemName) {
const keyInfoEntries = Object.entries(keyInfos);
if (keyInfoEntries.length > 1) {
throw new Error("Multiple storage key requests not implemented");
@ -37,7 +73,7 @@ async function getSecretStorageKey({ keys: keyInfos }) {
const [name, info] = keyInfoEntries[0];
// Check the in-memory cache
if (cachingAllowed && secretStorageKeys[name]) {
if (isCachingAllowed() && secretStorageKeys[name]) {
return [name, secretStorageKeys[name]];
}
@ -56,32 +92,111 @@ async function getSecretStorageKey({ keys: keyInfos }) {
sdk.getComponent("dialogs.secretstorage.AccessSecretStorageDialog");
const { finished } = Modal.createTrackedDialog("Access Secret Storage dialog", "",
AccessSecretStorageDialog,
/* props= */
{
keyInfo: info,
checkPrivateKey: async (input) => {
if (!info.pubkey) {
return true;
}
const key = await inputToKey(input);
return MatrixClientPeg.get().checkSecretStoragePrivateKey(key, info.pubkey);
},
},
/* className= */ null,
/* isPriorityModal= */ false,
/* isStaticModal= */ false,
/* options= */ {
onBeforeClose: async (reason) => {
if (reason === "backgroundClick") {
return confirmToDismiss(ssssItemName);
}
return true;
},
},
);
const [input] = await finished;
if (!input) {
throw new Error("Secret storage access canceled");
throw new AccessCancelledError();
}
const key = await inputToKey(input);
// Save to cache to avoid future prompts in the current session
if (cachingAllowed) {
if (isCachingAllowed()) {
secretStorageKeys[name] = key;
}
return [name, key];
}
const onSecretRequested = async function({
user_id: userId,
device_id: deviceId,
request_id: requestId,
name,
device_trust: deviceTrust,
}) {
console.log("onSecretRequested", userId, deviceId, requestId, name, deviceTrust);
const client = MatrixClientPeg.get();
if (userId !== client.getUserId()) {
return;
}
if (!deviceTrust || !deviceTrust.isVerified()) {
console.log(`CrossSigningManager: Ignoring request from untrusted device ${deviceId}`);
return;
}
if (name.startsWith("m.cross_signing")) {
const callbacks = client.getCrossSigningCacheCallbacks();
if (!callbacks.getCrossSigningKeyCache) return;
/* Explicit enumeration here is deliberate never share the master key! */
if (name === "m.cross_signing.self_signing") {
const key = await callbacks.getCrossSigningKeyCache("self_signing");
if (!key) {
console.log(
`self_signing requested by ${deviceId}, but not found in cache`,
);
}
return key && encodeBase64(key);
} else if (name === "m.cross_signing.user_signing") {
const key = await callbacks.getCrossSigningKeyCache("user_signing");
if (!key) {
console.log(
`user_signing requested by ${deviceId}, but not found in cache`,
);
}
return key && encodeBase64(key);
}
} else if (name === "m.megolm_backup.v1") {
const key = await client._crypto.getSessionBackupPrivateKey();
if (!key) {
console.log(
`session backup key requested by ${deviceId}, but not found in cache`,
);
}
return key && encodeBase64(key);
}
console.warn("onSecretRequested didn't recognise the secret named ", name);
};
export const crossSigningCallbacks = {
getSecretStorageKey,
onSecretRequested,
};
export async function promptForBackupPassphrase() {
let key;
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
const { finished } = Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, {
showSummary: false, keyCallback: k => key = k,
}, null, /* priority = */ false, /* static = */ true);
const success = await finished;
if (!success) throw new Error("Key backup prompt cancelled");
return key;
}
/**
* This helper should be used whenever you need to access secret storage. It
* ensures that secret storage (and also cross-signing since they each depend on
@ -101,18 +216,21 @@ export const crossSigningCallbacks = {
*
* @param {Function} [func] An operation to perform once secret storage has been
* bootstrapped. Optional.
* @param {bool} [forceReset] Reset secret storage even if it's already set up
*/
export async function accessSecretStorage(func = async () => { }) {
export async function accessSecretStorage(func = async () => { }, forceReset = false) {
const cli = MatrixClientPeg.get();
cachingAllowed = true;
secretStorageBeingAccessed = true;
try {
if (!cli.hasSecretStorageKey()) {
if (!await cli.hasSecretStorageKey() || forceReset) {
// This dialog calls bootstrap itself after guiding the user through
// passphrase creation.
const { finished } = Modal.createTrackedDialogAsync('Create Secret Storage dialog', '',
import("./async-components/views/dialogs/secretstorage/CreateSecretStorageDialog"),
null, null, /* priority = */ false, /* static = */ true,
{
force: forceReset,
},
null, /* priority = */ false, /* static = */ true,
);
const [confirmed] = await finished;
if (!confirmed) {
@ -125,7 +243,7 @@ export async function accessSecretStorage(func = async () => { }) {
const { finished } = Modal.createTrackedDialog(
'Cross-signing keys dialog', '', InteractiveAuthDialog,
{
title: _t("Send cross-signing keys to homeserver"),
title: _t("Setting up keys"),
matrixClient: MatrixClientPeg.get(),
makeRequest,
},
@ -135,6 +253,7 @@ export async function accessSecretStorage(func = async () => { }) {
throw new Error("Cross-signing key upload auth canceled");
}
},
getBackupPassphrase: promptForBackupPassphrase,
});
}
@ -143,7 +262,9 @@ export async function accessSecretStorage(func = async () => { }) {
return await func();
} finally {
// Clear secret storage key cache now that work is complete
cachingAllowed = false;
secretStorageKeys = {};
secretStorageBeingAccessed = false;
if (!isCachingAllowed()) {
secretStorageKeys = {};
}
}
}

203
src/DeviceListener.js Normal file
View file

@ -0,0 +1,203 @@
/*
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.
*/
import { MatrixClientPeg } from './MatrixClientPeg';
import SettingsStore from './settings/SettingsStore';
import * as sdk from './index';
import { _t } from './languageHandler';
import ToastStore from './stores/ToastStore';
function toastKey(deviceId) {
return 'unverified_session_' + deviceId;
}
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
const THIS_DEVICE_TOAST_KEY = 'setupencryption';
export default class DeviceListener {
static sharedInstance() {
if (!global.mx_DeviceListener) global.mx_DeviceListener = new DeviceListener();
return global.mx_DeviceListener;
}
constructor() {
// set of device IDs we're currently showing toasts for
this._activeNagToasts = new Set();
// device IDs for which the user has dismissed the verify toast ('Later')
this._dismissed = new Set();
// has the user dismissed any of the various nag toasts to setup encryption on this device?
this._dismissedThisDeviceToast = false;
// cache of the key backup info
this._keyBackupInfo = null;
this._keyBackupFetchedAt = null;
}
start() {
MatrixClientPeg.get().on('crypto.devicesUpdated', this._onDevicesUpdated);
MatrixClientPeg.get().on('deviceVerificationChanged', this._onDeviceVerificationChanged);
MatrixClientPeg.get().on('userTrustStatusChanged', this._onUserTrustStatusChanged);
MatrixClientPeg.get().on('accountData', this._onAccountData);
this._recheck();
}
stop() {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('crypto.devicesUpdated', this._onDevicesUpdated);
MatrixClientPeg.get().removeListener('deviceVerificationChanged', this._onDeviceVerificationChanged);
MatrixClientPeg.get().removeListener('userTrustStatusChanged', this._onUserTrustStatusChanged);
MatrixClientPeg.get().removeListener('accountData', this._onAccountData);
}
this._dismissed.clear();
}
dismissVerification(deviceId) {
this._dismissed.add(deviceId);
this._recheck();
}
dismissEncryptionSetup() {
this._dismissedThisDeviceToast = true;
this._recheck();
}
_onDevicesUpdated = (users) => {
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
this._recheck();
}
_onDeviceVerificationChanged = (userId) => {
if (userId !== MatrixClientPeg.get().getUserId()) return;
this._recheck();
}
_onUserTrustStatusChanged = (userId, trustLevel) => {
if (userId !== MatrixClientPeg.get().getUserId()) return;
this._recheck();
}
_onAccountData = (ev) => {
// User may have migrated SSSS to symmetric, in which case we can dismiss that toast
if (ev.getType().startsWith('m.secret_storage.key.')) {
this._recheck();
}
}
// The server doesn't tell us when key backup is set up, so we poll
// & cache the result
async _getKeyBackupInfo() {
const now = (new Date()).getTime();
if (!this._keyBackupInfo || this._keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) {
this._keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
this._keyBackupFetchedAt = now;
}
return this._keyBackupInfo;
}
async _recheck() {
const cli = MatrixClientPeg.get();
if (
!SettingsStore.isFeatureEnabled("feature_cross_signing") ||
!await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
) return;
if (!cli.isCryptoEnabled()) return;
const crossSigningReady = await cli.isCrossSigningReady();
if (this._dismissedThisDeviceToast) {
ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY);
} else {
if (!crossSigningReady) {
// cross signing isn't enabled - nag to enable it
// There are 3 different toasts for:
if (cli.getStoredCrossSigningForUser(cli.getUserId())) {
// Cross-signing on account but this device doesn't trust the master key (verify this session)
ToastStore.sharedInstance().addOrReplaceToast({
key: THIS_DEVICE_TOAST_KEY,
title: _t("Verify this session"),
icon: "verification_warning",
props: {kind: 'verify_this_session'},
component: sdk.getComponent("toasts.SetupEncryptionToast"),
});
} else {
const backupInfo = await this._getKeyBackupInfo();
if (backupInfo) {
// No cross-signing on account but key backup available (upgrade encryption)
ToastStore.sharedInstance().addOrReplaceToast({
key: THIS_DEVICE_TOAST_KEY,
title: _t("Encryption upgrade available"),
icon: "verification_warning",
props: {kind: 'upgrade_encryption'},
component: sdk.getComponent("toasts.SetupEncryptionToast"),
});
} else {
// No cross-signing or key backup on account (set up encryption)
ToastStore.sharedInstance().addOrReplaceToast({
key: THIS_DEVICE_TOAST_KEY,
title: _t("Set up encryption"),
icon: "verification_warning",
props: {kind: 'set_up_encryption'},
component: sdk.getComponent("toasts.SetupEncryptionToast"),
});
}
}
return;
} else if (await cli.secretStorageKeyNeedsUpgrade()) {
ToastStore.sharedInstance().addOrReplaceToast({
key: THIS_DEVICE_TOAST_KEY,
title: _t("Encryption upgrade available"),
icon: "verification_warning",
props: {kind: 'upgrade_ssss'},
component: sdk.getComponent("toasts.SetupEncryptionToast"),
});
}
}
// as long as cross-signing isn't ready,
// you can't see or dismiss any device toasts
if (crossSigningReady) {
const newActiveToasts = new Set();
const devices = await cli.getStoredDevicesForUser(cli.getUserId());
for (const device of devices) {
if (device.deviceId == cli.deviceId) continue;
const deviceTrust = await cli.checkDeviceTrust(cli.getUserId(), device.deviceId);
if (deviceTrust.isCrossSigningVerified() || this._dismissed.has(device.deviceId)) {
ToastStore.sharedInstance().dismissToast(toastKey(device.deviceId));
} else {
this._activeNagToasts.add(device.deviceId);
ToastStore.sharedInstance().addOrReplaceToast({
key: toastKey(device.deviceId),
title: _t("Unverified login. Was this you?"),
icon: "verification_warning",
props: { device },
component: sdk.getComponent("toasts.UnverifiedSessionToast"),
});
newActiveToasts.add(device.deviceId);
}
}
// clear any other outstanding toasts (eg. logged out devices)
for (const deviceId of this._activeNagToasts) {
if (!newActiveToasts.has(deviceId)) ToastStore.sharedInstance().dismissToast(toastKey(deviceId));
}
this._activeNagToasts = newActiveToasts;
}
}
}

View file

@ -1,137 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as sdk from './index';
function isMatch(query, name, uid) {
query = query.toLowerCase();
name = name.toLowerCase();
uid = uid.toLowerCase();
// direct prefix matches
if (name.indexOf(query) === 0 || uid.indexOf(query) === 0) {
return true;
}
// strip @ on uid and try matching again
if (uid.length > 1 && uid[0] === "@" && uid.substring(1).indexOf(query) === 0) {
return true;
}
// split spaces in name and try matching constituent parts
const parts = name.split(" ");
for (let i = 0; i < parts.length; i++) {
if (parts[i].indexOf(query) === 0) {
return true;
}
}
return false;
}
/*
* Converts various data models to Entity objects.
*
* Entity objects provide an interface for UI components to use to display
* members in a data-agnostic way. This means they don't need to care if the
* underlying data model is a RoomMember, User or 3PID data structure, it just
* cares about rendering.
*/
class Entity {
constructor(model) {
this.model = model;
}
getJsx() {
return null;
}
matches(queryString) {
return false;
}
}
class MemberEntity extends Entity {
getJsx() {
const MemberTile = sdk.getComponent("rooms.MemberTile");
return (
<MemberTile key={this.model.userId} member={this.model} />
);
}
matches(queryString) {
return isMatch(queryString, this.model.name, this.model.userId);
}
}
class UserEntity extends Entity {
constructor(model, showInviteButton, inviteFn) {
super(model);
this.showInviteButton = Boolean(showInviteButton);
this.inviteFn = inviteFn;
this.onClick = this.onClick.bind(this);
}
onClick() {
if (this.inviteFn) {
this.inviteFn(this.model.userId);
}
}
getJsx() {
const UserTile = sdk.getComponent("rooms.UserTile");
return (
<UserTile key={this.model.userId} user={this.model}
showInviteButton={this.showInviteButton} onClick={this.onClick} />
);
}
matches(queryString) {
const name = this.model.displayName || this.model.userId;
return isMatch(queryString, name, this.model.userId);
}
}
export function newEntity(jsx, matchFn) {
const entity = new Entity();
entity.getJsx = function() {
return jsx;
};
entity.matches = matchFn;
return entity;
}
/**
* @param {RoomMember[]} members
* @return {Entity[]}
*/
export function fromRoomMembers(members) {
return members.map(function(m) {
return new MemberEntity(m);
});
}
/**
* @param {User[]} users
* @param {boolean} showInviteButton
* @param {Function} inviteFn Called with the user ID.
* @return {Entity[]}
*/
export function fromUsers(users, showInviteButton, inviteFn) {
return users.map(function(u) {
return new UserEntity(u, showInviteButton, inviteFn);
});
}

View file

@ -24,6 +24,8 @@ import {MatrixClientPeg} from "./MatrixClientPeg";
import RoomViewStore from "./stores/RoomViewStore";
import {IntegrationManagers} from "./integrations/IntegrationManagers";
import SettingsStore from "./settings/SettingsStore";
import {Capability, KnownWidgetActions} from "./widgets/WidgetApi";
import SdkConfig from "./SdkConfig";
const WIDGET_API_VERSION = '0.0.2'; // Current API version
const SUPPORTED_WIDGET_API_VERSIONS = [
@ -213,11 +215,18 @@ export default class FromWidgetPostMessageApi {
const data = event.data.data;
const val = data.value;
if (ActiveWidgetStore.widgetHasCapability(widgetId, 'm.always_on_screen')) {
if (ActiveWidgetStore.widgetHasCapability(widgetId, Capability.AlwaysOnScreen)) {
ActiveWidgetStore.setWidgetPersistence(widgetId, val);
}
} else if (action === 'get_openid') {
// Handled by caller
} else if (action === KnownWidgetActions.GetRiotWebConfig) {
if (ActiveWidgetStore.widgetHasCapability(widgetId, Capability.GetRiotWebConfig)) {
this.sendResponse(event, {
api: INBOUND_API_NAME,
config: SdkConfig.get(),
});
}
} else {
console.warn('Widget postMessage event unhandled');
this.sendError(event, {message: 'The postMessage was unhandled'});

View file

@ -23,7 +23,6 @@ import ReplyThread from "./components/views/elements/ReplyThread";
import React from 'react';
import sanitizeHtml from 'sanitize-html';
import highlight from 'highlight.js';
import * as linkify from 'linkifyjs';
import linkifyMatrix from './linkify-matrix';
import _linkifyElement from 'linkifyjs/element';
@ -160,7 +159,7 @@ const transformTags = { // custom to matrix
delete attribs.target;
}
}
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
attribs.rel = 'noreferrer noopener'; // https://mathiasbynens.github.io/rel-noopener/
return { tagName, attribs };
},
'img': function(tagName, attribs) {
@ -467,11 +466,12 @@ export function bodyToHtml(content, highlights, opts={}) {
/**
* Linkifies the given string. This is a wrapper around 'linkifyjs/string'.
*
* @param {string} str
* @returns {string}
* @param {string} str string to linkify
* @param {object} [options] Options for linkifyString. Default: linkifyMatrix.options
* @returns {string} Linkified string
*/
export function linkifyString(str) {
return _linkifyString(str);
export function linkifyString(str, options = linkifyMatrix.options) {
return _linkifyString(str, options);
}
/**
@ -489,10 +489,11 @@ export function linkifyElement(element, options = linkifyMatrix.options) {
* Linkify the given string and sanitize the HTML afterwards.
*
* @param {string} dirtyHtml The HTML string to sanitize and linkify
* @param {object} [options] Options for linkifyString. Default: linkifyMatrix.options
* @returns {string}
*/
export function linkifyAndSanitizeHtml(dirtyHtml) {
return sanitizeHtml(linkifyString(dirtyHtml), sanitizeHtmlParams);
export function linkifyAndSanitizeHtml(dirtyHtml, options = linkifyMatrix.options) {
return sanitizeHtml(linkifyString(dirtyHtml, options), sanitizeHtmlParams);
}
/**

View file

@ -181,24 +181,12 @@ export default class IdentityAuthClient {
}
async registerForToken(check=true) {
try {
const hsOpenIdToken = await MatrixClientPeg.get().getOpenIdToken();
// XXX: The spec is `token`, but we used `access_token` for a Sydent release.
const { access_token: accessToken, token } =
await this._matrixClient.registerWithIdentityServer(hsOpenIdToken);
const identityAccessToken = token ? token : accessToken;
if (check) await this._checkToken(identityAccessToken);
return identityAccessToken;
} catch (e) {
if (e.cors === "rejected" || e.httpStatus === 404) {
// Assume IS only supports deprecated v1 API for now
// TODO: Remove this path once v2 is only supported version
// See https://github.com/vector-im/riot-web/issues/10443
console.warn("IS doesn't support v2 auth");
this.authEnabled = false;
return;
}
throw e;
}
const hsOpenIdToken = await MatrixClientPeg.get().getOpenIdToken();
// XXX: The spec is `token`, but we used `access_token` for a Sydent release.
const { access_token: accessToken, token } =
await this._matrixClient.registerWithIdentityServer(hsOpenIdToken);
const identityAccessToken = token ? token : accessToken;
if (check) await this._checkToken(identityAccessToken);
return identityAccessToken;
}
}

View file

@ -1,5 +1,6 @@
/*
Copyright 2017 Vector Creations Ltd
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.
@ -16,7 +17,10 @@ limitations under the License.
import * as sdk from './index';
import Modal from './Modal';
import SettingsStore from './settings/SettingsStore';
// TODO: We can remove this once cross-signing is the only way.
// https://github.com/vector-im/riot-web/issues/11908
export default class KeyRequestHandler {
constructor(matrixClient) {
this._matrixClient = matrixClient;
@ -30,6 +34,11 @@ export default class KeyRequestHandler {
}
handleKeyRequest(keyRequest) {
// Ignore own device key requests if cross-signing lab enabled
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
return;
}
const userId = keyRequest.userId;
const deviceId = keyRequest.deviceId;
const requestId = keyRequest.requestId;
@ -60,6 +69,11 @@ export default class KeyRequestHandler {
}
handleKeyRequestCancellation(cancellation) {
// Ignore own device key requests if cross-signing lab enabled
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
return;
}
// see if we can find the request in the queue
const userId = cancellation.userId;
const deviceId = cancellation.deviceId;

View file

@ -22,6 +22,7 @@ export const Key = {
PAGE_UP: "PageUp",
PAGE_DOWN: "PageDown",
BACKSPACE: "Backspace",
DELETE: "Delete",
ARROW_UP: "ArrowUp",
ARROW_DOWN: "ArrowDown",
ARROW_LEFT: "ArrowLeft",
@ -36,10 +37,12 @@ export const Key = {
CONTEXT_MENU: "ContextMenu",
COMMA: ",",
PERIOD: ".",
LESS_THAN: "<",
GREATER_THAN: ">",
BACKTICK: "`",
SPACE: " ",
SLASH: "/",
A: "a",
B: "b",
C: "c",
@ -68,8 +71,9 @@ export const Key = {
Z: "z",
};
export const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
export function isOnlyCtrlOrCmdKeyEvent(ev) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (isMac) {
return ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
} else {
@ -78,7 +82,6 @@ export function isOnlyCtrlOrCmdKeyEvent(ev) {
}
export function isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
if (isMac) {
return ev.metaKey && !ev.altKey && !ev.ctrlKey;
} else {

View file

@ -2,6 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
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.
@ -35,8 +36,10 @@ import { sendLoginRequest } from "./Login";
import * as StorageManager from './utils/StorageManager';
import SettingsStore from "./settings/SettingsStore";
import TypingStore from "./stores/TypingStore";
import ToastStore from "./stores/ToastStore";
import {IntegrationManagers} from "./integrations/IntegrationManagers";
import {Mjolnir} from "./mjolnir/Mjolnir";
import DeviceListener from "./DeviceListener";
/**
* Called at startup, to attempt to build a logged-in Matrix session. It tries
@ -310,7 +313,7 @@ async function _restoreFromLocalStorage(opts) {
}
}
function _handleLoadSessionFailure(e) {
async function _handleLoadSessionFailure(e) {
console.error("Unable to load session", e);
const SessionRestoreErrorDialog =
@ -320,16 +323,15 @@ function _handleLoadSessionFailure(e) {
error: e.message,
});
return modal.finished.then(([success]) => {
if (success) {
// user clicked continue.
_clearStorage();
return false;
}
const [success] = await modal.finished;
if (success) {
// user clicked continue.
await _clearStorage();
return false;
}
// try, try again
return loadSession();
});
// try, try again
return loadSession();
}
/**
@ -375,7 +377,7 @@ export function hydrateSession(credentials) {
const overwrite = credentials.userId !== oldUserId || credentials.deviceId !== oldDeviceId;
if (overwrite) {
console.warn("Clearing all data: Old session belongs to a different user/device");
console.warn("Clearing all data: Old session belongs to a different user/session");
}
return _doSetLoggedIn(credentials, overwrite);
@ -432,7 +434,7 @@ async function _doSetLoggedIn(credentials, clearStorage) {
}
}
Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl);
Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl);
if (localStorage) {
try {
@ -575,6 +577,7 @@ async function startMatrixClient(startSyncing=true) {
Notifier.start();
UserActivity.sharedInstance().start();
TypingStore.sharedInstance().reset(); // just in case
ToastStore.sharedInstance().reset();
if (!SettingsStore.getValue("lowBandwidth")) {
Presence.start();
}
@ -588,13 +591,19 @@ async function startMatrixClient(startSyncing=true) {
Mjolnir.sharedInstance().start();
if (startSyncing) {
await MatrixClientPeg.start();
// The client might want to populate some views with events from the
// index (e.g. the FilePanel), therefore initialize the event index
// before the client.
await EventIndexPeg.init();
await MatrixClientPeg.start();
} else {
console.warn("Caller requested only auxiliary services be started");
await MatrixClientPeg.assign();
}
// This needs to be started after crypto is set up
DeviceListener.sharedInstance().start();
// dispatch that we finished starting up to wire up any other bits
// of the matrix client that cannot be set prior to starting up.
dis.dispatch({action: 'client_started'});
@ -622,7 +631,7 @@ export async function onLoggedOut() {
* @returns {Promise} promise which resolves once the stores have been cleared
*/
async function _clearStorage() {
Analytics.logout();
Analytics.disable();
if (window.localStorage) {
window.localStorage.clear();
@ -651,6 +660,7 @@ export function stopMatrixClient(unsetClient=true) {
ActiveWidgetStore.stop();
IntegrationManagers.sharedInstance().stopWatching();
Mjolnir.sharedInstance().stop();
DeviceListener.sharedInstance().stop();
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
EventIndexPeg.stop();
const cli = MatrixClientPeg.get();

View file

@ -3,6 +3,7 @@ Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
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.
@ -19,8 +20,6 @@ limitations under the License.
import Matrix from "matrix-js-sdk";
import url from 'url';
export default class Login {
constructor(hsUrl, isUrl, fallbackHsUrl, opts) {
this._hsUrl = hsUrl;
@ -29,6 +28,7 @@ export default class Login {
this._currentFlowIndex = 0;
this._flows = [];
this._defaultDeviceDisplayName = opts.defaultDeviceDisplayName;
this._tempClient = null; // memoize
}
getHomeserverUrl() {
@ -40,10 +40,12 @@ export default class Login {
}
setHomeserverUrl(hsUrl) {
this._tempClient = null; // clear memoization
this._hsUrl = hsUrl;
}
setIdentityServerUrl(isUrl) {
this._tempClient = null; // clear memoization
this._isUrl = isUrl;
}
@ -52,8 +54,9 @@ export default class Login {
* requests.
* @returns {MatrixClient}
*/
_createTemporaryClient() {
return Matrix.createClient({
createTemporaryClient() {
if (this._tempClient) return this._tempClient; // use memoization
return this._tempClient = Matrix.createClient({
baseUrl: this._hsUrl,
idBaseUrl: this._isUrl,
});
@ -61,7 +64,7 @@ export default class Login {
getFlows() {
const self = this;
const client = this._createTemporaryClient();
const client = this.createTemporaryClient();
return client.loginFlows().then(function(result) {
self._flows = result.flows;
self._currentFlowIndex = 0;
@ -139,21 +142,6 @@ export default class Login {
throw error;
});
}
getSsoLoginUrl(loginType) {
const client = this._createTemporaryClient();
const parsedUrl = url.parse(window.location.href, true);
// XXX: at this point, the fragment will always be #/login, which is no
// use to anyone. Ideally, we would get the intended fragment from
// MatrixChat.screenAfterLogin so that you could follow #/room links etc
// through an SSO login.
parsedUrl.hash = "";
parsedUrl.query["homeserver"] = client.getHomeserverUrl();
parsedUrl.query["identityServer"] = client.getIdentityServerUrl();
return client.getSsoLoginUrl(url.format(parsedUrl), loginType);
}
}

View file

@ -91,7 +91,7 @@ export default class Markdown {
return true;
}
toHTML() {
toHTML({ externalLinks = false } = {}) {
const renderer = new commonmark.HtmlRenderer({
safe: false,
@ -125,6 +125,24 @@ export default class Markdown {
}
};
renderer.link = function(node, entering) {
const attrs = this.attrs(node);
if (entering) {
attrs.push(['href', this.esc(node.destination)]);
if (node.title) {
attrs.push(['title', this.esc(node.title)]);
}
// Modified link behaviour to treat them all as external and
// thus opening in a new tab.
if (externalLinks) {
attrs.push(['target', '_blank']);
attrs.push(['rel', 'noreferrer noopener']);
}
this.tag('a', attrs);
} else {
this.tag('/a');
}
};
renderer.html_inline = html_if_tag_allowed;

View file

@ -2,7 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations 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");
you may not use this file except in compliance with the License.
@ -32,6 +32,7 @@ import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientB
import * as StorageManager from './utils/StorageManager';
import IdentityAuthClient from './IdentityAuthClient';
import { crossSigningCallbacks } from './CrossSigningManager';
import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
interface MatrixClientCreds {
homeserverUrl: string,
@ -147,6 +148,9 @@ class _MatrixClientPeg {
// check that we have a version of the js-sdk which includes initCrypto
if (!SettingsStore.getValue("lowBandwidth") && this.matrixClient.initCrypto) {
await this.matrixClient.initCrypto();
this.matrixClient.setCryptoTrustCrossSignedDevices(
!SettingsStore.getValue('e2ee.manuallyVerifyAllSessions'),
);
StorageManager.setCryptoInitialised(true);
}
} catch (e) {
@ -217,15 +221,20 @@ class _MatrixClientPeg {
timelineSupport: true,
forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false),
fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'),
verificationMethods: [verificationMethods.SAS],
verificationMethods: [
verificationMethods.SAS,
SHOW_QR_CODE_METHOD,
verificationMethods.RECIPROCATE_QR_CODE,
],
unstableClientRelationAggregation: true,
identityServer: new IdentityAuthClient(),
};
opts.cryptoCallbacks = {};
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
Object.assign(opts.cryptoCallbacks, crossSigningCallbacks);
}
// These are always installed regardless of the labs flag so that
// cross-signing features can toggle on without reloading and also be
// accessed immediately after login.
Object.assign(opts.cryptoCallbacks, crossSigningCallbacks);
this.matrixClient = createMatrixClient(opts);

View file

@ -17,87 +17,14 @@ limitations under the License.
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import Analytics from './Analytics';
import * as sdk from './index';
import dis from './dispatcher';
import { _t } from './languageHandler';
import {defer} from "./utils/promise";
import {defer} from './utils/promise';
import AsyncWrapper from './AsyncWrapper';
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer";
/**
* Wrap an asynchronous loader function with a react component which shows a
* spinner until the real component loads.
*/
const AsyncWrapper = createReactClass({
propTypes: {
/** A promise which resolves with the real component
*/
prom: PropTypes.object.isRequired,
},
getInitialState: function() {
return {
component: null,
error: null,
};
},
componentWillMount: function() {
this._unmounted = false;
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
console.log('Starting load of AsyncWrapper for modal');
this.props.prom.then((result) => {
if (this._unmounted) {
return;
}
// Take the 'default' member if it's there, then we support
// passing in just an import()ed module, since ES6 async import
// always returns a module *namespace*.
const component = result.default ? result.default : result;
this.setState({component});
}).catch((e) => {
console.warn('AsyncWrapper promise failed', e);
this.setState({error: e});
});
},
componentWillUnmount: function() {
this._unmounted = true;
},
_onWrapperCancelClick: function() {
this.props.onFinished(false);
},
render: function() {
if (this.state.component) {
const Component = this.state.component;
return <Component {...this.props} />;
} else if (this.state.error) {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <BaseDialog onFinished={this.props.onFinished}
title={_t("Error")}
>
{_t("Unable to load! Check your network connectivity and try again.")}
<DialogButtons primaryButton={_t("Dismiss")}
onPrimaryButtonClick={this._onWrapperCancelClick}
hasCancel={false}
/>
</BaseDialog>;
} else {
// show a spinner until the component is loaded.
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />;
}
},
});
class ModalManager {
constructor() {
this._counter = 0;
@ -120,7 +47,7 @@ class ModalManager {
} */
];
this.closeAll = this.closeAll.bind(this);
this.onBackgroundClick = this.onBackgroundClick.bind(this);
}
hasDialogs() {
@ -179,7 +106,7 @@ class ModalManager {
return this.appendDialogAsync(...rest);
}
_buildModal(prom, props, className) {
_buildModal(prom, props, className, options) {
const modal = {};
// never call this from onFinished() otherwise it will loop
@ -197,13 +124,27 @@ class ModalManager {
);
modal.onFinished = props ? props.onFinished : null;
modal.className = className;
modal.onBeforeClose = options.onBeforeClose;
modal.beforeClosePromise = null;
modal.close = closeDialog;
modal.closeReason = null;
return {modal, closeDialog, onFinishedProm};
}
_getCloseFn(modal, props) {
const deferred = defer();
return [(...args) => {
return [async (...args) => {
if (modal.beforeClosePromise) {
await modal.beforeClosePromise;
} else if (modal.onBeforeClose) {
modal.beforeClosePromise = modal.onBeforeClose(modal.closeReason);
const shouldClose = await modal.beforeClosePromise;
modal.beforeClosePromise = null;
if (!shouldClose) {
return;
}
}
deferred.resolve(args);
if (props && props.onFinished) props.onFinished.apply(null, args);
const i = this._modals.indexOf(modal);
@ -229,6 +170,12 @@ class ModalManager {
}, deferred.promise];
}
/**
* @callback onBeforeClose
* @param {string?} reason either "backgroundClick" or null
* @return {Promise<bool>} whether the dialog should close
*/
/**
* Open a modal view.
*
@ -256,11 +203,12 @@ class ModalManager {
* also be removed from the stack. This is not compatible
* with being a priority modal. Only one modal can be
* static at a time.
* @param {Object} options? extra options for the dialog
* @param {onBeforeClose} options.onBeforeClose a callback to decide whether to close the dialog
* @returns {object} Object with 'close' parameter being a function that will close the dialog
*/
createDialogAsync(prom, props, className, isPriorityModal, isStaticModal) {
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className);
createDialogAsync(prom, props, className, isPriorityModal, isStaticModal, options = {}) {
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className, options);
if (isPriorityModal) {
// XXX: This is destructive
this._priorityModal = modal;
@ -279,7 +227,7 @@ class ModalManager {
}
appendDialogAsync(prom, props, className) {
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className);
const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className, {});
this._modals.push(modal);
this._reRender();
@ -289,24 +237,22 @@ class ModalManager {
};
}
closeAll() {
const modalsToClose = [...this._modals, this._priorityModal];
this._modals = [];
this._priorityModal = null;
if (this._staticModal && modalsToClose.length === 0) {
modalsToClose.push(this._staticModal);
this._staticModal = null;
onBackgroundClick() {
const modal = this._getCurrentModal();
if (!modal) {
return;
}
// we want to pass a reason to the onBeforeClose
// callback, but close is currently defined to
// pass all number of arguments to the onFinished callback
// so, pass the reason to close through a member variable
modal.closeReason = "backgroundClick";
modal.close();
modal.closeReason = null;
}
for (let i = 0; i < modalsToClose.length; i++) {
const m = modalsToClose[i];
if (m && m.onFinished) {
m.onFinished(false);
}
}
this._reRender();
_getCurrentModal() {
return this._priorityModal ? this._priorityModal : (this._modals[0] || this._staticModal);
}
_reRender() {
@ -337,7 +283,7 @@ class ModalManager {
<div className="mx_Dialog">
{ this._staticModal.elem }
</div>
<div className="mx_Dialog_background mx_Dialog_staticBackground" onClick={this.closeAll}></div>
<div className="mx_Dialog_background mx_Dialog_staticBackground" onClick={this.onBackgroundClick}></div>
</div>
);
@ -347,8 +293,8 @@ class ModalManager {
ReactDOM.unmountComponentAtNode(this.getOrCreateStaticContainer());
}
const modal = this._priorityModal ? this._priorityModal : this._modals[0];
if (modal) {
const modal = this._getCurrentModal();
if (modal !== this._staticModal) {
const classes = "mx_Dialog_wrapper "
+ (this._staticModal ? "mx_Dialog_wrapperWithStaticUnder " : '')
+ (modal.className ? modal.className : '');
@ -358,7 +304,7 @@ class ModalManager {
<div className="mx_Dialog">
{modal.elem}
</div>
<div className="mx_Dialog_background" onClick={this.closeAll}></div>
<div className="mx_Dialog_background" onClick={this.onBackgroundClick}></div>
</div>
);

View file

@ -153,10 +153,12 @@ const Notifier = {
},
start: function() {
this.boundOnEvent = this.onEvent.bind(this);
this.boundOnSyncStateChange = this.onSyncStateChange.bind(this);
this.boundOnRoomReceipt = this.onRoomReceipt.bind(this);
this.boundOnEventDecrypted = this.onEventDecrypted.bind(this);
// do not re-bind in the case of repeated call
this.boundOnEvent = this.boundOnEvent || this.onEvent.bind(this);
this.boundOnSyncStateChange = this.boundOnSyncStateChange || this.onSyncStateChange.bind(this);
this.boundOnRoomReceipt = this.boundOnRoomReceipt || this.onRoomReceipt.bind(this);
this.boundOnEventDecrypted = this.boundOnEventDecrypted || this.onEventDecrypted.bind(this);
MatrixClientPeg.get().on('event', this.boundOnEvent);
MatrixClientPeg.get().on('Room.receipt', this.boundOnRoomReceipt);
MatrixClientPeg.get().on('Event.decrypted', this.boundOnEventDecrypted);
@ -166,7 +168,7 @@ const Notifier = {
},
stop: function() {
if (MatrixClientPeg.get() && this.boundOnRoomTimeline) {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener('Event', this.boundOnEvent);
MatrixClientPeg.get().removeListener('Room.receipt', this.boundOnRoomReceipt);
MatrixClientPeg.get().removeListener('Event.decrypted', this.boundOnEventDecrypted);

View file

@ -39,6 +39,8 @@ export const SAFE_LOCALPART_REGEX = /^[a-z0-9=_\-./]+$/;
* If true, goes to the home page if the user cancels the action
* @param {bool} options.go_welcome_on_cancel
* If true, goes to the welcome page if the user cancels the action
* @param {bool} options.screen_after
* If present the screen to redirect to after a successful login or register.
*/
export async function startAnyRegistrationFlow(options) {
if (options === undefined) options = {};
@ -66,13 +68,21 @@ export async function startAnyRegistrationFlow(options) {
// });
//} else {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Registration required', '', QuestionDialog, {
title: _t("Registration Required"),
description: _t("You need to register to do this. Would you like to register now?"),
button: _t("Register"),
const modal = Modal.createTrackedDialog('Registration required', '', QuestionDialog, {
hasCancelButton: true,
quitOnly: true,
title: _t("Sign In or Create Account"),
description: _t("Use your account or create a new one to continue."),
button: _t("Create Account"),
extraButtons: [
<button key="start_login" onClick={() => {
modal.close();
dis.dispatch({action: 'start_login', screenAfterLogin: options.screen_after});
}}>{ _t('Sign In') }</button>,
],
onFinished: (proceed) => {
if (proceed) {
dis.dispatch({action: 'start_registration'});
dis.dispatch({action: 'start_registration', screenAfterLogin: options.screen_after});
} else if (options.go_home_on_cancel) {
dis.dispatch({action: 'view_home_page'});
} else if (options.go_welcome_on_cancel) {
@ -101,4 +111,3 @@ export async function startAnyRegistrationFlow(options) {
// }
// throw new Error("Register request succeeded when it should have returned 401!");
// }

View file

@ -1,6 +1,7 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2017, 2018 New Vector Ltd
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.
@ -19,13 +20,9 @@ import React from 'react';
import {MatrixClientPeg} from './MatrixClientPeg';
import MultiInviter from './utils/MultiInviter';
import Modal from './Modal';
import { getAddressType } from './UserAddress';
import createRoom from './createRoom';
import * as sdk from './';
import dis from './dispatcher';
import DMRoomMap from './utils/DMRoomMap';
import { _t } from './languageHandler';
import SettingsStore from "./settings/SettingsStore";
import {KIND_DM, KIND_INVITE} from "./components/views/dialogs/InviteDialog";
/**
* Invites multiple addresses to a room
@ -36,62 +33,27 @@ import SettingsStore from "./settings/SettingsStore";
* @param {string[]} addrs Array of strings of addresses to invite. May be matrix IDs or 3pids.
* @returns {Promise} Promise
*/
function inviteMultipleToRoom(roomId, addrs) {
export function inviteMultipleToRoom(roomId, addrs) {
const inviter = new MultiInviter(roomId);
return inviter.invite(addrs).then(states => Promise.resolve({states, inviter}));
}
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");
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
title: _t('Start a chat'),
description: _t("Who would you like to communicate with?"),
placeholder: (validAddressTypes) => {
// The set of valid address type can be mutated inside the dialog
// when you first have no IS but agree to use one in the dialog.
if (validAddressTypes.includes('email')) {
return _t("Email, name or Matrix ID");
}
return _t("Name or Matrix ID");
},
validAddressTypes: ['mx-user-id', 'email'],
button: _t("Start Chat"),
onFinished: _onStartDmFinished,
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
// This dialog handles the room creation internally - we don't need to worry about it.
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
Modal.createTrackedDialog(
'Start DM', '', InviteDialog, {kind: KIND_DM},
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
);
}
export function showRoomInviteDialog(roomId) {
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
Modal.createTrackedDialog('Chat Invite', '', AddressPickerDialog, {
title: _t('Invite new room members'),
button: _t('Send Invites'),
placeholder: (validAddressTypes) => {
// The set of valid address type can be mutated inside the dialog
// when you first have no IS but agree to use one in the dialog.
if (validAddressTypes.includes('email')) {
return _t("Email, name or Matrix ID");
}
return _t("Name or Matrix ID");
},
validAddressTypes: ['mx-user-id', 'email'],
onFinished: (shouldInvite, addrs) => {
_onRoomInviteFinished(roomId, shouldInvite, addrs);
},
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
// This dialog handles the room creation internally - we don't need to worry about it.
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
Modal.createTrackedDialog(
'Invite Users', '', InviteDialog, {kind: KIND_INVITE, roomId},
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
);
}
/**
@ -112,60 +74,6 @@ export function isValid3pidInvite(event) {
return true;
}
// TODO: Canonical DMs replaces this
function _onStartDmFinished(shouldInvite, addrs) {
if (!shouldInvite) return;
const addrTexts = addrs.map((addr) => addr.address);
if (_isDmChat(addrTexts)) {
const rooms = _getDirectMessageRooms(addrTexts[0]);
if (rooms.length > 0) {
// A Direct Message room already exists for this user, so reuse it
dis.dispatch({
action: 'view_room',
room_id: rooms[0],
should_peek: false,
joining: false,
});
} else {
// Start a new DM chat
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to start chat', '', ErrorDialog, {
title: _t("Failed to start chat"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
});
}
} else if (addrTexts.length === 1) {
// Start a new DM chat
createRoom({dmUserId: addrTexts[0]}).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to start chat', '', ErrorDialog, {
title: _t("Failed to start chat"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
});
} else {
// Start multi user chat
let room;
createRoom().then((roomId) => {
room = MatrixClientPeg.get().getRoom(roomId);
return inviteMultipleToRoom(roomId, addrTexts);
}).then((result) => {
return _showAnyInviteErrors(result.states, room, result.inviter);
}).catch((err) => {
console.error(err.stack);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
title: _t("Failed to invite"),
description: ((err && err.message) ? err.message : _t("Operation failed")),
});
});
}
}
export function inviteUsersToRoom(roomId, userIds) {
return inviteMultipleToRoom(roomId, userIds).then((result) => {
const room = MatrixClientPeg.get().getRoom(roomId);
@ -180,24 +88,6 @@ export function inviteUsersToRoom(roomId, userIds) {
});
}
function _onRoomInviteFinished(roomId, shouldInvite, addrs) {
if (!shouldInvite) return;
const addrTexts = addrs.map((addr) => addr.address);
// Invite new users to a room
inviteUsersToRoom(roomId, addrTexts);
}
// TODO: Immutable DMs replaces this
function _isDmChat(addrTexts) {
if (addrTexts.length === 1 && getAddressType(addrTexts[0]) === 'mx-user-id') {
return true;
} else {
return false;
}
}
function _showAnyInviteErrors(addrs, room, inviter) {
// Show user any errors
const failedUsers = Object.keys(addrs).filter(a => addrs[a] === 'error');
@ -233,15 +123,3 @@ function _showAnyInviteErrors(addrs, room, inviter) {
return addrs;
}
function _getDirectMessageRooms(addr) {
const dmRoomMap = new DMRoomMap(MatrixClientPeg.get());
const dmRooms = dmRoomMap.getDMRoomsForUserId(addr);
const rooms = dmRooms.filter((dmRoom) => {
const room = MatrixClientPeg.get().getRoom(dmRoom);
if (room) {
return room.getMyMembership() === 'join';
}
});
return rooms;
}

View file

@ -23,7 +23,7 @@ import {MatrixClientPeg} from './MatrixClientPeg';
* of aliases. Otherwise return null;
*/
export function getDisplayAliasForRoom(room) {
return room.getCanonicalAlias() || room.getAliases()[0];
return room.getCanonicalAlias() || room.getAltAliases()[0];
}
/**

View file

@ -26,6 +26,13 @@ export const DEFAULTS: ConfigOptions = {
integrations_rest_url: "https://scalar.vector.im/api",
// Where to send bug reports. If not specified, bugs cannot be sent.
bug_report_endpoint_url: null,
// Jitsi conference options
jitsi: {
// Default conference domain
preferredDomain: "jitsi.riot.im",
// Default Jitsi Meet API location
externalApiUrl: "https://jitsi.riot.im/libs/external_api.min.js",
},
};
export default class SdkConfig {

View file

@ -87,6 +87,13 @@ async function localSearch(searchTerm, roomId = undefined) {
searchArgs.room_id = roomId;
}
const emptyResult = {
results: [],
highlights: [],
};
if (searchTerm === "") return emptyResult;
const eventIndex = EventIndexPeg.get();
const localResult = await eventIndex.search(searchArgs);
@ -97,11 +104,6 @@ async function localSearch(searchTerm, roomId = undefined) {
},
};
const emptyResult = {
results: [],
highlights: [],
};
const result = MatrixClientPeg.get()._processRoomEventsSearch(
emptyResult, response);

Some files were not shown because too many files have changed in this diff Show more