diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 3fb847ad18..0000000000 --- a/.babelrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "presets": [ - "react", - "es2015", - "es2016" - ], - "plugins": [ - [ - "transform-builtin-extend", - { - "globals": ["Error"] - } - ], - "transform-class-properties", - "transform-object-rest-spread", - "transform-async-to-bluebird", - "transform-runtime", - "add-module-exports", - "syntax-dynamic-import" - ] -} diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml deleted file mode 100644 index 946e417676..0000000000 --- a/.buildkite/pipeline.yaml +++ /dev/null @@ -1,91 +0,0 @@ -steps: - - label: ":eslint: Lint" - command: - - "yarn install" - - "yarn lintwithexclusions" - - "yarn stylelint" - plugins: - - docker#v3.0.1: - image: "node:10" - - - label: ":chains: End-to-End Tests" - agents: - # We use a medium sized instance instead of the normal small ones because - # e2e tests otherwise take +-8min - queue: "medium" - command: - # TODO: Remove hacky chmod for BuildKite - - "echo '--- Setup'" - - "chmod +x ./scripts/ci/*.sh" - - "chmod +x ./scripts/*" - - "echo '--- Install js-sdk'" - - "./scripts/ci/install-deps.sh" - - "./scripts/ci/end-to-end-tests.sh" - plugins: - - docker#v3.0.1: - image: "matrixdotorg/riotweb-ci-e2etests-env:latest" - propagate-environment: true - - - label: ":karma: Tests" - agents: - # We use a medium sized instance instead of the normal small ones because - # webpack loves to gorge itself on resources. - queue: "medium" - command: - # Install chrome - - "echo '--- Installing Chrome'" - - "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -" - - "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'" - - "apt-get update" - - "apt-get install -y google-chrome-stable" - # Run tests - # TODO: Remove hacky chmod for BuildKite - - "chmod +x ./scripts/ci/*.sh" - - "chmod +x ./scripts/*" - - "echo '--- Installing Dependencies'" - - "./scripts/ci/install-deps.sh" - - "echo '+++ Running Tests'" - - "./scripts/ci/unit-tests.sh" - env: - CHROME_BIN: "/usr/bin/google-chrome-stable" - plugins: - - docker#v3.0.1: - image: "node:10" - propagate-environment: true - - - label: "🔧 Riot Tests" - agents: - # We use a medium sized instance instead of the normal small ones because - # webpack loves to gorge itself on resources. - queue: "medium" - command: - # Install chrome - - "echo '--- Installing Chrome'" - - "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -" - - "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'" - - "apt-get update" - - "apt-get install -y google-chrome-stable" - # Run tests - # TODO: Remove hacky chmod for BuildKite - - "chmod +x ./scripts/ci/*.sh" - - "chmod +x ./scripts/*" - - "echo '--- Installing Dependencies'" - - "./scripts/ci/install-deps.sh" - - "echo '+++ Running Tests'" - - "./scripts/ci/riot-unit-tests.sh" - env: - CHROME_BIN: "/usr/bin/google-chrome-stable" - plugins: - - docker#v3.0.1: - image: "node:10" - propagate-environment: true - - - wait - - - label: "🐴 Trigger riot-web" - trigger: "riot-web" - branches: "develop" - build: - branch: "develop" - message: "[react-sdk] ${BUILDKITE_MESSAGE}" - async: true diff --git a/.eslintignore b/.eslintignore index c4c7fe5067..c4f7298047 100644 --- a/.eslintignore +++ b/.eslintignore @@ -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/ diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 3636a5e563..9973cfb120 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -1,9 +1,5 @@ # autogenerated file: run scripts/generate-eslint-error-ignore-file to update. -src/component-index.js -src/components/structures/BottomLeftMenu.js -src/components/structures/CreateRoom.js -src/components/structures/MessagePanel.js src/components/structures/RoomDirectory.js src/components/structures/RoomStatusBar.js src/components/structures/RoomView.js @@ -13,63 +9,40 @@ 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 src/components/views/elements/DirectorySearchBox.js -src/components/views/elements/ImageView.js src/components/views/elements/MemberEventListSummary.js -src/components/views/elements/TintableSvg.js src/components/views/elements/UserSelector.js src/components/views/globals/MatrixToolbar.js src/components/views/globals/NewVersionBar.js src/components/views/globals/UpdateCheckBar.js src/components/views/messages/MFileBody.js -src/components/views/messages/RoomAvatarEvent.js src/components/views/messages/TextualBody.js src/components/views/room_settings/ColorSettings.js src/components/views/rooms/Autocomplete.js src/components/views/rooms/AuxPanel.js -src/components/views/rooms/EntityTile.js src/components/views/rooms/LinkPreviewWidget.js src/components/views/rooms/MemberDeviceInfo.js src/components/views/rooms/MemberInfo.js src/components/views/rooms/MemberList.js -src/components/views/rooms/MessageComposer.js -src/components/views/rooms/PinnedEventTile.js src/components/views/rooms/RoomList.js src/components/views/rooms/RoomPreviewBar.js -src/components/views/rooms/SearchableEntityList.js -src/components/views/rooms/SearchBar.js src/components/views/rooms/SearchResultTile.js -src/components/views/rooms/TopUnreadMessagesBar.js -src/components/views/rooms/UserTile.js src/components/views/settings/ChangeAvatar.js src/components/views/settings/ChangePassword.js src/components/views/settings/DevicesPanel.js -src/components/views/settings/IntegrationsManager.js src/components/views/settings/Notifications.js -src/GroupAddressPicker.js src/HtmlUtils.js src/ImageUtils.js -src/languageHandler.js -src/linkify-matrix.js src/Markdown.js -src/MatrixClientPeg.js src/notifications/ContentRules.js src/notifications/PushRuleVectorState.js -src/notifications/VectorPushRulesDefinitions.js -src/Notifier.js src/PlatformPeg.js -src/Presence.js src/rageshake/rageshake.js -src/rageshake/submit-rageshake.js src/ratelimitedfunc.js -src/Roles.js src/Rooms.js -src/ScalarAuthClient.js -src/UiEffects.js src/Unread.js src/utils/DecryptFile.js src/utils/DirectoryUtils.js @@ -77,17 +50,13 @@ src/utils/DMRoomMap.js src/utils/FormattingUtils.js src/utils/MultiInviter.js src/utils/Receipt.js -src/VectorConferenceHandler.js src/Velociraptor.js -src/WhoIsTyping.js -src/wrappers/withMatrixClient.js test/components/structures/MessagePanel-test.js -test/components/structures/ScrollPanel-test.js -test/components/structures/TimelinePanel-test.js test/components/views/dialogs/InteractiveAuthDialog-test.js -test/components/views/rooms/MessageComposerInput-test.js -test/components/views/rooms/RoomSettings-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/ diff --git a/.eslintrc.js b/.eslintrc.js index fdf0bb351e..6a0576c58a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,13 +5,17 @@ const path = require('path'); // but only if they come from a module that starts with eslint-config- // So we load the filename directly (and it could be in node_modules/ // or or ../node_modules/ etc) -const matrixJsSdkPath = path.dirname(require.resolve('matrix-js-sdk')); +// +// We add a `..` to the end because the js-sdk lives out of lib/, but the eslint +// config is at the project root. +const matrixJsSdkPath = path.join(path.dirname(require.resolve('matrix-js-sdk')), '..'); module.exports = { parser: "babel-eslint", extends: [matrixJsSdkPath + "/.eslintrc.js"], plugins: [ "react", + "react-hooks", "flowtype", "babel" ], @@ -24,6 +28,7 @@ module.exports = { parserOptions: { ecmaFeatures: { jsx: true, + legacyDecorators: true, } }, rules: { @@ -104,6 +109,9 @@ module.exports = { // crashes currently: https://github.com/eslint/eslint/issues/6274 "generator-star-spacing": "off", + + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", }, settings: { flowtype: { diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..afc29f0142 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +patreon: matrixdotorg +liberapay: matrixdotorg diff --git a/.stylelintrc.js b/.stylelintrc.js index fc00b643a0..1690f2186f 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -1,5 +1,8 @@ module.exports = { "extends": "stylelint-config-standard", + "plugins": [ + "stylelint-scss", + ], "rules": { "indentation": 4, "comment-empty-line-before": null, @@ -11,5 +14,11 @@ module.exports = { "number-no-trailing-zeros": null, "number-leading-zero": null, "selector-list-comma-newline-after": null, + "at-rule-no-unknown": null, + "no-descending-specificity": null, + "scss/at-rule-no-unknown": [true, { + // https://github.com/vector-im/riot-web/issues/10544 + "ignoreAtRules": ["define-mixin"], + }], } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ed4db44bf..02c085d0b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,2690 @@ +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 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) + + * Repair community member info panel + [\#3834](https://github.com/matrix-org/matrix-react-sdk/pull/3834) + * Add feature flag around the presence indicator in room list + [\#3833](https://github.com/matrix-org/matrix-react-sdk/pull/3833) + +Changes in [1.7.6-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6-rc.2) (2020-01-08) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.6-rc.1...v1.7.6-rc.2) + + * Strip all variation selectors on emoji + [\#3818](https://github.com/matrix-org/matrix-react-sdk/pull/3818) + +Changes in [1.7.6-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6-rc.1) (2020-01-06) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5...v1.7.6-rc.1) + + * Deduplicate recent emoji + [\#3806](https://github.com/matrix-org/matrix-react-sdk/pull/3806) + * Fix ability to remove avatars + [\#3803](https://github.com/matrix-org/matrix-react-sdk/pull/3803) + * Update from Weblate + [\#3810](https://github.com/matrix-org/matrix-react-sdk/pull/3810) + * User Info fetch latest RoomMember instead of showing historical data + [\#3788](https://github.com/matrix-org/matrix-react-sdk/pull/3788) + * Remove all usages of slate in favour of CIDER + [\#3808](https://github.com/matrix-org/matrix-react-sdk/pull/3808) + * Use display name when pinned messages are changed + [\#3809](https://github.com/matrix-org/matrix-react-sdk/pull/3809) + * Fix inverted diff line highlighting in dark theme + [\#3790](https://github.com/matrix-org/matrix-react-sdk/pull/3790) + * Bridge info settings tab + [\#3693](https://github.com/matrix-org/matrix-react-sdk/pull/3693) + * Send the labs flags the client is running with in rageshake + [\#3805](https://github.com/matrix-org/matrix-react-sdk/pull/3805) + * Initial implementation of FTUE user lists design + [\#3792](https://github.com/matrix-org/matrix-react-sdk/pull/3792) + * Update key backup creation and recovery paths for SSSS + [\#3800](https://github.com/matrix-org/matrix-react-sdk/pull/3800) + * Don't fail if logs exists and is an empty dir + [\#3798](https://github.com/matrix-org/matrix-react-sdk/pull/3798) + * Comment remaining non-cross-signing-compliant components + [\#3799](https://github.com/matrix-org/matrix-react-sdk/pull/3799) + * Remove 'unverify' from UserInfoPanel + [\#3797](https://github.com/matrix-org/matrix-react-sdk/pull/3797) + * Use deviceTrust when displaying key backup trust status + [\#3795](https://github.com/matrix-org/matrix-react-sdk/pull/3795) + * Don't crash if a keyshare request is removed + [\#3793](https://github.com/matrix-org/matrix-react-sdk/pull/3793) + * Convert /verify to checkDeviceTrust + [\#3794](https://github.com/matrix-org/matrix-react-sdk/pull/3794) + * Remove E2eIcon onClick + [\#3791](https://github.com/matrix-org/matrix-react-sdk/pull/3791) + * support channel names with slash in name/alias + [\#3778](https://github.com/matrix-org/matrix-react-sdk/pull/3778) + * Fix NPE when filtering the room list + [\#3787](https://github.com/matrix-org/matrix-react-sdk/pull/3787) + * Turn RoomAliasField into properly controlled and use in RoomSettings + [\#3782](https://github.com/matrix-org/matrix-react-sdk/pull/3782) + * fuzzy-sort MemberList + [\#3783](https://github.com/matrix-org/matrix-react-sdk/pull/3783) + * Serialize file uploads into room to match confirmation dialog order + [\#3786](https://github.com/matrix-org/matrix-react-sdk/pull/3786) + * Do not show Top Unread Messages Bar and Jump to bottom button if searching + [\#3785](https://github.com/matrix-org/matrix-react-sdk/pull/3785) + * Fix sticker picker chevron offset calculation + [\#3784](https://github.com/matrix-org/matrix-react-sdk/pull/3784) + * Fix not being able to promote others to the same power level as your own + [\#3781](https://github.com/matrix-org/matrix-react-sdk/pull/3781) + * Room Tile DMs online/active green dot + [\#3751](https://github.com/matrix-org/matrix-react-sdk/pull/3751) + * Fix spelling and grammar in README + [\#3780](https://github.com/matrix-org/matrix-react-sdk/pull/3780) + * Reintroduce working resizer code for right panel + [\#3776](https://github.com/matrix-org/matrix-react-sdk/pull/3776) + * Fix wrong scope binding on openHelp for TopLeftMenu + [\#3775](https://github.com/matrix-org/matrix-react-sdk/pull/3775) + * UserInfo hide kick/mute buttons if they make no sense + [\#3774](https://github.com/matrix-org/matrix-react-sdk/pull/3774) + * Fix duplicate Incoming Call prompt on Community Invite sublist + [\#3773](https://github.com/matrix-org/matrix-react-sdk/pull/3773) + * Apply new design to highlighted tags and add toggle mechanic + [\#3755](https://github.com/matrix-org/matrix-react-sdk/pull/3755) + * stop using ReactDOM.findDOMNode in componentWillUnmount, use refs + [\#3771](https://github.com/matrix-org/matrix-react-sdk/pull/3771) + * Add alt="" to presentational images + [\#3772](https://github.com/matrix-org/matrix-react-sdk/pull/3772) + * Fix room list filtering weird case sensitivity + [\#3759](https://github.com/matrix-org/matrix-react-sdk/pull/3759) + * Don't show the 'verify' button if the user is verified + [\#3758](https://github.com/matrix-org/matrix-react-sdk/pull/3758) + * Switch to using checkDeviceTrust + [\#3757](https://github.com/matrix-org/matrix-react-sdk/pull/3757) + * Migrate away from React Legacy contexts API + [\#3743](https://github.com/matrix-org/matrix-react-sdk/pull/3743) + * Migrate key backups to SSSS + [\#3749](https://github.com/matrix-org/matrix-react-sdk/pull/3749) + * Get rid of stripped-emoji.json in favour of an in-memory single source of + truth + [\#3745](https://github.com/matrix-org/matrix-react-sdk/pull/3745) + * Combine cross signing and verification over DM feature flags + [\#3753](https://github.com/matrix-org/matrix-react-sdk/pull/3753) + * apply unhomoglyph when filtering room list to fuzzify it + [\#3754](https://github.com/matrix-org/matrix-react-sdk/pull/3754) + * Make EmojiPicker an unmanaged Context Menu as it is too complex to be + managed + [\#3746](https://github.com/matrix-org/matrix-react-sdk/pull/3746) + * Internationalise M_TOO_LARGE error from Synapse + [\#3750](https://github.com/matrix-org/matrix-react-sdk/pull/3750) + * Replace UserInfo avatar with for fallback logic + [\#3748](https://github.com/matrix-org/matrix-react-sdk/pull/3748) + * Dropdown stop keyboard propagation if key handled + [\#3741](https://github.com/matrix-org/matrix-react-sdk/pull/3741) + * Fix right panel for multiple member info viewings + [\#3742](https://github.com/matrix-org/matrix-react-sdk/pull/3742) + * Fix Field validation tooltip sticking if blurred before async validation + resolved + [\#3740](https://github.com/matrix-org/matrix-react-sdk/pull/3740) + * Fix UserInfo exploding without a room being passed to it + [\#3738](https://github.com/matrix-org/matrix-react-sdk/pull/3738) + * Fix room directory maintaining and error state + [\#3737](https://github.com/matrix-org/matrix-react-sdk/pull/3737) + * Stop trapping tab in AddressPickerDialog + [\#3735](https://github.com/matrix-org/matrix-react-sdk/pull/3735) + * Stop using KeyboardEvent.keyCode as it is deprecated + [\#3736](https://github.com/matrix-org/matrix-react-sdk/pull/3736) + * Implement new design for uploading/removing avatars + [\#3733](https://github.com/matrix-org/matrix-react-sdk/pull/3733) + * Fix aspect ratio on room/profile avatar preview + [\#3731](https://github.com/matrix-org/matrix-react-sdk/pull/3731) + * Switch to react-focus-lock for it to comprehend Portals + [\#3732](https://github.com/matrix-org/matrix-react-sdk/pull/3732) + * Make combobox dropdown keyboard and screen reader accessible + [\#3729](https://github.com/matrix-org/matrix-react-sdk/pull/3729) + * Verify users when cross-signing enabled + [\#3728](https://github.com/matrix-org/matrix-react-sdk/pull/3728) + * Update from Weblate + [\#3730](https://github.com/matrix-org/matrix-react-sdk/pull/3730) + * Improve a11y of the unignore button in Settings + [\#3727](https://github.com/matrix-org/matrix-react-sdk/pull/3727) + * Fix ToggleSwitch A11Y (trapping tab and switch v. checkbox) + [\#3726](https://github.com/matrix-org/matrix-react-sdk/pull/3726) + * Make URL previews dismissable via keyboard and accessible to screen readers + [\#3725](https://github.com/matrix-org/matrix-react-sdk/pull/3725) + * Create new key backups using secret storage + [\#3720](https://github.com/matrix-org/matrix-react-sdk/pull/3720) + * Replace sign-ins with sessions + [\#3721](https://github.com/matrix-org/matrix-react-sdk/pull/3721) + * Refactor RightPanel to match expected behaviour + [\#3703](https://github.com/matrix-org/matrix-react-sdk/pull/3703) + * Render policy room event updates in the timeline + [\#3716](https://github.com/matrix-org/matrix-react-sdk/pull/3716) + * Wrap the await call for unknown device lookups + [\#3718](https://github.com/matrix-org/matrix-react-sdk/pull/3718) + * Add testing flow to bootstrap secret storage + [\#3640](https://github.com/matrix-org/matrix-react-sdk/pull/3640) + * Fix remaining context menu regressions + [\#3715](https://github.com/matrix-org/matrix-react-sdk/pull/3715) + * Migrate away from React Legacy string refs + [\#3712](https://github.com/matrix-org/matrix-react-sdk/pull/3712) + * Update copy for DM invites + [\#3706](https://github.com/matrix-org/matrix-react-sdk/pull/3706) + * Fix message action bar reaction picker regression + [\#3714](https://github.com/matrix-org/matrix-react-sdk/pull/3714) + * Add what-input to allow different scoping to focus-visible for MAB a11y + [\#3709](https://github.com/matrix-org/matrix-react-sdk/pull/3709) + * Mark the This/All Rooms scope buttons as radios for a11y + [\#3708](https://github.com/matrix-org/matrix-react-sdk/pull/3708) + * Switch ReactionsRowButton to an AccessibleButton for space/enter handling + [\#3707](https://github.com/matrix-org/matrix-react-sdk/pull/3707) + * Change the (edited) link to an AccessibleButton for a11y + [\#3710](https://github.com/matrix-org/matrix-react-sdk/pull/3710) + * Update from Weblate + [\#3713](https://github.com/matrix-org/matrix-react-sdk/pull/3713) + * Fix ?via= args in SpecPermalinkConstructor.js + [\#3694](https://github.com/matrix-org/matrix-react-sdk/pull/3694) + * Don't mark a room as unread when server ACLs are set + [\#3705](https://github.com/matrix-org/matrix-react-sdk/pull/3705) + * Make reaction buttons more accessible + [\#3704](https://github.com/matrix-org/matrix-react-sdk/pull/3704) + * yarn upgrade + [\#3701](https://github.com/matrix-org/matrix-react-sdk/pull/3701) + * Make CI scripts executable + [\#3698](https://github.com/matrix-org/matrix-react-sdk/pull/3698) + * ARIA compliant context menus + [\#3611](https://github.com/matrix-org/matrix-react-sdk/pull/3611) + +Changes in [1.7.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.5) (2019-12-09) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5-rc.1...v1.7.5) + + * No changes since rc.1 + +Changes in [1.7.5-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.5-rc.1) (2019-12-04) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.4...v1.7.5-rc.1) + + * Remove logs before running end-to-end tests + [\#3700](https://github.com/matrix-org/matrix-react-sdk/pull/3700) + * Update from Weblate + [\#3699](https://github.com/matrix-org/matrix-react-sdk/pull/3699) + * Match e2e icons on events to expectations + [\#3697](https://github.com/matrix-org/matrix-react-sdk/pull/3697) + * Match room upgrade warning to the new design + [\#3695](https://github.com/matrix-org/matrix-react-sdk/pull/3695) + * Remove unused translations + [\#3683](https://github.com/matrix-org/matrix-react-sdk/pull/3683) + * Remove broken velocity-ui animation + [\#3678](https://github.com/matrix-org/matrix-react-sdk/pull/3678) + * Update from Weblate + [\#3696](https://github.com/matrix-org/matrix-react-sdk/pull/3696) + * Hide Remove button in message editing history if you don't have permission + to redact + [\#3685](https://github.com/matrix-org/matrix-react-sdk/pull/3685) + * Add an option to invite users to upgraded private rooms + [\#3684](https://github.com/matrix-org/matrix-react-sdk/pull/3684) + * Do not trap Key ContextMenu into composer for keyboard a11y + [\#3689](https://github.com/matrix-org/matrix-react-sdk/pull/3689) + * Make EmojiPicker filtering case-insensitive + [\#3690](https://github.com/matrix-org/matrix-react-sdk/pull/3690) + * Ensure the settings page accurately represents theme choices + [\#3686](https://github.com/matrix-org/matrix-react-sdk/pull/3686) + * Ensure read receipts end up with a valid reference to checkUnmounting + [\#3688](https://github.com/matrix-org/matrix-react-sdk/pull/3688) + * Convert Velociraptor component to a class + [\#3687](https://github.com/matrix-org/matrix-react-sdk/pull/3687) + * Add a link to the labs feature documentation + [\#3675](https://github.com/matrix-org/matrix-react-sdk/pull/3675) + * Improve translatable strings for calls + [\#3682](https://github.com/matrix-org/matrix-react-sdk/pull/3682) + * Don't assume that diffs will have an appropriate child node + [\#3680](https://github.com/matrix-org/matrix-react-sdk/pull/3680) + * Fix persisted widgets getting stuck at loading screens + [\#3681](https://github.com/matrix-org/matrix-react-sdk/pull/3681) + * Add button to clear all notification counts, sometimes stuck in historical + [\#2959](https://github.com/matrix-org/matrix-react-sdk/pull/2959) + * Fix multi-invite error dialog messaging + [\#3679](https://github.com/matrix-org/matrix-react-sdk/pull/3679) + * Make the communities button behave more like a toggle + [\#3670](https://github.com/matrix-org/matrix-react-sdk/pull/3670) + * Change read markers to use CSS transitions + [\#3674](https://github.com/matrix-org/matrix-react-sdk/pull/3674) + * fix font smoothing to match figma + [\#3677](https://github.com/matrix-org/matrix-react-sdk/pull/3677) + * Update breadcrumbs when we do eventually see upgraded rooms + [\#3669](https://github.com/matrix-org/matrix-react-sdk/pull/3669) + * Fix override behaviour of system vs defined themes + [\#3673](https://github.com/matrix-org/matrix-react-sdk/pull/3673) + * console.log doesn't take %s substitutions + [\#3671](https://github.com/matrix-org/matrix-react-sdk/pull/3671) + * EventIndex: Move the checkpoint loading logic into the init method. + [\#3648](https://github.com/matrix-org/matrix-react-sdk/pull/3648) + * Clarify that cross-signing is in development + [\#3668](https://github.com/matrix-org/matrix-react-sdk/pull/3668) + * Hide tooltips with CSS when they aren't visible + [\#3665](https://github.com/matrix-org/matrix-react-sdk/pull/3665) + * a11y: adjustments for toasts + [\#3667](https://github.com/matrix-org/matrix-react-sdk/pull/3667) + * Update from Weblate + [\#3666](https://github.com/matrix-org/matrix-react-sdk/pull/3666) + * Null check on thumbnail_file + [\#3664](https://github.com/matrix-org/matrix-react-sdk/pull/3664) + * Fix double date separator for room upgrade tiles + [\#3662](https://github.com/matrix-org/matrix-react-sdk/pull/3662) + * Show incoming verification requests in in-app notifications + [\#3661](https://github.com/matrix-org/matrix-react-sdk/pull/3661) + * Show m.room.create event before the ELS on room upgrade + [\#3655](https://github.com/matrix-org/matrix-react-sdk/pull/3655) + * Convert MessagePanel to React class + [\#3656](https://github.com/matrix-org/matrix-react-sdk/pull/3656) + * Make addEventListener conditional + [\#3657](https://github.com/matrix-org/matrix-react-sdk/pull/3657) + * Fix e2e icons + [\#3653](https://github.com/matrix-org/matrix-react-sdk/pull/3653) + * Workaround for soft-crash with calls on startup + [\#3654](https://github.com/matrix-org/matrix-react-sdk/pull/3654) + * Catch exceptions when we can't play audio + [\#3652](https://github.com/matrix-org/matrix-react-sdk/pull/3652) + * Rename section heading for integrations in settings + [\#3650](https://github.com/matrix-org/matrix-react-sdk/pull/3650) + * Update copy for widgets not using message encryption + [\#3651](https://github.com/matrix-org/matrix-react-sdk/pull/3651) + * Ignore media actions + [\#3649](https://github.com/matrix-org/matrix-react-sdk/pull/3649) + * Add an option to disable the use of integration managers for provisioning + [\#3646](https://github.com/matrix-org/matrix-react-sdk/pull/3646) + * Move many widget options to a context menu + [\#3645](https://github.com/matrix-org/matrix-react-sdk/pull/3645) + * Re-add encryption warning to widget permission prompt + [\#3644](https://github.com/matrix-org/matrix-react-sdk/pull/3644) + * Update CIDER docs now that it is used for main composer as well + [\#3647](https://github.com/matrix-org/matrix-react-sdk/pull/3647) + * get rid of bluebird + [\#3593](https://github.com/matrix-org/matrix-react-sdk/pull/3593) + * Remove getBaseTheme + [\#3638](https://github.com/matrix-org/matrix-react-sdk/pull/3638) + * ReactionsRowButtonTooltip: fix null dereference if emoji owner left room + [\#3643](https://github.com/matrix-org/matrix-react-sdk/pull/3643) + * Add eslint-plugin-jest because we inherit js-sdk's eslintrc and it wants + [\#3642](https://github.com/matrix-org/matrix-react-sdk/pull/3642) + +Changes in [1.7.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.4) (2019-11-27) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.3...v1.7.4) + +* Upgrade to JS SDK 2.5.4 to relax identity server discovery and E2EE debugging +* Fix override behaviour of system vs defined theme +* Clarify that cross-signing is in development + +Changes in [1.7.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.3) (2019-11-25) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.3-rc.2...v1.7.3) + + * No changes since rc.2 + +Changes in [1.7.3-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.3-rc.2) (2019-11-22) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.3-rc.1...v1.7.3-rc.2) + + * Fix double date separator for room upgrade tiles + [\#3663](https://github.com/matrix-org/matrix-react-sdk/pull/3663) + * Show m.room.create event before the ELS on room upgrade + [\#3660](https://github.com/matrix-org/matrix-react-sdk/pull/3660) + * Make addEventListener conditional + [\#3659](https://github.com/matrix-org/matrix-react-sdk/pull/3659) + * Fix e2e icons + [\#3658](https://github.com/matrix-org/matrix-react-sdk/pull/3658) + +Changes in [1.7.3-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.3-rc.1) (2019-11-20) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.2...v1.7.3-rc.1) + + * Fix positioning, size, and colour of the composer e2e icon + [\#3641](https://github.com/matrix-org/matrix-react-sdk/pull/3641) + * upgrade nunito from 3.500 to 3.504 + [\#3639](https://github.com/matrix-org/matrix-react-sdk/pull/3639) + * Wire up the widget permission prompt to the cross-platform setting + [\#3630](https://github.com/matrix-org/matrix-react-sdk/pull/3630) + * Get theme automatically from system setting + [\#3637](https://github.com/matrix-org/matrix-react-sdk/pull/3637) + * Update code style for our 90 char life + [\#3636](https://github.com/matrix-org/matrix-react-sdk/pull/3636) + * use general warning icon instead of e2e one for room status + [\#3633](https://github.com/matrix-org/matrix-react-sdk/pull/3633) + * Add support for platform specific event indexing and search + [\#3550](https://github.com/matrix-org/matrix-react-sdk/pull/3550) + * Update from Weblate + [\#3635](https://github.com/matrix-org/matrix-react-sdk/pull/3635) + * Use a settings watcher to set the theme + [\#3634](https://github.com/matrix-org/matrix-react-sdk/pull/3634) + * Merge the `feature_user_info_panel` flag into `feature_dm_verification` + [\#3632](https://github.com/matrix-org/matrix-react-sdk/pull/3632) + * Fix some styling regressions in member panel + [\#3631](https://github.com/matrix-org/matrix-react-sdk/pull/3631) + * Add a bit more safety around breadcrumbs + [\#3629](https://github.com/matrix-org/matrix-react-sdk/pull/3629) + * Ensure widgets always have a sender associated with them + [\#3628](https://github.com/matrix-org/matrix-react-sdk/pull/3628) + * re-add missing case of codepath + [\#3627](https://github.com/matrix-org/matrix-react-sdk/pull/3627) + * Implement the bulk of the new widget permission prompt design + [\#3622](https://github.com/matrix-org/matrix-react-sdk/pull/3622) + * Relax identity server discovery error handling + [\#3588](https://github.com/matrix-org/matrix-react-sdk/pull/3588) + * Add cross-signing feature flag + [\#3626](https://github.com/matrix-org/matrix-react-sdk/pull/3626) + * Attempt number two at ripping out Bluebird from rageshake.js + [\#3624](https://github.com/matrix-org/matrix-react-sdk/pull/3624) + * Update from Weblate + [\#3625](https://github.com/matrix-org/matrix-react-sdk/pull/3625) + * Remove Bluebird: phase 2.1 + [\#3618](https://github.com/matrix-org/matrix-react-sdk/pull/3618) + * Add better error handling to Synapse user deactivation + [\#3619](https://github.com/matrix-org/matrix-react-sdk/pull/3619) + * New design for member panel + [\#3620](https://github.com/matrix-org/matrix-react-sdk/pull/3620) + * Show server details on login for unreachable homeserver + [\#3617](https://github.com/matrix-org/matrix-react-sdk/pull/3617) + * Add a function to get the "base" theme for a theme + [\#3615](https://github.com/matrix-org/matrix-react-sdk/pull/3615) + * Remove Bluebird: phase 2 + [\#3616](https://github.com/matrix-org/matrix-react-sdk/pull/3616) + * Remove Bluebird: phase 1 + [\#3612](https://github.com/matrix-org/matrix-react-sdk/pull/3612) + * Move notification count to in front of the room name in the page title + [\#3613](https://github.com/matrix-org/matrix-react-sdk/pull/3613) + * Add some logging/recovery for lost rooms + [\#3614](https://github.com/matrix-org/matrix-react-sdk/pull/3614) + * Add Mjolnir ban list support + [\#3585](https://github.com/matrix-org/matrix-react-sdk/pull/3585) + * Improve room switching performance with alias cache + [\#3610](https://github.com/matrix-org/matrix-react-sdk/pull/3610) + * Fix draw order when hovering composer format buttons + [\#3609](https://github.com/matrix-org/matrix-react-sdk/pull/3609) + * Use a ternary operator instead of relying on AND semantics in + EditHistoryDialog + [\#3606](https://github.com/matrix-org/matrix-react-sdk/pull/3606) + * Update from Weblate + [\#3608](https://github.com/matrix-org/matrix-react-sdk/pull/3608) + * Fix HTML fallback in replies + [\#3607](https://github.com/matrix-org/matrix-react-sdk/pull/3607) + * Fix rounded corners for the formatting toolbar + [\#3605](https://github.com/matrix-org/matrix-react-sdk/pull/3605) + * Check for a message type before assuming it is a room message + [\#3604](https://github.com/matrix-org/matrix-react-sdk/pull/3604) + * Remove lint comments about no-descending-specificity + [\#3603](https://github.com/matrix-org/matrix-react-sdk/pull/3603) + * Show verification requests in the timeline + [\#3601](https://github.com/matrix-org/matrix-react-sdk/pull/3601) + * Match identity server registration to the IS r0.3.0 spec + [\#3602](https://github.com/matrix-org/matrix-react-sdk/pull/3602) + * Restore thumbs after variation selector removal + [\#3600](https://github.com/matrix-org/matrix-react-sdk/pull/3600) + * Fix breadcrumbs so the bar is a toolbar and the buttons are buttons. + [\#3599](https://github.com/matrix-org/matrix-react-sdk/pull/3599) + * Now that part of spacing is padding, make it smaller when collapsed + [\#3597](https://github.com/matrix-org/matrix-react-sdk/pull/3597) + * Remove variation selectors from quick reactions + [\#3598](https://github.com/matrix-org/matrix-react-sdk/pull/3598) + * Fix linkify imports + [\#3595](https://github.com/matrix-org/matrix-react-sdk/pull/3595) + +Changes in [1.7.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.2) (2019-11-06) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.1...v1.7.2) + + * Fix softcrash if editing silly events + [\#3596](https://github.com/matrix-org/matrix-react-sdk/pull/3596) + * Fix: file and notifications panel back-paginating forever. + [\#3594](https://github.com/matrix-org/matrix-react-sdk/pull/3594) + * Fix focus-within on EventTile and more showing onClick + [\#3591](https://github.com/matrix-org/matrix-react-sdk/pull/3591) + * Support RTL language in message composer + [\#3592](https://github.com/matrix-org/matrix-react-sdk/pull/3592) + * Update from Weblate + [\#3590](https://github.com/matrix-org/matrix-react-sdk/pull/3590) + * Improve A11Y of timeline. Show timestamp & Actions on focus-within + [\#3587](https://github.com/matrix-org/matrix-react-sdk/pull/3587) + * Fix SVG mask-image usage in a bunch of places for correct outlining + [\#3589](https://github.com/matrix-org/matrix-react-sdk/pull/3589) + * Handle breadcrumbs, integration manager provisioning, and allowed widgets + Riot settings + [\#3577](https://github.com/matrix-org/matrix-react-sdk/pull/3577) + * Add a prompt when interacting with an identity server without terms + [\#3582](https://github.com/matrix-org/matrix-react-sdk/pull/3582) + * Fix bug where rooms would not appear when filtering + [\#3584](https://github.com/matrix-org/matrix-react-sdk/pull/3584) + * Guard against misconfigured homeservers when adding / binding phone numbers + [\#3583](https://github.com/matrix-org/matrix-react-sdk/pull/3583) + * Fix error message which is shown when unknown slash command attempted + [\#3580](https://github.com/matrix-org/matrix-react-sdk/pull/3580) + * Attempt to fix soft crash on some pinned events by null guarding member + [\#3581](https://github.com/matrix-org/matrix-react-sdk/pull/3581) + +Changes in [1.7.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.1) (2019-11-04) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.1-rc.2...v1.7.1) + + * No changes since rc.2 + +Changes in [1.7.1-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.1-rc.2) (2019-11-01) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.1-rc.1...v1.7.1-rc.2) + + * Fix bug where rooms would not appear when filtering + [\#3586](https://github.com/matrix-org/matrix-react-sdk/pull/3586) + +Changes in [1.7.1-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.1-rc.1) (2019-10-30) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.0...v1.7.1-rc.1) + + * Add ability to hide tray icon on non-Mac + [\#3573](https://github.com/matrix-org/matrix-react-sdk/pull/3573) + * Update from Weblate + [\#3579](https://github.com/matrix-org/matrix-react-sdk/pull/3579) + * Fix call state logging + [\#3578](https://github.com/matrix-org/matrix-react-sdk/pull/3578) + * Match widgets up with their integration manager + [\#3576](https://github.com/matrix-org/matrix-react-sdk/pull/3576) + * Add diagnostic log to catch events without an ID + [\#3575](https://github.com/matrix-org/matrix-react-sdk/pull/3575) + * Fix missing i18n for RoomTile ARIA labels and add a case for notif-off bold + [\#3574](https://github.com/matrix-org/matrix-react-sdk/pull/3574) + * LifeCycle onLoggedOut unmount before stopping client + [\#3566](https://github.com/matrix-org/matrix-react-sdk/pull/3566) + * Remove unneeded help about identity servers + [\#3572](https://github.com/matrix-org/matrix-react-sdk/pull/3572) + * Remove messages implying you need an identity server for email recovery + [\#3571](https://github.com/matrix-org/matrix-react-sdk/pull/3571) + * Fix quick reactions to be aligned with other emoji + [\#3570](https://github.com/matrix-org/matrix-react-sdk/pull/3570) + * If ToS gets rejected/any Scalar error then don't make Jitsi widget + [\#3569](https://github.com/matrix-org/matrix-react-sdk/pull/3569) + * Update from Weblate + [\#3568](https://github.com/matrix-org/matrix-react-sdk/pull/3568) + * Fix Room Create ELS using MXID instead of newly set Displayname/Avatar + [\#3567](https://github.com/matrix-org/matrix-react-sdk/pull/3567) + * Improve opening emoji picker performance + [\#3565](https://github.com/matrix-org/matrix-react-sdk/pull/3565) + * Update ServerTypeSelector for new matrix.org CS API URL + [\#3564](https://github.com/matrix-org/matrix-react-sdk/pull/3564) + * Accessibility Improvements + [\#3563](https://github.com/matrix-org/matrix-react-sdk/pull/3563) + * A11Y fixes in the Left Panel + [\#3562](https://github.com/matrix-org/matrix-react-sdk/pull/3562) + * Fix lint and i18n test failures + [\#3560](https://github.com/matrix-org/matrix-react-sdk/pull/3560) + * Fix: editor tests + [\#3561](https://github.com/matrix-org/matrix-react-sdk/pull/3561) + * Use Navigation Treeview pattern for RoomList Accessibility + [\#3556](https://github.com/matrix-org/matrix-react-sdk/pull/3556) + * Abort scroll updates when already unmounted + [\#3557](https://github.com/matrix-org/matrix-react-sdk/pull/3557) + * UserInfo consolidation of GroupMemberInfo and MemberInfo panels + [\#3465](https://github.com/matrix-org/matrix-react-sdk/pull/3465) + * Fix some things in the edit HTML parser + [\#3552](https://github.com/matrix-org/matrix-react-sdk/pull/3552) + * Update from Weblate + [\#3559](https://github.com/matrix-org/matrix-react-sdk/pull/3559) + * Merge end-to-end tests + [\#3537](https://github.com/matrix-org/matrix-react-sdk/pull/3537) + * Add full emoji picker for reactions + [\#3554](https://github.com/matrix-org/matrix-react-sdk/pull/3554) + * Accessibility fixes to autocomplete and tabpanels + [\#3555](https://github.com/matrix-org/matrix-react-sdk/pull/3555) + * Show warning dialog when changing unreachable IS + [\#3549](https://github.com/matrix-org/matrix-react-sdk/pull/3549) + * Fix reply fallback being included in edit m.new_content + [\#3551](https://github.com/matrix-org/matrix-react-sdk/pull/3551) + * Document composer features + [\#3548](https://github.com/matrix-org/matrix-react-sdk/pull/3548) + * Correctly update the banned users list when a user is unbanned + [\#3547](https://github.com/matrix-org/matrix-react-sdk/pull/3547) + * Summarise state events after room creation + [\#3433](https://github.com/matrix-org/matrix-react-sdk/pull/3433) + * Don't intercept TAB on the app outside of the composer, fix tabIndex > 0 + [\#3543](https://github.com/matrix-org/matrix-react-sdk/pull/3543) + * Add some type checking on event body + [\#3546](https://github.com/matrix-org/matrix-react-sdk/pull/3546) + * Fix: crash while canceling editing an event when no selection + [\#3544](https://github.com/matrix-org/matrix-react-sdk/pull/3544) + * SettingsFlag always run ToggleSwitch fully-controlled + [\#3541](https://github.com/matrix-org/matrix-react-sdk/pull/3541) + * Use Keyboard Key consts instead of hardcoded strings + [\#3540](https://github.com/matrix-org/matrix-react-sdk/pull/3540) + +Changes in [1.7.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.0) (2019-10-18) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.0-rc.1...v1.7.0) + + * Upgrade to JS SDK v2.4.2 + * Fix: edit unmount when no selection + [\#3545](https://github.com/matrix-org/matrix-react-sdk/pull/3545) + * "SettingsFlag always run ToggleSwitch fully controlled" to release + [\#3542](https://github.com/matrix-org/matrix-react-sdk/pull/3542) + +Changes in [1.7.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.0-rc.1) (2019-10-09) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.2...v1.7.0-rc.1) + + * Update from Weblate + [\#3539](https://github.com/matrix-org/matrix-react-sdk/pull/3539) + * React error/warning cleanup + [\#3529](https://github.com/matrix-org/matrix-react-sdk/pull/3529) + * Add label to rageshakes for React soft crashes + [\#3535](https://github.com/matrix-org/matrix-react-sdk/pull/3535) + * Support UI Auth on adding email addresses & phone numbers + [\#3534](https://github.com/matrix-org/matrix-react-sdk/pull/3534) + * Unmount React components before stopping the client + [\#3533](https://github.com/matrix-org/matrix-react-sdk/pull/3533) + * Fix soft crash on room join + [\#3532](https://github.com/matrix-org/matrix-react-sdk/pull/3532) + * Fix: Unable to verify email address error + [\#3528](https://github.com/matrix-org/matrix-react-sdk/pull/3528) + * Fix: submit create room dialog when pressing enter + [\#3509](https://github.com/matrix-org/matrix-react-sdk/pull/3509) + * Allow cyclic objects in console logs + [\#3531](https://github.com/matrix-org/matrix-react-sdk/pull/3531) + * Fix: watch emoticon autoreplace setting + [\#3530](https://github.com/matrix-org/matrix-react-sdk/pull/3530) + * Make "remove recent messages" more robust + [\#3508](https://github.com/matrix-org/matrix-react-sdk/pull/3508) + * Label submit button in UI auth password prompt + [\#3527](https://github.com/matrix-org/matrix-react-sdk/pull/3527) + * Null-guard the recaptcha setup + [\#3526](https://github.com/matrix-org/matrix-react-sdk/pull/3526) + * Use a mask instead of an img for "Show image" eye + [\#3513](https://github.com/matrix-org/matrix-react-sdk/pull/3513) + * Only limit the rageshake log size in one place + [\#3523](https://github.com/matrix-org/matrix-react-sdk/pull/3523) + * Rename UPPER_CAMEL_CASE to UPPER_SNAKE_CASE in Coding Style + [\#3525](https://github.com/matrix-org/matrix-react-sdk/pull/3525) + * Revert "Run yarn upgrade" + [\#3524](https://github.com/matrix-org/matrix-react-sdk/pull/3524) + * Run yarn upgrade + [\#3521](https://github.com/matrix-org/matrix-react-sdk/pull/3521) + * Limit Backspace-consuming workaround to just Slate, tidy Keyboard :) + [\#3522](https://github.com/matrix-org/matrix-react-sdk/pull/3522) + * Enable CIDER composer by default + [\#3519](https://github.com/matrix-org/matrix-react-sdk/pull/3519) + * Update from Weblate + [\#3520](https://github.com/matrix-org/matrix-react-sdk/pull/3520) + * Cull some easily fixable errors which make the console a mess + [\#3516](https://github.com/matrix-org/matrix-react-sdk/pull/3516) + +Changes in [1.6.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.2) (2019-10-04) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.2-rc.1...v1.6.2) + + * Handle null from TimelinePanel.getScrollState in RoomView _getScrollState + [\#3518](https://github.com/matrix-org/matrix-react-sdk/pull/3518) + * Fix Community Panel preference not taking effect until some refreshing + [\#3517](https://github.com/matrix-org/matrix-react-sdk/pull/3517) + * Fix backspace without slate focus killing everything + [\#3515](https://github.com/matrix-org/matrix-react-sdk/pull/3515) + * Fix integration manager not updating when set + [\#3510](https://github.com/matrix-org/matrix-react-sdk/pull/3510) + * Various ARIA a11y fixes. + [\#3514](https://github.com/matrix-org/matrix-react-sdk/pull/3514) + * Add a bit of debugging for where MatrixClient stops + [\#3511](https://github.com/matrix-org/matrix-react-sdk/pull/3511) + * Add error boundaries to catch rendering errors + [\#3512](https://github.com/matrix-org/matrix-react-sdk/pull/3512) + * Add an option to hide image previews + [\#3492](https://github.com/matrix-org/matrix-react-sdk/pull/3492) + * Fix: stuck tooltip with composer formatting bar + [\#3507](https://github.com/matrix-org/matrix-react-sdk/pull/3507) + * Fix: don't let composer placeholder show horizontal scrollbar when too + narrow + [\#3506](https://github.com/matrix-org/matrix-react-sdk/pull/3506) + +Changes in [1.6.2-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.2-rc.1) (2019-10-02) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.1...v1.6.2-rc.1) + + * Custom theming MVP + [\#3503](https://github.com/matrix-org/matrix-react-sdk/pull/3503) + * Don't mark a room as unread when someone adds an alias + [\#3505](https://github.com/matrix-org/matrix-react-sdk/pull/3505) + * Fix upgraderoom slashcommand + [\#3504](https://github.com/matrix-org/matrix-react-sdk/pull/3504) + * Update stripped emoji and bump emojidata to 4.0.2 + [\#3501](https://github.com/matrix-org/matrix-react-sdk/pull/3501) + * Support local permalinks for unfederated instances + [\#3500](https://github.com/matrix-org/matrix-react-sdk/pull/3500) + * Accessibility fixes to the LeftPanel + [\#3499](https://github.com/matrix-org/matrix-react-sdk/pull/3499) + * Make Autocomplete more accessible to screen reader users + [\#3497](https://github.com/matrix-org/matrix-react-sdk/pull/3497) + * Fix: even more resilience to detect slash commands + [\#3496](https://github.com/matrix-org/matrix-react-sdk/pull/3496) + * Fix PropTypes usage, sdk import path and GroupMemberTile letter-avatar + [\#3486](https://github.com/matrix-org/matrix-react-sdk/pull/3486) + +Changes in [1.6.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.1) (2019-10-01) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0...v1.6.1) + + * Upgrade to JS SDK 2.4.1 to ignore crypto events with empty content + * Update from Weblate + [\#3502](https://github.com/matrix-org/matrix-react-sdk/pull/3502) + * Adjust details of terms dialog + [\#3489](https://github.com/matrix-org/matrix-react-sdk/pull/3489) + * Okay -> OK + [\#3491](https://github.com/matrix-org/matrix-react-sdk/pull/3491) + * Guard against falsy names in getInitialLetter + [\#3498](https://github.com/matrix-org/matrix-react-sdk/pull/3498) + * Update from Weblate + [\#3495](https://github.com/matrix-org/matrix-react-sdk/pull/3495) + * Upgrade deps + [\#3488](https://github.com/matrix-org/matrix-react-sdk/pull/3488) + * Fix: allow mass redaction for members with same or larger power level + [\#3487](https://github.com/matrix-org/matrix-react-sdk/pull/3487) + * Truncate debug logs at the start, not the end + [\#3484](https://github.com/matrix-org/matrix-react-sdk/pull/3484) + * Fix: don't block Shift+Tab in CIDER autocomplete + [\#3481](https://github.com/matrix-org/matrix-react-sdk/pull/3481) + * Fix: make command detection more resilient + [\#3479](https://github.com/matrix-org/matrix-react-sdk/pull/3479) + +Changes in [1.6.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.0) (2019-09-27) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0-rc.2...v1.6.0) + + * Upgrade to JS SDK 2.4.0 + * Improve keyboard/screen reader accesibility some more + [\#3483](https://github.com/matrix-org/matrix-react-sdk/pull/3483) + +Changes in [1.6.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.0-rc.2) (2019-09-26) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.6.0-rc.1...v1.6.0-rc.2) + + * Settings a11y tweaks + [\#3475](https://github.com/matrix-org/matrix-react-sdk/pull/3475) + +Changes in [1.6.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.6.0-rc.1) (2019-09-25) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.3...v1.6.0-rc.1) + + * Upgrade to JS SDK 2.4.0-rc.1 + * Update from Weblate + [\#3480](https://github.com/matrix-org/matrix-react-sdk/pull/3480) + * Fix: latin input through Chinese IME + [\#3477](https://github.com/matrix-org/matrix-react-sdk/pull/3477) + * Move focus to first field in create room dialog when showing + [\#3478](https://github.com/matrix-org/matrix-react-sdk/pull/3478) + * Send MSISDN validation token to submit_url during registration + [\#3476](https://github.com/matrix-org/matrix-react-sdk/pull/3476) + * Hide browser a11y outline on context menus + [\#3474](https://github.com/matrix-org/matrix-react-sdk/pull/3474) + * Remove id_server param from threepid_creds + [\#3473](https://github.com/matrix-org/matrix-react-sdk/pull/3473) + * Fix: make sure client exists while logging out + [\#3472](https://github.com/matrix-org/matrix-react-sdk/pull/3472) + * Fix: hide details summary arrow in create room dialog on webkit too + [\#3470](https://github.com/matrix-org/matrix-react-sdk/pull/3470) + * put the room name in the title tag + [\#3471](https://github.com/matrix-org/matrix-react-sdk/pull/3471) + * Fix: when using autocomplete, ensure command is not sent as text, and @room + notifs gets needed suffix + [\#3468](https://github.com/matrix-org/matrix-react-sdk/pull/3468) + * Fix: paragraphs not being preserved when editing in CIDER + [\#3469](https://github.com/matrix-org/matrix-react-sdk/pull/3469) + * Fix: formatbar appearing below other elements + [\#3467](https://github.com/matrix-org/matrix-react-sdk/pull/3467) + * Use alternate MSISDN submit URL when returned by HS + [\#3466](https://github.com/matrix-org/matrix-react-sdk/pull/3466) + * FTUE: Create Room Dialog update + [\#3464](https://github.com/matrix-org/matrix-react-sdk/pull/3464) + * Use separate 3PID add and bind flow for supporting HSes + [\#3462](https://github.com/matrix-org/matrix-react-sdk/pull/3462) + * Add roomavatar slash command to set avatar of the current room + [\#3460](https://github.com/matrix-org/matrix-react-sdk/pull/3460) + * Don't show spinner if registration is disabled + [\#3458](https://github.com/matrix-org/matrix-react-sdk/pull/3458) + * Use same initial device name rules for SSO login as password login + [\#3457](https://github.com/matrix-org/matrix-react-sdk/pull/3457) + * Fix disabled save button on message editor when pasting + [\#3459](https://github.com/matrix-org/matrix-react-sdk/pull/3459) + * Some delinting + [\#3456](https://github.com/matrix-org/matrix-react-sdk/pull/3456) + * Fix failure to render newly verified phone number and nested forms + [\#3455](https://github.com/matrix-org/matrix-react-sdk/pull/3455) + * Focus context menus so screen readers can find them + [\#3454](https://github.com/matrix-org/matrix-react-sdk/pull/3454) + * Fix unit tests: Use correct ShallowRenderer and updated lolex + [\#3453](https://github.com/matrix-org/matrix-react-sdk/pull/3453) + * make the lifetimes of the RM configurable + [\#3450](https://github.com/matrix-org/matrix-react-sdk/pull/3450) + * Point to develop dependencies and fixed react-gemini-scrollbar + [\#3451](https://github.com/matrix-org/matrix-react-sdk/pull/3451) + * Fix address type props to state conversion + [\#3449](https://github.com/matrix-org/matrix-react-sdk/pull/3449) + * Only put a room in Historical if === 'leave' not peeked + [\#3445](https://github.com/matrix-org/matrix-react-sdk/pull/3445) + * Reuse showRoom for onJoinClick so we join using alias if its available + [\#3444](https://github.com/matrix-org/matrix-react-sdk/pull/3444) + * Fix: make sure scalarUrls is never undefined + [\#3446](https://github.com/matrix-org/matrix-react-sdk/pull/3446) + * Tweak lock file to pull in only one React version + [\#3447](https://github.com/matrix-org/matrix-react-sdk/pull/3447) + * Wrap deactivation check with sanity conditions + [\#3437](https://github.com/matrix-org/matrix-react-sdk/pull/3437) + * Fix identity server text in AddressPickerDialog + [\#3408](https://github.com/matrix-org/matrix-react-sdk/pull/3408) + * Change copy for sharing an email + [\#3438](https://github.com/matrix-org/matrix-react-sdk/pull/3438) + * Fix: stop propagation click handler for doesn't run + [\#3441](https://github.com/matrix-org/matrix-react-sdk/pull/3441) + * Fix: clicking on a room directory item takes you to the room + [\#3439](https://github.com/matrix-org/matrix-react-sdk/pull/3439) + * Fix: only hide clear filter button when blurred & no more search term + [\#3432](https://github.com/matrix-org/matrix-react-sdk/pull/3432) + * Dont wrap text in room directory buttons + [\#3430](https://github.com/matrix-org/matrix-react-sdk/pull/3430) + * Add way to report the content of a message + [\#3290](https://github.com/matrix-org/matrix-react-sdk/pull/3290) + * Breadcrumbs: only show after having joined at least 10 rooms + [\#3428](https://github.com/matrix-org/matrix-react-sdk/pull/3428) + * Breadcrumbs: show placeholder for empty slot + [\#3429](https://github.com/matrix-org/matrix-react-sdk/pull/3429) + * Hide the change HS url button on SSO login flow if custom urls disabled + [\#3421](https://github.com/matrix-org/matrix-react-sdk/pull/3421) + * Fix register page selector buttons growing too wide + [\#3423](https://github.com/matrix-org/matrix-react-sdk/pull/3423) + * Left panel: visual fixes + [\#3422](https://github.com/matrix-org/matrix-react-sdk/pull/3422) + * EditMessageComposer: disable Save button until a change has been made + [\#3410](https://github.com/matrix-org/matrix-react-sdk/pull/3410) + * RoomDirectory: show spinner if loading more results + [\#3416](https://github.com/matrix-org/matrix-react-sdk/pull/3416) + * SSO Login: don't assume m.login.password, ask server before showing + [\#3417](https://github.com/matrix-org/matrix-react-sdk/pull/3417) + * Switch to React 16 + [\#3270](https://github.com/matrix-org/matrix-react-sdk/pull/3270) + * Fix replying from search results for this and all rooms + [\#3406](https://github.com/matrix-org/matrix-react-sdk/pull/3406) + * Support Synapse deactivate on MemberInfo without Room (timeline pill) + [\#3411](https://github.com/matrix-org/matrix-react-sdk/pull/3411) + * Lift 3PID state management up to Settings tab + [\#3419](https://github.com/matrix-org/matrix-react-sdk/pull/3419) + * Room directory: update design + [\#3418](https://github.com/matrix-org/matrix-react-sdk/pull/3418) + * Room directory: add action buttons on room directly + [\#3413](https://github.com/matrix-org/matrix-react-sdk/pull/3413) + * Add explore button in left panel to go show room directory + [\#3412](https://github.com/matrix-org/matrix-react-sdk/pull/3412) + * Only update m.accepted_terms if there were changes + [\#3415](https://github.com/matrix-org/matrix-react-sdk/pull/3415) + * Make uses of AddressPickerDialog static dialogs + [\#3414](https://github.com/matrix-org/matrix-react-sdk/pull/3414) + * Support sending hidden read receipts + [\#3395](https://github.com/matrix-org/matrix-react-sdk/pull/3395) + * Catch error from changing room power level requirements and show modal + [\#3407](https://github.com/matrix-org/matrix-react-sdk/pull/3407) + * Add "Remove recent messages" button in member info for administrator + [\#3409](https://github.com/matrix-org/matrix-react-sdk/pull/3409) + * Add bound 3PID warning when changing IS as well + [\#3394](https://github.com/matrix-org/matrix-react-sdk/pull/3394) + * Stregthen bound 3PID warning dialog + [\#3401](https://github.com/matrix-org/matrix-react-sdk/pull/3401) + * Stop setting IS input field on account change + [\#3398](https://github.com/matrix-org/matrix-react-sdk/pull/3398) + * New composer: use underscore for italics so it doesn't collide with bold + when toggling + [\#3403](https://github.com/matrix-org/matrix-react-sdk/pull/3403) + * Switch to createReactClass: views/elements & views/groups. React 16 :D + [\#3404](https://github.com/matrix-org/matrix-react-sdk/pull/3404) + * Switch to createReactClass: *everything else*. React 16 :D + [\#3405](https://github.com/matrix-org/matrix-react-sdk/pull/3405) + * Switch to createReactClass: views/rooms and test/components. React 16 :D + [\#3400](https://github.com/matrix-org/matrix-react-sdk/pull/3400) + * New composer: support toggling inline formatting + [\#3402](https://github.com/matrix-org/matrix-react-sdk/pull/3402) + * Fix format bar not appearing in edit composer + [\#3399](https://github.com/matrix-org/matrix-react-sdk/pull/3399) + * Correct case of propTypes property in ES6 React Components. React 16 + [\#3397](https://github.com/matrix-org/matrix-react-sdk/pull/3397) + * Fix error on load in composer + [\#3396](https://github.com/matrix-org/matrix-react-sdk/pull/3396) + * New composer: ensure undo history is persisted before applying formatting + [\#3391](https://github.com/matrix-org/matrix-react-sdk/pull/3391) + * New composer: fix pasting from word processors + [\#3392](https://github.com/matrix-org/matrix-react-sdk/pull/3392) + * New composer: fix needing to push arrow-up twice after sending first message + after switching to a room + [\#3393](https://github.com/matrix-org/matrix-react-sdk/pull/3393) + * Remove accent color as selection color + [\#3390](https://github.com/matrix-org/matrix-react-sdk/pull/3390) + * New composer: formatting keyboard shortcuts + [\#3389](https://github.com/matrix-org/matrix-react-sdk/pull/3389) + * New composer: format bar tooltips + [\#3387](https://github.com/matrix-org/matrix-react-sdk/pull/3387) + * New composer: show format bar on selection + [\#3386](https://github.com/matrix-org/matrix-react-sdk/pull/3386) + * Add new agreed URLs to account data instead of overwriting + [\#3388](https://github.com/matrix-org/matrix-react-sdk/pull/3388) + * Fix member power levels in room settings + [\#3384](https://github.com/matrix-org/matrix-react-sdk/pull/3384) + * New composer: allow escaping the first slash to not write a command + [\#3382](https://github.com/matrix-org/matrix-react-sdk/pull/3382) + * /plain command to bypass markdown conversion + [\#3381](https://github.com/matrix-org/matrix-react-sdk/pull/3381) + * Improve email invite preview messaging + [\#3385](https://github.com/matrix-org/matrix-react-sdk/pull/3385) + * Revert "New composer: show markdown legend on focus" + [\#3383](https://github.com/matrix-org/matrix-react-sdk/pull/3383) + * Disable MSISDN registration if the homeserver doesn't support it + [\#3368](https://github.com/matrix-org/matrix-react-sdk/pull/3368) + * Add a button to MemberInfo to deactivate a user + [\#3371](https://github.com/matrix-org/matrix-react-sdk/pull/3371) + * New composer: show markdown legend on focus + [\#3378](https://github.com/matrix-org/matrix-react-sdk/pull/3378) + * New composer: allow submitting after inserting newline + [\#3380](https://github.com/matrix-org/matrix-react-sdk/pull/3380) + * Message editing: deserialize headers from html back to markdown + [\#3379](https://github.com/matrix-org/matrix-react-sdk/pull/3379) + * New composer: share user pill postfix between autocomplete and insert + mention + [\#3375](https://github.com/matrix-org/matrix-react-sdk/pull/3375) + * New composer: make command detection not break with pill candidates in + command + [\#3374](https://github.com/matrix-org/matrix-react-sdk/pull/3374) + * New composer: dont allow sending empty messages + [\#3377](https://github.com/matrix-org/matrix-react-sdk/pull/3377) + * New composer: disable spell check for pills + [\#3376](https://github.com/matrix-org/matrix-react-sdk/pull/3376) + * Restrict green link colours to address picker dialog + [\#3373](https://github.com/matrix-org/matrix-react-sdk/pull/3373) + * New composer: fix tab-complete in commands + [\#3372](https://github.com/matrix-org/matrix-react-sdk/pull/3372) + * Add a dialog when inviting via slash command without IS + [\#3367](https://github.com/matrix-org/matrix-react-sdk/pull/3367) + * Migrate away from React.createClass for auth and views/auth. React 16 :D + [\#3363](https://github.com/matrix-org/matrix-react-sdk/pull/3363) + * Migrate away from React.createClass for non-auth structures. React 16 :D + [\#3365](https://github.com/matrix-org/matrix-react-sdk/pull/3365) + * New composer: put display name in user pill text fallback instead of mxid + [\#3366](https://github.com/matrix-org/matrix-react-sdk/pull/3366) + * New composer: force model update after IME composition finishes + [\#3364](https://github.com/matrix-org/matrix-react-sdk/pull/3364) + * New composer: append colon to completed user pill when at start of message + [\#3361](https://github.com/matrix-org/matrix-react-sdk/pull/3361) + * Expose power level toggle for enabling e2ee to room settings + [\#3351](https://github.com/matrix-org/matrix-react-sdk/pull/3351) + * Allow connecting to an IS from address picker + [\#3359](https://github.com/matrix-org/matrix-react-sdk/pull/3359) + * New composer: don't capture enter to close autocomplete + [\#3362](https://github.com/matrix-org/matrix-react-sdk/pull/3362) + * Add Spoilers as per MSC2010 + [\#3018](https://github.com/matrix-org/matrix-react-sdk/pull/3018) + * New composer: support pasting files + [\#3358](https://github.com/matrix-org/matrix-react-sdk/pull/3358) + * New composer: don't update model while doing IME compositions + [\#3357](https://github.com/matrix-org/matrix-react-sdk/pull/3357) + * New composer: handle newlines properly when pasting + [\#3356](https://github.com/matrix-org/matrix-react-sdk/pull/3356) + * Bump eslint-utils from 1.4.0 to 1.4.2 + [\#3360](https://github.com/matrix-org/matrix-react-sdk/pull/3360) + * Reveal custom IS field only when required + [\#3343](https://github.com/matrix-org/matrix-react-sdk/pull/3343) + * Only Destroy the expected persistent widget, not *ANY* + [\#3338](https://github.com/matrix-org/matrix-react-sdk/pull/3338) + * Expose upgrade room permissions in room settings and fix command + [\#3352](https://github.com/matrix-org/matrix-react-sdk/pull/3352) + * Don't use cursor: pointer on roomsettings avatar if you can't change it + [\#3354](https://github.com/matrix-org/matrix-react-sdk/pull/3354) + * New composer: support forcing auto complete on name by hitting tab + [\#3349](https://github.com/matrix-org/matrix-react-sdk/pull/3349) + * Update email help text + [\#3355](https://github.com/matrix-org/matrix-react-sdk/pull/3355) + * Don't infinite loop on server change + [\#3350](https://github.com/matrix-org/matrix-react-sdk/pull/3350) + * Support multiple integration managers behind a labs flag + [\#3341](https://github.com/matrix-org/matrix-react-sdk/pull/3341) + * Support homeserver-configured integration managers + [\#3340](https://github.com/matrix-org/matrix-react-sdk/pull/3340) + * Auto-replace emoticons with emojis in new composer + [\#3342](https://github.com/matrix-org/matrix-react-sdk/pull/3342) + * Adjust copy and include identity server changing when terms are pending + [\#3348](https://github.com/matrix-org/matrix-react-sdk/pull/3348) + * Migrate away from React.createClass for views/dialogs. React 16 :D + [\#3347](https://github.com/matrix-org/matrix-react-sdk/pull/3347) + * Migrate away from React.createClass for async-components. React 16 :D + [\#3346](https://github.com/matrix-org/matrix-react-sdk/pull/3346) + * Switch from react-addons-test-utils to react-dom/test-utils. React 16 :D + [\#3345](https://github.com/matrix-org/matrix-react-sdk/pull/3345) + * Iterate over all instances of variable/tag for _t substitutions + [\#3339](https://github.com/matrix-org/matrix-react-sdk/pull/3339) + * Treat 404 errors on IS as having no terms + [\#3344](https://github.com/matrix-org/matrix-react-sdk/pull/3344) + * Add IS access token callback + [\#3337](https://github.com/matrix-org/matrix-react-sdk/pull/3337) + * Tweak rageshake logging messages + [\#3331](https://github.com/matrix-org/matrix-react-sdk/pull/3331) + * Support editing composer to be used as main composer (feature flagged) + [\#3287](https://github.com/matrix-org/matrix-react-sdk/pull/3287) + * Remove tooltipClassName from the Field's input + [\#3333](https://github.com/matrix-org/matrix-react-sdk/pull/3333) + * Fix alignment of discovery section addresses + [\#3335](https://github.com/matrix-org/matrix-react-sdk/pull/3335) + * No-op removals of widgets that don't exist + [\#3336](https://github.com/matrix-org/matrix-react-sdk/pull/3336) + * Remove extraneous logging + [\#3334](https://github.com/matrix-org/matrix-react-sdk/pull/3334) + * Handle terms agreement in Discovery section of user settings + [\#3327](https://github.com/matrix-org/matrix-react-sdk/pull/3327) + * Don't double translate labs settings + [\#3332](https://github.com/matrix-org/matrix-react-sdk/pull/3332) + * Explicitly check for modifier keydown events before focusing composer + [\#3328](https://github.com/matrix-org/matrix-react-sdk/pull/3328) + * Touch up settings: alignment, spacing, error states + [\#3330](https://github.com/matrix-org/matrix-react-sdk/pull/3330) + * Fix regression on widget panel edit button + [\#3329](https://github.com/matrix-org/matrix-react-sdk/pull/3329) + * Clarify that device names are publicly visible + [\#3326](https://github.com/matrix-org/matrix-react-sdk/pull/3326) + * Tweak privacy settings copy and whitespace + [\#3325](https://github.com/matrix-org/matrix-react-sdk/pull/3325) + * Persist and maintain identity server in account data + [\#3320](https://github.com/matrix-org/matrix-react-sdk/pull/3320) + * Prompt for terms of service on identity server changes + [\#3317](https://github.com/matrix-org/matrix-react-sdk/pull/3317) + * Allow 3pids to be added with no ID server set + [\#3323](https://github.com/matrix-org/matrix-react-sdk/pull/3323) + * Fix up remove threepid confirmation UX + [\#3324](https://github.com/matrix-org/matrix-react-sdk/pull/3324) + * Improve Discovery section when no IS set + [\#3322](https://github.com/matrix-org/matrix-react-sdk/pull/3322) + * Allow password reset without an ID Server + [\#3319](https://github.com/matrix-org/matrix-react-sdk/pull/3319) + * Allow registering with email if no ID Server + [\#3318](https://github.com/matrix-org/matrix-react-sdk/pull/3318) + * Update from Weblate + [\#3321](https://github.com/matrix-org/matrix-react-sdk/pull/3321) + * Fix showing events which were replied to and then redacted + [\#3310](https://github.com/matrix-org/matrix-react-sdk/pull/3310) + * Prompt for terms of service on integration manager changes + [\#3316](https://github.com/matrix-org/matrix-react-sdk/pull/3316) + * Warn on disconnecting from IS + [\#3314](https://github.com/matrix-org/matrix-react-sdk/pull/3314) + * Fix set integration manager tooltip + [\#3315](https://github.com/matrix-org/matrix-react-sdk/pull/3315) + * Prompt for ICE server fallback permission + [\#3309](https://github.com/matrix-org/matrix-react-sdk/pull/3309) + * Fix Persisted Widgets (Jitsi) randomly closing on room change + [\#3313](https://github.com/matrix-org/matrix-react-sdk/pull/3313) + * Verifying your own device should not ask you to "contact its owner" + [\#3312](https://github.com/matrix-org/matrix-react-sdk/pull/3312) + * Support changing your integration manager in the UI + [\#3308](https://github.com/matrix-org/matrix-react-sdk/pull/3308) + * delint languageHandler + [\#3311](https://github.com/matrix-org/matrix-react-sdk/pull/3311) + * Disconnect from IS Button + [\#3305](https://github.com/matrix-org/matrix-react-sdk/pull/3305) + * Add UI in settings to change ID Server + [\#3300](https://github.com/matrix-org/matrix-react-sdk/pull/3300) + * Read integration managers from account data (widgets) + [\#3302](https://github.com/matrix-org/matrix-react-sdk/pull/3302) + * Refactor integration manager handling into a common place + [\#3301](https://github.com/matrix-org/matrix-react-sdk/pull/3301) + * Fix wrong default-letter-avatar for RoomAvatarEvent + [\#3291](https://github.com/matrix-org/matrix-react-sdk/pull/3291) + * Fix alignment of add email/phone number inputs in settings + [\#3307](https://github.com/matrix-org/matrix-react-sdk/pull/3307) + * Bump matrix-react-test-utils for React 16 compatibility + [\#3306](https://github.com/matrix-org/matrix-react-sdk/pull/3306) + * Consolidate Themes into ThemeController. Remove hardcoded themes in view + [\#3304](https://github.com/matrix-org/matrix-react-sdk/pull/3304) + * Deduplicate code in ModularServerConfig by extending ServerConfig + [\#3303](https://github.com/matrix-org/matrix-react-sdk/pull/3303) + * RegistrationForm: the Fields are controlled, fix default values + [\#3299](https://github.com/matrix-org/matrix-react-sdk/pull/3299) + * change Modal async/await signature to use raw promises + [\#3298](https://github.com/matrix-org/matrix-react-sdk/pull/3298) + * Modal.createX return thenable which extends onFinished, for async/await + [\#3297](https://github.com/matrix-org/matrix-react-sdk/pull/3297) + * Add mount-guards to MImageBody + [\#3294](https://github.com/matrix-org/matrix-react-sdk/pull/3294) + * Get rid of support for legacy login params + [\#3296](https://github.com/matrix-org/matrix-react-sdk/pull/3296) + * Change throttle to debounce + [\#3295](https://github.com/matrix-org/matrix-react-sdk/pull/3295) + * Misc fixes to karma webpack + [\#3293](https://github.com/matrix-org/matrix-react-sdk/pull/3293) + * Remove 3PID binding during registration + [\#3289](https://github.com/matrix-org/matrix-react-sdk/pull/3289) + * Add controls for toggling discovery in user settings + [\#3288](https://github.com/matrix-org/matrix-react-sdk/pull/3288) + * Reuse DMs whenever possible instead of asking to reuse them + [\#3286](https://github.com/matrix-org/matrix-react-sdk/pull/3286) + * Work with no ID server set + [\#3285](https://github.com/matrix-org/matrix-react-sdk/pull/3285) + * Split MessageEditor up in edit-specifics & reusable parts for main composer + [\#3282](https://github.com/matrix-org/matrix-react-sdk/pull/3282) + * Fix waste of space of collapsed left panel + [\#2668](https://github.com/matrix-org/matrix-react-sdk/pull/2668) + * Add help slash command + [\#3257](https://github.com/matrix-org/matrix-react-sdk/pull/3257) + * Allow setting in electron whether or not to auto hide menu bar + [\#3278](https://github.com/matrix-org/matrix-react-sdk/pull/3278) + * Progress towards React 16 compatibility + [\#3276](https://github.com/matrix-org/matrix-react-sdk/pull/3276) + * Hide settings button on Room Tile Context Menu for myMembership=invite + [\#3284](https://github.com/matrix-org/matrix-react-sdk/pull/3284) + * Remove withMatrixClient as we are committed to using Contexts + [\#3279](https://github.com/matrix-org/matrix-react-sdk/pull/3279) + * Update existing 3PID management UX + [\#3283](https://github.com/matrix-org/matrix-react-sdk/pull/3283) + * Let user know their account has been deactivated upon trying to login + [\#3280](https://github.com/matrix-org/matrix-react-sdk/pull/3280) + * Fix newline not being appended to code block while converting message to + markdown + [\#3274](https://github.com/matrix-org/matrix-react-sdk/pull/3274) + * Don't load guest sessions on post-registration login link + [\#3273](https://github.com/matrix-org/matrix-react-sdk/pull/3273) + * Show terms modal when inviting by email + [\#3271](https://github.com/matrix-org/matrix-react-sdk/pull/3271) + * Add developer documentation for the new editor + [\#3272](https://github.com/matrix-org/matrix-react-sdk/pull/3272) + * Undo/redo support for new editor + [\#3267](https://github.com/matrix-org/matrix-react-sdk/pull/3267) + * Check IS v2 account tokens for validity + [\#3268](https://github.com/matrix-org/matrix-react-sdk/pull/3268) + * Fix dialog button border colours + [\#3269](https://github.com/matrix-org/matrix-react-sdk/pull/3269) + * Upgrade dependencies + [\#3266](https://github.com/matrix-org/matrix-react-sdk/pull/3266) + +Changes in [1.5.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.3) (2019-09-16) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.3-rc.3...v1.5.3) + + * Release: Directory should use the alias or server information to join the + room + [\#3448](https://github.com/matrix-org/matrix-react-sdk/pull/3448) + +Changes in [1.5.3-rc.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.3-rc.3) (2019-09-13) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.3-rc.2...v1.5.3-rc.3) + + * js-sdk rc.1 for report API + +Changes in [1.5.3-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.3-rc.2) (2019-09-13) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.3-rc.1...v1.5.3-rc.2) + + * Fix: stop propagation click handler for doesn't run + [\#3443](https://github.com/matrix-org/matrix-react-sdk/pull/3443) + * Add way to report the content of a message + [\#3442](https://github.com/matrix-org/matrix-react-sdk/pull/3442) + * Fix synapse deactivate button for release + [\#3436](https://github.com/matrix-org/matrix-react-sdk/pull/3436) + * Fix: clicking on a room directory item takes you to the room + [\#3440](https://github.com/matrix-org/matrix-react-sdk/pull/3440) + +Changes in [1.5.3-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.3-rc.1) (2019-09-12) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.2...v1.5.3-rc.1) + + * Fix: only hide clear filter button when blurred & no more search term + [\#3435](https://github.com/matrix-org/matrix-react-sdk/pull/3435) + * Dont wrap text in room directory buttons + [\#3434](https://github.com/matrix-org/matrix-react-sdk/pull/3434) + +Changes in [1.5.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.2) (2019-09-12) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.2-rc.1...v1.5.2) + + * Fix register page selector buttons growing too wide + [\#3427](https://github.com/matrix-org/matrix-react-sdk/pull/3427) + * Left panel: visual fixes + [\#3426](https://github.com/matrix-org/matrix-react-sdk/pull/3426) + * Hide the change HS url button on SSO login flow if custom urls disabled + [\#3425](https://github.com/matrix-org/matrix-react-sdk/pull/3425) + * RoomDirectory: show spinner if loading more results + [\#3424](https://github.com/matrix-org/matrix-react-sdk/pull/3424) + +Changes in [1.5.2-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.2-rc.1) (2019-09-11) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.1...v1.5.2-rc.1) + + * Merge first pass of First Time User Experience to release branch + [\#3420](https://github.com/matrix-org/matrix-react-sdk/pull/3420) + +Changes in [1.5.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.1) (2019-08-05) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.0-rc.1...v1.5.1) + + * Let user know their account has been deactivated upon trying to login + [\#3281](https://github.com/matrix-org/matrix-react-sdk/pull/3281) + +Changes in [1.5.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.0) (2019-08-05) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.5.0-rc.1...v1.5.0) + + * Don't load guest sessions on post-registration login link + [\#3277](https://github.com/matrix-org/matrix-react-sdk/pull/3277) + +Changes in [1.5.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.5.0-rc.1) (2019-07-31) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.4.0...v1.5.0-rc.1) + + * Upgrade to JS SDK 2.3.0-rc.1 + * Update from Weblate + [\#3265](https://github.com/matrix-org/matrix-react-sdk/pull/3265) + * Replace React.PropTypes with usage of the `prop-types` package + [\#3263](https://github.com/matrix-org/matrix-react-sdk/pull/3263) + * strikethrough & underline deletions & insertions + [\#3264](https://github.com/matrix-org/matrix-react-sdk/pull/3264) + * Get rid of warning of required prop + [\#3261](https://github.com/matrix-org/matrix-react-sdk/pull/3261) + * Fix html diffs repeating text sometimes + [\#3262](https://github.com/matrix-org/matrix-react-sdk/pull/3262) + * Introduce RoomContext for sharing state between RoomView and children + [\#3260](https://github.com/matrix-org/matrix-react-sdk/pull/3260) + * Upgrade emojibase to fix :anxious: + [\#3259](https://github.com/matrix-org/matrix-react-sdk/pull/3259) + * Add support for IS v2 API with authentication + [\#3256](https://github.com/matrix-org/matrix-react-sdk/pull/3256) + * Fix autocomplete for editing being broken + [\#3258](https://github.com/matrix-org/matrix-react-sdk/pull/3258) + * Unit tests for new editor + [\#3247](https://github.com/matrix-org/matrix-react-sdk/pull/3247) + * Show MessageActionBar buttons conditionally on room state permissions + [\#3255](https://github.com/matrix-org/matrix-react-sdk/pull/3255) + * Handle onPaste AddressPickerDialog, allow addressing CSV/NL/Space delim list + [\#3249](https://github.com/matrix-org/matrix-react-sdk/pull/3249) + * Move history with alt up/down regardless of where selection is + [\#3254](https://github.com/matrix-org/matrix-react-sdk/pull/3254) + * Update from Weblate + [\#3253](https://github.com/matrix-org/matrix-react-sdk/pull/3253) + * Fix /rainbowme and /rainbow breaking apart utf-16 emoji + [\#3248](https://github.com/matrix-org/matrix-react-sdk/pull/3248) + * Tweak interactive tooltip buffer area allow for overshoot + [\#3245](https://github.com/matrix-org/matrix-react-sdk/pull/3245) + * Keep widget URL in permission screen to one line + [\#3243](https://github.com/matrix-org/matrix-react-sdk/pull/3243) + * Avoid visual glitch when terms appear for Integration Manager + [\#3242](https://github.com/matrix-org/matrix-react-sdk/pull/3242) + * Show diff for formatted messages in the edit history + [\#3244](https://github.com/matrix-org/matrix-react-sdk/pull/3244) + * Redirect paste to composer when event target can't receive input + [\#3239](https://github.com/matrix-org/matrix-react-sdk/pull/3239) + * Restore manual composing focusing logic + [\#3241](https://github.com/matrix-org/matrix-react-sdk/pull/3241) + * ToS for ISes/IMs: prompt on use screen + [\#3199](https://github.com/matrix-org/matrix-react-sdk/pull/3199) + * Defer IM token until widget is shown and permission granted + [\#3240](https://github.com/matrix-org/matrix-react-sdk/pull/3240) + * Move read marker past invisible events + [\#3226](https://github.com/matrix-org/matrix-react-sdk/pull/3226) + * Basic diff visualisation for plain text edits + [\#3238](https://github.com/matrix-org/matrix-react-sdk/pull/3238) + * Don't focus composer on keydown with modifier + [\#3237](https://github.com/matrix-org/matrix-react-sdk/pull/3237) + * Focus composer when typing anywhere in the app + [\#3224](https://github.com/matrix-org/matrix-react-sdk/pull/3224) + * Don't show remove button for original event in edit history + [\#3235](https://github.com/matrix-org/matrix-react-sdk/pull/3235) + * Remove feature flags for reactions and edits + [\#3233](https://github.com/matrix-org/matrix-react-sdk/pull/3233) + * Enable reactions and edits by default + [\#3229](https://github.com/matrix-org/matrix-react-sdk/pull/3229) + * Improve interactive tooltip safe mousing area + [\#3228](https://github.com/matrix-org/matrix-react-sdk/pull/3228) + * Add a previous event safe area around action bar + [\#3227](https://github.com/matrix-org/matrix-react-sdk/pull/3227) + * Parse integration manager origins more sensibly + [\#3217](https://github.com/matrix-org/matrix-react-sdk/pull/3217) + * ChatCreateOrReuse show only rooms both you and the other party still in + [\#3225](https://github.com/matrix-org/matrix-react-sdk/pull/3225) + * Check for liveliness on submission when the server was previously dead + [\#3218](https://github.com/matrix-org/matrix-react-sdk/pull/3218) + * Fix autocomplete delay text field not accepting text + [\#3219](https://github.com/matrix-org/matrix-react-sdk/pull/3219) + * Don't show a reason if there's no reason for a kick/ban + [\#3220](https://github.com/matrix-org/matrix-react-sdk/pull/3220) + * Take adjacent invisible events into account for read receipt, even if any + but first should be ignored. + [\#3221](https://github.com/matrix-org/matrix-react-sdk/pull/3221) + * Check content and content.users in power levels + [\#3216](https://github.com/matrix-org/matrix-react-sdk/pull/3216) + * Autojoin rooms when clicking the tombstone + [\#3206](https://github.com/matrix-org/matrix-react-sdk/pull/3206) + * Verify i18n in CI + [\#3209](https://github.com/matrix-org/matrix-react-sdk/pull/3209) + * Send the correct UIA alongside the wrong UIA for backwards comaptibility + [\#3211](https://github.com/matrix-org/matrix-react-sdk/pull/3211) + * Remove unused identityEnabled property from ValidatedServerConfig + [\#3213](https://github.com/matrix-org/matrix-react-sdk/pull/3213) + * Remove misleading text about admins logging people out from soft logout + [\#3205](https://github.com/matrix-org/matrix-react-sdk/pull/3205) + +Changes in [1.4.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.4.0) (2019-07-18) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.4.0-rc.3...v1.4.0) + + * Upgrade to JS SDK 2.2.0 + * Don't show remove button for original event in edit history + [\#3236](https://github.com/matrix-org/matrix-react-sdk/pull/3236) + * Remove feature flags for reactions and edits + [\#3234](https://github.com/matrix-org/matrix-react-sdk/pull/3234) + * Enable reactions and edits by default + [\#3232](https://github.com/matrix-org/matrix-react-sdk/pull/3232) + * Improve interactive tooltip safe mousing area + [\#3231](https://github.com/matrix-org/matrix-react-sdk/pull/3231) + * Add a previous event safe area around action bar + [\#3230](https://github.com/matrix-org/matrix-react-sdk/pull/3230) + +Changes in [1.4.0-rc.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.4.0-rc.3) (2019-07-15) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.4.0-rc.2...v1.4.0-rc.3) + + * Check content and content.users in power levels + [\#3223](https://github.com/matrix-org/matrix-react-sdk/pull/3223) + * Take adjacent invisible events into account for read receipt, even if any + but first should be ignored. + [\#3222](https://github.com/matrix-org/matrix-react-sdk/pull/3222) + +Changes in [1.4.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.4.0-rc.2) (2019-07-12) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.4.0-rc.1...v1.4.0-rc.2) + + * Upgrade to JS SDK 2.2.0-rc.2 to fix regresion in listing devices + * Remove misleading text about admins logging people out from soft logout + [\#3215](https://github.com/matrix-org/matrix-react-sdk/pull/3215) + +Changes in [1.4.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.4.0-rc.1) (2019-07-12) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.3.1...v1.4.0-rc.1) + + * Update from Weblate + [\#3214](https://github.com/matrix-org/matrix-react-sdk/pull/3214) + * Prevent autocomplete on paste, and verserev-ing text before and after : + [\#3210](https://github.com/matrix-org/matrix-react-sdk/pull/3210) + * Close settings after deactivating + [\#3212](https://github.com/matrix-org/matrix-react-sdk/pull/3212) + * Require an issue URL (or notes) on rageshakes + [\#3207](https://github.com/matrix-org/matrix-react-sdk/pull/3207) + * Use r0 media endpoints for group tests + [\#3202](https://github.com/matrix-org/matrix-react-sdk/pull/3202) + * Fix field styling regression + [\#3204](https://github.com/matrix-org/matrix-react-sdk/pull/3204) + * Upgrade dependencies + [\#3203](https://github.com/matrix-org/matrix-react-sdk/pull/3203) + * Show anything other than ban/invite -> leave as a kick + [\#3198](https://github.com/matrix-org/matrix-react-sdk/pull/3198) + * Run stylelint on all SCSS files + [\#3200](https://github.com/matrix-org/matrix-react-sdk/pull/3200) + * Show original event in edit history + [\#3195](https://github.com/matrix-org/matrix-react-sdk/pull/3195) + * Use the state variable for the password when deactivating + [\#3201](https://github.com/matrix-org/matrix-react-sdk/pull/3201) + * Support SSO for rehydrating a soft-logged-out session. + [\#3197](https://github.com/matrix-org/matrix-react-sdk/pull/3197) + * Change highlight colour on dark theme + [\#3196](https://github.com/matrix-org/matrix-react-sdk/pull/3196) + * Dress up the soft logout page to look like the design + [\#3190](https://github.com/matrix-org/matrix-react-sdk/pull/3190) + * Overwrite the old session if the new creds are for a different user + [\#3189](https://github.com/matrix-org/matrix-react-sdk/pull/3189) + * Fix React crash when using a non-default homeserver on soft logout + [\#3188](https://github.com/matrix-org/matrix-react-sdk/pull/3188) + * Change soft logout rehydrate text if there's pending key backups + [\#3187](https://github.com/matrix-org/matrix-react-sdk/pull/3187) + * Ask for the user's password to rehydrate their soft logged out session + [\#3182](https://github.com/matrix-org/matrix-react-sdk/pull/3182) + * Don't try to call bodyToHtml with an empty content + [\#3194](https://github.com/matrix-org/matrix-react-sdk/pull/3194) + * Take server-side aggregation into account for timestamp on (edited) tooltip + [\#3193](https://github.com/matrix-org/matrix-react-sdk/pull/3193) + * Fix some React errors + [\#3164](https://github.com/matrix-org/matrix-react-sdk/pull/3164) + * Preserve reply fallback on edit + [\#3192](https://github.com/matrix-org/matrix-react-sdk/pull/3192) + * Don't show Remove button in ImageView if can't redact, delint ImageView + [\#3191](https://github.com/matrix-org/matrix-react-sdk/pull/3191) + * Edit history actions + [\#3180](https://github.com/matrix-org/matrix-react-sdk/pull/3180) + * Don't allow editing via up-arrow when Replying + [\#3183](https://github.com/matrix-org/matrix-react-sdk/pull/3183) + * If oldContent matches newContent, skip sending the edit + [\#3103](https://github.com/matrix-org/matrix-react-sdk/pull/3103) + * Track live events in timeline and use for read receipts and read markers + [\#3184](https://github.com/matrix-org/matrix-react-sdk/pull/3184) + * Upgrade dependencies + [\#3179](https://github.com/matrix-org/matrix-react-sdk/pull/3179) + * Allow diplayed reaction values to contain anything + [\#3186](https://github.com/matrix-org/matrix-react-sdk/pull/3186) + * Fix interactive tooltip null target error + [\#3185](https://github.com/matrix-org/matrix-react-sdk/pull/3185) + * Require that users go to the soft logout page if they're soft logged out + [\#3181](https://github.com/matrix-org/matrix-react-sdk/pull/3181) + * Emojibase data includes blank variations, accept these when searching + [\#3163](https://github.com/matrix-org/matrix-react-sdk/pull/3163) + * Implement basic soft logout handling + [\#3177](https://github.com/matrix-org/matrix-react-sdk/pull/3177) + * De-lint ScalarAuthClient + [\#3178](https://github.com/matrix-org/matrix-react-sdk/pull/3178) + * show /relations error in edit history dialog + [\#3174](https://github.com/matrix-org/matrix-react-sdk/pull/3174) + +Changes in [1.3.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.3.1) (2019-07-11) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.3.0...v1.3.1) + + * Fix account deactivation + [\#3201](https://github.com/matrix-org/matrix-react-sdk/pull/3201) + * Upgrade lodash dependencies + * Upgrade to JS SDK 2.1.1 + +Changes in [1.3.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.3.0) (2019-07-08) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.3.0-rc.1...v1.3.0) + +No changes since rc.1 + +Changes in [1.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.3.0-rc.1) (2019-07-03) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.2.2...v1.3.0-rc.1) + + * MELS handle m.room.third_party_invite + [\#3173](https://github.com/matrix-org/matrix-react-sdk/pull/3173) + * Fix logic around MemberList invites section, specifically regarding 3pid + [\#3172](https://github.com/matrix-org/matrix-react-sdk/pull/3172) + * Update from Weblate + [\#3176](https://github.com/matrix-org/matrix-react-sdk/pull/3176) + * Track the user's own typing state external to the composer + [\#3150](https://github.com/matrix-org/matrix-react-sdk/pull/3150) + * Handle associated event send failures + [\#3170](https://github.com/matrix-org/matrix-react-sdk/pull/3170) + * Improve interactive tooltip hover behaviour + [\#3169](https://github.com/matrix-org/matrix-react-sdk/pull/3169) + * Fix login type selector border + [\#3171](https://github.com/matrix-org/matrix-react-sdk/pull/3171) + * Use the event sender instead of event ID for viaServers off a tombstone + [\#3159](https://github.com/matrix-org/matrix-react-sdk/pull/3159) + * Append keyshare request dialogs instead of replacing the current dialog + [\#3160](https://github.com/matrix-org/matrix-react-sdk/pull/3160) + * Add AccessibleTooltipButton and use it for RoomSubList buttons + [\#3165](https://github.com/matrix-org/matrix-react-sdk/pull/3165) + * MemberInfo wrap Device Name/ID + [\#3166](https://github.com/matrix-org/matrix-react-sdk/pull/3166) + * Correctly populate the dispatch for joining a room via servers + [\#3161](https://github.com/matrix-org/matrix-react-sdk/pull/3161) + * Clean up legacy breadcrumbs persistence fallback + [\#3162](https://github.com/matrix-org/matrix-react-sdk/pull/3162) + * Update from Weblate + [\#3168](https://github.com/matrix-org/matrix-react-sdk/pull/3168) + * Add ability to render null-rejoins in Timeline and MELS + [\#3135](https://github.com/matrix-org/matrix-react-sdk/pull/3135) + * Add /myavatar command + [\#3155](https://github.com/matrix-org/matrix-react-sdk/pull/3155) + * Update config.json docs location + [\#3158](https://github.com/matrix-org/matrix-react-sdk/pull/3158) + * If on trackpad, don't mess with horizontal scrolling. + [\#3148](https://github.com/matrix-org/matrix-react-sdk/pull/3148) + * Limit reactions row on initial display + [\#3152](https://github.com/matrix-org/matrix-react-sdk/pull/3152) + * Unpin highlight.js + [\#3156](https://github.com/matrix-org/matrix-react-sdk/pull/3156) + * Flexboxify generic error page + [\#3154](https://github.com/matrix-org/matrix-react-sdk/pull/3154) + * Fix weird scrollbar when devtools is in a narrow browser + [\#3153](https://github.com/matrix-org/matrix-react-sdk/pull/3153) + * Show a loading state for slow peeks + [\#3142](https://github.com/matrix-org/matrix-react-sdk/pull/3142) + * Don't show error dialog when user has no webcam + [\#3146](https://github.com/matrix-org/matrix-react-sdk/pull/3146) + * Make edit history work in encrypted rooms. + [\#3151](https://github.com/matrix-org/matrix-react-sdk/pull/3151) + * Change interactive tooltip to only flip when required + [\#3147](https://github.com/matrix-org/matrix-react-sdk/pull/3147) + * Edit history dialog + [\#3144](https://github.com/matrix-org/matrix-react-sdk/pull/3144) + * Fix the scrollbar in the community bar + [\#3143](https://github.com/matrix-org/matrix-react-sdk/pull/3143) + * Add focus border to edit composer + [\#3145](https://github.com/matrix-org/matrix-react-sdk/pull/3145) + * Supply oobData to RoomPreviewBar + [\#3141](https://github.com/matrix-org/matrix-react-sdk/pull/3141) + * Don't boost trackpad users in breadcrumbs + [\#3140](https://github.com/matrix-org/matrix-react-sdk/pull/3140) + * Fix room upgrade warning being chopped off and a spelling mistake + [\#3139](https://github.com/matrix-org/matrix-react-sdk/pull/3139) + * Add quick reaction buttons in tooltip + [\#3138](https://github.com/matrix-org/matrix-react-sdk/pull/3138) + * When joining from room directory, use auto_join + [\#3136](https://github.com/matrix-org/matrix-react-sdk/pull/3136) + * Improve API and interactivity of new tooltip + [\#3137](https://github.com/matrix-org/matrix-react-sdk/pull/3137) + * Use feature flag for displaying edits as well + [\#3132](https://github.com/matrix-org/matrix-react-sdk/pull/3132) + * Add interactive tooltip style + [\#3131](https://github.com/matrix-org/matrix-react-sdk/pull/3131) + * Remove redundant extra chevrons from ContextualMenu + [\#3129](https://github.com/matrix-org/matrix-react-sdk/pull/3129) + * Editor caret improvements + [\#3126](https://github.com/matrix-org/matrix-react-sdk/pull/3126) + * Disable left/right arrow navigating completions for now + [\#3130](https://github.com/matrix-org/matrix-react-sdk/pull/3130) + * Take list nesting into account for indenting + [\#3128](https://github.com/matrix-org/matrix-react-sdk/pull/3128) + * Add file size to UploadConfirmDialog + [\#3127](https://github.com/matrix-org/matrix-react-sdk/pull/3127) + * Consider cancelled verifications when mounting IncomingSasDialog + [\#3123](https://github.com/matrix-org/matrix-react-sdk/pull/3123) + * Make the verification cancelled dialog say OK instead of Cancel + [\#3124](https://github.com/matrix-org/matrix-react-sdk/pull/3124) + * Update from Weblate + [\#3125](https://github.com/matrix-org/matrix-react-sdk/pull/3125) + * Remove unused ContextualMenu features + [\#3122](https://github.com/matrix-org/matrix-react-sdk/pull/3122) + * Fix casing of TooltipButton + [\#3119](https://github.com/matrix-org/matrix-react-sdk/pull/3119) + * De-duplicate notif badge code + [\#3120](https://github.com/matrix-org/matrix-react-sdk/pull/3120) + * Fix favicon/title badge count + [\#3121](https://github.com/matrix-org/matrix-react-sdk/pull/3121) + * Switch ugly password boxes to Field or styled input + [\#3071](https://github.com/matrix-org/matrix-react-sdk/pull/3071) + * Restore warning for if you're already logged in + [\#3118](https://github.com/matrix-org/matrix-react-sdk/pull/3118) + * Provide default name if device label is missing + [\#3113](https://github.com/matrix-org/matrix-react-sdk/pull/3113) + * Support @room pills while editing + [\#3108](https://github.com/matrix-org/matrix-react-sdk/pull/3108) + +Changes in [1.2.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.2.2) (2019-06-19) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.2.2-rc.2...v1.2.2) + +No changes since rc.2 + +Changes in [1.2.2-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.2.2-rc.2) (2019-06-18) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.2.2-rc.1...v1.2.2-rc.2) + + * Defer scalar API calls until they are needed + [\#3115](https://github.com/matrix-org/matrix-react-sdk/pull/3115) + * Blend pending redactions + [\#3117](https://github.com/matrix-org/matrix-react-sdk/pull/3117) + * Keep old arrow-up behaviour when editing is not enabled + [\#3116](https://github.com/matrix-org/matrix-react-sdk/pull/3116) + * Restore Composer History under shift-up & down + [\#3098](https://github.com/matrix-org/matrix-react-sdk/pull/3098) + * Allow changing server if validation has failed + [\#3114](https://github.com/matrix-org/matrix-react-sdk/pull/3114) + * Add Upload All button to UploadConfirmDialog + [\#3109](https://github.com/matrix-org/matrix-react-sdk/pull/3109) + * Re-enable register button + [\#3112](https://github.com/matrix-org/matrix-react-sdk/pull/3112) + * keep mx_Field stretching + [\#3111](https://github.com/matrix-org/matrix-react-sdk/pull/3111) + * Fix double-spinner + [\#3107](https://github.com/matrix-org/matrix-react-sdk/pull/3107) + * Fix display of canonicalAlias in group room info + [\#3110](https://github.com/matrix-org/matrix-react-sdk/pull/3110) + * Fix welcome user + [\#3106](https://github.com/matrix-org/matrix-react-sdk/pull/3106) + * Support editing emote messages + [\#3105](https://github.com/matrix-org/matrix-react-sdk/pull/3105) + * Use flex: 1 for mx_Field to replace all the calc(100% - 20px) and more + [\#3104](https://github.com/matrix-org/matrix-react-sdk/pull/3104) + * Use overflow on MemberInfo name/mxid so that the back button stays + [\#3099](https://github.com/matrix-org/matrix-react-sdk/pull/3099) + * Allow changing servers on nonfatal errors + [\#3102](https://github.com/matrix-org/matrix-react-sdk/pull/3102) + * Simplify email registration + [\#3101](https://github.com/matrix-org/matrix-react-sdk/pull/3101) + * Allow arrow keys navigation in autocomplete list + [\#2966](https://github.com/matrix-org/matrix-react-sdk/pull/2966) + * Edit unsent messages + [\#3097](https://github.com/matrix-org/matrix-react-sdk/pull/3097) + * Fix registration with email + non-default HS + [\#3096](https://github.com/matrix-org/matrix-react-sdk/pull/3096) + * Raise action bar above read marker + [\#3095](https://github.com/matrix-org/matrix-react-sdk/pull/3095) + * Console log more helpfully + [\#3094](https://github.com/matrix-org/matrix-react-sdk/pull/3094) + +Changes in [1.2.2-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.2.2-rc.1) (2019-06-12) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.2.1...v1.2.2-rc.1) + + * Align message context menu to right and vertically where space available + [\#3087](https://github.com/matrix-org/matrix-react-sdk/pull/3087) + * Allow registration to submit for non-fatal errors + [\#3093](https://github.com/matrix-org/matrix-react-sdk/pull/3093) + * Clear the login busy state after .well-known discovery + [\#3092](https://github.com/matrix-org/matrix-react-sdk/pull/3092) + * Update from Weblate + [\#3091](https://github.com/matrix-org/matrix-react-sdk/pull/3091) + * Fix registration after fail-fast + [\#3090](https://github.com/matrix-org/matrix-react-sdk/pull/3090) + * Use setBusy interface of js-sdk interactive auth + [\#3085](https://github.com/matrix-org/matrix-react-sdk/pull/3085) + * Don't handle identity server failure as fatal, and use the right message + [\#3088](https://github.com/matrix-org/matrix-react-sdk/pull/3088) + * Recheck message actions on decrypt + [\#3084](https://github.com/matrix-org/matrix-react-sdk/pull/3084) + * Fix exception on logout + [\#3086](https://github.com/matrix-org/matrix-react-sdk/pull/3086) + * Remember we were trying to accept an invite + [\#3083](https://github.com/matrix-org/matrix-react-sdk/pull/3083) + * Add funding details for GitHub sponsor button + [\#3079](https://github.com/matrix-org/matrix-react-sdk/pull/3079) + * Remove highlight from reactions + [\#3081](https://github.com/matrix-org/matrix-react-sdk/pull/3081) + * Clarify that only lowercase letters are allowed + [\#3080](https://github.com/matrix-org/matrix-react-sdk/pull/3080) + * Don't handle identity server liveliness errors as fatal + [\#3082](https://github.com/matrix-org/matrix-react-sdk/pull/3082) + * truncate long display names in timeline headings + [\#3078](https://github.com/matrix-org/matrix-react-sdk/pull/3078) + * Fail more softly on homeserver liveliness errors + [\#3067](https://github.com/matrix-org/matrix-react-sdk/pull/3067) + * Fix AddressPickerDialog adding wrong entry to selected list case + [\#3076](https://github.com/matrix-org/matrix-react-sdk/pull/3076) + * change profile keybind to backtick from i due to italics conflict + [\#3077](https://github.com/matrix-org/matrix-react-sdk/pull/3077) + * Look busy whilst requesting the email token + [\#3075](https://github.com/matrix-org/matrix-react-sdk/pull/3075) + * Fix email invites address-match checking + [\#3074](https://github.com/matrix-org/matrix-react-sdk/pull/3074) + * Add license info for Twemoji + [\#3073](https://github.com/matrix-org/matrix-react-sdk/pull/3073) + * Show read receipts on top of message + [\#3072](https://github.com/matrix-org/matrix-react-sdk/pull/3072) + * Be somewhat fuzzier when matching emojis to complete on space + [\#3070](https://github.com/matrix-org/matrix-react-sdk/pull/3070) + * Restrict reactions to a single emoji + [\#3069](https://github.com/matrix-org/matrix-react-sdk/pull/3069) + * Fix live updates to reaction row buttons + [\#3068](https://github.com/matrix-org/matrix-react-sdk/pull/3068) + * Don't refresh custom status on logout + [\#3065](https://github.com/matrix-org/matrix-react-sdk/pull/3065) + * Add a logged in class to EmbeddedPage and react to MatrixClient changes + [\#3066](https://github.com/matrix-org/matrix-react-sdk/pull/3066) + * Don't show "can't redact" dialog on network error, with redaction having + local echo & queuing now. + [\#3058](https://github.com/matrix-org/matrix-react-sdk/pull/3058) + * Fix login page breaking on wrong password + [\#3062](https://github.com/matrix-org/matrix-react-sdk/pull/3062) + * Update from Weblate + [\#3064](https://github.com/matrix-org/matrix-react-sdk/pull/3064) + * Install latest JS SDK when linting + [\#3063](https://github.com/matrix-org/matrix-react-sdk/pull/3063) + * Ensure we always show read receipts even with hidden events + [\#3056](https://github.com/matrix-org/matrix-react-sdk/pull/3056) + * Advance read receipts into trailing events without tiles + [\#3059](https://github.com/matrix-org/matrix-react-sdk/pull/3059) + * Remove unused errorText prop + [\#3061](https://github.com/matrix-org/matrix-react-sdk/pull/3061) + * Remove SettingsStore reference in RoomSettingsDialog + [\#3060](https://github.com/matrix-org/matrix-react-sdk/pull/3060) + * Custom notification sounds for rooms + [\#2928](https://github.com/matrix-org/matrix-react-sdk/pull/2928) + * Fix comments in unread room tracking + [\#3054](https://github.com/matrix-org/matrix-react-sdk/pull/3054) + * Allow source tile handler for replacements + [\#3057](https://github.com/matrix-org/matrix-react-sdk/pull/3057) + * Fix linting in MessagePanel + [\#3055](https://github.com/matrix-org/matrix-react-sdk/pull/3055) + * Convert breadcrumbs from labs to real setting + [\#3053](https://github.com/matrix-org/matrix-react-sdk/pull/3053) + * Add local echo on badges in breadcrumbs + [\#3052](https://github.com/matrix-org/matrix-react-sdk/pull/3052) + * Counteract smooth scrolling on breadcrumbs + [\#3051](https://github.com/matrix-org/matrix-react-sdk/pull/3051) + * add sbix fallback twemoji font (and bump to emoji 12) + [\#3050](https://github.com/matrix-org/matrix-react-sdk/pull/3050) + * Add option to change the default country code + [\#3049](https://github.com/matrix-org/matrix-react-sdk/pull/3049) + * Accept JSX into the GenericErrorPage and expose local session vars + [\#3043](https://github.com/matrix-org/matrix-react-sdk/pull/3043) + * Don't try and low encryption info when signing out in low bandwidth mode + [\#3048](https://github.com/matrix-org/matrix-react-sdk/pull/3048) + * only capture enter if something was selected in completions + [\#3047](https://github.com/matrix-org/matrix-react-sdk/pull/3047) + * Fix: better HTML > MD conversion for editing, including lists and quotes + [\#3040](https://github.com/matrix-org/matrix-react-sdk/pull/3040) + * Native emoji require extra line-height + [\#3044](https://github.com/matrix-org/matrix-react-sdk/pull/3044) + * port over low_bandwidth mode to develop + [\#2598](https://github.com/matrix-org/matrix-react-sdk/pull/2598) + * Fix: maintain caret at current line when position is on newline part + [\#3029](https://github.com/matrix-org/matrix-react-sdk/pull/3029) + * Remove username on HS input label + [\#3042](https://github.com/matrix-org/matrix-react-sdk/pull/3042) + * Exclude chrome in ua from safari version check for colr support + [\#3038](https://github.com/matrix-org/matrix-react-sdk/pull/3038) + * fix COLR font check being racy + [\#3034](https://github.com/matrix-org/matrix-react-sdk/pull/3034) + * Override font for usercontent download link + [\#3035](https://github.com/matrix-org/matrix-react-sdk/pull/3035) + * Revert "Make the timeline less noisy for screen readers (mk II) #3019" + [\#3033](https://github.com/matrix-org/matrix-react-sdk/pull/3033) + * Hide autocomplete on Enter key press instead of sending message + [\#2968](https://github.com/matrix-org/matrix-react-sdk/pull/2968) + * Message editing: arrow key (up/down) navigation between editable events + [\#3025](https://github.com/matrix-org/matrix-react-sdk/pull/3025) + * Message editing: fix reply text appearing in edit + [\#3032](https://github.com/matrix-org/matrix-react-sdk/pull/3032) + * Do not try to request thumbnails with non-integer widths + [\#3031](https://github.com/matrix-org/matrix-react-sdk/pull/3031) + * Message editing: preserve strikethrough as well + [\#3030](https://github.com/matrix-org/matrix-react-sdk/pull/3030) + * Add some logging for COLR checks + [\#3027](https://github.com/matrix-org/matrix-react-sdk/pull/3027) + * Fixup for tab completion: take part length into account as well + [\#3026](https://github.com/matrix-org/matrix-react-sdk/pull/3026) + * Message editing: tab completion + [\#3024](https://github.com/matrix-org/matrix-react-sdk/pull/3024) + * Message editing: dont jump to next part when inserting at *start* of + uneditable part + [\#3021](https://github.com/matrix-org/matrix-react-sdk/pull/3021) + * Message editing: preserve and re-apply formatting + [\#3013](https://github.com/matrix-org/matrix-react-sdk/pull/3013) + * Fix relationship between guests, .well-known, and auth + [\#3001](https://github.com/matrix-org/matrix-react-sdk/pull/3001) + * Restore use of full mxid login + [\#2972](https://github.com/matrix-org/matrix-react-sdk/pull/2972) + * Only expose the fallback_hs_url if the homeserver is the default homeserver + [\#2971](https://github.com/matrix-org/matrix-react-sdk/pull/2971) + * Refactor "Next" button into ServerConfig components + [\#2964](https://github.com/matrix-org/matrix-react-sdk/pull/2964) + * Render underlines and tooltips on custom server names in auth pages + [\#2965](https://github.com/matrix-org/matrix-react-sdk/pull/2965) + * Use validated server config for login, registration, and password reset + [\#2941](https://github.com/matrix-org/matrix-react-sdk/pull/2941) + Changes in [1.2.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.2.1) (2019-05-31) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.2.0...v1.2.1) diff --git a/README.md b/README.md index e944b04ff2..69aafeb724 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ matrix-react-sdk This is a react-based SDK for inserting a Matrix chat/voip client into a web page. This package provides the React components needed to build a Matrix web client -using React. It is not useable in isolation, and instead must must be used from +using React. It is not useable in isolation, and instead must be used from a 'skin'. A skin provides: * Customised implementations of presentation components. * Custom CSS @@ -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 @@ -45,7 +45,7 @@ Code should be committed as follows: * In practice, `matrix-react-sdk` is still evolving so fast that the maintenance burden of customising and overriding these components for Riot can seriously impede development. So right now, there should be very few (if any) customisations for Riot. - * CSS: https://github.com/vector-im/riot-web/tree/master/src/skins/vector/css/matrix-react-sdk + * CSS: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/css * Theme specific CSS & resources: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/themes React components in matrix-react-sdk are come in two different flavours: @@ -67,6 +67,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold: * After creating a new component you must run `yarn reskindex` to regenerate the `component-index.js` for the SDK (used in future for skinning) + * The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css). CSS for matrix-react-sdk currently resides in @@ -82,7 +83,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold: 'Stealing' styling information from other components (including parents) is not cool, as it breaks the independence of the components. - * CSS classes are named with an app-specific namespacing prefix to try to avoid + * CSS classes are named with an app-specific name-spacing prefix to try to avoid CSS collisions. The base skin shipped by Matrix.org with the matrix-react-sdk uses the naming prefix "mx_". A company called Yoyodyne Inc might use a prefix like "yy_" for its app-specific classes. @@ -107,7 +108,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold: .mx_RoomTile {} in RoomList.css - only RoomTile.css is allowed to define its own CSS. Instead, say .mx_RoomList .mx_RoomTile {} to scope the override only to the context of RoomList views. N.B. overrides should be relatively - rare as in general CSS inheritence should be enough. + rare as in general CSS inheritance should be enough. * Components should render only within the bounding box of their outermost DOM element. Page-absolute positioning and negative CSS margins and similar are @@ -168,3 +169,8 @@ Ensure you've followed the above development instructions and then: ```bash yarn test ``` + +## End-to-End tests + +Make sure you've got your Riot development server running (by doing `yarn start` in riot-web), and then in this project, run `yarn run e2etests`. +See `test/end-to-end-tests/README.md` for more information. diff --git a/__mocks__/browser-request.js b/__mocks__/browser-request.js new file mode 100644 index 0000000000..7d231fb9db --- /dev/null +++ b/__mocks__/browser-request.js @@ -0,0 +1,17 @@ +const en = require("../src/i18n/strings/en_EN"); + +module.exports = jest.fn((opts, cb) => { + const url = opts.url || opts.uri; + if (url && url.endsWith("languages.json")) { + cb(undefined, {status: 200}, JSON.stringify({ + "en": { + "fileName": "en_EN.json", + "label": "English", + }, + })); + } else if (url && url.endsWith("en_EN.json")) { + cb(undefined, {status: 200}, JSON.stringify(en)); + } else { + cb(true, {status: 404}, ""); + } +}); diff --git a/__mocks__/imageMock.js b/__mocks__/imageMock.js new file mode 100644 index 0000000000..474ac702b4 --- /dev/null +++ b/__mocks__/imageMock.js @@ -0,0 +1 @@ +module.exports = "image-file-stub"; diff --git a/__mocks__/languages.json b/__mocks__/languages.json new file mode 100644 index 0000000000..f62fe9b9b4 --- /dev/null +++ b/__mocks__/languages.json @@ -0,0 +1,10 @@ +{ + "en": { + "fileName": "en_EN.json", + "label": "English" + }, + "en-us": { + "fileName": "en_US.json", + "label": "English (US)" + } +} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000..d5a97d56ce --- /dev/null +++ b/babel.config.js @@ -0,0 +1,23 @@ +module.exports = { + "sourceMaps": "inline", + "presets": [ + ["@babel/preset-env", { + "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-export-default-from", + "@babel/plugin-proposal-numeric-separator", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-object-rest-spread", + "@babel/plugin-transform-flow-comments", + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-transform-runtime" + ] +}; diff --git a/code_style.md b/code_style.md index 96f3879ebc..3ad0d38873 100644 --- a/code_style.md +++ b/code_style.md @@ -22,7 +22,7 @@ number throgh from the original code to the final application. General Style ------------- - 4 spaces to indent, for consistency with Matrix Python. -- 120 columns per line, but try to keep JavaScript code around the 80 column mark. +- 120 columns per line, but try to keep JavaScript code around the 90 column mark. Inline JSX in particular can be nicer with more columns per line. - No trailing whitespace at end of lines. - Don't indent empty lines. @@ -34,7 +34,7 @@ General Style - UpperCamelCase for class and type names - lowerCamelCase for functions and variables. - Single line ternary operators are fine. -- UPPER_CAMEL_CASE for constants +- UPPER_SNAKE_CASE for constants - Single quotes for strings by default, for consistency with most JavaScript styles: ```javascript @@ -174,12 +174,6 @@ React // Best, if onFooClick would do anything other than directly calling doStuff ``` - Not doing so is acceptable in a single case: in function-refs: - - ```jsx - this.component = self}> - ``` - - Prefer classes that extend `React.Component` (or `React.PureComponent`) instead of `React.createClass` - You can avoid the need to bind handler functions by using [property initializers](https://reactjs.org/docs/react-component.html#constructor): @@ -208,3 +202,5 @@ React ``` - Think about whether your component really needs state: are you duplicating information in component state that could be derived from the model? + +- Avoid things marked as Legacy or Deprecated in React 16 (e.g string refs and legacy contexts) diff --git a/docs/ciderEditor.md b/docs/ciderEditor.md new file mode 100644 index 0000000000..00033b5b8c --- /dev/null +++ b/docs/ciderEditor.md @@ -0,0 +1,71 @@ +# The CIDER (Contenteditable-Input-Diff-Error-Reconcile) editor + +The CIDER editor is a custom editor written for Riot. +Most of the code can be found in the `/editor/` directory of the `matrix-react-sdk` project. +It is used to power the composer main composer (both to send and edit messages), and might be used for other usecases where autocomplete is desired (invite box, ...). + +## High-level overview. + +The editor is backed by a model that contains parts. +A part has some text and a type (plain text, pill, ...). When typing in the editor, +the model validates the input and updates the parts. +The parts are then reconciled with the DOM. + +## Inner workings + +When typing in the `contenteditable` element, the `input` event fires and +the DOM of the editor is turned into a string. The way this is done has +some logic to it to deal with adding newlines for block elements, to make sure +the caret offset is calculated in the same way as the content string, and to ignore +caret nodes (more on that later). +For these reasons it doesn't use `innerText`, `textContent` or anything similar. +The model addresses any content in the editor within as an offset within this string. +The caret position is thus also converted from a position in the DOM tree +to an offset in the content string. This happens in `getCaretOffsetAndText` in `dom.js`. + +Once the content string and caret offset is calculated, it is passed to the `update()` +method of the model. The model first calculates the same content string of its current parts, +basically just concatenating their text. It then looks for differences between +the current and the new content string. The diffing algorithm is very basic, +and assumes there is only one change around the caret offset, +so this should be very inexpensive. See `diff.js` for details. + +The result of the diffing is the strings that were added and/or removed from +the current content. These differences are then applied to the parts, +where parts can apply validation logic to these changes. + +For example, if you type an @ in some plain text, the plain text part rejects +that character, and this character is then presented to the part creator, +which will turn it into a pill candidate part. +Pill candidate parts are what opens the auto completion, and upon picking a completion, +replace themselves with an actual pill which can't be edited anymore. + +The diffing is needed to preserve state in the parts apart from their text +(which is the only thing the model receives from the DOM), e.g. to build +the model incrementally. Any text that didn't change is assumed +to leave the parts it intersects alone. + +The benefit of this is that we can use the `input` event, which is broadly supported, +to find changes in the editor. We don't have to rely on keyboard events, +which relate poorly to text input or changes, and don't need the `beforeinput` event, +which isn't broadly supported yet. + +Once the parts of the model are updated, the DOM of the editor is then reconciled +with the new model state, see `renderModel` in `render.js` for this. +If the model didn't reject the input and didn't make any additional changes, +this won't make any changes to the DOM at all, and should thus be fairly efficient. + +For the browser to allow the user to place the caret between two pills, +or between a pill and the start and end of the line, we need some extra DOM nodes. +These DOM nodes are called caret nodes, and contain an invisble character, so +the caret can be placed into them. The model is unaware of caret nodes, and they +are only added to the DOM during the render phase. Likewise, when calculating +the content string, caret nodes need to be ignored, as they would confuse the model. + +As part of the reconciliation, the caret position is also adjusted to any changes +the model made to the input. The caret is passed around in two formats. +The model receives the caret *offset* within the content string (which includes +an atNodeEnd flag to make it unambiguous if it is at a part and or the next part start). +The model converts this to a caret *position* internally, which has a partIndex +and an offset within the part text, which is more natural to work with. +From there on, the caret *position* is used, also during reconciliation. diff --git a/docs/features/README.md b/docs/features/README.md new file mode 100644 index 0000000000..05a38f029b --- /dev/null +++ b/docs/features/README.md @@ -0,0 +1,6 @@ +# Feature documention + +The idea of this folder is to document the features we support in different parts of the app. +In case anyone needs to work on a given part, and isn't aware of all the features in the area, +they will hopefully get an idea for all the supported functionality to know what to take into account +when making changes. diff --git a/docs/features/composer.md b/docs/features/composer.md new file mode 100644 index 0000000000..112a597001 --- /dev/null +++ b/docs/features/composer.md @@ -0,0 +1,37 @@ +# Composer Features +## Auto Complete + + - Hitting tab tries to auto-complete the word before the caret as a room member + - If no matching name is found, a visual bell is shown + - @ + a letter opens auto complete for members starting with the given letter + - When inserting a user pill at the start in the composer, a colon and space is appended to the pill + - When inserting a user pill anywhere else in composer, only a space is appended to the pill + - # + a letter opens auto complete for rooms starting with the given letter + - : open auto complete for emoji + - Pressing arrow-up/arrow-down while the autocomplete is open navigates between auto complete options + - Pressing tab while the autocomplete is open goes to the next autocomplete option, + wrapping around at the end after reverting to the typed text first. + +## Formatting + + - When selecting text, a formatting bar appears above the selection. + - The formatting bar allows to format the selected test as: + bold, italic, strikethrough, a block quote, and a code block (inline if no linebreak is selected). + - Formatting is applied as markdown syntax. + - Hitting ctrl/cmd+B also marks the selected text as bold + - Hitting ctrl/cmd+I also marks the selected text as italic + - Hitting ctrl/cmd+> also marks the selected text as a blockquote + +## Misc + + - When hitting the arrow-up button while having the caret at the start in the composer, + the last message sent by the syncing user is edited. + - Clicking a display name on an event in the timeline inserts a user pill into the composer + - Emoticons (like :-), >:-), :-/, ...) are replaced by emojis while typing if the relevant setting is enabled + - Typing in the composer sends typing notifications in the room + - Pressing ctrl/mod+z and ctrl/mod+y undoes/redoes modifications + - Pressing shift+enter inserts a line break + - Pressing enter sends the message. + - Choosing "Quote" in the context menu of an event inserts a quote of the event body in the composer. + - Choosing "Reply" in the context menu of an event shows a preview above the composer to reply to. + - Pressing alt+arrow up/arrow down navigates in previously sent messages, putting them in the composer. \ No newline at end of file diff --git a/docs/jitsi.md b/docs/jitsi.md new file mode 100644 index 0000000000..779ef79d3a --- /dev/null +++ b/docs/jitsi.md @@ -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. diff --git a/docs/scrolling.md b/docs/scrolling.md new file mode 100644 index 0000000000..71329e5c32 --- /dev/null +++ b/docs/scrolling.md @@ -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. diff --git a/docs/settings.md b/docs/settings.md index 777636359e..46e4a68fdb 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -15,7 +15,7 @@ order of prioirty, are: * `room-account` - The current user's account, but only when in a specific room * `account` - The current user's account * `room` - A specific room (setting for all members of the room) -* `config` - Values are defined by `config.json` +* `config` - Values are defined by the `settingDefaults` key (usually) in `config.json` * `default` - The hardcoded default for the settings Individual settings may control which levels are appropriate for them as part of the defaults. This is often to ensure diff --git a/docs/skinning.md b/docs/skinning.md new file mode 100644 index 0000000000..229bc78372 --- /dev/null +++ b/docs/skinning.md @@ -0,0 +1,71 @@ +# Skinning + +The react-sdk can be skinned to replace presentation components, CSS, or +other relevant parts of the SDK. Typically consumers will replace entire +components and get the ability for custom CSS as a result. + +This doc isn't exhaustive on how skinning works, though it should cover +some of the more complicated parts such as component replacement. + +## Loading a skin + +1. Generate a `component-index.js` (preferably using the tools that the react-sdk +exposes). This can typically be done with a npm script like `"reskindex -h src/header"`. +2. In your app's entry point, add something like this code: + ```javascript + import {loadSkin} from "matrix-react-sdk"; + loadSkin(import("component-index").components); + // The rest of your imports go under this. + ``` +3. Import the remainder of the SDK and bootstrap your app. + +It is extremely important that you **do not** import anything else from the +SDK prior to loading your skin as otherwise the skin might not work. Loading +the skin should be one of the first things your app does, if not the very +first thing. + +Additionally, **do not** provide `loadSkin` with the react-sdk components +themselves otherwise the app might explode. The SDK is already aware of its +components and doesn't need to be told. + +## Replacing components + +Components that replace the react-sdk ones MUST have a `replaces` static +key on the component's class to describe which component it overrides. For +example, if your `VectorAuthPage` component is meant to replace the react-sdk +`AuthPage` component then you'd add `static replaces = 'views.auth.AuthPage';` +to the `VectorAuthPage` class. + +Other than that, the skin just needs to be loaded normally as mentioned above. +Consumers of the SDK likely will not be interested in the rest of this section. + +### SDK developer notes + +Components in the react-sdk MUST be decorated with the `@replaceableComponent` +function. For components that can't use the decorator, they must use a +variation that provides similar functionality. The decorator gives consumers +an opportunity to load skinned components by abusing import ordering and +behaviour. + +Decorators are executed at import time which is why we can abuse the import +ordering behaviour: importing `loadSkin` doesn't trigger any components to +be imported, allowing the consumer to specify a skin. When the consumer does +import a component (for example, `MatrixChat`), it starts to pull in all the +components via `import` statements. When the components get pulled in the +decorator checks with the skinned components to see if it should be replacing +the component being imported. The decorator then effectively replaces the +components when needed by specifying the skinned component as an override for +the SDK's component, which should in theory override critical functions like +`render()` and lifecycle event handlers. + +The decorator also means that older usage of `getComponent()` is no longer +required because components should be replaced by the decorator. Eventually +the react-sdk should only have one usage of `getComponent()`: the decorator. + +The decorator assumes that if `getComponent()` returns null that there is +no skinned version of the component and continues on using the SDK's component. +In previous versions of the SDK, the function would throw an error instead +because it also expected the skin to list the SDK's components as well, however +that is no longer possible due to the above. + +In short, components should always be `import`ed. diff --git a/docs/usercontent.md b/docs/usercontent.md new file mode 100644 index 0000000000..e54851dd0d --- /dev/null +++ b/docs/usercontent.md @@ -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 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. diff --git a/jenkins.sh b/jenkins.sh deleted file mode 100755 index 70bc12e42d..0000000000 --- a/jenkins.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -set -e - -export NVM_DIR="$HOME/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" -nvm use 10 - -set -x - -scripts/fetchdep.sh matrix-org matrix-js-sdk - -pushd matrix-js-sdk -yarn link -yarn install -popd - -yarn link matrix-js-sdk - -# install the other dependencies -yarn install - -# run the mocha tests -yarn test --no-colors - -# run eslint -yarn lintall -f checkstyle -o eslint.xml || true - -# re-run the linter, excluding any files known to have errors or warnings. -yarn lintwithexclusions - -# lint styles -yarn stylelint - -# delete the old tarball, if it exists -rm -f matrix-react-sdk-*.tgz - -# build our tarball -yarn pack diff --git a/karma.conf.js b/karma.conf.js deleted file mode 100644 index e2728cdc09..0000000000 --- a/karma.conf.js +++ /dev/null @@ -1,228 +0,0 @@ -// karma.conf.js - the config file for karma, which runs our tests. - -var path = require('path'); -var fs = require('fs'); - -/* - * We use webpack to build our tests. It's a pain to have to wait for webpack - * to build everything; however it's the easiest way to load our dependencies - * from node_modules. - * - * If you run karma in multi-run mode (with `yarn test-multi`), it will watch - * the tests for changes, and webpack will rebuild using a cache. This is much quicker - * than a clean rebuild. - */ - -// the name of the test file. By default, a special file which runs all tests. -// -// TODO: this could be a pattern, and karma would run each file, with a -// separate webpack bundle for each file. But then we get a separate instance -// of the sdk, and each of the dependencies, for each test file, and everything -// gets very confused. Can we persuade webpack to put all of the dependencies -// in a 'common' bundle? -// -var testFile = process.env.KARMA_TEST_FILE || 'test/all-tests.js'; - - -process.env.PHANTOMJS_BIN = 'node_modules/.bin/phantomjs'; - -function fileExists(name) { - try { - fs.statSync(gsCss); - return true; - } catch (e) { - return false; - } -} - -// try find the gemini-scrollbar css in an version-agnostic way -var gsCss = 'node_modules/gemini-scrollbar/gemini-scrollbar.css'; -if (!fileExists(gsCss)) { - gsCss = 'node_modules/react-gemini-scrollbar/'+gsCss; -} - - -module.exports = function (config) { - config.set({ - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['mocha'], - - // list of files / patterns to load in the browser - files: [ - testFile, - gsCss, - - // some images to reduce noise from the tests - {pattern: 'test/img/*', watched: false, included: false, - served: true, nocache: false}, - // translation files - {pattern: 'src/i18n/strings/*', watcheed: false, included: false, served: true}, - {pattern: 'test/i18n/*', watched: false, included: false, served: true}, - ], - - proxies: { - // redirect img links to the karma server - "/img/": "/base/test/img/", - // special languages.json file for the tests - "/i18n/languages.json": "/base/test/i18n/languages.json", - // and redirect i18n requests - "/i18n/": "/base/src/i18n/strings/", - }, - - // list of files to exclude - // - // This doesn't work. It turns out that it's webpack which does the - // watching of the /test directory (karma only watches `testFile` - // itself). Webpack watches the directory so that it can spot - // new tests, which is fair enough; unfortunately it triggers a rebuild - // every time a lockfile is created in that directory, and there - // doesn't seem to be any way to tell webpack to ignore particular - // files in a watched directory. - // - // exclude: [ - // '**/.#*' - // ], - - // preprocess matching files before serving them to the browser - // available preprocessors: - // https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - 'test/**/*.js': ['webpack', 'sourcemap'] - }, - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['logcapture', 'spec', 'summary'], - - specReporter: { - suppressErrorSummary: false, // do print error summary - suppressFailed: false, // do print information about failed tests - suppressPassed: false, // do print information about passed tests - showSpecTiming: true, // print the time elapsed for each spec - }, - - client: { - captureLogs: true, - }, - - // web server port - port: 9876, - - // enable / disable colors in the output (reporters and logs) - colors: true, - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || - // config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - // - // This is strictly for logs that would be generated by the browser itself and we - // don't want to log about missing images, which are emitted on LOG_WARN. - logLevel: config.LOG_ERROR, - - // enable / disable watching file and executing tests whenever any file - // changes - autoWatch: true, - - // start these browsers - // available browser launchers: - // https://npmjs.org/browse/keyword/karma-launcher - browsers: [ - 'Chrome', - //'PhantomJS', - //'ChromeHeadless', - ], - - customLaunchers: { - 'VectorChromeHeadless': { - base: 'Chrome', - flags: [ - '--no-sandbox', - // See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md - '--headless', - '--disable-gpu', - // Without a remote debugging port, Google Chrome exits immediately. - '--remote-debugging-port=9222', - ], - } - }, - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - // singleRun: false, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity, - - webpack: { - module: { - rules: [ - { - test: /\.js$/, loader: "babel-loader", - include: [path.resolve('./src'), - path.resolve('./test'), - ] - }, - { - test: /\.(gif|png|svg|ttf)$/, - loader: 'file-loader', - }, - ], - noParse: [ - // for cross platform compatibility use [\\\/] as the path separator - // this ensures that the regex trips on both Windows and *nix - - // don't parse the languages within highlight.js. They - // cause stack overflows - // (https://github.com/webpack/webpack/issues/1721), and - // there is no need for webpack to parse them - they can - // just be included as-is. - /highlight\.js[\\\/]lib[\\\/]languages/, - - // olm takes ages for webpack to process, and it's already heavily - // optimised, so there is little to gain by us uglifying it. - /olm[\\\/](javascript[\\\/])?olm\.js$/, - - // also disable parsing for sinon, because it - // tries to do voodoo with 'require' which upsets - // webpack (https://github.com/webpack/webpack/issues/304) - /sinon[\\\/]pkg[\\\/]sinon\.js$/, - ], - }, - resolve: { - alias: { - // alias any requires to the react module to the one in our - // path, otherwise we tend to get the react source included - // twice when using `npm link` / `yarn link`. - react: path.resolve('./node_modules/react'), - - 'matrix-react-sdk': path.resolve('test/skinned-sdk.js'), - 'sinon': 'sinon/pkg/sinon.js', - }, - modules: [ - path.resolve('./test'), - "node_modules" - ], - }, - devtool: 'inline-source-map', - externals: { - // Don't try to bundle electron: leave it as a commonjs dependency - // (the 'commonjs' here means it will output a 'require') - "electron": "commonjs electron", - }, - // make sure we're flagged as development to avoid wasting time optimising - mode: 'development', - }, - - webpackMiddleware: { - stats: { - // don't fill the console up with a mahoosive list of modules - chunks: false, - }, - }, - - browserNoActivityTimeout: 15000, - }); -}; diff --git a/package.json b/package.json index a9be315b46..7001b1cb21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "1.2.1", + "version": "2.3.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -8,153 +8,163 @@ "url": "https://github.com/matrix-org/matrix-react-sdk" }, "license": "Apache-2.0", - "main": "lib/index.js", "files": [ - ".babelrc", - ".eslintrc.js", + "lib", + "res", + "src", + "scripts", + "git-revision.txt", + "docs", + "header", "CHANGELOG.md", "CONTRIBUTING.rst", "LICENSE", "README.md", - "code_style.md", - "git-revision.txt", - "header", - "jenkins.sh", - "karma.conf.js", - "lib", - "package.json", - "release.sh", - "scripts", - "src", - "test", - "res" + "package.json" ], "bin": { "reskindex": "scripts/reskindex.js", "matrix-gen-i18n": "scripts/gen-i18n.js", "matrix-prune-i18n": "scripts/prune-i18n.js" }, + "main": "./lib/index.js", + "typings": "./lib/index.d.ts", + "matrix_src_main": "./src/index.js", "scripts": { + "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", "reskindex": "node scripts/reskindex.js -h header", "reskindex:watch": "node scripts/reskindex.js -h header -w", "rethemendex": "res/css/rethemendex.sh", - "i18n": "matrix-gen-i18n", - "prunei18n": "matrix-prune-i18n", - "build": "yarn reskindex && yarn start:init", - "build:watch": "babel src -w --skip-initial-build -d lib --source-maps --copy-files", - "emoji-data-strip": "node scripts/emoji-data-strip.js", - "start": "yarn start:init && yarn start:all", - "start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn build:watch\" \"yarn reskindex:watch\"", - "start:init": "babel src -d lib --source-maps --copy-files", - "lint": "eslint src/", - "lintall": "eslint src/ test/", - "lintwithexclusions": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test", - "stylelint": "stylelint res/css/**/*.scss", "clean": "rimraf lib", - "prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt", - "test": "karma start --single-run=true --browsers VectorChromeHeadless", - "test-multi": "karma start" + "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,.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 --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-plugin-syntax-dynamic-import": "^6.18.0", - "babel-runtime": "^6.26.0", - "bluebird": "^3.5.0", + "@babel/runtime": "^7.8.3", "blueimp-canvas-to-blob": "^3.5.0", "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", "classnames": "^2.1.2", "commonmark": "^0.28.1", "counterpart": "^0.18.0", - "emojibase-data": "^4.0.0", + "create-react-class": "^15.6.0", + "diff-dom": "^4.1.3", + "diff-match-patch": "^1.0.4", + "emojibase-data": "^4.0.2", "emojibase-regex": "^3.0.0", + "escape-html": "^1.0.3", "file-saver": "^1.3.3", "filesize": "3.5.6", "flux": "2.1.1", - "focus-trap-react": "^3.0.5", + "focus-visible": "^5.0.2", "fuse.js": "^2.2.0", - "gemini-scrollbar": "github:matrix-org/gemini-scrollbar#b302279", "gfm.css": "^1.1.1", - "glob": "^5.0.14", - "highlight.js": "9.14.2", + "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.13.1", - "lolex": "2.3.2", - "matrix-js-sdk": "2.0.0", - "optimist": "^0.6.1", + "lodash": "^4.17.14", + "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": "^15.6.0", - "react-addons-css-transition-group": "15.3.2", + "react": "^16.9.0", + "react-addons-css-transition-group": "15.6.2", "react-beautiful-dnd": "^4.0.1", - "react-dom": "^15.6.0", - "react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#5e97aef", + "react-dom": "^16.9.0", + "react-focus-lock": "^2.2.1", "resize-observer-polyfill": "^1.5.0", "sanitize-html": "^1.18.4", - "slate": "^0.41.2", - "slate-html-serializer": "^0.6.1", - "slate-md-serializer": "github:matrix-org/slate-md-serializer#f7c4ad3", - "slate-react": "^0.18.10", "text-encoding-utf-8": "^1.0.1", "url": "^0.11.0", "velocity-animate": "^1.5.2", - "whatwg-fetch": "^1.1.1", + "what-input": "^5.2.6", "zxcvbn": "^4.4.2" }, "devDependencies": { - "babel-cli": "^6.26.0", - "babel-core": "^6.26.3", - "babel-eslint": "^10.0.1", - "babel-loader": "^7.1.5", - "babel-plugin-add-module-exports": "^0.2.1", - "babel-plugin-transform-async-to-bluebird": "^1.1.1", - "babel-plugin-transform-builtin-extend": "^1.1.2", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-polyfill": "^6.26.0", - "babel-preset-es2015": "^6.24.1", - "babel-preset-es2016": "^6.24.1", - "babel-preset-es2017": "^6.24.1", - "babel-preset-react": "^6.24.1", - "chokidar": "^2.1.2", + "@babel/cli": "^7.7.5", + "@babel/core": "^7.7.5", + "@babel/plugin-proposal-class-properties": "^7.7.4", + "@babel/plugin-proposal-decorators": "^7.7.4", + "@babel/plugin-proposal-export-default-from": "^7.7.4", + "@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.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", + "@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": "^3.3.1", "concurrently": "^4.0.1", + "enzyme": "^3.10.0", + "enzyme-adapter-react-16": "^1.15.1", "eslint": "^5.12.0", "eslint-config-google": "^0.7.1", "eslint-plugin-babel": "^5.2.1", "eslint-plugin-flowtype": "^2.30.0", + "eslint-plugin-jest": "^23.0.4", "eslint-plugin-react": "^7.7.0", + "eslint-plugin-react-hooks": "^2.0.1", "estree-walker": "^0.5.0", - "expect": "^24.1.0", "file-loader": "^3.0.1", "flow-parser": "^0.57.3", - "jest-mock": "^23.2.0", - "karma": "^4.0.1", - "karma-chrome-launcher": "^2.2.0", - "karma-cli": "^1.0.1", - "karma-logcapture-reporter": "0.0.1", - "karma-mocha": "^1.3.0", - "karma-sourcemap-loader": "^0.3.7", - "karma-spec-reporter": "^0.0.31", - "karma-summary-reporter": "^1.5.1", - "karma-webpack": "^4.0.0-beta.0", + "glob": "^5.0.14", + "jest": "^24.9.0", + "lolex": "^5.1.2", "matrix-mock-request": "^1.2.3", - "matrix-react-test-utils": "^0.1.1", - "mocha": "^5.0.5", - "react-addons-test-utils": "^15.4.0", - "require-json": "0.0.1", + "matrix-react-test-utils": "^0.2.2", + "react-test-renderer": "^16.9.0", "rimraf": "^2.4.3", - "sinon": "^5.0.7", "source-map-loader": "^0.2.3", "stylelint": "^9.10.1", "stylelint-config-standard": "^18.2.0", + "stylelint-scss": "^3.9.0", + "tslint": "^5.20.1", + "typescript": "^3.7.3", "walk": "^2.3.9", "webpack": "^4.20.2", "webpack-cli": "^3.1.1" + }, + "jest": { + "testMatch": [ + "/test/**/*-test.js" + ], + "setupFilesAfterEnv": [ + "/test/setupTests.js" + ], + "moduleNameMapper": { + "\\.(gif|png|svg|ttf|woff2)$": "/__mocks__/imageMock.js", + "\\$webapp/i18n/languages.json": "/__mocks__/languages.json" + }, + "transformIgnorePatterns": [ + "/node_modules/(?!matrix-js-sdk).+$" + ] } } diff --git a/release.sh b/release.sh index 1f287bc839..23b8822041 100755 --- a/release.sh +++ b/release.sh @@ -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 diff --git a/res/css/_common.scss b/res/css/_common.scss index 973103899d..ad64aced50 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -30,6 +30,11 @@ body { color: $primary-fg-color; border: 0px; margin: 0px; + + // needed to match the designs correctly on macOS + // see https://github.com/vector-im/riot-web/issues/11425 + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } pre, code { @@ -37,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 `` is `bolder` which results in no bold // effect since we only have specific weights of our fonts available. @@ -104,22 +114,22 @@ input[type=text], input[type=password], textarea { color: $primary-fg-color; } -input[type=text]:focus, input[type=password]:focus, textarea:focus { - outline: none; - box-shadow: none; -} - /* Required by Firefox */ textarea { font-family: $font-family; color: $primary-fg-color; } +input[type=text]:focus, input[type=password]:focus, textarea:focus { + outline: none; + box-shadow: none; +} + // This is used to hide the standard outline added by browsers for // accessible (focusable) components. Not intended for buttons, but // should be used on things like focusable containers where the outline // is usually not helping anyone. -.mx_HiddenFocusable { +*:focus:not(.focus-visible) { outline: none; } @@ -129,6 +139,13 @@ textarea { // appear to be part of the input .mx_Dialog, .mx_MatrixChat { + .mx_textinput > input[type=text], + .mx_textinput > input[type=search] { + border: none; + flex: 1; + color: $primary-fg-color; + } + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], .mx_textinput { @@ -147,13 +164,6 @@ textarea { .mx_textinput { display: flex; align-items: center; - - > input[type=text], - > input[type=search] { - border: none; - flex: 1; - color: $primary-fg-color; - } } :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text]::placeholder, @@ -166,14 +176,12 @@ textarea { /*** panels ***/ .dark-panel { background-color: $dark-panel-bg-color; -} -.dark-panel { :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], .mx_textinput { color: $input-darker-fg-color; - background-color: $input-darker-bg-color; + background-color: $primary-bg-color; border: none; } } @@ -199,37 +207,6 @@ textarea { 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. @@ -271,14 +248,6 @@ textarea { justify-content: center; } -/* Spinner Dialog overide */ -.mx_Dialog_wrapper.mx_Dialog_spinner .mx_Dialog { - width: auto; - border-radius: 8px; - padding: 0px; - box-shadow: none; -} - .mx_Dialog { background-color: $primary-bg-color; color: $light-fg-color; @@ -340,7 +309,15 @@ textarea { .mx_Dialog_header { position: relative; - margin-bottom: 20px; + margin-bottom: 10px; +} + +.mx_Dialog_titleImage { + vertical-align: middle; + width: 25px; + height: 25px; + margin-left: -2px; + margin-right: 4px; } .mx_Dialog_title { @@ -383,14 +360,20 @@ textarea { 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; // flip colours for the secondary ones font-weight: 600; - border: 1px solid $accent-color ! important; + border: 1px solid $accent-color; color: $accent-color; background-color: $button-secondary-bg-color; } @@ -399,32 +382,45 @@ textarea { 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; } +/* Spinner Dialog overide */ +.mx_Dialog_wrapper.mx_Dialog_spinner .mx_Dialog { + width: auto; + border-radius: 8px; + padding: 0px; + box-shadow: none; +} + // TODO: Review mx_GeneralButton usage to see if it can use a different class // These classes were brought in from the old UserSettings and are included here to avoid // breaking the app. @@ -458,16 +454,6 @@ textarea { background-color: $primary-bg-color; } -::-moz-selection { - background-color: $accent-color; - color: $selection-fg-color; -} - -::selection { - background-color: $accent-color; - color: $selection-fg-color; -} - .mx_textButton { @mixin mx_DialogButton_small; } @@ -561,3 +547,28 @@ textarea { .mx_Username_color8 { color: $username-variant8-color; } + +@define-mixin mx_Tooltip_dark { + box-shadow: none; + background-color: $tooltip-timeline-bg-color; + color: $tooltip-timeline-fg-color; + border: none; + border-radius: 3px; + padding: 6px 8px; +} + +// This is a workaround for our mixins not supporting child selectors +.mx_Tooltip_dark { + .mx_Tooltip_chevron::after { + border-right-color: $tooltip-timeline-bg-color; + } +} + +@define-mixin mx_Settings_fullWidthField { + margin-right: 100px; +} + +@define-mixin mx_Settings_tooltip { + // So it fits in the space provided by the page + max-width: 120px; +} diff --git a/res/css/_components.scss b/res/css/_components.scss index 2a91f08ee4..b959b1f1cd 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -24,10 +24,11 @@ @import "./structures/_SearchBox.scss"; @import "./structures/_TabbedView.scss"; @import "./structures/_TagPanel.scss"; -@import "./structures/_TagPanelButtons.scss"; +@import "./structures/_ToastContainer.scss"; @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"; @@ -48,9 +50,9 @@ @import "./views/context_menus/_StatusMessageContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss"; @import "./views/context_menus/_TopLeftMenu.scss"; +@import "./views/context_menus/_WidgetContextMenu.scss"; @import "./views/dialogs/_AddressPickerDialog.scss"; @import "./views/dialogs/_Analytics.scss"; -@import "./views/dialogs/_BugReportDialog.scss"; @import "./views/dialogs/_ChangelogDialog.scss"; @import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss"; @import "./views/dialogs/_ConfirmUserActionDialog.scss"; @@ -62,14 +64,22 @@ @import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_IncomingSasDialog.scss"; -@import "./views/dialogs/_RestoreKeyBackupDialog.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"; @import "./views/dialogs/_SetMxIdDialog.scss"; @import "./views/dialogs/_SetPasswordDialog.scss"; @import "./views/dialogs/_SettingsDialog.scss"; @import "./views/dialogs/_ShareDialog.scss"; +@import "./views/dialogs/_SlashCommandHelpDialog.scss"; +@import "./views/dialogs/_TabbedIntegrationManagerDialog.scss"; +@import "./views/dialogs/_TermsDialog.scss"; @import "./views/dialogs/_UnknownDeviceDialog.scss"; @import "./views/dialogs/_UploadConfirmDialog.scss"; @import "./views/dialogs/_UserSettingsDialog.scss"; @@ -77,6 +87,8 @@ @import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss"; @import "./views/dialogs/keybackup/_KeyBackupFailedDialog.scss"; @import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss"; +@import "./views/dialogs/secretstorage/_AccessSecretStorageDialog.scss"; +@import "./views/dialogs/secretstorage/_CreateSecretStorageDialog.scss"; @import "./views/directory/_NetworkDropdown.scss"; @import "./views/elements/_AccessibleButton.scss"; @import "./views/elements/_AddressSelector.scss"; @@ -84,25 +96,30 @@ @import "./views/elements/_DirectorySearchBox.scss"; @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; +@import "./views/elements/_ErrorBoundary.scss"; +@import "./views/elements/_EventListSummary.scss"; @import "./views/elements/_Field.scss"; +@import "./views/elements/_FormButton.scss"; +@import "./views/elements/_IconButton.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; +@import "./views/elements/_InteractiveTooltip.scss"; @import "./views/elements/_ManageIntegsButton.scss"; -@import "./views/elements/_MemberEventListSummary.scss"; -@import "./views/elements/_MessageEditor.scss"; @import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; @import "./views/elements/_ReplyThread.scss"; @import "./views/elements/_ResizeHandle.scss"; @import "./views/elements/_RichText.scss"; @import "./views/elements/_RoleButton.scss"; +@import "./views/elements/_RoomAliasField.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; @import "./views/elements/_TextWithTooltip.scss"; @import "./views/elements/_ToggleSwitch.scss"; -@import "./views/elements/_ToolTipButton.scss"; @import "./views/elements/_Tooltip.scss"; +@import "./views/elements/_TooltipButton.scss"; @import "./views/elements/_Validation.scss"; +@import "./views/emojipicker/_EmojiPicker.scss"; @import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; @import "./views/groups/_GroupRoomList.scss"; @@ -117,7 +134,7 @@ @import "./views/messages/_MTextBody.scss"; @import "./views/messages/_MessageActionBar.scss"; @import "./views/messages/_MessageTimestamp.scss"; -@import "./views/messages/_ReactionDimension.scss"; +@import "./views/messages/_MjolnirBody.scss"; @import "./views/messages/_ReactionsRow.scss"; @import "./views/messages/_ReactionsRowButton.scss"; @import "./views/messages/_ReactionsRowButtonTooltip.scss"; @@ -126,20 +143,28 @@ @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"; @import "./views/rooms/_Autocomplete.scss"; @import "./views/rooms/_AuxPanel.scss"; +@import "./views/rooms/_BasicMessageComposer.scss"; @import "./views/rooms/_E2EIcon.scss"; +@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"; @import "./views/rooms/_MemberInfo.scss"; @import "./views/rooms/_MemberList.scss"; @import "./views/rooms/_MessageComposer.scss"; +@import "./views/rooms/_MessageComposerFormatBar.scss"; @import "./views/rooms/_PinnedEventTile.scss"; @import "./views/rooms/_PinnedEventsPanel.scss"; @import "./views/rooms/_PresenceLabel.scss"; @@ -153,27 +178,35 @@ @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"; +@import "./views/rooms/_UserOnlineDot.scss"; @import "./views/rooms/_WhoIsTypingTile.scss"; +@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/_IntegrationsManager.scss"; +@import "./views/settings/_IntegrationManager.scss"; @import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_Notifications.scss"; @import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_ProfileSettings.scss"; +@import "./views/settings/_SetIdServer.scss"; +@import "./views/settings/_SetIntegrationManager.scss"; @import "./views/settings/tabs/_SettingsTab.scss"; @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss"; @import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss"; @import "./views/settings/tabs/user/_HelpUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss"; @import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss"; @import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss"; @import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss"; @import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss"; +@import "./views/terms/_InlineTermsAgreement.scss"; @import "./views/verification/_VerificationShowSas.scss"; @import "./views/voip/_CallView.scss"; @import "./views/voip/_IncomingCallbox.scss"; diff --git a/res/css/structures/_AutoHideScrollbar.scss b/res/css/structures/_AutoHideScrollbar.scss index db86a6fbd6..50842c71bc 100644 --- a/res/css/structures/_AutoHideScrollbar.scss +++ b/res/css/structures/_AutoHideScrollbar.scss @@ -14,70 +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 logic to read but violates stylelints descending - * specificity rule, so turn it off for this file. It also duplicates - * a selector to separate the hiding/showing from the sizing. - */ -/* stylelint-disable no-descending-specificity, 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; @@ -85,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; } diff --git a/res/css/structures/_ContextualMenu.scss b/res/css/structures/_ContextualMenu.scss index fc1538a13d..fa2d87029d 100644 --- a/res/css/structures/_ContextualMenu.scss +++ b/res/css/structures/_ContextualMenu.scss @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,7 +40,11 @@ limitations under the License. z-index: 5001; } -.mx_ContextualMenu.mx_ContextualMenu_right { +.mx_ContextualMenu_right { + right: 0; +} + +.mx_ContextualMenu.mx_ContextualMenu_withChevron_right { right: 8px; } @@ -54,19 +59,11 @@ limitations under the License. border-bottom: 8px solid transparent; } -.mx_ContextualMenu_chevron_right::after { - content: ''; - width: 0; - height: 0; - border-top: 7px solid transparent; - border-left: 7px solid $menu-bg-color; - border-bottom: 7px solid transparent; - position: absolute; - top: -7px; - right: 1px; +.mx_ContextualMenu_left { + left: 0; } -.mx_ContextualMenu.mx_ContextualMenu_left { +.mx_ContextualMenu.mx_ContextualMenu_withChevron_left { left: 8px; } @@ -81,19 +78,11 @@ limitations under the License. border-bottom: 8px solid transparent; } -.mx_ContextualMenu_chevron_left::after { - content: ''; - width: 0; - height: 0; - border-top: 7px solid transparent; - border-right: 7px solid $menu-bg-color; - border-bottom: 7px solid transparent; - position: absolute; - top: -7px; - left: 1px; +.mx_ContextualMenu_top { + top: 0; } -.mx_ContextualMenu.mx_ContextualMenu_top { +.mx_ContextualMenu.mx_ContextualMenu_withChevron_top { top: 8px; } @@ -108,19 +97,11 @@ limitations under the License. border-right: 8px solid transparent; } -.mx_ContextualMenu_chevron_top::after { - content: ''; - width: 0; - height: 0; - border-left: 7px solid transparent; - border-bottom: 7px solid $menu-bg-color; - border-right: 7px solid transparent; - position: absolute; - left: -7px; - top: 1px; +.mx_ContextualMenu_bottom { + bottom: 0; } -.mx_ContextualMenu.mx_ContextualMenu_bottom { +.mx_ContextualMenu.mx_ContextualMenu_withChevron_bottom { bottom: 8px; } @@ -135,24 +116,6 @@ limitations under the License. border-right: 8px solid transparent; } -.mx_ContextualMenu_chevron_bottom::after { - content: ''; - width: 0; - height: 0; - border-left: 7px solid transparent; - border-top: 7px solid $menu-bg-color; - border-right: 7px solid transparent; - position: absolute; - left: -7px; - bottom: 1px; -} - -.mx_ContextualMenu_field { - padding: 3px 6px 3px 6px; - cursor: pointer; - white-space: nowrap; -} - .mx_ContextualMenu_spinner { display: block; margin: 0 auto; diff --git a/res/css/structures/_CustomRoomTagPanel.scss b/res/css/structures/_CustomRoomTagPanel.scss index 45961d7be1..1fb18ec41e 100644 --- a/res/css/structures/_CustomRoomTagPanel.scss +++ b/res/css/structures/_CustomRoomTagPanel.scss @@ -26,11 +26,16 @@ limitations under the License. .mx_CustomRoomTagPanel_scroller { max-height: inherit; + display: flex; + flex-direction: column; + align-items: center; } .mx_CustomRoomTagPanel .mx_AccessibleButton { - margin: 9px auto; + margin: 0 auto; width: 40px; + padding: 10px 0 9px 0; + position: relative; } .mx_CustomRoomTagPanel .mx_BaseAvatar_image { @@ -39,7 +44,13 @@ limitations under the License. height: 40px; } -.mx_CustomRoomTagPanel .mx_AccessibleButton.CustomRoomTagPanel_tileSelected .mx_BaseAvatar_image { - border: 3px solid $warning-color; - border-radius: 40px; +.mx_CustomRoomTagPanel .mx_AccessibleButton.CustomRoomTagPanel_tileSelected::before { + content: ''; + height: 56px; + background-color: $accent-color-alt; + width: 5px; + position: absolute; + left: -15px; + border-radius: 0 3px 3px 0; + top: 2px; // 10 [padding-top] - (56 - 40)/2 } diff --git a/res/css/structures/_FilePanel.scss b/res/css/structures/_FilePanel.scss index 703e90f402..87e885e668 100644 --- a/res/css/structures/_FilePanel.scss +++ b/res/css/structures/_FilePanel.scss @@ -18,6 +18,7 @@ limitations under the License. order: 2; flex: 1 1 0; overflow-y: auto; + display: flex; } .mx_FilePanel .mx_RoomView_messageListWrapper { diff --git a/res/css/structures/_GenericErrorPage.scss b/res/css/structures/_GenericErrorPage.scss index 9c973af411..7e9d7bbdaa 100644 --- a/res/css/structures/_GenericErrorPage.scss +++ b/res/css/structures/_GenericErrorPage.scss @@ -2,18 +2,16 @@ width: 100%; height: 100%; background-color: #fff; + display: flex; + align-items: center; + justify-content: center; } .mx_GenericErrorPage_box { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; + display: inline; width: 500px; - height: 200px; + min-height: 125px; border: 1px solid #f22; - padding: 10px; + padding: 10px 10px 20px; background-color: #fcc; } diff --git a/res/css/structures/_GroupView.scss b/res/css/structures/_GroupView.scss index ae86f68fd0..72a1132c15 100644 --- a/res/css/structures/_GroupView.scss +++ b/res/css/structures/_GroupView.scss @@ -44,21 +44,29 @@ limitations under the License. } .mx_GroupHeader_button { + position: relative; margin-left: 5px; margin-right: 5px; cursor: pointer; height: 20px; width: 20px; - background-color: $groupheader-button-color; - mask-repeat: no-repeat; - mask-size: contain; + + &::before { + content: ''; + position: absolute; + height: 20px; + width: 20px; + background-color: $groupheader-button-color; + mask-repeat: no-repeat; + mask-size: contain; + } } -.mx_GroupHeader_editButton { - mask-image: url('$(res)/img/icons-settings-room.svg'); +.mx_GroupHeader_editButton::before { + mask-image: url('$(res)/img/feather-customised/settings.svg'); } -.mx_GroupHeader_shareButton { +.mx_GroupHeader_shareButton::before { mask-image: url('$(res)/img/icons-share.svg'); } @@ -172,10 +180,6 @@ limitations under the License. line-height: 2em; } -.mx_GroupView > .mx_MainSplit { - flex: 1; -} - .mx_GroupView_body { flex-grow: 1; } @@ -333,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 { @@ -362,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; } diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index a8d8669285..85fdfa092d 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -24,13 +24,13 @@ limitations under the License. .mx_LeftPanel_container.collapsed { min-width: unset; - /* Collapsed LeftPanel 70px */ - flex: 0 0 70px; + /* Collapsed LeftPanel 50px */ + flex: 0 0 50px; } .mx_LeftPanel_container.collapsed.mx_LeftPanel_container_hasTagPanel { - /* TagPanel 70px + Collapsed LeftPanel 70px */ - flex: 0 0 140px; + /* TagPanel 70px + Collapsed LeftPanel 50px */ + flex: 0 0 120px; } .mx_LeftPanel_tagPanelContainer { @@ -125,3 +125,53 @@ limitations under the License. margin-top: 12px; } } + +.mx_LeftPanel_exploreAndFilterRow { + display: flex; + + .mx_SearchBox { + flex: 1 1 0; + min-width: 0; + margin: 4px 9px 1px 9px; + } +} + +.mx_LeftPanel_explore { + flex: 0 0 50%; + overflow: hidden; + transition: flex-basis 0.2s; + box-sizing: border-box; + + &.mx_LeftPanel_explore_hidden { + flex-basis: 0; + } + + .mx_AccessibleButton { + font-size: 14px; + margin: 4px 0 1px 9px; + padding: 9px; + padding-left: 42px; + font-weight: 600; + color: $notice-secondary-color; + position: relative; + border-radius: 4px; + + &:hover { + background-color: $primary-bg-color; + } + + &::before { + cursor: pointer; + mask: url('$(res)/img/explore.svg'); + mask-repeat: no-repeat; + mask-position: center center; + content: ""; + left: 14px; + top: 10px; + width: 16px; + height: 16px; + background-color: $notice-secondary-color; + position: absolute; + } + } +} diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 4d73953cd7..25e1153fce 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -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 diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index f2ce7e1d5c..c5a5d50068 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -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. diff --git a/res/css/structures/_MyGroups.scss b/res/css/structures/_MyGroups.scss index d25789ab94..36150c33a5 100644 --- a/res/css/structures/_MyGroups.scss +++ b/res/css/structures/_MyGroups.scss @@ -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 { diff --git a/res/css/structures/_NotificationPanel.scss b/res/css/structures/_NotificationPanel.scss index 78b3522d4e..c9e0261ec9 100644 --- a/res/css/structures/_NotificationPanel.scss +++ b/res/css/structures/_NotificationPanel.scss @@ -18,6 +18,7 @@ limitations under the License. order: 2; flex: 1 1 0; overflow-y: auto; + display: flex; } .mx_NotificationPanel .mx_RoomView_messageListWrapper { diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index c63db5d274..3c373e8883 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -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; } @@ -50,18 +51,18 @@ limitations under the License. height: 20px; width: 20px; position: relative; -} -.mx_RightPanel_headerButton::before { - content: ''; - position: absolute; - top: 0; - left: 0; - height: 20px; - width: 20px; - background-color: $rightpanel-button-color; - mask-repeat: no-repeat; - mask-size: contain; + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: $rightpanel-button-color; + mask-repeat: no-repeat; + mask-size: contain; + } } .mx_RightPanel_membersButton::before { diff --git a/res/css/structures/_RoomDirectory.scss b/res/css/structures/_RoomDirectory.scss index bcfe3aefd6..f3a7b0e243 100644 --- a/res/css/structures/_RoomDirectory.scss +++ b/res/css/structures/_RoomDirectory.scss @@ -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. @@ -17,7 +18,6 @@ limitations under the License. .mx_RoomDirectory_dialogWrapper > .mx_Dialog { max-width: 960px; height: 100%; - padding: 20px; } .mx_RoomDirectory_dialog { @@ -35,24 +35,6 @@ limitations under the License. flex: 1; } -.mx_RoomDirectory .gm-scroll-view { - // little hack because gemini doesn't seem to detect - // the scrollbar width well in this instance - // when using css scrollbars - scrollbar-width: thin; -} - -.mx_RoomDirectory_createRoom { - background-color: $button-bg-color; - border-radius: 4px; - padding: 8px; - color: $button-fg-color; - font-weight: 600; - position: absolute; - top: 0; - left: 0; -} - .mx_RoomDirectory_list { flex: 1; display: flex; @@ -64,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 { @@ -83,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; @@ -91,9 +72,8 @@ limitations under the License. } .mx_RoomDirectory_roomAvatar { - width: 24px; - padding-left: 12px; - padding-right: 24px; + width: 32px; + padding-right: 14px; vertical-align: top; } @@ -101,8 +81,38 @@ limitations under the License. padding-bottom: 16px; } +.mx_RoomDirectory_roomMemberCount { + color: $light-fg-color; + width: 60px; + padding: 0 10px; + text-align: center; + + &::before { + background-color: $light-fg-color; + display: inline-block; + vertical-align: text-top; + margin-right: 2px; + content: ""; + mask: url('$(res)/img/feather-customised/user.svg'); + mask-repeat: no-repeat; + mask-position: center; + // scale it down and make the size slightly bigger (16 instead of 14px) + // to avoid rendering artifacts + mask-size: 80%; + width: 16px; + height: 16px; + } +} + +.mx_RoomDirectory_join, .mx_RoomDirectory_preview { + width: 80px; + text-align: center; + white-space: nowrap; +} + .mx_RoomDirectory_name { display: inline-block; + font-size: 18px; font-weight: 600; } @@ -111,21 +121,18 @@ limitations under the License. } .mx_RoomDirectory_perm { - display: inline; - padding-left: 5px; - padding-right: 5px; - margin-right: 5px; - height: 15px; - border-radius: 11px; - background-color: $plinth-bg-color; - text-transform: uppercase; - font-weight: 600; - font-size: 11px; - color: $accent-color; + 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; } .mx_RoomDirectory_alias { @@ -133,13 +140,20 @@ limitations under the License. color: $settings-grey-fg-color; } -.mx_RoomDirectory_roomMemberCount { - text-align: right; - width: 100px; - padding-right: 10px; -} - .mx_RoomDirectory_table tr { padding-bottom: 10px; cursor: pointer; } + +.mx_RoomDirectory .mx_RoomView_MessageList { + padding: 0; +} + +.mx_RoomDirectory > span { + font-size: 15px; + margin-top: 0; + + .mx_AccessibleButton { + padding: 0; + } +} diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 15fddba817..1934e681c2 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -20,7 +20,7 @@ limitations under the License. so they ideally wouldn't affect each other. lowest category: .mx_RoomSubList flex-shrink: 10000000 - distribute size of items within the same categery by their size + distribute size of items within the same category by their size middle category: .mx_RoomSubList.resized-sized flex-shrink: 1000 applied when using the resizer, will have a max-height set to it, @@ -46,10 +46,15 @@ limitations under the License. flex-direction: row; align-items: center; flex: 0 0 auto; - margin: 0 16px; + margin: 0 8px; + padding: 0 8px; height: 36px; } +.mx_RoomSubList_labelContainer.focus-visible:focus-within { + background-color: $roomtile-focused-bg-color; +} + .mx_RoomSubList_label { flex: 1; cursor: pointer; @@ -67,13 +72,13 @@ limitations under the License. margin-left: 8px; } -.mx_RoomSubList_badge { +.mx_RoomSubList_badge > div { flex: 0 0 auto; border-radius: 8px; - color: $accent-fg-color; font-weight: 600; font-size: 12px; padding: 0 5px; + color: $roomtile-badge-fg-color; background-color: $roomtile-name-color; cursor: pointer; } @@ -103,7 +108,8 @@ limitations under the License. } } -.mx_RoomSubList_badgeHighlight { +.mx_RoomSubList_badgeHighlight > div { + color: $accent-fg-color; background-color: $warning-color; } @@ -143,8 +149,9 @@ limitations under the License. } .mx_RoomSubList_labelContainer { - margin-right: 14px; + margin-right: 8px; margin-left: 2px; + padding: 0; } .mx_RoomSubList_addRoom { @@ -159,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; } - */ } diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 50d412ad58..5e826306c6 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -221,6 +221,9 @@ hr.mx_RoomView_myReadMarker { position: relative; top: -1px; z-index: 1; + transition: width 400ms easeInSine 1s, opacity 400ms easeInSine 1s; + width: 99%; + opacity: 1; } .mx_RoomView_callStatusBar .mx_UploadBar_uploadProgressInner { diff --git a/res/css/structures/_SearchBox.scss b/res/css/structures/_SearchBox.scss index 9434d93bd2..23ee06f7b3 100644 --- a/res/css/structures/_SearchBox.scss +++ b/res/css/structures/_SearchBox.scss @@ -14,12 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_SearchBox_closeButton { - cursor: pointer; - background-image: url('$(res)/img/icons-close.svg'); - background-repeat: no-repeat; - width: 16px; - height: 16px; - background-position: center; - padding: 9px; +.mx_SearchBox { + flex: 1 1 0; + min-width: 0; + + &.mx_SearchBox_blurred:not(:hover) { + background-color: transparent; + } + + .mx_SearchBox_closeButton { + cursor: pointer; + background-image: url('$(res)/img/icons-close.svg'); + background-repeat: no-repeat; + width: 16px; + height: 16px; + background-position: center; + padding: 9px; + } } diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index a818f52125..c1c5d92d3c 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -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,19 +58,19 @@ limitations under the License. .mx_TagPanel .mx_TagPanel_scroller { flex-grow: 1; + width: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer { display: flex; flex-direction: column; align-items: center; - margin-top: 5px; height: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer > div { height: 40px; - padding: 5px 0 4px 0; + padding: 10px 0 9px 0; } .mx_TagPanel .mx_TagTile { @@ -83,21 +84,39 @@ limitations under the License. // opacity: 1; } -.mx_TagPanel .mx_TagTile.mx_TagTile_selected .mx_TagTile_avatar .mx_BaseAvatar { - background-color: $accent-color; - border-radius: 40px; - - /* In case this is a "initial" avatar */ - display: block; +.mx_TagPanel .mx_TagTile_plus { + margin-bottom: 12px; height: 40px; width: 40px; + border-radius: 20px; + background-color: $roomheader-addroom-bg-color; + position: relative; + /* overwrite mx_RoleButton inline-block */ + display: block !important; + + &::before { + background-color: $roomheader-addroom-fg-color; + mask-image: url('$(res)/img/feather-customised/plus.svg'); + mask-position: center; + mask-repeat: no-repeat; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } } -.mx_TagPanel .mx_TagTile_selected .mx_BaseAvatar_image { - border: 3px solid $accent-color; - height: 40px; - width: 40px; - box-sizing: border-box; +.mx_TagPanel .mx_TagTile.mx_TagTile_selected::before { + content: ''; + height: 56px; + background-color: $accent-color; + width: 5px; + position: absolute; + left: -15px; + border-radius: 0 3px 3px 0; + top: -8px; // (56 - 40)/2 } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { @@ -118,7 +137,7 @@ limitations under the License. top: -8px; border-radius: 8px; background-color: $neutral-badge-color; - color: #ffffff; + color: #000; font-weight: 600; font-size: 10px; text-align: center; diff --git a/res/css/structures/_TagPanelButtons.scss b/res/css/structures/_TagPanelButtons.scss deleted file mode 100644 index 70fea92959..0000000000 --- a/res/css/structures/_TagPanelButtons.scss +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2019 New Vector Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_TagPanelButtons { - background-color: $tagpanel-bg-color; - display: flex; - flex-direction: column; - align-items: center; - justify-content: space-between; - padding: 17px 0 3px 0; -} - -.mx_TagPanelButtons > .mx_GroupsButton::before { - mask: url('$(res)/img/feather-customised/users.svg'); - mask-position: center 11px; -} - -.mx_TagPanelButtons > .mx_TagPanelButtons_report::before { - mask: url('$(res)/img/feather-customised/life-buoy.svg'); - mask-position: center 9px; -} - -.mx_TagPanelButtons > .mx_AccessibleButton { - margin-bottom: 12px; - height: 40px; - width: 40px; - border-radius: 20px; - background-color: $tagpanel-button-color; - position: relative; - /* overwrite mx_RoleButton inline-block */ - display: block !important; - - &::before { - background-color: $tagpanel-bg-color; - mask-repeat: no-repeat; - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - } -} diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss new file mode 100644 index 0000000000..d1687743d6 --- /dev/null +++ b/res/css/structures/_ToastContainer.scss @@ -0,0 +1,106 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_ToastContainer { + position: absolute; + top: 0; + left: 70px; + z-index: 101; + padding: 4px; + display: grid; + grid-template-rows: 1fr 14px 6px; + + &.mx_ToastContainer_stacked::before { + content: ""; + margin: 0 4px; + grid-row: 2 / 4; + grid-column: 1; + background-color: white; + box-shadow: 0px 4px 12px $menu-box-shadow-color; + border-radius: 8px; + } + + .mx_Toast_toast { + grid-row: 1 / 3; + grid-column: 1; + color: $primary-fg-color; + background-color: $primary-bg-color; + box-shadow: 0px 4px 12px $menu-box-shadow-color; + border-radius: 8px; + overflow: hidden; + display: grid; + grid-template-columns: 20px 1fr; + column-gap: 10px; + row-gap: 4px; + padding: 8px; + padding-right: 16px; + + &.mx_Toast_hasIcon { + &::after { + content: ""; + width: 22px; + height: 22px; + grid-column: 1; + grid-row: 1; + mask-size: 100%; + mask-repeat: no-repeat; + } + + &.mx_Toast_icon_verification::after { + mask-image: url("$(res)/img/e2e/normal.svg"); + 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; + } + } + + h2 { + grid-column: 1 / 3; + grid-row: 1; + margin: 0; + font-size: 15px; + font-weight: 600; + } + + .mx_Toast_body { + grid-column: 1 / 3; + grid-row: 2; + } + + .mx_Toast_buttons { + display: flex; + } + + .mx_Toast_description { + max-width: 400px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin: 4px 0 11px 0; + font-size: 12px; + } + + .mx_Toast_deviceID { + font-size: 10px; + } + } +} diff --git a/res/css/structures/_TopLeftMenuButton.scss b/res/css/structures/_TopLeftMenuButton.scss index 94a391ae70..ee03978f18 100644 --- a/res/css/structures/_TopLeftMenuButton.scss +++ b/res/css/structures/_TopLeftMenuButton.scss @@ -22,7 +22,7 @@ limitations under the License. display: flex; align-items: center; min-width: 0; - padding: 0 7px; + padding: 0 4px; overflow: hidden; } diff --git a/res/css/structures/auth/_CompleteSecurity.scss b/res/css/structures/auth/_CompleteSecurity.scss new file mode 100644 index 0000000000..601492d43c --- /dev/null +++ b/res/css/structures/auth/_CompleteSecurity.scss @@ -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; + } + } +} diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index 4eff5c33e4..4ce90cc6bd 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -30,6 +30,7 @@ limitations under the License. .mx_Login_submit:disabled { opacity: 0.3; + cursor: default; } .mx_AuthBody a.mx_Login_sso_link:link, @@ -62,6 +63,15 @@ limitations under the License. margin-bottom: 12px; } +.mx_Login_error.mx_Login_serverError { + text-align: left; + font-weight: normal; +} + +.mx_Login_error.mx_Login_serverError.mx_Login_serverErrorNonFatal { + color: $orange-warning-color; +} + .mx_Login_type_container { display: flex; align-items: center; @@ -73,11 +83,7 @@ limitations under the License. } .mx_Login_type_label { - flex-grow: 1; -} - -.mx_Login_type_dropdown { - min-width: 200px; + flex: 1; } .mx_Login_underlinedServerName { diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 16ac876869..7c5b008535 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -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; @@ -39,8 +40,7 @@ limitations under the License. a:link, a:hover, a:visited { - color: $accent-color; - text-decoration: none; + @mixin mx_Dialog_link; } input[type=text], @@ -58,12 +58,12 @@ limitations under the License. color: $authpage-primary-color; } + .mx_Field_labelAlwaysTopLeft label, + .mx_Field select + label /* Always show a select's label on top to not collide with the value */, .mx_Field input:focus + label, .mx_Field input:not(:placeholder-shown) + label, .mx_Field textarea:focus + label, - .mx_Field textarea:not(:placeholder-shown) + label, - .mx_Field select + label /* Always show a select's label on top to not collide with the value */, - .mx_Field_labelAlwaysTopLeft label { + .mx_Field textarea:not(:placeholder-shown) + label { background-color: $authpage-body-bg-color; } @@ -72,7 +72,6 @@ limitations under the License. } .mx_Field input { - width: 100%; box-sizing: border-box; } @@ -110,7 +109,6 @@ limitations under the License. .mx_AuthBody_fieldRow > .mx_Field { margin: 0 5px; - flex: 1; } .mx_AuthBody_fieldRow > .mx_Field:first-child { diff --git a/res/css/views/auth/_CompleteSecurityBody.scss b/res/css/views/auth/_CompleteSecurityBody.scss new file mode 100644 index 0000000000..c7860fbe74 --- /dev/null +++ b/res/css/views/auth/_CompleteSecurityBody.scss @@ -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; + } +} diff --git a/res/css/views/auth/_InteractiveAuthEntryComponents.scss b/res/css/views/auth/_InteractiveAuthEntryComponents.scss index e2ea7d86fb..85007aeecb 100644 --- a/res/css/views/auth/_InteractiveAuthEntryComponents.scss +++ b/res/css/views/auth/_InteractiveAuthEntryComponents.scss @@ -49,10 +49,14 @@ limitations under the License. } .mx_InteractiveAuthEntryComponents_termsSubmit:disabled { - background-color: $accent-color-50pct; + background-color: $accent-color-darker; cursor: default; } .mx_InteractiveAuthEntryComponents_termsPolicy { display: block; -} \ No newline at end of file +} + +.mx_InteractiveAuthEntryComponents_passwordSection { + width: 300px; +} diff --git a/res/css/views/auth/_ServerConfig.scss b/res/css/views/auth/_ServerConfig.scss index fe96da2019..a7e0057ab3 100644 --- a/res/css/views/auth/_ServerConfig.scss +++ b/res/css/views/auth/_ServerConfig.scss @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,24 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_ServerConfig_fields { - display: flex; - margin: 1em 0; -} - -.mx_ServerConfig_fields .mx_Field { - flex: 1; - margin: 0 5px; -} - -.mx_ServerConfig_fields .mx_Field:first-child { - margin-left: 0; -} - -.mx_ServerConfig_fields .mx_Field:last-child { - margin-right: 0; -} - .mx_ServerConfig_help:link { opacity: 0.8; } @@ -40,3 +23,13 @@ limitations under the License. display: block; color: $warning-color; } + +.mx_ServerConfig_identityServer { + transform: scaleY(0); + transform-origin: top; + transition: transform 0.25s; + + &.mx_ServerConfig_identityServer_shown { + transform: scaleY(1); + } +} diff --git a/res/css/views/avatars/_BaseAvatar.scss b/res/css/views/avatars/_BaseAvatar.scss index a085034758..e59598278f 100644 --- a/res/css/views/avatars/_BaseAvatar.scss +++ b/res/css/views/avatars/_BaseAvatar.scss @@ -40,6 +40,7 @@ limitations under the License. } .mx_BaseAvatar_image { + object-fit: cover; border-radius: 40px; vertical-align: top; background-color: $avatar-bg-color; diff --git a/res/css/views/context_menus/_RoomTileContextMenu.scss b/res/css/views/context_menus/_RoomTileContextMenu.scss index f832691be4..308cecfe1e 100644 --- a/res/css/views/context_menus/_RoomTileContextMenu.scss +++ b/res/css/views/context_menus/_RoomTileContextMenu.scss @@ -18,6 +18,18 @@ limitations under the License. padding: 6px; } +.mx_RoomTileContextMenu_tag_icon { + padding-right: 8px; + padding-left: 4px; + display: inline-block; +} + +.mx_RoomTileContextMenu_tag_icon_set { + padding-right: 8px; + padding-left: 4px; + display: none; +} + .mx_RoomTileContextMenu_tag_field, .mx_RoomTileContextMenu_leave { padding-top: 8px; padding-right: 20px; @@ -45,18 +57,6 @@ limitations under the License. color: rgba(0, 0, 0, 0.2); } -.mx_RoomTileContextMenu_tag_icon { - padding-right: 8px; - padding-left: 4px; - display: inline-block -} - -.mx_RoomTileContextMenu_tag_icon_set { - padding-right: 8px; - padding-left: 4px; - display: none; -} - .mx_RoomTileContextMenu_separator { margin-top: 0; margin-bottom: 0; @@ -72,10 +72,6 @@ limitations under the License. color: $warning-color; } -.mx_RoomTileContextMenu_tag_fieldSet .mx_RoomTileContextMenu_tag_icon { - /* Something to indicate that the icon is the set tag */ -} - .mx_RoomTileContextMenu_notif_picker { position: absolute; top: 16px; diff --git a/res/css/views/context_menus/_StatusMessageContextMenu.scss b/res/css/views/context_menus/_StatusMessageContextMenu.scss index 972f608caf..2c8d608950 100644 --- a/res/css/views/context_menus/_StatusMessageContextMenu.scss +++ b/res/css/views/context_menus/_StatusMessageContextMenu.scss @@ -61,5 +61,5 @@ input.mx_StatusMessageContextMenu_message { } .mx_StatusMessageContextMenu_actionContainer .mx_Spinner { - justify-content: start; + justify-content: flex-start; } diff --git a/res/css/views/context_menus/_TagTileContextMenu.scss b/res/css/views/context_menus/_TagTileContextMenu.scss index 799151b198..46b279ce2d 100644 --- a/res/css/views/context_menus/_TagTileContextMenu.scss +++ b/res/css/views/context_menus/_TagTileContextMenu.scss @@ -33,7 +33,7 @@ limitations under the License. .mx_TagTileContextMenu_item_icon { padding-right: 8px; padding-left: 4px; - display: inline-block + display: inline-block; } .mx_TagTileContextMenu_separator { diff --git a/res/css/views/context_menus/_TopLeftMenu.scss b/res/css/views/context_menus/_TopLeftMenu.scss index 113da004b8..ed0d0106bc 100644 --- a/res/css/views/context_menus/_TopLeftMenu.scss +++ b/res/css/views/context_menus/_TopLeftMenu.scss @@ -49,23 +49,27 @@ limitations under the License. padding: 0; list-style: none; - li.mx_TopLeftMenu_icon_home::after { + .mx_TopLeftMenu_icon_home::after { mask-image: url('$(res)/img/feather-customised/home.svg'); } - li.mx_TopLeftMenu_icon_settings::after { + .mx_TopLeftMenu_icon_help::after { + mask-image: url('$(res)/img/feather-customised/life-buoy.svg'); + } + + .mx_TopLeftMenu_icon_settings::after { mask-image: url('$(res)/img/feather-customised/settings.svg'); } - li.mx_TopLeftMenu_icon_signin::after { + .mx_TopLeftMenu_icon_signin::after { mask-image: url('$(res)/img/feather-customised/sign-in.svg'); } - li.mx_TopLeftMenu_icon_signout::after { + .mx_TopLeftMenu_icon_signout::after { mask-image: url('$(res)/img/feather-customised/sign-out.svg'); } - li::after { + .mx_AccessibleButton::after { mask-repeat: no-repeat; mask-position: 0 center; mask-size: 16px; @@ -78,16 +82,15 @@ limitations under the License. background-color: $primary-fg-color; } - li { + .mx_AccessibleButton { position: relative; cursor: pointer; white-space: nowrap; padding: 5px 20px 5px 43px; } - li:hover { + .mx_AccessibleButton:hover { background-color: $menu-selected-color; } } - } diff --git a/res/css/views/context_menus/_WidgetContextMenu.scss b/res/css/views/context_menus/_WidgetContextMenu.scss new file mode 100644 index 0000000000..60b7b93f99 --- /dev/null +++ b/res/css/views/context_menus/_WidgetContextMenu.scss @@ -0,0 +1,36 @@ +/* +Copyright 2019 The Matrix.org Foundaction 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_WidgetContextMenu { + padding: 6px; + + .mx_WidgetContextMenu_option { + padding: 3px 6px 3px 6px; + cursor: pointer; + white-space: nowrap; + } + + .mx_WidgetContextMenu_separator { + margin-top: 0; + margin-bottom: 0; + border-bottom-style: none; + border-left-style: none; + border-right-style: none; + border-top-style: solid; + border-top-width: 1px; + border-color: $menu-border-color; + } +} diff --git a/res/css/views/dialogs/_AddressPickerDialog.scss b/res/css/views/dialogs/_AddressPickerDialog.scss index b4d4a74cb5..39a9260ba3 100644 --- a/res/css/views/dialogs/_AddressPickerDialog.scss +++ b/res/css/views/dialogs/_AddressPickerDialog.scss @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,10 +16,17 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_AddressPickerDialog { + a:link, + a:hover, + a:visited { + @mixin mx_Dialog_link; + } +} + /* Using a textarea for this element, to circumvent autofill */ .mx_AddressPickerDialog_input, -.mx_AddressPickerDialog_input:focus -{ +.mx_AddressPickerDialog_input:focus { height: 26px; font-size: 14px; font-family: $font-family; @@ -36,7 +44,7 @@ limitations under the License. } .mx_AddressPickerDialog .mx_Dialog_content { - min-height: 50px + min-height: 50px; } .mx_AddressPickerDialog_inputContainer { @@ -68,3 +76,6 @@ limitations under the License. pointer-events: none; } +.mx_AddressPickerDialog_identityServer { + margin-top: 1em; +} diff --git a/res/css/views/dialogs/_CreateRoomDialog.scss b/res/css/views/dialogs/_CreateRoomDialog.scss index 05d5bfcebf..7416ec2ef4 100644 --- a/res/css/views/dialogs/_CreateRoomDialog.scss +++ b/res/css/views/dialogs/_CreateRoomDialog.scss @@ -14,8 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_CreateRoomDialog_details_summary { - outline: none; +.mx_CreateRoomDialog_details { + .mx_CreateRoomDialog_details_summary { + outline: none; + list-style: none; + font-weight: 600; + cursor: pointer; + color: $accent-color; + + // list-style doesn't do it for webkit + &::-webkit-details-marker { + display: none; + } + } + + > div { + display: flex; + align-items: flex-start; + margin: 5px 0; + + input[type=checkbox] { + margin-right: 10px; + } + } } .mx_CreateRoomDialog_label { @@ -36,3 +57,38 @@ limitations under the License. background-color: $primary-bg-color; width: 100%; } + +// needed to make the alias field only grow as wide as needed +// as opposed to full width +.mx_CreateRoomDialog_aliasContainer { + display: flex; + // put margin on container so it can collapse with siblings + margin: 10px 0; + + .mx_RoomAliasField { + margin: 0; + } +} + +.mx_CreateRoomDialog { + + &.mx_Dialog_fixedWidth { + width: 450px; + } + + .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; + } +} + diff --git a/res/css/views/dialogs/_DeactivateAccountDialog.scss b/res/css/views/dialogs/_DeactivateAccountDialog.scss index dc76da5b15..192917b2d0 100644 --- a/res/css/views/dialogs/_DeactivateAccountDialog.scss +++ b/res/css/views/dialogs/_DeactivateAccountDialog.scss @@ -21,3 +21,7 @@ limitations under the License. .mx_DeactivateAccountDialog .mx_DeactivateAccountDialog_input_section { margin-top: 60px; } + +.mx_DeactivateAccountDialog .mx_DeactivateAccountDialog_input_section .mx_Field { + width: 300px; +} diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index 1f5d36b57a..500c46b5fd 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -23,7 +23,11 @@ limitations under the License. cursor: default !important; } -.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_ServersInRoomList_button, .mx_DevTools_RoomStateExplorer_query { +.mx_DevTools_RoomStateExplorer_query { + margin-bottom: 10px; +} + +.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_ServersInRoomList_button { margin-bottom: 10px; width: 100%; } @@ -41,13 +45,11 @@ limitations under the License. border-bottom: 1px solid #e5e5e5; } -.mx_DevTools_inputRow -{ +.mx_DevTools_inputRow { display: table-row; } -.mx_DevTools_inputLabelCell -{ +.mx_DevTools_inputLabelCell { display: table-cell; font-weight: bold; padding-right: 24px; @@ -58,8 +60,7 @@ limitations under the License. width: 240px; } -.mx_DevTools_inputCell input -{ +.mx_DevTools_inputCell input { display: inline-block; border: 0; border-bottom: 1px solid $input-underline-color; @@ -75,11 +76,6 @@ limitations under the License. max-width: 684px; min-height: 250px; padding: 10px; - width: 100%; -} - -.mx_DevTools_content .mx_Field_input { - display: inline-block; } .mx_DevTools_eventTypeStateKeyGroup { @@ -96,11 +92,11 @@ limitations under the License. // add default box-sizing for this scope &, - &:after, - &:before, + &::after, + &::before, & *, - & *:after, - & *:before, + & *::after, + & *::before, & + .mx_DevTools_tgl-btn { box-sizing: border-box; &::selection { @@ -116,8 +112,8 @@ limitations under the License. position: relative; cursor: pointer; user-select: none; - &:after, - &:before { + &::after, + &::before { position: relative; display: block; content: ""; @@ -125,16 +121,16 @@ limitations under the License. height: 100%; } - &:after { + &::after { left: 0; } - &:before { + &::before { display: none; } } - &:checked + .mx_DevTools_tgl-btn:after { + &:checked + .mx_DevTools_tgl-btn::after { left: 50%; } } @@ -145,8 +141,8 @@ limitations under the License. transition: all .2s ease; font-family: sans-serif; perspective: 100px; - &:after, - &:before { + &::after, + &::before { display: inline-block; transition: all .4s ease; width: 100%; @@ -161,35 +157,69 @@ limitations under the License. border-radius: 4px; } - &:after { + &::after { content: attr(data-tg-on); - background: #02C66F; + background: #02c66f; transform: rotateY(-180deg); } - &:before { - background: #FF3A19; + &::before { + background: #ff3a19; content: attr(data-tg-off); } - &:active:before { + &:active::before { transform: rotateY(-20deg); } } &:checked + .mx_DevTools_tgl-btn { - &:before { + &::before { transform: rotateY(180deg); } - &:after { + &::after { transform: rotateY(0); left: 0; - background: #7FC6A6; + background: #7fc6a6; } - &:active:after { + &:active::after { transform: rotateY(20deg); } } } + +.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: ":"; + } +} diff --git a/res/css/views/dialogs/_GroupAddressPicker.scss b/res/css/views/dialogs/_GroupAddressPicker.scss index d6c961c0ec..20a7cc1047 100644 --- a/res/css/views/dialogs/_GroupAddressPicker.scss +++ b/res/css/views/dialogs/_GroupAddressPicker.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_GroupAddressPicker_checkboxContainer{ +.mx_GroupAddressPicker_checkboxContainer { margin-top: 10px; display: flex; } diff --git a/res/css/views/dialogs/_InviteDialog.scss b/res/css/views/dialogs/_InviteDialog.scss new file mode 100644 index 0000000000..5e0893b8fd --- /dev/null +++ b/res/css/views/dialogs/_InviteDialog.scss @@ -0,0 +1,228 @@ +/* +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_InviteDialog_addressBar { + display: flex; + flex-direction: row; + + .mx_InviteDialog_editor { + flex: 1; + width: 100%; // Needed to make the Field inside grow + background-color: $user-tile-hover-bg-color; + border-radius: 4px; + min-height: 25px; + padding-left: 8px; + overflow-x: hidden; + overflow-y: auto; + + .mx_InviteDialog_userTile { + display: inline-block; + float: left; + position: relative; + top: 7px; + } + + // Using a textarea for this element, to circumvent autofill + // Mostly copied from AddressPickerDialog + textarea, + textarea:focus { + height: 34px; + line-height: 34px; + font-size: 14px; + padding-left: 12px; + margin: 0 !important; + border: 0 !important; + outline: 0 !important; + resize: none; + overflow: hidden; + box-sizing: border-box; + word-wrap: nowrap; + + // Roughly fill about 2/5ths of the available space. This is to try and 'fill' the + // remaining space after a bunch of pills, but is a bit hacky. Ideally we'd have + // support for "fill remaining width", but traditional tricks don't work with what + // we're pushing into this "field". Flexbox just makes things worse. The theory is + // that users won't need more than about 2/5ths of the input to find the person + // they're looking for. + width: 40%; + } + } + + .mx_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_InviteDialog_section { + padding-bottom: 10px; + + h3 { + font-size: 12px; + color: $muted-fg-color; + font-weight: bold; + text-transform: uppercase; + } +} + +.mx_InviteDialog_roomTile { + cursor: pointer; + padding: 5px 10px; + + &:hover { + background-color: $user-tile-hover-bg-color; + border-radius: 4px; + } + + * { + vertical-align: middle; + } + + .mx_InviteDialog_roomTile_avatarStack { + display: inline-block; + position: relative; + width: 36px; + height: 36px; + + & > * { + position: absolute; + top: 0; + left: 0; + } + } + + .mx_InviteDialog_roomTile_selected { + width: 36px; + height: 36px; + border-radius: 36px; + background-color: $username-variant1-color; + display: inline-block; + position: relative; + + &::before { + content: ""; + width: 24px; + height: 24px; + grid-column: 1; + grid-row: 1; + mask-image: url("$(res)/img/feather-customised/check.svg"); + mask-size: 100%; + mask-repeat: no-repeat; + position: absolute; + top: 6px; // 50% + left: 6px; // 50% + background-color: #ffffff; // this is fine without a var because it's for both themes + } + } + + .mx_InviteDialog_roomTile_name { + font-weight: 600; + font-size: 14px; + color: $primary-fg-color; + margin-left: 7px; + } + + .mx_InviteDialog_roomTile_userId { + font-size: 12px; + color: $muted-fg-color; + margin-left: 7px; + } + + .mx_InviteDialog_roomTile_time { + text-align: right; + font-size: 12px; + color: $muted-fg-color; + float: right; + line-height: 36px; // Height of the avatar to keep the time vertically aligned + } + + .mx_InviteDialog_roomTile_highlight { + font-weight: 900; + } +} + +// Many of these styles are stolen from mx_UserPill, but adjusted for the invite dialog. +.mx_InviteDialog_userTile { + margin-right: 8px; + + .mx_InviteDialog_userTile_pill { + background-color: $username-variant1-color; + border-radius: 12px; + display: inline-block; + height: 24px; + line-height: 24px; + padding-left: 8px; + padding-right: 8px; + color: #ffffff; // this is fine without a var because it's for both themes + + .mx_InviteDialog_userTile_avatar { + border-radius: 20px; + position: relative; + left: -5px; + top: 2px; + } + + img.mx_InviteDialog_userTile_avatar { + vertical-align: top; + } + + .mx_InviteDialog_userTile_name { + vertical-align: top; + } + + .mx_InviteDialog_userTile_threepidAvatar { + background-color: #ffffff; // this is fine without a var because it's for both themes + } + } + + .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; +} diff --git a/res/css/views/dialogs/_KeyboardShortcutsDialog.scss b/res/css/views/dialogs/_KeyboardShortcutsDialog.scss new file mode 100644 index 0000000000..638cacd41f --- /dev/null +++ b/res/css/views/dialogs/_KeyboardShortcutsDialog.scss @@ -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; + } +} diff --git a/res/css/views/dialogs/_MessageEditHistoryDialog.scss b/res/css/views/dialogs/_MessageEditHistoryDialog.scss new file mode 100644 index 0000000000..0066faccae --- /dev/null +++ b/res/css/views/dialogs/_MessageEditHistoryDialog.scss @@ -0,0 +1,67 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MessageEditHistoryDialog .mx_Dialog_header > .mx_Dialog_title { + text-align: center; +} + +.mx_MessageEditHistoryDialog { + display: flex; + flex-direction: column; + max-height: 60vh; +} + +.mx_MessageEditHistoryDialog_scrollPanel { + flex: 1 1 auto; +} + +.mx_MessageEditHistoryDialog_error { + color: $warning-color; + text-align: center; +} + +.mx_MessageEditHistoryDialog_edits { + list-style-type: none; + font-size: 14px; + padding: 0; + color: $primary-fg-color; + + span.mx_EditHistoryMessage_deletion, span.mx_EditHistoryMessage_insertion { + padding: 0px 2px; + } + + .mx_EditHistoryMessage_deletion { + color: rgb(255, 76, 85); + background-color: rgba(255, 76, 85, 0.1); + text-decoration: line-through; + } + + .mx_EditHistoryMessage_insertion { + color: rgb(26, 169, 123); + background-color: rgba(26, 169, 123, 0.1); + text-decoration: underline; + } + + .mx_EventTile_line, .mx_EventTile_content { + margin-right: 0px; + } + + .mx_MessageActionBar .mx_AccessibleButton { + font-size: 10px; + padding: 0 8px; + } +} + diff --git a/res/css/views/dialogs/_NewSessionReviewDialog.scss b/res/css/views/dialogs/_NewSessionReviewDialog.scss new file mode 100644 index 0000000000..7e35fe941e --- /dev/null +++ b/res/css/views/dialogs/_NewSessionReviewDialog.scss @@ -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; +} diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index 60b35528a1..2a4e62f9aa 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -17,19 +17,24 @@ limitations under the License. // ICONS // ========================================================== -.mx_RoomSettingsDialog_settingsIcon:before { +.mx_RoomSettingsDialog_settingsIcon::before { mask-image: url('$(res)/img/feather-customised/settings.svg'); } -.mx_RoomSettingsDialog_securityIcon:before { +.mx_RoomSettingsDialog_securityIcon::before { mask-image: url('$(res)/img/feather-customised/lock.svg'); } -.mx_RoomSettingsDialog_rolesIcon:before { +.mx_RoomSettingsDialog_rolesIcon::before { mask-image: url('$(res)/img/feather-customised/users-sm.svg'); } -.mx_RoomSettingsDialog_warningIcon:before { +.mx_RoomSettingsDialog_bridgesIcon::before { + // This icon is pants, please improve :) + mask-image: url('$(res)/img/feather-customised/bridge.svg'); +} + +.mx_RoomSettingsDialog_warningIcon::before { mask-image: url('$(res)/img/feather-customised/warning-triangle.svg'); } @@ -42,3 +47,12 @@ limitations under the License. padding-left: 40px; padding-right: 80px; } + +// show a different AvatarSetting placeholder for RoomProfileSettings which is basically a clone of ProfileSettings +.mx_RoomSettingsDialog .mx_AvatarSetting_avatar .mx_AvatarSetting_avatarPlaceholder::before { + mask: url("$(res)/img/feather-customised/image.svg"); + mask-repeat: no-repeat; + mask-size: 36px; + mask-position: center; +} + diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss new file mode 100644 index 0000000000..a1793cc75e --- /dev/null +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -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; + } + } +} diff --git a/res/css/views/dialogs/_RoomUpgradeWarningDialog.scss b/res/css/views/dialogs/_RoomUpgradeWarningDialog.scss new file mode 100644 index 0000000000..5b9978eba0 --- /dev/null +++ b/res/css/views/dialogs/_RoomUpgradeWarningDialog.scss @@ -0,0 +1,37 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RoomUpgradeWarningDialog { + max-width: 38vw; + width: 38vw; +} + +.mx_RoomUpgradeWarningDialog .mx_SettingsFlag { + font-weight: 700; + + .mx_ToggleSwitch { + display: inline-block; + vertical-align: middle; + margin-left: 8px; + float: right; + } + + .mx_SettingsFlag_label { + display: inline-block; + vertical-align: middle; + } +} + diff --git a/res/css/views/dialogs/_SetEmailDialog.scss b/res/css/views/dialogs/_SetEmailDialog.scss index 588f10c9cb..9d09a208df 100644 --- a/res/css/views/dialogs/_SetEmailDialog.scss +++ b/res/css/views/dialogs/_SetEmailDialog.scss @@ -31,6 +31,3 @@ limitations under the License. box-shadow: none; border: 1px solid $accent-color; } - -.mx_SetEmailDialog_email_input_placeholder { -} diff --git a/res/css/views/dialogs/_SetPasswordDialog.scss b/res/css/views/dialogs/_SetPasswordDialog.scss index 28a8b7c9d7..325ff6c6ed 100644 --- a/res/css/views/dialogs/_SetPasswordDialog.scss +++ b/res/css/views/dialogs/_SetPasswordDialog.scss @@ -21,7 +21,6 @@ limitations under the License. color: $primary-fg-color; background-color: $primary-bg-color; font-size: 15px; - width: 100%; max-width: 280px; margin-bottom: 10px; } diff --git a/res/css/views/dialogs/_BugReportDialog.scss b/res/css/views/dialogs/_SlashCommandHelpDialog.scss similarity index 68% rename from res/css/views/dialogs/_BugReportDialog.scss rename to res/css/views/dialogs/_SlashCommandHelpDialog.scss index 90ef55b945..786a28deef 100644 --- a/res/css/views/dialogs/_BugReportDialog.scss +++ b/res/css/views/dialogs/_SlashCommandHelpDialog.scss @@ -1,5 +1,5 @@ /* -Copyright 2017 OpenMarket Ltd +Copyright Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,12 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_BugReportDialog .mx_Field { - flex: 1; +.mx_SlashCommandHelpDialog .mx_SlashCommandHelpDialog_headerRow h2 { + margin-bottom: 2px; } -.mx_BugReportDialog_field_input { - // TODO: We should really apply this to all .mx_Field inputs. - // See https://github.com/vector-im/riot-web/issues/9344. - flex: 1; +.mx_SlashCommandHelpDialog .mx_Dialog_content { + margin-top: 12px; + margin-bottom: 34px; } diff --git a/res/css/views/dialogs/_TabbedIntegrationManagerDialog.scss b/res/css/views/dialogs/_TabbedIntegrationManagerDialog.scss new file mode 100644 index 0000000000..0ab59c44a7 --- /dev/null +++ b/res/css/views/dialogs/_TabbedIntegrationManagerDialog.scss @@ -0,0 +1,62 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_TabbedIntegrationManagerDialog .mx_Dialog { + width: 60%; + height: 70%; + overflow: hidden; + padding: 0; + max-width: initial; + max-height: initial; + position: relative; +} + +.mx_TabbedIntegrationManagerDialog_container { + // Full size of the dialog, whatever it is + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + + .mx_TabbedIntegrationManagerDialog_currentManager { + width: 100%; + height: 100%; + border-top: 1px solid $accent-color; + + iframe { + background-color: #fff; + border: 0; + width: 100%; + height: 100%; + } + } +} + +.mx_TabbedIntegrationManagerDialog_tab { + display: inline-block; + border: 1px solid $accent-color; + border-bottom: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + padding: 10px 8px; + margin-right: 5px; +} + +.mx_TabbedIntegrationManagerDialog_currentTab { + background-color: $accent-color; + color: $accent-fg-color; +} diff --git a/res/css/views/dialogs/_TermsDialog.scss b/res/css/views/dialogs/_TermsDialog.scss new file mode 100644 index 0000000000..beb507e778 --- /dev/null +++ b/res/css/views/dialogs/_TermsDialog.scss @@ -0,0 +1,48 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/* + * To avoid visual glitching of two modals stacking briefly, we customise the + * terms dialog sizing when it will appear for the integration manager so that + * it gets the same basic size as the IM's own modal. + */ +.mx_TermsDialog_forIntegrationManager .mx_Dialog { + width: 60%; + height: 70%; + box-sizing: border-box; +} + +.mx_TermsDialog_termsTableHeader { + font-weight: bold; + text-align: left; +} + +.mx_TermsDialog_termsTable { + font-size: 12px; + width: 100%; +} + +.mx_TermsDialog_service, .mx_TermsDialog_summary { + padding-right: 10px; +} + +.mx_TermsDialog_link { + display: inline-block; + mask-image: url('$(res)/img/external-link.svg'); + background-color: $accent-color; + width: 10px; + height: 10px; +} diff --git a/res/css/views/dialogs/_UnknownDeviceDialog.scss b/res/css/views/dialogs/_UnknownDeviceDialog.scss index 02e0fb1fe5..2b0f8dceca 100644 --- a/res/css/views/dialogs/_UnknownDeviceDialog.scss +++ b/res/css/views/dialogs/_UnknownDeviceDialog.scss @@ -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 { diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 9665ee06b4..4d831d7858 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -17,34 +17,38 @@ limitations under the License. // ICONS // ========================================================== -.mx_UserSettingsDialog_settingsIcon:before { +.mx_UserSettingsDialog_settingsIcon::before { mask-image: url('$(res)/img/feather-customised/settings.svg'); } -.mx_UserSettingsDialog_voiceIcon:before { +.mx_UserSettingsDialog_voiceIcon::before { mask-image: url('$(res)/img/feather-customised/phone.svg'); } -.mx_UserSettingsDialog_bellIcon:before { +.mx_UserSettingsDialog_bellIcon::before { mask-image: url('$(res)/img/feather-customised/notifications.svg'); } -.mx_UserSettingsDialog_preferencesIcon:before { +.mx_UserSettingsDialog_preferencesIcon::before { mask-image: url('$(res)/img/feather-customised/sliders.svg'); } -.mx_UserSettingsDialog_securityIcon:before { +.mx_UserSettingsDialog_securityIcon::before { mask-image: url('$(res)/img/feather-customised/lock.svg'); } -.mx_UserSettingsDialog_helpIcon:before { +.mx_UserSettingsDialog_helpIcon::before { mask-image: url('$(res)/img/feather-customised/help-circle.svg'); } -.mx_UserSettingsDialog_labsIcon:before { +.mx_UserSettingsDialog_labsIcon::before { mask-image: url('$(res)/img/feather-customised/flag.svg'); } -.mx_UserSettingsDialog_flairIcon:before { +.mx_UserSettingsDialog_mjolnirIcon::before { + mask-image: url('$(res)/img/feather-customised/face.svg'); +} + +.mx_UserSettingsDialog_flairIcon::before { mask-image: url('$(res)/img/feather-customised/flair.svg'); } diff --git a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss index 3f4c8d2da4..b9babd05f5 100644 --- a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss @@ -20,8 +20,8 @@ limitations under the License. } .mx_CreateKeyBackupDialog_primaryContainer { - /*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/ - padding: 20px + /* FIXME: plinth colour in new theme(s). background-color: $accent-color; */ + padding: 20px; } .mx_CreateKeyBackupDialog_primaryContainer::after { @@ -32,7 +32,7 @@ limitations under the License. .mx_CreateKeyBackupDialog_passPhraseContainer { display: flex; - align-items: start; + align-items: flex-start; } .mx_CreateKeyBackupDialog_passPhraseHelp { @@ -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 + } +} diff --git a/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss b/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss index f905516bd4..05ce158413 100644 --- a/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss +++ b/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss @@ -23,7 +23,7 @@ limitations under the License. padding-left: 45px; padding-bottom: 10px; - &:before { + &::before { mask: url("$(res)/img/e2e/lock-warning-filled.svg"); mask-repeat: no-repeat; background-color: $primary-fg-color; diff --git a/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss index 612c921038..9cba8e0da9 100644 --- a/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_RestoreKeyBackupDialog.scss @@ -1,5 +1,6 @@ /* Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,10 +14,14 @@ 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_RestoreKeyBackupDialog_keyStatus { + height: 30px; +} + .mx_RestoreKeyBackupDialog_primaryContainer { - /*FIXME: plinth colour in new theme(s). background-color: $accent-color;*/ - padding: 20px + /* FIXME: plinth colour in new theme(s). background-color: $accent-color; */ + padding: 20px; } .mx_RestoreKeyBackupDialog_passPhraseInput, diff --git a/res/css/views/dialogs/secretstorage/_AccessSecretStorageDialog.scss b/res/css/views/dialogs/secretstorage/_AccessSecretStorageDialog.scss new file mode 100644 index 0000000000..db11e91bdb --- /dev/null +++ b/res/css/views/dialogs/secretstorage/_AccessSecretStorageDialog.scss @@ -0,0 +1,34 @@ +/* +Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_AccessSecretStorageDialog_keyStatus { + height: 30px; +} + +.mx_AccessSecretStorageDialog_primaryContainer { + /* FIXME: plinth colour in new theme(s). background-color: $accent-color; */ + padding: 20px; +} + +.mx_AccessSecretStorageDialog_passPhraseInput, +.mx_AccessSecretStorageDialog_recoveryKeyInput { + width: 300px; + border: 1px solid $accent-color; + border-radius: 5px; + padding: 10px; +} + diff --git a/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss b/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss new file mode 100644 index 0000000000..a9ebd54b31 --- /dev/null +++ b/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss @@ -0,0 +1,116 @@ +/* +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. +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_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; +} + +.mx_CreateSecretStorageDialog_primaryContainer { + /* FIXME: plinth colour in new theme(s). background-color: $accent-color; */ + padding-top: 20px; +} + +.mx_CreateSecretStorageDialog_primaryContainer::after { + content: ""; + clear: both; + display: block; +} + +.mx_CreateSecretStorageDialog_passPhraseContainer { + display: flex; + align-items: flex-start; +} + +.mx_Field.mx_CreateSecretStorageDialog_passPhraseField { + margin-top: 0px; +} + +.mx_CreateSecretStorageDialog_passPhraseHelp { + flex: 1; + height: 64px; + margin-left: 20px; + font-size: 80%; +} + +.mx_CreateSecretStorageDialog_passPhraseHelp progress { + width: 100%; +} + +.mx_CreateSecretStorageDialog_passPhraseMatch { + width: 200px; + margin-left: 20px; +} + +.mx_CreateSecretStorageDialog_recoveryKeyHeader { + margin-bottom: 1em; +} + +.mx_CreateSecretStorageDialog_recoveryKeyContainer { + display: flex; +} + +.mx_CreateSecretStorageDialog_recoveryKey { + width: 262px; + padding: 20px; + color: $info-plinth-fg-color; + background-color: $info-plinth-bg-color; + margin-right: 12px; +} + +.mx_CreateSecretStorageDialog_recoveryKeyButtons { + flex: 1; + display: flex; + align-items: center; +} + +.mx_CreateSecretStorageDialog_recoveryKeyButtons .mx_AccessibleButton { + margin-right: 10px; +} + +.mx_CreateSecretStorageDialog_recoveryKeyButtons button { + flex: 1; + white-space: nowrap; +} diff --git a/res/css/views/directory/_NetworkDropdown.scss b/res/css/views/directory/_NetworkDropdown.scss index 8d5fa5dc7b..106392f880 100644 --- a/res/css/views/directory/_NetworkDropdown.scss +++ b/res/css/views/directory/_NetworkDropdown.scss @@ -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; +} diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index fe1f283009..b87071745d 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -14,10 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_AccessibleButton:focus { - outline: 0; -} - .mx_AccessibleButton { cursor: pointer; } @@ -79,3 +75,22 @@ limitations under the License. color: $button-danger-disabled-fg-color; background-color: $button-danger-disabled-bg-color; } + +.mx_AccessibleButton_kind_link { + color: $button-link-fg-color; + background-color: $button-link-bg-color; +} + +.mx_AccessibleButton_kind_link.mx_AccessibleButton_disabled { + opacity: 0.4; +} + +.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_link_sm { + padding: 5px 12px; + color: $button-link-fg-color; + background-color: $button-link-bg-color; +} + +.mx_AccessibleButton_kind_link_sm.mx_AccessibleButton_disabled { + opacity: 0.4; +} diff --git a/res/css/views/elements/_AddressSelector.scss b/res/css/views/elements/_AddressSelector.scss index 9871a7e881..dd78fcc0f0 100644 --- a/res/css/views/elements/_AddressSelector.scss +++ b/res/css/views/elements/_AddressSelector.scss @@ -20,9 +20,8 @@ limitations under the License. width: 485px; max-height: 116px; overflow-y: auto; - border-radius: 3px; - background-color: $primary-bg-color; - border: solid 1px $accent-color; + border-radius: 3px; + border: solid 1px $accent-color; cursor: pointer; } diff --git a/res/css/views/elements/_DirectorySearchBox.scss b/res/css/views/elements/_DirectorySearchBox.scss index ef944f6fa0..75ef3fbabd 100644 --- a/res/css/views/elements/_DirectorySearchBox.scss +++ b/res/css/views/elements/_DirectorySearchBox.scss @@ -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 { diff --git a/res/css/views/elements/_Dropdown.scss b/res/css/views/elements/_Dropdown.scss index 2a59393499..102ac56bf9 100644 --- a/res/css/views/elements/_Dropdown.scss +++ b/res/css/views/elements/_Dropdown.scss @@ -51,13 +51,6 @@ limitations under the License. background: $primary-fg-color; } -.mx_Dropdown_input > .mx_Dropdown_option { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1; -} - .mx_Dropdown_option { height: 35px; line-height: 35px; @@ -65,6 +58,13 @@ limitations under the License. padding-right: 8px; } +.mx_Dropdown_input > .mx_Dropdown_option { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; +} + .mx_Dropdown_option div { overflow: hidden; text-overflow: ellipsis; diff --git a/res/css/views/elements/_EditableItemList.scss b/res/css/views/elements/_EditableItemList.scss index be96d811d3..ef60f006cc 100644 --- a/res/css/views/elements/_EditableItemList.scss +++ b/res/css/views/elements/_EditableItemList.scss @@ -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,18 +43,19 @@ limitations under the License. .mx_EditableItem_promptText { margin-right: 10px; + order: 2; } .mx_EditableItem_confirmBtn { margin-right: 5px; } -.mx_EditableItemList_newItem .mx_Field input { - // Use 100% of the space available for the input, but don't let the 10px - // padding on either side of the input to push it out of alignment. - width: calc(100% - 20px); +.mx_EditableItem_item { + flex: auto 1 0; + order: 1; } .mx_EditableItemList_label { margin-bottom: 5px; -} \ No newline at end of file +} + diff --git a/res/css/views/elements/_ErrorBoundary.scss b/res/css/views/elements/_ErrorBoundary.scss new file mode 100644 index 0000000000..e46ba69a7c --- /dev/null +++ b/res/css/views/elements/_ErrorBoundary.scss @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_ErrorBoundary { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.mx_ErrorBoundary_body { + display: flex; + flex-direction: column; + max-width: 400px; + align-items: center; + + .mx_AccessibleButton { + margin-top: 5px; + } +} diff --git a/res/css/views/elements/_MemberEventListSummary.scss b/res/css/views/elements/_EventListSummary.scss similarity index 75% rename from res/css/views/elements/_MemberEventListSummary.scss rename to res/css/views/elements/_EventListSummary.scss index 02ecb5d84a..99a5c06a5f 100644 --- a/res/css/views/elements/_MemberEventListSummary.scss +++ b/res/css/views/elements/_EventListSummary.scss @@ -14,28 +14,28 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MemberEventListSummary { +.mx_EventListSummary { position: relative; } -.mx_TextualEvent.mx_MemberEventListSummary_summary { +.mx_TextualEvent.mx_EventListSummary_summary { font-size: 14px; display: inline-flex; } -.mx_MemberEventListSummary_avatars { +.mx_EventListSummary_avatars { display: inline-block; margin-right: 8px; padding-top: 8px; line-height: 12px; } -.mx_MemberEventListSummary_avatars .mx_BaseAvatar { +.mx_EventListSummary_avatars .mx_BaseAvatar { margin-right: -4px; cursor: pointer; } -.mx_MemberEventListSummary_toggle { +.mx_EventListSummary_toggle { color: $accent-color; cursor: pointer; float: right; @@ -43,29 +43,29 @@ limitations under the License. margin-top: 8px; } -.mx_MemberEventListSummary_line { +.mx_EventListSummary_line { border-bottom: 1px solid $primary-hairline-color; margin-left: 63px; line-height: 30px; } .mx_MatrixChat_useCompactLayout { - .mx_MemberEventListSummary { + .mx_EventListSummary { font-size: 13px; .mx_EventTile_line { line-height: 20px; } } - .mx_MemberEventListSummary_line { + .mx_EventListSummary_line { line-height: 22px; } - .mx_MemberEventListSummary_toggle { + .mx_EventListSummary_toggle { margin-top: 3px; } - .mx_TextualEvent.mx_MemberEventListSummary_summary { + .mx_TextualEvent.mx_EventListSummary_summary { font-size: 13px; } } diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index 147bb3b471..b260d4b097 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -18,6 +18,8 @@ limitations under the License. .mx_Field { display: flex; + flex: 1; + min-width: 0; position: relative; margin: 1em 0; border-radius: 4px; @@ -29,6 +31,10 @@ limitations under the License. border-right: 1px solid $input-border-color; } +.mx_Field_postfix { + border-left: 1px solid $input-border-color; +} + .mx_Field input, .mx_Field select, .mx_Field textarea { @@ -42,6 +48,8 @@ limitations under the License. padding: 8px 9px; color: $primary-fg-color; background-color: $primary-bg-color; + flex: 1; + min-width: 0; } .mx_Field select { @@ -107,12 +115,12 @@ limitations under the License. max-width: calc(100% - 20px); // 100% of parent minus margin and padding } +.mx_Field_labelAlwaysTopLeft label, +.mx_Field select + label /* Always show a select's label on top to not collide with the value */, .mx_Field input:focus + label, .mx_Field input:not(:placeholder-shown) + label, .mx_Field textarea:focus + label, -.mx_Field textarea:not(:placeholder-shown) + label, -.mx_Field select + label /* Always show a select's label on top to not collide with the value */, -.mx_Field_labelAlwaysTopLeft label { +.mx_Field textarea:not(:placeholder-shown) + label { transition: font-size 0.25s ease-out 0s, color 0.25s ease-out 0s, diff --git a/res/css/views/elements/_FormButton.scss b/res/css/views/elements/_FormButton.scss new file mode 100644 index 0000000000..1483fe2091 --- /dev/null +++ b/res/css/views/elements/_FormButton.scss @@ -0,0 +1,36 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_FormButton { + line-height: 16px; + padding: 5px 15px; + font-size: 12px; + height: min-content; + + &:not(:last-child) { + margin-right: 8px; + } + + &.mx_AccessibleButton_kind_primary { + color: $accent-color; + background-color: $accent-bg-color; + } + + &.mx_AccessibleButton_kind_danger { + color: $notice-primary-color; + background-color: $notice-primary-bg-color; + } +} diff --git a/res/css/views/elements/_IconButton.scss b/res/css/views/elements/_IconButton.scss new file mode 100644 index 0000000000..d8ebbeb65e --- /dev/null +++ b/res/css/views/elements/_IconButton.scss @@ -0,0 +1,55 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_IconButton { + width: 32px; + height: 32px; + border-radius: 100%; + background-color: $accent-bg-color; + // don't shrink or grow if in a flex container + flex: 0 0 auto; + + &.mx_AccessibleButton_disabled { + background-color: none; + + &::before { + background-color: lightgrey; + } + } + + &:hover { + opacity: 90%; + } + + &::before { + content: ""; + display: block; + width: 100%; + height: 100%; + mask-repeat: no-repeat; + mask-position: center; + mask-size: 55%; + background-color: $accent-color; + } + + &.mx_IconButton_icon_check::before { + mask-image: url('$(res)/img/feather-customised/check.svg'); + } + + &.mx_IconButton_icon_edit::before { + mask-image: url('$(res)/img/feather-customised/edit.svg'); + } +} diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 88cf2ce8ba..67b0d6d7df 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -128,8 +128,8 @@ limitations under the License. } .mx_ImageView_link { - color: $lightbox-fg-color ! important; - text-decoration: none ! important; + color: $lightbox-fg-color !important; + text-decoration: none !important; } .mx_ImageView_button { diff --git a/res/css/views/elements/_InteractiveTooltip.scss b/res/css/views/elements/_InteractiveTooltip.scss new file mode 100644 index 0000000000..17a76436e8 --- /dev/null +++ b/res/css/views/elements/_InteractiveTooltip.scss @@ -0,0 +1,91 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_InteractiveTooltip_wrapper { + position: fixed; + z-index: 5000; +} + +.mx_InteractiveTooltip { + border-radius: 3px; + background-color: $interactive-tooltip-bg-color; + color: $interactive-tooltip-fg-color; + position: absolute; + font-size: 10px; + font-weight: 600; + padding: 6px; + z-index: 5001; +} + +.mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_top { + top: 10px; // 8px chevron + 2px spacing +} + +.mx_InteractiveTooltip_chevron_top { + position: absolute; + left: calc(50% - 8px); + top: -8px; + width: 0; + height: 0; + border-left: 8px solid transparent; + border-bottom: 8px solid $interactive-tooltip-bg-color; + border-right: 8px solid transparent; +} + +// Adapted from https://codyhouse.co/blog/post/css-rounded-triangles-with-clip-path +// by Sebastiano Guerriero (@guerriero_se) +@supports (clip-path: polygon(0% 0%, 100% 100%, 0% 100%)) { + .mx_InteractiveTooltip_chevron_top { + height: 16px; + width: 16px; + background-color: inherit; + border: none; + clip-path: polygon(0% 0%, 100% 100%, 0% 100%); + transform: rotate(135deg); + border-radius: 0 0 0 3px; + top: calc(-8px / 1.414); // sqrt(2) because of rotation + } +} + +.mx_InteractiveTooltip.mx_InteractiveTooltip_withChevron_bottom { + bottom: 10px; // 8px chevron + 2px spacing +} + +.mx_InteractiveTooltip_chevron_bottom { + position: absolute; + left: calc(50% - 8px); + bottom: -8px; + width: 0; + height: 0; + border-left: 8px solid transparent; + border-top: 8px solid $interactive-tooltip-bg-color; + border-right: 8px solid transparent; +} + +// Adapted from https://codyhouse.co/blog/post/css-rounded-triangles-with-clip-path +// by Sebastiano Guerriero (@guerriero_se) +@supports (clip-path: polygon(0% 0%, 100% 100%, 0% 100%)) { + .mx_InteractiveTooltip_chevron_bottom { + height: 16px; + width: 16px; + background-color: inherit; + border: none; + clip-path: polygon(0% 0%, 100% 100%, 0% 100%); + transform: rotate(-45deg); + border-radius: 0 0 0 3px; + bottom: calc(-8px / 1.414); // sqrt(2) because of rotation + } +} diff --git a/res/css/views/elements/_ManageIntegsButton.scss b/res/css/views/elements/_ManageIntegsButton.scss index 7c91b9dbde..fe8c76003b 100644 --- a/res/css/views/elements/_ManageIntegsButton.scss +++ b/res/css/views/elements/_ManageIntegsButton.scss @@ -16,6 +16,7 @@ limitations under the License. .mx_ManageIntegsButton_error { position: relative; + float: right; cursor: not-allowed; } @@ -25,18 +26,6 @@ limitations under the License. top: -5px; } -.mx_ManageIntegsButton_error { - float: right; -} - -.mx_ManageIntegsButton_error .mx_ManageIntegsButton_errorPopup { - display: none; -} - -.mx_ManageIntegsButton_error:hover .mx_ManageIntegsButton_errorPopup { - display: inline; -} - .mx_ManageIntegsButton_errorPopup { position: absolute; top: 110%; @@ -51,3 +40,11 @@ limitations under the License. text-align: center; z-index: 1000; } + +.mx_ManageIntegsButton_error .mx_ManageIntegsButton_errorPopup { + display: none; +} + +.mx_ManageIntegsButton_error:hover .mx_ManageIntegsButton_errorPopup { + display: inline; +} diff --git a/res/css/views/elements/_PowerSelector.scss b/res/css/views/elements/_PowerSelector.scss index 69f3a8eebb..799f6f246e 100644 --- a/res/css/views/elements/_PowerSelector.scss +++ b/res/css/views/elements/_PowerSelector.scss @@ -20,6 +20,5 @@ limitations under the License. .mx_PowerSelector .mx_Field select, .mx_PowerSelector .mx_Field input { - width: 100%; box-sizing: border-box; } diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index cf2c066589..5066ee10f3 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -13,10 +13,9 @@ padding-left: 5px; } -.mx_EventTile_body .mx_UserPill, -.mx_EventTile_body .mx_RoomPill, -.mx_EventTile_body .mx_GroupPill { - cursor: pointer; +a.mx_Pill { + word-break: break-all; + display: inline; } /* More specific to override `.markdown-body a` text-decoration */ @@ -33,7 +32,7 @@ } .mx_UserPill_selected { - background-color: $accent-color ! important; + background-color: $accent-color !important; } /* More specific to override `.markdown-body a` color */ @@ -64,6 +63,12 @@ padding-right: 5px; } +.mx_EventTile_body .mx_UserPill, +.mx_EventTile_body .mx_RoomPill, +.mx_EventTile_body .mx_GroupPill { + cursor: pointer; +} + .mx_UserPill .mx_BaseAvatar, .mx_RoomPill .mx_BaseAvatar, .mx_GroupPill .mx_BaseAvatar, @@ -79,7 +84,7 @@ .mx_Markdown_ITALIC { font-style: italic; -/* + /* // interestingly, *not* using the explicit italic font // variant seems yield better results. @@ -87,7 +92,7 @@ // https://github.com/google/fonts/issues/1726 transform: skewX(-14deg); display: inline-block; -*/ + */ } .mx_Markdown_CODE { diff --git a/res/css/views/elements/_RoomAliasField.scss b/res/css/views/elements/_RoomAliasField.scss new file mode 100644 index 0000000000..0fe53b2766 --- /dev/null +++ b/res/css/views/elements/_RoomAliasField.scss @@ -0,0 +1,56 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_RoomAliasField { + // if parent is a flex container, this allows the + // width to be as wide as needed, and not 100% + flex: 0 1 auto; + display: flex; + align-items: stretch; + min-width: 0; + max-width: 100%; + + input { + width: 150px; + padding-left: 0; + padding-right: 0; + } + + input::placeholder { + color: $greyed-fg-color; + font-weight: normal; + } + + .mx_Field_prefix, .mx_Field_postfix { + color: $greyed-fg-color; + border-left: none; + border-right: none; + font-weight: 600; + padding: 9px 10px; + flex: 0 0 auto; + } + + .mx_Field_postfix { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + // this allows the domain name to show + // as long as it doesn't make the input shrink + // if it's too big, it shows an ellipsis + // 180: 28 for prefix, 152 for input + max-width: calc(100% - 180px); + } +} diff --git a/res/css/views/elements/_Spinner.scss b/res/css/views/elements/_Spinner.scss index aea5737918..01b4f23c2c 100644 --- a/res/css/views/elements/_Spinner.scss +++ b/res/css/views/elements/_Spinner.scss @@ -25,4 +25,4 @@ limitations under the License. .mx_MatrixChat_middlePanel .mx_Spinner { height: auto; -} \ No newline at end of file +} diff --git a/res/css/views/elements/_TextWithTooltip.scss b/res/css/views/elements/_TextWithTooltip.scss index 5b34e7a3be..a7f9cb7483 100644 --- a/res/css/views/elements/_TextWithTooltip.scss +++ b/res/css/views/elements/_TextWithTooltip.scss @@ -16,4 +16,4 @@ limitations under the License. .mx_TextWithTooltip_tooltip { display: none; -} \ No newline at end of file +} diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 1bb3a74ab1..1f4445b88c 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -44,10 +44,10 @@ limitations under the License. top: 0; } -.mx_ToggleSwitch:not(.mx_ToggleSwitch_on) > .mx_ToggleSwitch_ball { - left: 2px; -} - .mx_ToggleSwitch_on > .mx_ToggleSwitch_ball { left: 23px; // 48px switch - 20px ball - 5px padding = 23px } + +.mx_ToggleSwitch:not(.mx_ToggleSwitch_on) > .mx_ToggleSwitch_ball { + left: 2px; +} diff --git a/res/css/views/elements/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss index 3a6b6fb936..cc4eb409df 100644 --- a/res/css/views/elements/_Tooltip.scss +++ b/res/css/views/elements/_Tooltip.scss @@ -36,8 +36,8 @@ limitations under the License. border-bottom: 7px solid transparent; } -.mx_Tooltip_chevron:after { - content:''; +.mx_Tooltip_chevron::after { + content: ''; width: 0; height: 0; border-top: 6px solid transparent; @@ -55,7 +55,7 @@ limitations under the License. border-radius: 4px; box-shadow: 4px 4px 12px 0 $menu-box-shadow-color; background-color: $menu-bg-color; - z-index: 2000; + z-index: 4000; // Higher than dialogs so tooltips can be used in dialogs padding: 10px; pointer-events: none; line-height: 14px; diff --git a/res/css/views/elements/_ToolTipButton.scss b/res/css/views/elements/_TooltipButton.scss similarity index 87% rename from res/css/views/elements/_ToolTipButton.scss rename to res/css/views/elements/_TooltipButton.scss index c496e67515..6ea36c800e 100644 --- a/res/css/views/elements/_ToolTipButton.scss +++ b/res/css/views/elements/_TooltipButton.scss @@ -1,5 +1,6 @@ /* Copyright 2017 New Vector Ltd. +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_ToolTipButton { +.mx_TooltipButton { display: inline-block; width: 11px; height: 11px; @@ -33,17 +34,17 @@ limitations under the License. cursor: pointer; } -.mx_ToolTipButton:hover { +.mx_TooltipButton:hover { opacity: 1.0; } -.mx_ToolTipButton_container { +.mx_TooltipButton_container { position: relative; top: -18px; left: 4px; } -.mx_ToolTipButton_helpText { +.mx_TooltipButton_helpText { width: 400px; text-align: start; line-height: 17px !important; diff --git a/res/css/views/emojipicker/_EmojiPicker.scss b/res/css/views/emojipicker/_EmojiPicker.scss new file mode 100644 index 0000000000..5d9b3f2687 --- /dev/null +++ b/res/css/views/emojipicker/_EmojiPicker.scss @@ -0,0 +1,229 @@ +/* +Copyright 2019 Tulir Asokan + +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_EmojiPicker { + width: 340px; + height: 450px; + + border-radius: 4px; + + display: flex; + flex-direction: column; +} + +.mx_EmojiPicker_body { + flex: 1; + overflow-y: scroll; + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.2) transparent; +} + +.mx_EmojiPicker_header { + padding: 4px 8px 0; + border-bottom: 1px solid $message-action-bar-border-color; +} + +.mx_EmojiPicker_anchor { + border: none; + padding: 8px 8px 6px; + border-bottom: 2px solid transparent; + background-color: transparent; + border-radius: 4px 4px 0 0; + + width: 36px; + height: 38px; + + &:not(:disabled) { + cursor: pointer; + } + + &:not(:disabled):hover { + background-color: $focus-bg-color; + border-bottom: 2px solid $button-bg-color; + } +} + +.mx_EmojiPicker_anchor::before { + background-color: $primary-fg-color; + content: ''; + display: inline-block; + mask-size: 100%; + mask-repeat: no-repeat; + width: 100%; + height: 100%; +} + +.mx_EmojiPicker_anchor:disabled::before { + background-color: $focus-bg-color; +} + +.mx_EmojiPicker_anchor_activity::before { mask-image: url('$(res)/img/emojipicker/activity.svg'); } +.mx_EmojiPicker_anchor_custom::before { mask-image: url('$(res)/img/emojipicker/custom.svg'); } +.mx_EmojiPicker_anchor_flags::before { mask-image: url('$(res)/img/emojipicker/flags.svg'); } +.mx_EmojiPicker_anchor_foods::before { mask-image: url('$(res)/img/emojipicker/foods.svg'); } +.mx_EmojiPicker_anchor_nature::before { mask-image: url('$(res)/img/emojipicker/nature.svg'); } +.mx_EmojiPicker_anchor_objects::before { mask-image: url('$(res)/img/emojipicker/objects.svg'); } +.mx_EmojiPicker_anchor_people::before { mask-image: url('$(res)/img/emojipicker/people.svg'); } +.mx_EmojiPicker_anchor_places::before { mask-image: url('$(res)/img/emojipicker/places.svg'); } +.mx_EmojiPicker_anchor_recent::before { mask-image: url('$(res)/img/emojipicker/recent.svg'); } +.mx_EmojiPicker_anchor_symbols::before { mask-image: url('$(res)/img/emojipicker/symbols.svg'); } + +.mx_EmojiPicker_anchor_visible { + border-bottom: 2px solid $button-bg-color; +} + +.mx_EmojiPicker_search { + margin: 8px; + border-radius: 4px; + border: 1px solid $input-border-color; + background-color: $primary-bg-color; + display: flex; + + input { + flex: 1; + border: none; + padding: 8px 12px; + border-radius: 4px 0; + } + + button { + border: none; + background-color: inherit; + margin: 0; + padding: 8px; + align-self: center; + width: 32px; + height: 32px; + } +} + +.mx_EmojiPicker_search_clear { + cursor: pointer; +} + +.mx_EmojiPicker_search_icon { + width: 16px; + margin: 8px; +} + +.mx_EmojiPicker_search_icon:not(.mx_EmojiPicker_search_clear) { + pointer-events: none; +} + +.mx_EmojiPicker_search_icon::after { + mask: url('$(res)/img/emojipicker/search.svg') no-repeat; + mask-size: 100%; + background-color: $primary-fg-color; + content: ''; + display: inline-block; + width: 100%; + height: 100%; +} + +.mx_EmojiPicker_search_clear::after { + mask-image: url('$(res)/img/emojipicker/delete.svg'); +} + +.mx_EmojiPicker_category { + padding: 0 12px; + display: flex; + flex-direction: column; + align-items: center; +} + +.mx_EmojiPicker_category_label { + width: 304px; +} + +.mx_EmojiPicker_list { + width: 304px; + padding: 0; + margin: 0; +} + +.mx_EmojiPicker_item_wrapper { + display: inline-block; + list-style: none; + width: 38px; + cursor: pointer; +} + +.mx_EmojiPicker_item { + display: inline-block; + font-size: 20px; + padding: 5px; + width: 100%; + height: 100%; + box-sizing: border-box; + text-align: center; + border-radius: 4px; + + &:hover { + background-color: $focus-bg-color; + } +} + +.mx_EmojiPicker_item_selected { + color: rgba(0, 0, 0, .5); + border: 1px solid $input-valid-border-color; + padding: 4px; +} + +.mx_EmojiPicker_category_label, .mx_EmojiPicker_preview_name { + font-size: 16px; + font-weight: 600; + margin: 0; +} + +.mx_EmojiPicker_footer { + border-top: 1px solid $message-action-bar-border-color; + height: 72px; + + display: flex; + align-items: center; +} + +.mx_EmojiPicker_preview_emoji { + font-size: 32px; + padding: 8px 16px; +} + +.mx_EmojiPicker_preview_text { + display: flex; + flex-direction: column; +} + +.mx_EmojiPicker_name { + text-transform: capitalize; +} + +.mx_EmojiPicker_shortcode { + color: $light-fg-color; + font-size: 14px; + + &::before, &::after { + content: ":"; + } +} + +.mx_EmojiPicker_quick { + flex-direction: column; + justify-content: space-around; +} + +.mx_EmojiPicker_quick_header .mx_EmojiPicker_name { + margin-right: 4px; +} diff --git a/res/css/views/globals/_MatrixToolbar.scss b/res/css/views/globals/_MatrixToolbar.scss index 1791d619ae..5fdf572f99 100644 --- a/res/css/views/globals/_MatrixToolbar.scss +++ b/res/css/views/globals/_MatrixToolbar.scss @@ -44,10 +44,9 @@ limitations under the License. flex: 1; } -.mx_MatrixToolbar_link -{ - color: $accent-fg-color ! important; - text-decoration: underline ! important; +.mx_MatrixToolbar_link { + color: $accent-fg-color !important; + text-decoration: underline !important; cursor: pointer; } diff --git a/res/css/views/messages/_CreateEvent.scss b/res/css/views/messages/_CreateEvent.scss index adf16d6c4a..d45645863f 100644 --- a/res/css/views/messages/_CreateEvent.scss +++ b/res/css/views/messages/_CreateEvent.scss @@ -37,6 +37,3 @@ limitations under the License. .mx_CreateEvent_header { font-weight: bold; } - -.mx_CreateEvent_link { -} diff --git a/res/css/views/messages/_DateSeparator.scss b/res/css/views/messages/_DateSeparator.scss index f8738f10e3..935ee1aba3 100644 --- a/res/css/views/messages/_DateSeparator.scss +++ b/res/css/views/messages/_DateSeparator.scss @@ -30,7 +30,7 @@ limitations under the License. border-bottom: 1px solid $panel-divider-color; } -.mx_DateSeparator > date { +.mx_DateSeparator > div { margin: 0 25px; flex: 0 0 auto; } diff --git a/res/css/views/messages/_MImageBody.scss b/res/css/views/messages/_MImageBody.scss index 86bc022829..547b16e9ad 100644 --- a/res/css/views/messages/_MImageBody.scss +++ b/res/css/views/messages/_MImageBody.scss @@ -59,3 +59,42 @@ limitations under the License. color: $imagebody-giflabel-color; pointer-events: none; } + +.mx_HiddenImagePlaceholder { + position: absolute; + left: 0; + top: 0; + bottom: 0; + right: 0; + + // To center the text in the middle of the frame + display: flex; + align-items: center; + justify-content: center; + text-align: center; + + cursor: pointer; + background-color: $header-panel-bg-color; + + .mx_HiddenImagePlaceholder_button { + color: $accent-color; + + span.mx_HiddenImagePlaceholder_eye { + margin-right: 8px; + + background-color: $accent-color; + mask-image: url('$(res)/img/feather-customised/eye.svg'); + display: inline-block; + width: 18px; + height: 14px; + } + + span:not(.mx_HiddenImagePlaceholder_eye) { + vertical-align: text-bottom; + } + } +} + +.mx_EventTile:hover .mx_HiddenImagePlaceholder { + background-color: $primary-bg-color; +} diff --git a/res/css/views/messages/_MStickerBody.scss b/res/css/views/messages/_MStickerBody.scss index e4977bcc34..162ee7da86 100644 --- a/res/css/views/messages/_MStickerBody.scss +++ b/res/css/views/messages/_MStickerBody.scss @@ -22,3 +22,14 @@ limitations under the License. position: absolute; top: 50%; } + +.mx_MStickerBody_hidden { + max-width: 220px; + text-decoration: none; + text-align: center; + + // To center the text in the middle of the frame + display: flex; + align-items: center; + justify-content: center; +} diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 749cfeebe6..c032051c36 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,11 +27,30 @@ limitations under the License. top: -18px; right: 8px; user-select: none; + // Ensure the action bar appears above over things, like the read marker. + z-index: 1; + + // Adds a previous event safe area so that you can't accidentally hover the + // previous event while trying to mouse into the action bar or from the + // react button to its tooltip. + &::before { + content: ''; + position: absolute; + // tooltip safe mousing area + tooltip overhang + + // action bar + action bar offset from event + width: calc(10px + 48px + 100% + 8px); + // safe area + action bar + height: calc(20px + 100%); + top: -20px; + left: -58px; + z-index: -1; + cursor: initial; + } > * { + white-space: nowrap; display: inline-block; position: relative; - width: 27px; border: 1px solid $message-action-bar-border-color; margin-left: -1px; @@ -53,6 +73,11 @@ limitations under the License. } } + +.mx_MessageActionBar_maskButton { + width: 27px; +} + .mx_MessageActionBar_maskButton::after { content: ''; position: absolute; @@ -65,6 +90,10 @@ limitations under the License. background-color: $message-action-bar-fg-color; } +.mx_MessageActionBar_reactButton::after { + mask-image: url('$(res)/img/react.svg'); +} + .mx_MessageActionBar_replyButton::after { mask-image: url('$(res)/img/reply.svg'); } diff --git a/res/css/views/messages/_MessageTimestamp.scss b/res/css/views/messages/_MessageTimestamp.scss index e21189c59e..e5c228aa68 100644 --- a/res/css/views/messages/_MessageTimestamp.scss +++ b/res/css/views/messages/_MessageTimestamp.scss @@ -15,4 +15,6 @@ limitations under the License. */ .mx_MessageTimestamp { + color: $event-timestamp-color; + font-size: 10px; } diff --git a/res/css/views/dialogs/_RestoreKeyBackupDialog.scss b/res/css/views/messages/_MjolnirBody.scss similarity index 85% rename from res/css/views/dialogs/_RestoreKeyBackupDialog.scss rename to res/css/views/messages/_MjolnirBody.scss index 69e00c416a..2760adfd7e 100644 --- a/res/css/views/dialogs/_RestoreKeyBackupDialog.scss +++ b/res/css/views/messages/_MjolnirBody.scss @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,6 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_RestoreKeyBackupDialog_keyStatus { - height: 30px; +.mx_MjolnirBody { + opacity: 0.4; } diff --git a/res/css/views/messages/_ReactionsRow.scss b/res/css/views/messages/_ReactionsRow.scss index fb66ffbb8c..57c02ed3e5 100644 --- a/res/css/views/messages/_ReactionsRow.scss +++ b/res/css/views/messages/_ReactionsRow.scss @@ -16,4 +16,19 @@ limitations under the License. .mx_ReactionsRow { margin: 6px 0; + color: $primary-fg-color; +} + +.mx_ReactionsRow_showAll { + text-decoration: none; + font-size: 10px; + font-weight: 600; + margin-left: 6px; + vertical-align: top; + + &:hover, + &:link, + &:visited { + color: $accent-color; + } } diff --git a/res/css/views/messages/_ReactionsRowButton.scss b/res/css/views/messages/_ReactionsRowButton.scss index 3c6d019b30..e54201d963 100644 --- a/res/css/views/messages/_ReactionsRowButton.scss +++ b/res/css/views/messages/_ReactionsRowButton.scss @@ -15,7 +15,7 @@ limitations under the License. */ .mx_ReactionsRowButton { - display: inline-block; + display: inline-flex; height: 20px; line-height: 21px; margin-right: 6px; @@ -35,3 +35,11 @@ limitations under the License. border-color: $reaction-row-button-selected-border-color; } } + +.mx_ReactionsRowButton_content { + max-width: 100px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + padding-right: 4px; +} diff --git a/res/css/views/messages/_RoomAvatarEvent.scss b/res/css/views/messages/_RoomAvatarEvent.scss index 9adce42eef..3db18c1d10 100644 --- a/res/css/views/messages/_RoomAvatarEvent.scss +++ b/res/css/views/messages/_RoomAvatarEvent.scss @@ -23,4 +23,4 @@ limitations under the License. display: inline; position: relative; top: 5px; -} \ No newline at end of file +} diff --git a/res/css/views/messages/_common_CryptoEvent.scss b/res/css/views/messages/_common_CryptoEvent.scss new file mode 100644 index 0000000000..98e1e97e39 --- /dev/null +++ b/res/css/views/messages/_common_CryptoEvent.scss @@ -0,0 +1,79 @@ +/* +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_cryptoEvent { + + display: grid; + grid-template-columns: 24px minmax(0, 1fr) min-content; + + &.mx_cryptoEvent_icon::after { + grid-column: 1; + grid-row: 1 / 3; + width: 16px; + height: 16px; + content: ""; + background-image: url("$(res)/img/e2e/normal.svg"); + background-repeat: no-repeat; + background-size: 100%; + margin-top: 4px; + } + + &.mx_cryptoEvent_icon_verified::after { + background-image: url("$(res)/img/e2e/verified.svg"); + } + + &.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_cryptoEvent_title { + font-weight: 600; + font-size: 15px; + grid-column: 2; + grid-row: 1; + } + + .mx_cryptoEvent_subtitle { + grid-column: 2; + grid-row: 2; + } + + .mx_cryptoEvent_state, .mx_cryptoEvent_subtitle { + font-size: 12px; + } + + .mx_cryptoEvent_state, .mx_cryptoEvent_buttons { + grid-column: 3; + grid-row: 1 / 3; + } + + .mx_cryptoEvent_buttons { + align-items: center; + display: flex; + } + + .mx_cryptoEvent_state { + width: 130px; + padding: 10px 20px; + margin: auto 0; + text-align: center; + color: $notice-secondary-color; + } +} diff --git a/res/css/views/right_panel/_EncryptionInfo.scss b/res/css/views/right_panel/_EncryptionInfo.scss new file mode 100644 index 0000000000..e13b1b6802 --- /dev/null +++ b/res/css/views/right_panel/_EncryptionInfo.scss @@ -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; + } +} diff --git a/res/css/views/right_panel/_UserInfo.scss b/res/css/views/right_panel/_UserInfo.scss new file mode 100644 index 0000000000..0e4b1bda9e --- /dev/null +++ b/res/css/views/right_panel/_UserInfo.scss @@ -0,0 +1,287 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_UserInfo { + display: flex; + flex-direction: column; + flex: 1; + overflow-y: auto; + font-size: 12px; + + .mx_UserInfo_cancel { + cursor: pointer; + 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 { + font-size: 18px; + font-weight: 600; + margin: 18px 0 0 0; + } + + .mx_UserInfo_container { + 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 { + width: 154px; + } + + .mx_RoomTile_badge { + display: none; + } + + .mx_RoomTile_name { + width: 160px; + } + + .mx_UserInfo_avatar { + margin: 24px 32px 0 32px; + } + + .mx_UserInfo_avatar > div { + max-width: 30vh; + margin: 0 auto; + transition: 0.5s; + } + + .mx_UserInfo_avatar > div > div { + /* use padding-top instead of height to make this element square, + as the % in padding is a % of the width (including margin, + that's why we had to put the margin to center on a parent div), + and not a % of the parent height. */ + padding-top: 100%; + position: relative; + } + + .mx_UserInfo_avatar > div > div * { + border-radius: 100%; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + + .mx_UserInfo_avatar .mx_BaseAvatar_initial { + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + + // override the calculated sizes so that the letter isn't HUGE + font-size: 56px !important; + width: 100% !important; + transition: font-size 0.5s; + } + + .mx_UserInfo_avatar .mx_BaseAvatar.mx_BaseAvatar_image { + cursor: zoom-in; + } + + h3 { + text-transform: uppercase; + color: $notice-secondary-color; + font-weight: bold; + font-size: 12px; + margin: 4px 0; + } + + p { + margin: 5px 0; + } + + .mx_UserInfo_profile { + text-align: center; + + h2 { + font-size: 18px; + line-height: 25px; + flex: 1; + 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; + } + } + + .mx_UserInfo_profileStatus { + margin-top: 12px; + } + } + + .mx_UserInfo_memberDetails .mx_UserInfo_profileField { + display: flex; + justify-content: center; + align-items: center; + + margin: 6px 0; + + .mx_IconButton, .mx_Spinner { + margin-left: 20px; + width: 16px; + height: 16px; + + &::before { + mask-size: 80%; + } + } + + .mx_UserInfo_roleDescription { + display: flex; + justify-content: center; + align-items: center; + // try to make it the same height as the dropdown + margin: 11px 0 12px 0; + + .mx_IconButton { + margin-left: 6px; + } + } + + .mx_Field { + margin: 0; + } + } + + .mx_UserInfo_field { + cursor: pointer; + color: $accent-color; + line-height: 16px; + margin: 8px 0; + + &.mx_UserInfo_destructive { + color: $warning-color; + } + } + + .mx_UserInfo_statusMessage { + font-size: 11px; + opacity: 0.5; + overflow: hidden; + white-space: nowrap; + text-overflow: clip; + } + + .mx_UserInfo_scrollContainer { + flex: 1 1 0; + padding-bottom: 16px; + } + + .mx_UserInfo_container:not(.mx_UserInfo_separator) { + padding-top: 16px; + padding-bottom: 0; + + > :not(h3) { + margin-left: 8px; + } + } + + .mx_UserInfo_devices { + .mx_UserInfo_device { + display: flex; + margin: 8px 0; + + + &.mx_UserInfo_device_verified { + .mx_UserInfo_device_trusted { + color: $accent-color; + } + } + &.mx_UserInfo_device_unverified { + .mx_UserInfo_device_trusted { + color: $warning-color; + } + } + + .mx_UserInfo_device_name { + flex: 1; + margin-right: 5px; + word-break: break-word; + } + } + + // both for icon in expand button and device item + .mx_E2EIcon { + // don't squeeze + flex: 0 0 auto; + margin: 2px 5px 0 0; + width: 12px; + height: 12px; + } + + .mx_UserInfo_expand { + display: flex; + margin-top: 11px; + } + } + + .mx_UserInfo_wideButton { + display: block; + 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 + } +} diff --git a/res/css/views/right_panel/_VerificationPanel.scss b/res/css/views/right_panel/_VerificationPanel.scss new file mode 100644 index 0000000000..459622b277 --- /dev/null +++ b/res/css/views/right_panel/_VerificationPanel.scss @@ -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; + } + } +} diff --git a/res/css/views/room_settings/_AliasSettings.scss b/res/css/views/room_settings/_AliasSettings.scss index d4ae58e5b0..f8d92e7828 100644 --- a/res/css/views/room_settings/_AliasSettings.scss +++ b/res/css/views/room_settings/_AliasSettings.scss @@ -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; + } +} diff --git a/res/css/views/room_settings/_ColorSettings.scss b/res/css/views/room_settings/_ColorSettings.scss index 39b087653d..fc6a4443ad 100644 --- a/res/css/views/room_settings/_ColorSettings.scss +++ b/res/css/views/room_settings/_ColorSettings.scss @@ -28,7 +28,7 @@ limitations under the License. position: absolute; left: 10px; top: 4px; - cursor: default ! important; + cursor: default !important; } .mx_ColorSettings_roomColorPrimary { diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index db38eebfca..a3fe573ad0 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -42,7 +43,6 @@ $AppsDrawerBodyHeight: 273px; .mx_AddWidget_button { order: 2; cursor: pointer; - padding-right: 12px; padding: 0; margin: 5px auto 5px auto; color: $accent-color; @@ -153,40 +153,12 @@ $AppsDrawerBodyHeight: 273px; background-color: $accent-color; } -.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_reload { - mask-image: url('$(res)/img/feather-customised/widget/refresh.svg'); - mask-size: 100%; -} - .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout { mask-image: url('$(res)/img/feather-customised/widget/external-link.svg'); } -.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_snapshot { - mask-image: url('$(res)/img/feather-customised/widget/camera.svg'); -} - -.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_edit { - mask-image: url('$(res)/img/feather-customised/widget/edit.svg'); -} - -.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_delete { - mask-image: url('$(res)/img/feather-customised/widget/bin.svg'); - background-color: $warning-color; -} - -.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_cancel { - mask-image: url('$(res)/img/feather-customised/widget/x-circle.svg'); -} - -/* delete ? */ -.mx_AppTileMenuBarWidget { - cursor: pointer; - width: 10px; - height: 10px; - padding: 1px; - transition-duration: 500ms; - border: 1px solid transparent; +.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_menu { + mask-image: url('$(res)/img/icon_context.svg'); } .mx_AppTileMenuBarWidgetDelete { @@ -198,7 +170,7 @@ $AppsDrawerBodyHeight: 273px; border-radius: 2px; } -.mx_AppTileBody{ +.mx_AppTileBody { height: $AppsDrawerBodyHeight; width: 100%; overflow: hidden; @@ -234,7 +206,7 @@ $AppsDrawerBodyHeight: 273px; background-color: $lightbox-bg-color; border: 1px solid rgba(0, 0, 0, 0); width: 200px; - box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); transition: 0.3s; border-radius: 3px; margin: 5px; @@ -248,7 +220,7 @@ $AppsDrawerBodyHeight: 273px; .mx_AppIconTile:hover { border: 1px solid $accent-color; - box-shadow: 0 0 10px 5px rgba(200,200,200,0.5); + box-shadow: 0 0 10px 5px rgba(200, 200, 200, 0.5); } .mx_AppIconTile_content { @@ -270,9 +242,8 @@ $AppsDrawerBodyHeight: 273px; .mx_AppIconTile_image { padding: 10px; - width: 75%; - max-width:100px; - max-height:100px; + max-width: 100px; + max-height: 100px; width: auto; height: auto; } @@ -295,44 +266,61 @@ form.mx_Custom_Widget_Form div { .mx_AppPermissionWarning { text-align: center; - background-color: $primary-bg-color; + background-color: $widget-menu-bar-bg-color; display: flex; height: 100%; flex-direction: column; justify-content: center; align-items: center; + font-size: 16px; } -.mx_AppPermissionWarningImage { - margin: 10px 0; +.mx_AppPermissionWarning_row { + margin-bottom: 12px; } -.mx_AppPermissionWarningImage img { - width: 100px; +.mx_AppPermissionWarning_smallText { + font-size: 12px; } -.mx_AppPermissionWarningText { - max-width: 400px; - margin: 10px auto 10px auto; - color: $primary-fg-color; +.mx_AppPermissionWarning_bolder { + font-weight: 600; } -.mx_AppPermissionWarningTextLabel { - font-weight: bold; - display: block; +.mx_AppPermissionWarning h4 { + margin: 0; + padding: 0; } -.mx_AppPermissionWarningTextURL { - color: $accent-color; +.mx_AppPermissionWarning_helpIcon { + margin-top: 1px; + margin-right: 2px; + width: 10px; + height: 10px; + display: inline-block; } -.mx_AppPermissionButton { - border: none; - padding: 5px 20px; - border-radius: 5px; - background-color: $button-bg-color; - color: $button-fg-color; - cursor: pointer; +.mx_AppPermissionWarning_helpIcon::before { + display: inline-block; + background-color: $accent-color; + mask-repeat: no-repeat; + mask-size: 12px; + width: 12px; + height: 12px; + mask-position: center; + content: ''; + vertical-align: middle; + mask-image: url('$(res)/img/feather-customised/help-circle.svg'); +} + +.mx_AppPermissionWarning_tooltip { + @mixin mx_Tooltip_dark; + + ul { + list-style-position: inside; + padding-left: 2px; + margin-left: 0; + } } .mx_AppLoading { @@ -361,11 +349,11 @@ form.mx_Custom_Widget_Form div { } @keyframes mx_AppLoading_spinner_fadeIn_animation { - from { opacity: 0 } - to { opacity: 1 } + from { opacity: 0; } + to { opacity: 1; } } .mx_AppLoading iframe { - display: none; + display: none; } diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index 3bc0ea25a4..e5316f5a46 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -8,18 +8,13 @@ border-bottom: none; border-radius: 4px 4px 0 0; max-height: 50vh; - overflow: auto + overflow: auto; } .mx_Autocomplete_ProviderSection { border-bottom: 1px solid $primary-hairline-color; } -.mx_Autocomplete_Completion_container_pill { - margin: 12px; - display: flex; -} - /* a "block" completion takes up a whole line */ .mx_Autocomplete_Completion_block { height: 34px; @@ -32,7 +27,7 @@ } .mx_Autocomplete_Completion_block * { - margin: 0 3px; + margin: 0 3px; } .mx_Autocomplete_Completion_pill { @@ -50,11 +45,27 @@ margin: 0 3px; } +/* styling for common completion elements */ +.mx_Autocomplete_Completion_subtitle { + font-style: italic; + flex: 1; +} + +.mx_Autocomplete_Completion_description { + color: gray; +} + +.mx_Autocomplete_Completion_container_pill { + margin: 12px; + display: flex; + flex-flow: wrap; +} + .mx_Autocomplete_Completion_container_truncate { .mx_Autocomplete_Completion_title, .mx_Autocomplete_Completion_subtitle, .mx_Autocomplete_Completion_description { - /* Ellipsis for long names/subtitles/descriptions*/ + /* Ellipsis for long names/subtitles/descriptions */ max-width: 150px; white-space: nowrap; overflow: hidden; @@ -62,13 +73,6 @@ } } -/* container for pill-style completions */ -.mx_Autocomplete_Completion_container_pill { - margin: 12px; - display: flex; - flex-flow: wrap; -} - .mx_Autocomplete_Completion.selected, .mx_Autocomplete_Completion:hover { background: $selected-color; @@ -81,14 +85,3 @@ font-weight: 400; opacity: 0.4; } - -/* styling for common completion elements */ -.mx_Autocomplete_Completion_subtitle { - font-style: italic; - flex: 1; -} - -.mx_Autocomplete_Completion_description { - color: gray; -} - diff --git a/res/css/views/rooms/_BasicMessageComposer.scss b/res/css/views/rooms/_BasicMessageComposer.scss new file mode 100644 index 0000000000..ce519b1ea7 --- /dev/null +++ b/res/css/views/rooms/_BasicMessageComposer.scss @@ -0,0 +1,76 @@ +/* +Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_BasicMessageComposer { + position: relative; + + .mx_BasicMessageComposer_inputEmpty > :first-child::before { + content: var(--placeholder); + opacity: 0.333; + width: 0; + height: 0; + overflow: visible; + display: inline-block; + pointer-events: none; + white-space: nowrap; + } + + @keyframes visualbell { + from { background-color: $visual-bell-bg-color; } + to { background-color: $primary-bg-color; } + } + + &.mx_BasicMessageComposer_input_error { + animation: 0.2s visualbell; + } + + .mx_BasicMessageComposer_input { + white-space: pre-wrap; + word-wrap: break-word; + outline: none; + overflow-x: hidden; + + span.mx_UserPill, span.mx_RoomPill { + padding-left: 21px; + position: relative; + + // avatar psuedo element + &::before { + position: absolute; + left: 2px; + top: 2px; + content: var(--avatar-letter); + width: 16px; + height: 16px; + background: var(--avatar-background), $avatar-bg-color; + color: $avatar-initial-color; + background-repeat: no-repeat; + background-size: 16px; + border-radius: 8px; + text-align: center; + font-weight: normal; + line-height: 16px; + font-size: 10.4px; + } + } + } + + .mx_BasicMessageComposer_AutoCompleteWrapper { + position: relative; + height: 0; + } +} diff --git a/res/css/views/rooms/_E2EIcon.scss b/res/css/views/rooms/_E2EIcon.scss index 84a16611de..584ea17433 100644 --- a/res/css/views/rooms/_E2EIcon.scss +++ b/res/css/views/rooms/_E2EIcon.scss @@ -15,19 +15,35 @@ limitations under the License. */ .mx_E2EIcon { - width: 25px; - height: 25px; - mask-repeat: no-repeat; - mask-position: center 0; + width: 16px; + height: 16px; margin: 0 9px; + position: relative; + display: block; } -.mx_E2EIcon_verified { - mask-image: url('$(res)/img/e2e/lock-verified.svg'); - background-color: $accent-color; +.mx_E2EIcon_warning::after, +.mx_E2EIcon_normal::after, +.mx_E2EIcon_verified::after { + content: ""; + display: block; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-repeat: no-repeat; + background-size: contain; } -.mx_E2EIcon_warning { - mask-image: url('$(res)/img/e2e/lock-warning.svg'); - background-color: $warning-color; +.mx_E2EIcon_warning::after { + background-image: url('$(res)/img/e2e/warning.svg'); +} + +.mx_E2EIcon_normal::after { + background-image: url('$(res)/img/e2e/normal.svg'); +} + +.mx_E2EIcon_verified::after { + background-image: url('$(res)/img/e2e/verified.svg'); } diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/rooms/_EditMessageComposer.scss similarity index 57% rename from res/css/views/elements/_MessageEditor.scss rename to res/css/views/rooms/_EditMessageComposer.scss index e721b267fa..214bfc4a1a 100644 --- a/res/css/views/elements/_MessageEditor.scss +++ b/res/css/views/rooms/_EditMessageComposer.scss @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,8 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MessageEditor { - border-radius: 4px; +.mx_EditMessageComposer { + padding: 3px; // this is to try not make the text move but still have some // padding around and in the editor. @@ -23,43 +24,20 @@ limitations under the License. margin: -7px -10px -5px -10px; overflow: visible !important; // override mx_EventTile_content - .mx_MessageEditor_editor { + + .mx_BasicMessageComposer_input { border-radius: 4px; border: solid 1px $primary-hairline-color; background-color: $primary-bg-color; - padding: 3px 6px; - white-space: pre-wrap; - word-wrap: break-word; - outline: none; max-height: 200px; - overflow-x: auto; + padding: 3px 6px; - span.mx_UserPill, span.mx_RoomPill { - padding-left: 21px; - position: relative; - - // avatar psuedo element - &::before { - position: absolute; - left: 2px; - top: 2px; - content: var(--avatar-letter); - width: 16px; - height: 16px; - background: var(--avatar-background), $avatar-bg-color; - color: $avatar-initial-color; - background-repeat: no-repeat; - background-size: 16px; - border-radius: 8px; - text-align: center; - font-weight: normal; - line-height: 16px; - font-size: 10.4px; - } + &:focus { + border-color: $accent-color-50pct; } } - .mx_MessageEditor_buttons { + .mx_EditMessageComposer_buttons { display: flex; flex-direction: row; justify-content: flex-end; @@ -77,14 +55,9 @@ limitations under the License. padding: 5px 40px; } } - - .mx_MessageEditor_AutoCompleteWrapper { - position: relative; - height: 0; - } } -.mx_EventTile_last .mx_MessageEditor_buttons { +.mx_EventTile_last .mx_EditMessageComposer_buttons { position: static; margin-right: -147px; } diff --git a/res/css/views/rooms/_EntityTile.scss b/res/css/views/rooms/_EntityTile.scss index 44528a5624..a2867de3a7 100644 --- a/res/css/views/rooms/_EntityTile.scss +++ b/res/css/views/rooms/_EntityTile.scss @@ -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,10 +85,7 @@ limitations under the License. .mx_EntityTile_details { overflow: hidden; -} - -.mx_EntityTile:not(.mx_EntityTile_noHover):hover .mx_EntityTile_name { - font-size: 13px; + flex: 1; } .mx_EntityTile_ellipsis .mx_EntityTile_name { @@ -102,20 +101,17 @@ limitations under the License. .mx_EntityTile_unavailable .mx_EntityTile_avatar, .mx_EntityTile_unavailable .mx_EntityTile_name, .mx_EntityTile_offline_beenactive .mx_EntityTile_avatar, -.mx_EntityTile_offline_beenactive .mx_EntityTile_name -{ +.mx_EntityTile_offline_beenactive .mx_EntityTile_name { opacity: 0.5; } .mx_EntityTile_offline_neveractive .mx_EntityTile_avatar, -.mx_EntityTile_offline_neveractive .mx_EntityTile_name -{ +.mx_EntityTile_offline_neveractive .mx_EntityTile_name { opacity: 0.25; } .mx_EntityTile_unknown .mx_EntityTile_avatar, -.mx_EntityTile_unknown .mx_EntityTile_name -{ +.mx_EntityTile_unknown .mx_EntityTile_name { opacity: 0.25; } @@ -126,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; +} diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 7d8a58f7a8..1cf9fb6edb 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -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. @@ -22,6 +23,15 @@ limitations under the License. position: relative; } +.mx_EventTile_bubble { + background-color: $dark-panel-bg-color; + padding: 10px; + border-radius: 5px; + margin: 10px auto; + max-width: 75%; + box-sizing: border-box; +} + .mx_EventTile.mx_EventTile_info { padding-top: 0px; } @@ -56,13 +66,17 @@ limitations under the License. color: $primary-fg-color; font-size: 14px; display: inline-block; /* anti-zalgo, with overflow hidden */ - overflow-y: hidden; + overflow: hidden; cursor: pointer; padding-left: 65px; /* left gutter */ padding-bottom: 0px; padding-top: 0px; margin: 0px; line-height: 17px; + /* the next three lines, along with overflow hidden, truncate long display names */ + white-space: nowrap; + text-overflow: ellipsis; + max-width: calc(100% - 65px); } .mx_EventTile .mx_SenderProfile .mx_Flair { @@ -89,8 +103,6 @@ limitations under the License. display: block; visibility: hidden; white-space: nowrap; - color: $event-timestamp-color; - font-size: 10px; left: 0px; width: 46px; /* 8 + 30 (avatar) + 8 */ text-align: center; @@ -100,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; @@ -110,6 +120,30 @@ limitations under the License. line-height: 22px; } +.mx_RoomView_timeline_rr_enabled, +// on ELS we need the margin to allow interaction with the expand/collapse button which is normally in the RR gutter +.mx_EventListSummary { + .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; + + .mx_EventTile_line { + margin-right: 0px; + grid-column: 1 / 3; + padding: 0; + } + + .mx_EventTile_msgOption { + grid-column: 2; + } +} + .mx_EventTile_reply { margin-right: 10px; } @@ -120,8 +154,32 @@ limitations under the License. /* HACK to override line-height which is already marked important elsewhere */ .mx_EventTile_bigEmoji.mx_EventTile_bigEmoji { - font-size: 48px ! important; - line-height: 57px ! important; + font-size: 48px !important; + line-height: 57px !important; +} + +.mx_MessagePanel_alwaysShowTimestamps .mx_MessageTimestamp { + visibility: visible; +} + +.mx_EventTile_selected > div > 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_last > div > a > .mx_MessageTimestamp, +.mx_EventTile:hover > div > a > .mx_MessageTimestamp, +.mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp, +.mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp { + visibility: visible; +} + +.mx_EventTile:hover .mx_MessageActionBar, +.mx_EventTile.mx_EventTile_actionBarFocused .mx_MessageActionBar, +[data-whatinput='keyboard'] .mx_EventTile:focus-within .mx_MessageActionBar, +.mx_EventTile.focus-visible:focus-within .mx_MessageActionBar { + visibility: visible; } /* this is used for the tile for the event which is selected via the URL. @@ -133,9 +191,22 @@ limitations under the License. background-color: $event-selected-color; } +.mx_EventTile_highlight, +.mx_EventTile_highlight .markdown-body { + color: $event-highlight-fg-color; + + .mx_EventTile_line { + background-color: $event-highlight-bg-color; + } +} + +.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line { + padding-left: 78px; +} + .mx_EventTile:hover .mx_EventTile_line, -.mx_EventTile.mx_EventTile_actionBarFocused .mx_EventTile_line -{ +.mx_EventTile.mx_EventTile_actionBarFocused .mx_EventTile_line, +.mx_EventTile.focus-visible:focus-within .mx_EventTile_line { background-color: $event-selected-color; } @@ -154,7 +225,7 @@ limitations under the License. } .mx_EventTile_encrypting { - color: $event-encrypting-color ! important; + color: $event-encrypting-color !important; } .mx_EventTile_sending { @@ -172,25 +243,30 @@ limitations under the License. .mx_EventTile_redacted .mx_EventTile_line .mx_UnknownBody, .mx_EventTile_redacted .mx_EventTile_reply .mx_UnknownBody { + --lozenge-color: $event-redacted-fg-color; + --lozenge-border-color: $event-redacted-border-color; display: block; - width: 100%; height: 22px; width: 250px; border-radius: 11px; - background: repeating-linear-gradient( - -45deg, - $event-redacted-fg-color, - $event-redacted-fg-color 3px, - transparent 3px, - transparent 6px - ); - box-shadow: 0px 0px 3px $event-redacted-border-color inset; + background: + repeating-linear-gradient( + -45deg, + var(--lozenge-color), + var(--lozenge-color) 3px, + transparent 3px, + transparent 6px + ); + box-shadow: 0px 0px 3px var(--lozenge-border-color) inset; } -.mx_EventTile_highlight, -.mx_EventTile_highlight .markdown-body - { - color: $warning-color; +.mx_EventTile_sending.mx_EventTile_redacted .mx_UnknownBody { + opacity: 0.4; +} + +div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody { + --lozenge-color: $event-notsent-color; + --lozenge-border-color: $event-notsent-color; } .mx_EventTile_contextual { @@ -215,27 +291,6 @@ limitations under the License. text-decoration: none; } -// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) -.mx_EventTile_last > div > a > .mx_MessageTimestamp, -.mx_EventTile:hover > div > a > .mx_MessageTimestamp, -.mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp { - visibility: visible; -} - -.mx_MessagePanel_alwaysShowTimestamps .mx_MessageTimestamp { - visibility: visible; -} - -.mx_EventTile_selected > div > a > .mx_MessageTimestamp { - left: 3px; - width: auto; -} - -.mx_EventTile:hover .mx_MessageActionBar, -.mx_EventTile.mx_EventTile_actionBarFocused .mx_MessageActionBar { - visibility: visible; -} - .mx_EventTile_readAvatars { position: relative; display: inline-block; @@ -243,6 +298,7 @@ limitations under the License. height: 14px; top: 29px; user-select: none; + z-index: 1; } .mx_EventTile_continuation .mx_EventTile_readAvatars, @@ -280,33 +336,52 @@ limitations under the License. overflow-y: hidden; } -/* End to end encryption stuff */ -.mx_EventTile:hover .mx_EventTile_e2eIcon { - opacity: 1; +/* Spoiler stuff */ +.mx_EventTile_spoiler { + cursor: pointer; +} + +.mx_EventTile_spoiler_reason { + color: $event-timestamp-color; + font-size: 11px; +} + +.mx_EventTile_spoiler_content { + filter: blur(5px) saturate(0.1) sepia(1); + transition-duration: 0.5s; +} + +.mx_EventTile_spoiler.visible > .mx_EventTile_spoiler_content { + filter: none; } .mx_EventTile_e2eIcon { - display: block; position: absolute; - top: 8px; + top: 6px; left: 46px; width: 15px; height: 15px; - cursor: pointer; - mask-size: 14px; - mask-repeat: no-repeat; - mask-position: 0; + display: block; + bottom: 0; + right: 0; opacity: 0.2; + background-repeat: no-repeat; + background-size: contain; } .mx_EventTile_e2eIcon_undecryptable, .mx_EventTile_e2eIcon_unverified { - mask-image: url('$(res)/img/e2e/warning.svg'); - background-color: $warning-color; + background-image: url('$(res)/img/e2e/warning.svg'); + opacity: 1; +} + +.mx_EventTile_e2eIcon_unknown { + background-image: url('$(res)/img/e2e/warning.svg'); + opacity: 1; } .mx_EventTile_e2eIcon_unencrypted { - mask-image: url('$(res)/img/e2e/warning.svg'); - background-color: $composer-e2e-icon-color; + background-image: url('$(res)/img/e2e/warning.svg'); + opacity: 1; } .mx_EventTile_e2eIcon_hidden { @@ -352,39 +427,46 @@ limitations under the License. } .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; } -.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line, -.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line, -.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line { - padding-left: 78px; -} - .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line { border-left: $e2e-verified-color 5px solid; } + .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line { 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_unknown.mx_EventTile_info .mx_EventTile_line { + padding-left: 78px; +} + +/* End to end encryption stuff */ +.mx_EventTile:hover .mx_EventTile_e2eIcon { + opacity: 1; +} + // 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; } -/* -.mx_EventTile_verified .mx_EventTile_e2eIcon { - display: none; -} -*/ - // 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; } @@ -395,39 +477,46 @@ limitations under the License. color: $roomtopic-color; display: inline-block; margin-left: 9px; + cursor: pointer; } /* Various markdown overrides */ +.mx_EventTile_body pre { + border: 1px solid transparent; +} + .mx_EventTile_content .markdown-body { - font-family: inherit ! important; - white-space: normal ! important; - line-height: inherit ! important; + font-family: inherit !important; + white-space: normal !important; + line-height: inherit !important; color: inherit; // inherit the colour from the dark or light theme by default (but not for code blocks) font-size: 14px; -} -/* have to use overlay rather than auto otherwise Linux and Windows - Chrome gets very confused about vertical spacing: - https://github.com/vector-im/vector-web/issues/754 -*/ -.mx_EventTile_content .markdown-body pre { - overflow-x: overlay; - overflow-y: visible; - max-height: 30vh; -} - -.mx_EventTile_content .markdown-body code { - // deliberate constants as we're behind an invert filter - background-color: #f8f8f8; -} - -.mx_EventTile_content .markdown-body { pre, code { - font-family: $monospace-font-family ! important; + font-family: $monospace-font-family !important; // deliberate constants as we're behind an invert filter color: #333; } + + pre { + // have to use overlay rather than auto otherwise Linux and Windows + // Chrome gets very confused about vertical spacing: + // https://github.com/vector-im/vector-web/issues/754 + overflow-x: overlay; + overflow-y: visible; + max-height: 30vh; + } + + code { + // deliberate constants as we're behind an invert filter + background-color: #f8f8f8; + } +} + +.mx_EventTile:hover .mx_EventTile_body pre, +.mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre { + border: 1px solid #e5e5e5; // deliberate constant as we're behind an invert filter } .mx_EventTile_pre_container { @@ -448,17 +537,8 @@ limitations under the License. background-image: url($copy-button-url); } -.mx_EventTile_body pre { - border: 1px solid transparent; -} - -.mx_EventTile:hover .mx_EventTile_body pre -{ - border: 1px solid #e5e5e5; // deliberate constant as we're behind an invert filter -} - -.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton -{ +.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton, +.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton { visibility: visible; } @@ -467,19 +547,17 @@ limitations under the License. .mx_EventTile_content .markdown-body h3, .mx_EventTile_content .markdown-body h4, .mx_EventTile_content .markdown-body h5, -.mx_EventTile_content .markdown-body h6 -{ - font-family: inherit ! important; +.mx_EventTile_content .markdown-body h6 { + font-family: inherit !important; color: inherit; } /* Make h1 and h2 the same size as h3. */ .mx_EventTile_content .markdown-body h1, -.mx_EventTile_content .markdown-body h2 -{ +.mx_EventTile_content .markdown-body h2 { font-size: 1.5em; - border-bottom: none ! important; // override GFM + border-bottom: none !important; // override GFM } .mx_EventTile_content .markdown-body a { @@ -488,7 +566,7 @@ limitations under the License. } .mx_EventTile_content .markdown-body .hljs { - display: inline ! important; + display: inline !important; } /* @@ -577,8 +655,8 @@ limitations under the License. } .mx_EventTile_content .markdown-body { - p, ul, ol, dl, blockquote, pre, table { - margin-bottom: 4px; // 1/4 of the non-compact margin-bottom - } + p, ul, ol, dl, blockquote, pre, table { + margin-bottom: 4px; // 1/4 of the non-compact margin-bottom + } } } diff --git a/res/css/views/rooms/_InviteOnlyIcon.scss b/res/css/views/rooms/_InviteOnlyIcon.scss new file mode 100644 index 0000000000..b71fd6348d --- /dev/null +++ b/res/css/views/rooms/_InviteOnlyIcon.scss @@ -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; + } +} diff --git a/res/css/views/rooms/_JumpToBottomButton.scss b/res/css/views/rooms/_JumpToBottomButton.scss index 968139671f..7f458092fb 100644 --- a/res/css/views/rooms/_JumpToBottomButton.scss +++ b/res/css/views/rooms/_JumpToBottomButton.scss @@ -55,7 +55,7 @@ limitations under the License. cursor: pointer; } -.mx_JumpToBottomButton_scrollDown:before { +.mx_JumpToBottomButton_scrollDown::before { content: ""; position: absolute; top: 0; diff --git a/res/css/views/rooms/_LinkPreviewWidget.scss b/res/css/views/rooms/_LinkPreviewWidget.scss index 4495b142e6..022cf3ed28 100644 --- a/res/css/views/rooms/_LinkPreviewWidget.scss +++ b/res/css/views/rooms/_LinkPreviewWidget.scss @@ -52,12 +52,18 @@ limitations under the License. } .mx_LinkPreviewWidget_cancel { - visibility: hidden; cursor: pointer; - flex: 0 0 40px; + width: 18px; + height: 18px; + + img { + flex: 0 0 40px; + visibility: hidden; + } } -.mx_LinkPreviewWidget:hover .mx_LinkPreviewWidget_cancel { +.mx_LinkPreviewWidget:hover .mx_LinkPreviewWidget_cancel img, +.mx_LinkPreviewWidget_cancel.focus-visible:focus img { visibility: visible; } diff --git a/res/css/views/rooms/_MemberDeviceInfo.scss b/res/css/views/rooms/_MemberDeviceInfo.scss index 3be6a0f7b4..15b4832dc5 100644 --- a/res/css/views/rooms/_MemberDeviceInfo.scss +++ b/res/css/views/rooms/_MemberDeviceInfo.scss @@ -17,7 +17,7 @@ limitations under the License. .mx_MemberDeviceInfo { display: flex; padding-bottom: 10px; - align-items: start; + align-items: flex-start; } .mx_MemberDeviceInfo_icon { @@ -25,6 +25,7 @@ limitations under the License. width: 12px; height: 12px; mask-repeat: no-repeat; + mask-size: 100%; } .mx_MemberDeviceInfo_icon_blacklisted { mask-image: url('$(res)/img/e2e/blacklisted.svg'); @@ -57,6 +58,7 @@ limitations under the License. } .mx_MemberDeviceInfo_deviceId { + word-break: break-word; font-size: 13px; } diff --git a/res/css/views/rooms/_MemberInfo.scss b/res/css/views/rooms/_MemberInfo.scss index c3b3ca2f7d..e3f746e9d3 100644 --- a/res/css/views/rooms/_MemberInfo.scss +++ b/res/css/views/rooms/_MemberInfo.scss @@ -43,6 +43,8 @@ limitations under the License. .mx_MemberInfo_name h2 { flex: 1; + overflow-x: auto; + max-height: 50px; } .mx_MemberInfo h2 { @@ -80,9 +82,6 @@ limitations under the License. display: block; } -.mx_MemberInfo_avatar .mx_BaseAvatar { -} - .mx_MemberInfo_avatar .mx_BaseAvatar.mx_BaseAvatar_image { cursor: zoom-in; } @@ -124,7 +123,7 @@ limitations under the License. } .mx_MemberInfo_createRoom_label { - width: initial ! important; + width: initial !important; cursor: pointer; } diff --git a/res/css/views/rooms/_MemberList.scss b/res/css/views/rooms/_MemberList.scss index 0b9c7e2368..6e4465583c 100644 --- a/res/css/views/rooms/_MemberList.scss +++ b/res/css/views/rooms/_MemberList.scss @@ -88,7 +88,7 @@ limitations under the License. } .mx_MemberList_invite.mx_AccessibleButton_disabled { - background-color: $greyed-fg-color;; + background-color: $greyed-fg-color; cursor: not-allowed; } @@ -97,5 +97,4 @@ limitations under the License. background-repeat: no-repeat; background-position: center left; padding-left: 25px; - } diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 708c29bb3e..a05b4c0c0e 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -23,10 +23,6 @@ limitations under the License. padding-left: 84px; } -.mx_MessageComposer_wrapper.mx_MessageComposer_hasE2EIcon { - padding-left: 109px; -} - .mx_MessageComposer_replaced_wrapper { margin-left: auto; margin-right: auto; @@ -78,7 +74,10 @@ limitations under the License. .mx_MessageComposer_e2eIcon.mx_E2EIcon { position: absolute; left: 60px; - background-color: $composer-e2e-icon-color; + margin-right: 0; // Counteract the E2EIcon class + margin-left: 3px; // Counteract the E2EIcon class + width: 15px; + height: 15px; } .mx_MessageComposer_noperm_error { @@ -104,7 +103,7 @@ limitations under the License. display: flex; flex-direction: column; min-height: 60px; - justify-content: start; + justify-content: flex-start; align-items: flex-start; font-size: 14px; margin-right: 6px; @@ -122,16 +121,15 @@ limitations under the License. // FIXME: rather unpleasant hack to get rid of

margins. // really we should be mixing in markdown-body from gfm.css instead .mx_MessageComposer_editor > :first-child { - margin-top: 0 ! important; + margin-top: 0 !important; } .mx_MessageComposer_editor > :last-child { - margin-bottom: 0 ! important; + margin-bottom: 0 !important; } -@keyframes visualbell -{ - from { background-color: #faa } - to { background-color: $primary-bg-color } +@keyframes visualbell { + from { background-color: $visual-bell-bg-color; } + to { background-color: $primary-bg-color; } } .mx_MessageComposer_input_error { @@ -181,34 +179,42 @@ limitations under the License. } .mx_MessageComposer_button { + position: relative; margin-right: 12px; cursor: pointer; - padding-top: 4px; height: 20px; width: 20px; - background-color: $composer-button-color; - mask-repeat: no-repeat; - mask-size: contain; - mask-position: center; + + &::before { + content: ''; + position: absolute; + + height: 20px; + width: 20px; + background-color: $composer-button-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; + } } -.mx_MessageComposer_upload { +.mx_MessageComposer_upload::before { mask-image: url('$(res)/img/feather-customised/paperclip.svg'); } -.mx_MessageComposer_hangup { +.mx_MessageComposer_hangup::before { mask-image: url('$(res)/img/hangup.svg'); } -.mx_MessageComposer_voicecall { +.mx_MessageComposer_voicecall::before { mask-image: url('$(res)/img/feather-customised/phone.svg'); } -.mx_MessageComposer_videocall { +.mx_MessageComposer_videocall::before { mask-image: url('$(res)/img/feather-customised/video.svg'); } -.mx_MessageComposer_stickers { +.mx_MessageComposer_stickers::before { mask-image: url('$(res)/img/feather-customised/face.svg'); } diff --git a/res/css/views/rooms/_MessageComposerFormatBar.scss b/res/css/views/rooms/_MessageComposerFormatBar.scss new file mode 100644 index 0000000000..1b5a21bed0 --- /dev/null +++ b/res/css/views/rooms/_MessageComposerFormatBar.scss @@ -0,0 +1,109 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MessageComposerFormatBar { + display: none; + width: calc(26px * 5); + height: 24px; + position: absolute; + cursor: pointer; + border-radius: 4px; + background-color: $message-action-bar-bg-color; + user-select: none; + // equal to z-index of mx_ReplyPreview and mx_RoomView_statusArea (1000) + // but as it appears after them in the DOM, will appear on top. + z-index: 1000; + + &.mx_MessageComposerFormatBar_shown { + display: block; + } + + > * { + white-space: nowrap; + display: inline-block; + position: relative; + border: 1px solid $message-action-bar-border-color; + margin-left: -1px; + + &:hover { + border-color: $message-action-bar-hover-border-color; + z-index: 1; + } + + &:first-child { + border-radius: 3px 0 0 3px; + } + + &:last-child { + border-radius: 0 3px 3px 0; + } + + &:only-child { + border-radius: 3px; + } + } + + .mx_MessageComposerFormatBar_button { + width: 27px; + height: 24px; + box-sizing: border-box; + } + + .mx_MessageComposerFormatBar_button::after { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + mask-repeat: no-repeat; + mask-position: center; + background-color: $message-action-bar-fg-color; + } + + .mx_MessageComposerFormatBar_buttonIconBold::after { + mask-image: url('$(res)/img/format/bold.svg'); + } + + .mx_MessageComposerFormatBar_buttonIconItalic::after { + mask-image: url('$(res)/img/format/italics.svg'); + } + + .mx_MessageComposerFormatBar_buttonIconStrikethrough::after { + mask-image: url('$(res)/img/format/strikethrough.svg'); + } + + .mx_MessageComposerFormatBar_buttonIconQuote::after { + mask-image: url('$(res)/img/format/quote.svg'); + } + + .mx_MessageComposerFormatBar_buttonIconCode::after { + mask-image: url('$(res)/img/format/code.svg'); + } +} + +.mx_MessageComposerFormatBar_buttonTooltip { + white-space: nowrap; + font-size: 13px; + font-weight: 600; + min-width: 54px; + text-align: center; + + .mx_MessageComposerFormatBar_tooltipShortcut { + font-size: 9px; + opacity: 0.7; + } +} diff --git a/res/css/views/rooms/_PinnedEventTile.scss b/res/css/views/rooms/_PinnedEventTile.scss index f7417272b6..030a76674a 100644 --- a/res/css/views/rooms/_PinnedEventTile.scss +++ b/res/css/views/rooms/_PinnedEventTile.scss @@ -44,6 +44,12 @@ limitations under the License. margin-right: 10px; } +.mx_PinnedEventTile_actions { + float: right; + margin-right: 10px; + display: none; +} + .mx_PinnedEventTile:hover .mx_PinnedEventTile_timestamp { display: inline-block; } @@ -52,12 +58,6 @@ limitations under the License. display: block; } -.mx_PinnedEventTile_actions { - float: right; - margin-right: 10px; - display: none; -} - .mx_PinnedEventTile_unpinButton { display: inline-block; cursor: pointer; @@ -74,4 +74,4 @@ limitations under the License. position: relative; top: 0; left: 0; -} \ No newline at end of file +} diff --git a/res/css/views/rooms/_PresenceLabel.scss b/res/css/views/rooms/_PresenceLabel.scss index 682c849cee..26ed1aa6a3 100644 --- a/res/css/views/rooms/_PresenceLabel.scss +++ b/res/css/views/rooms/_PresenceLabel.scss @@ -17,4 +17,4 @@ limitations under the License. .mx_PresenceLabel { font-size: 11px; opacity: 0.5; -} \ No newline at end of file +} diff --git a/res/css/views/rooms/_ReplyPreview.scss b/res/css/views/rooms/_ReplyPreview.scss index 5bf4adff27..4dc4cb2c40 100644 --- a/res/css/views/rooms/_ReplyPreview.scss +++ b/res/css/views/rooms/_ReplyPreview.scss @@ -24,7 +24,7 @@ limitations under the License. border-bottom: none; border-radius: 4px 4px 0 0; max-height: 50vh; - overflow: auto + overflow: auto; } .mx_ReplyPreview_section { diff --git a/res/css/views/rooms/_RoomBreadcrumbs.scss b/res/css/views/rooms/_RoomBreadcrumbs.scss index 6c3eb0420a..3858d836e6 100644 --- a/res/css/views/rooms/_RoomBreadcrumbs.scss +++ b/res/css/views/rooms/_RoomBreadcrumbs.scss @@ -22,13 +22,26 @@ limitations under the License. display: flex; flex-direction: row; + // repeating circles as empty placeholders + background: + radial-gradient( + circle at center, + $breadcrumb-placeholder-bg-color, + $breadcrumb-placeholder-bg-color 15px, + transparent 16px + ); + background-size: 36px; + background-position: 6px -1px; + background-repeat: repeat-x; + + // Autohide the scrollbar overflow-x: hidden; &:hover { overflow-x: visible; } - .mx_AutoHideScrollbar_offset { + .mx_AutoHideScrollbar { display: flex; flex-direction: row; height: 100%; @@ -78,6 +91,14 @@ limitations under the License. display: none; } + .mx_IndicatorScrollbar_leftOverflowIndicator { + background: linear-gradient(to left, $panel-gradient); + } + + .mx_IndicatorScrollbar_rightOverflowIndicator { + background: linear-gradient(to right, $panel-gradient); + } + &.mx_IndicatorScrollbar_leftOverflow .mx_IndicatorScrollbar_leftOverflowIndicator, &.mx_IndicatorScrollbar_rightOverflow .mx_IndicatorScrollbar_rightOverflowIndicator { position: absolute; @@ -88,13 +109,4 @@ limitations under the License. pointer-events: none; z-index: 100; } - - .mx_IndicatorScrollbar_leftOverflowIndicator { - background: linear-gradient(to left, $panel-gradient); - } - - .mx_IndicatorScrollbar_rightOverflowIndicator { - background: linear-gradient(to right, $panel-gradient); - } } - diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index e7589f0e90..47b8131ef0 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -17,12 +17,20 @@ limitations under the License. .mx_RoomHeader { flex: 0 0 52px; border-bottom: 1px solid $primary-hairline-color; + + .mx_E2EIcon { + margin: 0; + position: absolute; + bottom: -2px; + right: -6px; + height: 15px; + width: 15px; + } } .mx_RoomHeader_wrapper { margin: auto; height: 52px; - align-items: center; display: flex; align-items: center; min-width: 0; @@ -107,7 +115,7 @@ limitations under the License. } .mx_RoomHeader_settingsHint { - color: $settings-grey-fg-color ! important; + color: $settings-grey-fg-color !important; } .mx_RoomHeader_searchStatus { @@ -134,17 +142,17 @@ limitations under the License. } .mx_RoomHeader_placeholder { - color: $settings-grey-fg-color ! important; + color: $settings-grey-fg-color !important; } .mx_RoomHeader_editable { - border-bottom: 1px solid $strong-input-border-color ! important; + border-bottom: 1px solid $strong-input-border-color !important; min-width: 150px; cursor: text; } .mx_RoomHeader_editable:focus { - border-bottom: 1px solid $accent-color ! important; + border-bottom: 1px solid $accent-color !important; outline: none; box-shadow: none; } @@ -168,6 +176,7 @@ limitations under the License. width: 28px; height: 28px; margin: 0 7px; + position: relative; } .mx_RoomHeader_avatar .mx_BaseAvatar_image { @@ -193,33 +202,41 @@ limitations under the License. } .mx_RoomHeader_button { + position: relative; margin-left: 10px; cursor: pointer; height: 20px; width: 20px; - background-color: $roomheader-button-color; - mask-repeat: no-repeat; - mask-size: contain; + + &::before { + content: ''; + position: absolute; + height: 20px; + width: 20px; + background-color: $roomheader-button-color; + mask-repeat: no-repeat; + mask-size: contain; + } } -.mx_RoomHeader_settingsButton { +.mx_RoomHeader_settingsButton::before { mask-image: url('$(res)/img/feather-customised/settings.svg'); } -.mx_RoomHeader_forgetButton { +.mx_RoomHeader_forgetButton::before { mask-image: url('$(res)/img/leave.svg'); width: 26px; } -.mx_RoomHeader_searchButton { +.mx_RoomHeader_searchButton::before { mask-image: url('$(res)/img/feather-customised/search.svg'); } -.mx_RoomHeader_shareButton { +.mx_RoomHeader_shareButton::before { mask-image: url('$(res)/img/feather-customised/share.svg'); } -.mx_RoomHeader_manageIntegsButton { +.mx_RoomHeader_manageIntegsButton::before { mask-image: url('$(res)/img/feather-customised/grid.svg'); } @@ -235,8 +252,7 @@ limitations under the License. margin-top: 18px; } -.mx_RoomHeader_pinnedButton { - position: relative; +.mx_RoomHeader_pinnedButton::before { mask-image: url('$(res)/img/icons-pin.svg'); } diff --git a/res/css/views/rooms/_RoomList.scss b/res/css/views/rooms/_RoomList.scss index 886f10dc4c..5ed22f997d 100644 --- a/res/css/views/rooms/_RoomList.scss +++ b/res/css/views/rooms/_RoomList.scss @@ -27,12 +27,8 @@ limitations under the License. position: relative; } -.mx_SearchBox { - flex: none; -} - /* hide resize handles next to collapsed / empty sublists */ -.mx_RoomList .mx_RoomSubList:not(.mx_RoomSubList_nonEmpty) + .mx_ResizeHandle { +.mx_RoomList .mx_RoomSubList:not(.mx_RoomSubList_nonEmpty) + .mx_ResizeHandle { display: none; } diff --git a/res/css/views/rooms/_RoomPreviewBar.scss b/res/css/views/rooms/_RoomPreviewBar.scss index ea3b787971..981cf06c69 100644 --- a/res/css/views/rooms/_RoomPreviewBar.scss +++ b/res/css/views/rooms/_RoomPreviewBar.scss @@ -33,12 +33,29 @@ 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; margin: 10px 10px 10px 0; flex: 0 0 auto; } + + .mx_RoomPreviewBar_footer { + font-size: 12px; + line-height: 20px; + + .mx_Spinner { + vertical-align: middle; + display: inline-block; + } + } } .mx_RoomPreviewBar_dark { @@ -70,7 +87,7 @@ limitations under the License. flex-direction: row; padding: 3px 8px; - &>* { + & > * { margin-left: 12px; } } @@ -81,7 +98,7 @@ limitations under the License. display: flex; flex-direction: column; - &>* { + & > * { margin: 4px; } } @@ -99,7 +116,7 @@ limitations under the License. .mx_RoomPreviewBar_message { flex-direction: column; - &>* { + & > * { margin: 5px 0 20px 0; } } @@ -107,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; + } } } diff --git a/res/css/views/rooms/_RoomRecoveryReminder.scss b/res/css/views/rooms/_RoomRecoveryReminder.scss index 68e2bf861e..85d42ca4b4 100644 --- a/res/css/views/rooms/_RoomRecoveryReminder.scss +++ b/res/css/views/rooms/_RoomRecoveryReminder.scss @@ -40,4 +40,5 @@ limitations under the License. .mx_RoomRecoveryReminder_secondary { font-size: 90%; + margin-top: 1em; } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index a1fc9bdca1..31d887cbbb 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,6 +26,11 @@ limitations under the License. position: relative; } +.mx_RoomTile:focus { + filter: none !important; + background-color: $roomtile-focused-bg-color; +} + .mx_RoomTile_menuButton { display: none; flex: 0 0 16px; @@ -34,13 +40,6 @@ limitations under the License. background-position: center; } -// toggle menuButton and badge on hover/menu displayed -.mx_LeftPanel_container:not(.collapsed) .mx_RoomTile:hover, .mx_RoomTile_menuDisplayed { - .mx_RoomTile_menuButton { - display: block; - } -} - .mx_RoomTile_tooltip { display: inline-block; position: relative; @@ -48,7 +47,6 @@ limitations under the License. left: -12px; } - .mx_RoomTile_nameContainer { display: flex; align-items: center; @@ -100,18 +98,40 @@ 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; text-overflow: ellipsis; } +.mx_RoomTile_badge { + flex: 0 1 content; + border-radius: 0.8em; + padding: 0 0.4em; + color: $roomtile-badge-fg-color; + font-weight: 600; + font-size: 12px; +} + .collapsed { .mx_RoomTile { - margin: 0 2px; + margin: 0 6px; padding: 0 2px; position: relative; justify-content: center; @@ -135,13 +155,15 @@ limitations under the License. } } -.mx_RoomTile_badge { - flex: 0 1 content; - border-radius: 0.8em; - padding: 0 0.4em; - color: $accent-fg-color; - font-weight: 600; - font-size: 12px; +// toggle menuButton and badge on menu displayed +.mx_RoomTile_menuDisplayed, +// or on keyboard focus of room tile +.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; + } } .mx_RoomTile_unreadNotify .mx_RoomTile_badge, @@ -150,8 +172,8 @@ limitations under the License. } .mx_RoomTile_highlight .mx_RoomTile_badge, -.mx_RoomTile_badge.mx_RoomTile_badgeRed -{ +.mx_RoomTile_badge.mx_RoomTile_badgeRed { + color: $accent-fg-color; background-color: $warning-color; } @@ -176,11 +198,6 @@ limitations under the License. transform: scale(1.05, 1.05); } -.mx_RoomTile:focus { - filter: none !important; - background-color: $roomtile-focused-bg-color; -} - .mx_RoomTile_arrow { position: absolute; right: 0px; diff --git a/res/css/views/rooms/_RoomUpgradeWarningBar.scss b/res/css/views/rooms/_RoomUpgradeWarningBar.scss index fe81d3801a..1c477cedfe 100644 --- a/res/css/views/rooms/_RoomUpgradeWarningBar.scss +++ b/res/css/views/rooms/_RoomUpgradeWarningBar.scss @@ -15,17 +15,22 @@ limitations under the License. */ .mx_RoomUpgradeWarningBar { + max-height: 235px; + background-color: $preview-bar-bg-color; + padding-left: 20px; + padding-right: 20px; + overflow: scroll; +} + +.mx_RoomUpgradeWarningBar_wrapped { + width: 100%; + height: 100%; + display: flex; text-align: center; - height: 235px; - background-color: $event-selected-color; align-items: center; flex-direction: column; justify-content: center; - display: flex; - background-color: $preview-bar-bg-color; -webkit-align-items: center; - padding-left: 20px; - padding-right: 20px; } .mx_RoomUpgradeWarningBar_header { diff --git a/res/css/views/rooms/_SearchBar.scss b/res/css/views/rooms/_SearchBar.scss index 894473a5fe..b6748e5ad2 100644 --- a/res/css/views/rooms/_SearchBar.scss +++ b/res/css/views/rooms/_SearchBar.scss @@ -37,6 +37,10 @@ limitations under the License. mask-position: center; } + .mx_SearchBar_buttons { + display: inherit; + } + .mx_SearchBar_button { border: 0; margin: 0 0 0 22px; diff --git a/res/css/views/rooms/_SearchableEntityList.scss b/res/css/views/rooms/_SearchableEntityList.scss deleted file mode 100644 index 37a663123d..0000000000 --- a/res/css/views/rooms/_SearchableEntityList.scss +++ /dev/null @@ -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; -} diff --git a/res/css/views/rooms/_SendMessageComposer.scss b/res/css/views/rooms/_SendMessageComposer.scss new file mode 100644 index 0000000000..d20f7107b3 --- /dev/null +++ b/res/css/views/rooms/_SendMessageComposer.scss @@ -0,0 +1,53 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SendMessageComposer { + flex: 1; + display: flex; + flex-direction: column; + font-size: 14px; + justify-content: center; + margin-right: 6px; + // don't grow wider than available space + min-width: 0; + + .mx_BasicMessageComposer { + flex: 1; + display: flex; + flex-direction: column; + // min-height at this level so the mx_BasicMessageComposer_input + // still stays vertically centered when less than 50px + min-height: 50px; + + .mx_BasicMessageComposer_input { + padding: 3px 0; + // this will center the contenteditable + // in it's parent vertically + // while keeping the autocomplete at the top + // of the composer. The parent needs to be a flex container for this to work. + margin: auto 0; + // max-height at this level so autocomplete doesn't get scrolled too + max-height: 140px; + overflow-y: auto; + } + } + + .mx_SendMessageComposer_overlayWrapper { + position: relative; + height: 0; + } +} + diff --git a/res/css/views/rooms/_TopUnreadMessagesBar.scss b/res/css/views/rooms/_TopUnreadMessagesBar.scss index a4b7a6aa51..28eddf1fa2 100644 --- a/res/css/views/rooms/_TopUnreadMessagesBar.scss +++ b/res/css/views/rooms/_TopUnreadMessagesBar.scss @@ -24,20 +24,17 @@ limitations under the License. width: 38px; } -.mx_TopUnreadMessagesBar:after { - content: "·"; +.mx_TopUnreadMessagesBar::after { + 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 { @@ -49,13 +46,35 @@ limitations under the License. cursor: pointer; } -.mx_TopUnreadMessagesBar_scrollUp:before { +.mx_TopUnreadMessagesBar_scrollUp::before { content: ""; 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; +} diff --git a/res/css/views/rooms/_UserOnlineDot.scss b/res/css/views/rooms/_UserOnlineDot.scss new file mode 100644 index 0000000000..339e5cc48a --- /dev/null +++ b/res/css/views/rooms/_UserOnlineDot.scss @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_UserOnlineDot { + border-radius: 50%; + background-color: $accent-color; + height: 5px; + width: 5px; + display: inline-block; +} diff --git a/res/css/views/rooms/_WhoIsTypingTile.scss b/res/css/views/rooms/_WhoIsTypingTile.scss index ef20c24c84..579ea7e73e 100644 --- a/res/css/views/rooms/_WhoIsTypingTile.scss +++ b/res/css/views/rooms/_WhoIsTypingTile.scss @@ -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; diff --git a/res/css/views/settings/_AvatarSetting.scss b/res/css/views/settings/_AvatarSetting.scss new file mode 100644 index 0000000000..35dba90f85 --- /dev/null +++ b/res/css/views/settings/_AvatarSetting.scss @@ -0,0 +1,87 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_AvatarSetting_avatar { + width: 88px; + height: 88px; + margin-left: 13px; + position: relative; + + & > * { + width: 88px; + box-sizing: border-box; + } + + .mx_AccessibleButton.mx_AccessibleButton_kind_primary { + margin-top: 8px; + + div { + position: relative; + height: 12px; + width: 12px; + display: inline; + padding-right: 6px; // 0.5 * 12px + left: -6px; // 0.5 * 12px + top: 3px; + } + + div::before { + content: ''; + position: absolute; + height: 12px; + width: 12px; + + background-color: $button-primary-fg-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-image: url('$(res)/img/feather-customised/upload.svg'); + } + } + + .mx_AccessibleButton.mx_AccessibleButton_kind_link_sm { + color: $button-danger-bg-color; + } + + & > img { + cursor: pointer; + object-fit: cover; + } + + & > img, + .mx_AvatarSetting_avatarPlaceholder { + display: block; + height: 88px; + border-radius: 4px; + } + + .mx_AvatarSetting_avatarPlaceholder::before { + background-color: $settings-profile-overlay-placeholder-fg-color; + mask: url("$(res)/img/feather-customised/user.svg"); + mask-repeat: no-repeat; + mask-size: 36px; + mask-position: center; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } +} + +.mx_AvatarSetting_avatar .mx_AvatarSetting_avatarPlaceholder { + background-color: $settings-profile-placeholder-bg-color; +} diff --git a/res/css/views/settings/_CrossSigningPanel.scss b/res/css/views/settings/_CrossSigningPanel.scss new file mode 100644 index 0000000000..fa9f76a963 --- /dev/null +++ b/res/css/views/settings/_CrossSigningPanel.scss @@ -0,0 +1,31 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_CrossSigningPanel_statusList { + border-spacing: 0; + + td { + padding: 0; + + &:first-of-type { + padding-inline-end: 1em; + } + } +} + +.mx_CrossSigningPanel_buttonRow { + margin: 1em 0; +} diff --git a/res/css/views/settings/_DevicesPanel.scss b/res/css/views/settings/_DevicesPanel.scss index e4856531d9..49debe842f 100644 --- a/res/css/views/settings/_DevicesPanel.scss +++ b/res/css/views/settings/_DevicesPanel.scss @@ -18,7 +18,7 @@ limitations under the License. display: table; table-layout: fixed; width: 880px; - border-spacing: 2px; + border-spacing: 10px; } .mx_DevicesPanel_header { @@ -26,8 +26,17 @@ limitations under the License. font-weight: bold; } +.mx_DevicesPanel_header > .mx_DevicesPanel_deviceButtons { + height: 48px; // make this tall so the table doesn't move down when the delete button appears +} + .mx_DevicesPanel_header > div { display: table-cell; + vertical-align: middle; +} + +.mx_DevicesPanel_header .mx_DevicesPanel_deviceName { + width: 50%; } .mx_DevicesPanel_header .mx_DevicesPanel_deviceLastSeen { @@ -48,4 +57,4 @@ limitations under the License. .mx_DevicesPanel_myDevice { font-weight: bold; -} \ No newline at end of file +} diff --git a/res/css/views/messages/_ReactionDimension.scss b/res/css/views/settings/_E2eAdvancedPanel.scss similarity index 75% rename from res/css/views/messages/_ReactionDimension.scss rename to res/css/views/settings/_E2eAdvancedPanel.scss index 9a891d05cf..9e32685d12 100644 --- a/res/css/views/messages/_ReactionDimension.scss +++ b/res/css/views/settings/_E2eAdvancedPanel.scss @@ -1,5 +1,5 @@ /* -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. @@ -14,12 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_ReactionDimension { - width: 42px; - display: flex; - justify-content: space-evenly; +.mx_E2eAdvancedPanel_settingLongDescription { + margin-right: 150px; } -.mx_ReactionDimension_disabled { - opacity: 0.4; -} diff --git a/res/css/views/settings/_EmailAddresses.scss b/res/css/views/settings/_EmailAddresses.scss index eef804a33b..1c9ce724d1 100644 --- a/res/css/views/settings/_EmailAddresses.scss +++ b/res/css/views/settings/_EmailAddresses.scss @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +16,8 @@ limitations under the License. */ .mx_ExistingEmailAddress { + display: flex; + align-items: center; margin-bottom: 5px; } @@ -24,20 +27,12 @@ limitations under the License. vertical-align: middle; } -.mx_ExistingEmailAddress_email { - vertical-align: middle; -} - +.mx_ExistingEmailAddress_email, .mx_ExistingEmailAddress_promptText { + flex: 1; margin-right: 10px; } .mx_ExistingEmailAddress_confirmBtn { - margin-right: 5px; -} - -.mx_EmailAddresses_new .mx_Field input { - // Use 100% of the space available for the input, but don't let the 10px - // padding on either side of the input to push it out of alignment. - width: calc(100% - 20px); + margin-left: 5px; } diff --git a/res/css/views/settings/_IntegrationsManager.scss b/res/css/views/settings/_IntegrationManager.scss similarity index 73% rename from res/css/views/settings/_IntegrationsManager.scss rename to res/css/views/settings/_IntegrationManager.scss index 93ee0e20fe..81b01ab8de 100644 --- a/res/css/views/settings/_IntegrationsManager.scss +++ b/res/css/views/settings/_IntegrationManager.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_IntegrationsManager .mx_Dialog { +.mx_IntegrationManager .mx_Dialog { width: 60%; height: 70%; overflow: hidden; @@ -23,9 +23,22 @@ limitations under the License. max-height: initial; } -.mx_IntegrationsManager iframe { +.mx_IntegrationManager iframe { background-color: #fff; border: 0px; width: 100%; height: 100%; } + +.mx_IntegrationManager_loading h3 { + text-align: center; +} + +.mx_IntegrationManager_error { + text-align: center; + padding-top: 20px; +} + +.mx_IntegrationManager_error h3 { + color: $warning-color; +} diff --git a/res/css/views/settings/_KeyBackupPanel.scss b/res/css/views/settings/_KeyBackupPanel.scss index 1bcc0ab10d..872162caad 100644 --- a/res/css/views/settings/_KeyBackupPanel.scss +++ b/res/css/views/settings/_KeyBackupPanel.scss @@ -1,5 +1,6 @@ /* Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,3 +31,7 @@ limitations under the License. .mx_KeyBackupPanel_deviceName { font-style: italic; } + +.mx_KeyBackupPanel_buttonRow { + margin: 1em 0; +} diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index d6c0b5dbeb..e6d09b9a2a 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_UserNotifSettings_tableRow -{ +.mx_UserNotifSettings_tableRow { display: table-row; } @@ -26,8 +25,7 @@ limitations under the License. width: 16px; } -.mx_UserNotifSettings_labelCell -{ +.mx_UserNotifSettings_labelCell { padding-bottom: 8px; width: 400px; display: table-cell; @@ -71,3 +69,26 @@ limitations under the License. .mx_UserNotifSettings_notifTable .mx_Spinner { position: absolute; } + +.mx_NotificationSound_soundUpload { + display: none; +} + +.mx_NotificationSound_browse { + color: $accent-color; + border: 1px solid $accent-color; + background-color: transparent; +} + +.mx_NotificationSound_save { + margin-left: 5px; + color: white; + background-color: $accent-color; +} + +.mx_NotificationSound_resetSound { + margin-top: 5px; + color: white; + border: $warning-color; + background-color: $warning-color; +} diff --git a/res/css/views/settings/_PhoneNumbers.scss b/res/css/views/settings/_PhoneNumbers.scss index 2f54babd6f..507b07334e 100644 --- a/res/css/views/settings/_PhoneNumbers.scss +++ b/res/css/views/settings/_PhoneNumbers.scss @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +16,8 @@ limitations under the License. */ .mx_ExistingPhoneNumber { + display: flex; + align-items: center; margin-bottom: 5px; } @@ -24,22 +27,23 @@ limitations under the License. vertical-align: middle; } -.mx_ExistingPhoneNumber_address { - vertical-align: middle; -} - +.mx_ExistingPhoneNumber_address, .mx_ExistingPhoneNumber_promptText { + flex: 1; margin-right: 10px; } .mx_ExistingPhoneNumber_confirmBtn { - margin-right: 5px; + margin-left: 5px; } -.mx_PhoneNumbers_new .mx_Field input { - // Use 100% of the space available for the input, but don't let the 10px - // padding on either side of the input to push it out of alignment. - width: calc(100% - 20px); +.mx_ExistingPhoneNumber_verification { + display: inline-flex; + align-items: center; + + .mx_Field { + margin: 0 0 0 1em; + } } .mx_PhoneNumbers_input { diff --git a/res/css/views/settings/_ProfileSettings.scss b/res/css/views/settings/_ProfileSettings.scss index b2e449ac34..58624d1597 100644 --- a/res/css/views/settings/_ProfileSettings.scss +++ b/res/css/views/settings/_ProfileSettings.scss @@ -22,11 +22,6 @@ limitations under the License. flex-grow: 1; } -.mx_ProfileSettings_controls .mx_Field #profileDisplayName, -.mx_ProfileSettings_controls .mx_Field #profileTopic { - width: calc(100% - 20px); // subtract 10px padding on left and right -} - .mx_ProfileSettings_controls .mx_Field #profileTopic { height: 4em; } @@ -43,91 +38,6 @@ limitations under the License. } } -.mx_ProfileSettings_avatar { - width: 88px; - height: 88px; - margin-left: 13px; - position: relative; - cursor: pointer; -} - -.mx_ProfileSettings_avatar > * { - display: block; - width: 88px; - height: 88px; - border-radius: 4px; -} - -.mx_ProfileSettings_avatar .mx_ProfileSettings_avatarOverlay_disabled { - cursor: default; -} - -.mx_ProfileSettings_avatar .mx_ProfileSettings_avatarPlaceholder { - background-color: $settings-profile-placeholder-bg-color; -} - -.mx_ProfileSettings_avatarOverlay { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - display: none; - text-align: center; - vertical-align: middle; - font-size: 10px; -} - -.mx_ProfileSettings_avatar:hover .mx_ProfileSettings_avatarOverlay:not(.mx_ProfileSettings_avatarOverlay_disabled) { - display: inline-block; - opacity: 0.5 !important; - color: $settings-profile-overlay-fg-color !important; - background-color: $settings-profile-overlay-bg-color !important; -} - -.mx_ProfileSettings_avatarOverlay_show { - display: inline-block; - opacity: 1; - color: $settings-profile-overlay-placeholder-fg-color; - background-color: $settings-profile-overlay-placeholder-bg-color; -} - -.mx_ProfileSettings_avatarOverlayText { - display: block; - margin-top: 17px; - margin-bottom: 8px; -} - -.mx_ProfileSettings_noAvatarText { - display: block; - margin: 34px auto auto; -} - -.mx_ProfileSettings_avatarOverlayImgContainer { - position: relative; - width: 14px; - height: 14px; - margin: auto; -} - -.mx_ProfileSettings_avatarOverlayImg:before { - background-color: $settings-profile-overlay-placeholder-fg-color; - mask: url("$(res)/img/feather-customised/upload.svg"); - mask-repeat: no-repeat; - mask-size: 14px; - mask-position: center; - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -.mx_ProfileSettings_avatar:hover .mx_ProfileSettings_avatarOverlayImg:before { - background-color: $settings-profile-overlay-fg-color !important; -} - .mx_ProfileSettings_avatarUpload { display: none; } diff --git a/res/css/views/settings/_SetIdServer.scss b/res/css/views/settings/_SetIdServer.scss new file mode 100644 index 0000000000..98c64b7218 --- /dev/null +++ b/res/css/views/settings/_SetIdServer.scss @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SetIdServer .mx_Field_input { + @mixin mx_Settings_fullWidthField; +} + +.mx_SetIdServer_tooltip { + @mixin mx_Settings_tooltip; +} diff --git a/res/css/views/settings/_SetIntegrationManager.scss b/res/css/views/settings/_SetIntegrationManager.scss new file mode 100644 index 0000000000..3e59ac73ac --- /dev/null +++ b/res/css/views/settings/_SetIntegrationManager.scss @@ -0,0 +1,37 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_SetIntegrationManager { + margin-top: 10px; + margin-bottom: 10px; +} + +.mx_SetIntegrationManager > .mx_SettingsTab_heading { + margin-bottom: 10px; +} + +.mx_SetIntegrationManager > .mx_SettingsTab_heading > .mx_SettingsTab_subheading { + display: inline-block; + padding-left: 5px; +} + +.mx_SetIntegrationManager .mx_ToggleSwitch { + display: inline-block; + float: right; + top: 9px; + + @mixin mx_Settings_fullWidthField; +} diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index def28bfbd2..01a1d94956 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -24,6 +24,10 @@ limitations under the License. color: $primary-fg-color; } +.mx_SettingsTab_heading:nth-child(n + 2) { + margin-top: 30px; +} + .mx_SettingsTab_subheading { font-size: 16px; display: block; @@ -37,14 +41,21 @@ limitations under the License. .mx_SettingsTab_subsectionText { color: $settings-subsection-fg-color; font-size: 14px; - padding-bottom: 12px; display: block; - margin: 0 100px 0 0; // Align with the rest of the view + 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 { @@ -67,12 +78,6 @@ limitations under the License. word-break: break-all; } -.mx_SettingsTab .mx_SettingsTab_subheading:nth-child(n + 2) { - // These views have a lot of the same repetitive information on it, so - // give them more visual distinction between the sections. - margin-top: 30px; -} - .mx_SettingsTab a { color: $accent-color-alt; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss index 91d7ed2c7d..af55820d66 100644 --- a/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss @@ -17,7 +17,3 @@ limitations under the License. .mx_GeneralRoomSettingsTab_profileSection { margin-top: 10px; } - -.mx_GeneralRoomSettingsTab .mx_AliasSettings .mx_Field select { - width: 100%; -} diff --git a/res/css/views/settings/tabs/room/_RolesRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_RolesRoomSettingsTab.scss index 8bfc792dc3..5d0a8ed142 100644 --- a/res/css/views/settings/tabs/room/_RolesRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_RolesRoomSettingsTab.scss @@ -21,4 +21,4 @@ limitations under the License. .mx_RolesRoomSettingsTab_unbanBtn { margin-right: 10px; margin-bottom: 5px; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss index dfd046e672..b5a57dfefb 100644 --- a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss @@ -31,4 +31,4 @@ limitations under the License. .mx_SecurityRoomSettingsTab_encryptionSection { margin-bottom: 25px; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index bec013674a..62d230e752 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -14,33 +14,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_GeneralUserSettingsTab_changePassword, -.mx_GeneralUserSettingsTab_themeSection { - display: block; -} - .mx_GeneralUserSettingsTab_changePassword .mx_Field, .mx_GeneralUserSettingsTab_themeSection .mx_Field { - display: block; - margin-right: 100px; // Align with the other fields on the page -} - -.mx_GeneralUserSettingsTab_changePassword .mx_Field input { - display: block; - width: calc(100% - 20px); // subtract 10px padding on left and right + @mixin mx_Settings_fullWidthField; } .mx_GeneralUserSettingsTab_changePassword .mx_Field:first-child { margin-top: 0; } -.mx_GeneralUserSettingsTab_themeSection .mx_Field select { - display: block; - width: 100%; +.mx_GeneralUserSettingsTab_accountSection .mx_EmailAddresses, +.mx_GeneralUserSettingsTab_accountSection .mx_PhoneNumbers, +.mx_GeneralUserSettingsTab_discovery .mx_ExistingEmailAddress, +.mx_GeneralUserSettingsTab_discovery .mx_ExistingPhoneNumber, +.mx_GeneralUserSettingsTab_languageInput { + @mixin mx_Settings_fullWidthField; } -.mx_GeneralUserSettingsTab_accountSection > .mx_EmailAddresses, -.mx_GeneralUserSettingsTab_accountSection > .mx_PhoneNumbers, -.mx_GeneralUserSettingsTab_languageInput { - margin-right: 100px; // Align with the other fields on the page -} \ No newline at end of file +.mx_GeneralUserSettingsTab_warningIcon { + vertical-align: middle; +} diff --git a/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss index fa0d0edeb7..109edfff81 100644 --- a/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss @@ -21,4 +21,4 @@ limitations under the License. .mx_HelpUserSettingsTab span.mx_AccessibleButton { word-break: break-word; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss b/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss new file mode 100644 index 0000000000..2a3fd12f31 --- /dev/null +++ b/res/css/views/settings/tabs/user/_MjolnirUserSettingsTab.scss @@ -0,0 +1,23 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MjolnirUserSettingsTab .mx_Field { + @mixin mx_Settings_fullWidthField; +} + +.mx_MjolnirUserSettingsTab_listItem { + margin-bottom: 2px; +} diff --git a/res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss b/res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss index 3cebd2958e..b57c46a1d9 100644 --- a/res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss @@ -16,4 +16,4 @@ limitations under the License. .mx_NotificationUserSettingsTab .mx_SettingsTab_heading { margin-bottom: 10px; // Give some spacing between the title and the first elements -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss index f447221b7a..be0af9123b 100644 --- a/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss @@ -14,14 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_PreferencesUserSettingsTab .mx_Field { - margin-right: 100px; // Align with the rest of the controls -} +.mx_PreferencesUserSettingsTab { + .mx_Field { + @mixin mx_Settings_fullWidthField; + } -.mx_PreferencesUserSettingsTab .mx_Field input { - display: block; - - // Subtract 10px padding on left and right - // This is to keep the input aligned with the rest of the tab's controls. - width: calc(100% - 20px); + .mx_SettingsTab_section { + margin-bottom: 30px; + } } diff --git a/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss b/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss index 1c0a4b5864..b5a6693006 100644 --- a/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss @@ -54,4 +54,4 @@ limitations under the License. .mx_SecurityUserSettingsTab_ignoredUser .mx_AccessibleButton { margin-right: 10px; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss index f5dba9831e..69d57bdba1 100644 --- a/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss @@ -14,13 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_VoiceUserSettingsTab .mx_Field select { - width: 100%; - max-width: 100%; -} - .mx_VoiceUserSettingsTab .mx_Field { - margin-right: 100px; // align with the rest of the fields + @mixin mx_Settings_fullWidthField; } .mx_VoiceUserSettingsTab_missingMediaPermissions { diff --git a/res/css/views/terms/_InlineTermsAgreement.scss b/res/css/views/terms/_InlineTermsAgreement.scss new file mode 100644 index 0000000000..e00dcf31d1 --- /dev/null +++ b/res/css/views/terms/_InlineTermsAgreement.scss @@ -0,0 +1,45 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_InlineTermsAgreement_cbContainer { + margin-bottom: 10px; + font-size: 14px; + + a { + color: $accent-color; + text-decoration: none; + } + + .mx_InlineTermsAgreement_checkbox { + margin-top: 10px; + + input { + vertical-align: text-bottom; + } + } +} + +.mx_InlineTermsAgreement_link { + display: inline-block; + mask-image: url('$(res)/img/external-link.svg'); + background-color: $accent-color; + mask-repeat: no-repeat; + mask-size: contain; + width: 12px; + height: 12px; + margin-left: 3px; + vertical-align: middle; +} diff --git a/res/css/views/verification/_VerificationShowSas.scss b/res/css/views/verification/_VerificationShowSas.scss index a0da7e2539..5038d40b73 100644 --- a/res/css/views/verification/_VerificationShowSas.scss +++ b/res/css/views/verification/_VerificationShowSas.scss @@ -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%; } diff --git a/res/css/views/voip/_CallView.scss b/res/css/views/voip/_CallView.scss index deb89a837c..b01fbf8c66 100644 --- a/res/css/views/voip/_CallView.scss +++ b/res/css/views/voip/_CallView.scss @@ -22,4 +22,4 @@ limitations under the License. padding: 6px; font-weight: bold; font-size: 13px; -} \ No newline at end of file +} diff --git a/res/fonts/Nunito/Nunito-Bold.ttf b/res/fonts/Nunito/Nunito-Bold.ttf index c70de76bbd..c8fabf7d92 100644 Binary files a/res/fonts/Nunito/Nunito-Bold.ttf and b/res/fonts/Nunito/Nunito-Bold.ttf differ diff --git a/res/fonts/Nunito/Nunito-Regular.ttf b/res/fonts/Nunito/Nunito-Regular.ttf index 064e805431..86ce522f60 100644 Binary files a/res/fonts/Nunito/Nunito-Regular.ttf and b/res/fonts/Nunito/Nunito-Regular.ttf differ diff --git a/res/fonts/Nunito/Nunito-SemiBold.ttf b/res/fonts/Nunito/Nunito-SemiBold.ttf index a84b3b35a6..8bf953b59a 100644 Binary files a/res/fonts/Nunito/Nunito-SemiBold.ttf and b/res/fonts/Nunito/Nunito-SemiBold.ttf differ diff --git a/res/fonts/Nunito/XRXQ3I6Li01BKofIMN44Y9vKUTo.ttf b/res/fonts/Nunito/XRXQ3I6Li01BKofIMN44Y9vKUTo.ttf deleted file mode 100644 index 4387fb67c4..0000000000 Binary files a/res/fonts/Nunito/XRXQ3I6Li01BKofIMN44Y9vKUTo.ttf and /dev/null differ diff --git a/res/fonts/Nunito/XRXQ3I6Li01BKofIMN5cYtvKUTo.ttf b/res/fonts/Nunito/XRXQ3I6Li01BKofIMN5cYtvKUTo.ttf deleted file mode 100644 index 68fb3ff5cb..0000000000 Binary files a/res/fonts/Nunito/XRXQ3I6Li01BKofIMN5cYtvKUTo.ttf and /dev/null differ diff --git a/res/fonts/Nunito/XRXV3I6Li01BKofINeaE.ttf b/res/fonts/Nunito/XRXV3I6Li01BKofINeaE.ttf deleted file mode 100644 index c40e599260..0000000000 Binary files a/res/fonts/Nunito/XRXV3I6Li01BKofINeaE.ttf and /dev/null differ diff --git a/res/fonts/Nunito/XRXW3I6Li01BKofA6sKUYevN.ttf b/res/fonts/Nunito/XRXW3I6Li01BKofA6sKUYevN.ttf deleted file mode 100644 index 0c4fd17dfa..0000000000 Binary files a/res/fonts/Nunito/XRXW3I6Li01BKofA6sKUYevN.ttf and /dev/null differ diff --git a/res/fonts/Nunito/XRXW3I6Li01BKofAjsOUYevN.ttf b/res/fonts/Nunito/XRXW3I6Li01BKofAjsOUYevN.ttf deleted file mode 100644 index 339d59ac00..0000000000 Binary files a/res/fonts/Nunito/XRXW3I6Li01BKofAjsOUYevN.ttf and /dev/null differ diff --git a/res/fonts/Nunito/XRXX3I6Li01BKofIMNaDRss.ttf b/res/fonts/Nunito/XRXX3I6Li01BKofIMNaDRss.ttf deleted file mode 100644 index b5fcd891af..0000000000 Binary files a/res/fonts/Nunito/XRXX3I6Li01BKofIMNaDRss.ttf and /dev/null differ diff --git a/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 b/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 index 70f9b03c4d..593d7c8f5c 100644 Binary files a/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 and b/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 differ diff --git a/res/fonts/Twemoji_Mozilla/TwemojiMozilla-sbix.woff2 b/res/fonts/Twemoji_Mozilla/TwemojiMozilla-sbix.woff2 new file mode 100644 index 0000000000..277324851f Binary files /dev/null and b/res/fonts/Twemoji_Mozilla/TwemojiMozilla-sbix.woff2 differ diff --git a/res/img/admin.svg b/res/img/admin.svg deleted file mode 100644 index 7ea7459304..0000000000 --- a/res/img/admin.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - icons_owner - Created with sketchtool. - - - - - - - - - - - - diff --git a/res/img/e2e/normal.svg b/res/img/e2e/normal.svg new file mode 100644 index 0000000000..5b848bc27f --- /dev/null +++ b/res/img/e2e/normal.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/e2e/verified.svg b/res/img/e2e/verified.svg index 459a552a40..464b443dcf 100644 --- a/res/img/e2e/verified.svg +++ b/res/img/e2e/verified.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/res/img/e2e/warning.svg b/res/img/e2e/warning.svg index 3d5fba550c..209ae0f71f 100644 --- a/res/img/e2e/warning.svg +++ b/res/img/e2e/warning.svg @@ -1,6 +1,5 @@ - - - - - + + + + diff --git a/res/img/emojipicker/activity.svg b/res/img/emojipicker/activity.svg new file mode 100644 index 0000000000..d921667e7a --- /dev/null +++ b/res/img/emojipicker/activity.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/res/img/emojipicker/custom.svg b/res/img/emojipicker/custom.svg new file mode 100644 index 0000000000..814cd8ec13 --- /dev/null +++ b/res/img/emojipicker/custom.svg @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/res/img/emojipicker/delete.svg b/res/img/emojipicker/delete.svg new file mode 100644 index 0000000000..5f5d4e52eb --- /dev/null +++ b/res/img/emojipicker/delete.svg @@ -0,0 +1,15 @@ + + + + + diff --git a/res/img/emojipicker/flags.svg b/res/img/emojipicker/flags.svg new file mode 100644 index 0000000000..bd0a935265 --- /dev/null +++ b/res/img/emojipicker/flags.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/res/img/emojipicker/foods.svg b/res/img/emojipicker/foods.svg new file mode 100644 index 0000000000..57a15976d8 --- /dev/null +++ b/res/img/emojipicker/foods.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/res/img/emojipicker/nature.svg b/res/img/emojipicker/nature.svg new file mode 100644 index 0000000000..a4778be927 --- /dev/null +++ b/res/img/emojipicker/nature.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/res/img/emojipicker/objects.svg b/res/img/emojipicker/objects.svg new file mode 100644 index 0000000000..e0d39f985a --- /dev/null +++ b/res/img/emojipicker/objects.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/res/img/emojipicker/people.svg b/res/img/emojipicker/people.svg new file mode 100644 index 0000000000..c2fdb579f6 --- /dev/null +++ b/res/img/emojipicker/people.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/res/img/emojipicker/places.svg b/res/img/emojipicker/places.svg new file mode 100644 index 0000000000..0947baaf04 --- /dev/null +++ b/res/img/emojipicker/places.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/res/img/emojipicker/recent.svg b/res/img/emojipicker/recent.svg new file mode 100644 index 0000000000..2fdcc65cd2 --- /dev/null +++ b/res/img/emojipicker/recent.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/res/img/emojipicker/search.svg b/res/img/emojipicker/search.svg new file mode 100644 index 0000000000..b5f660b3ac --- /dev/null +++ b/res/img/emojipicker/search.svg @@ -0,0 +1,15 @@ + + + + + diff --git a/res/img/emojipicker/symbols.svg b/res/img/emojipicker/symbols.svg new file mode 100644 index 0000000000..a2b86d9ec8 --- /dev/null +++ b/res/img/emojipicker/symbols.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/res/img/explore.svg b/res/img/explore.svg new file mode 100644 index 0000000000..3956e912ac --- /dev/null +++ b/res/img/explore.svg @@ -0,0 +1,97 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/res/img/feather-customised/bridge.svg b/res/img/feather-customised/bridge.svg new file mode 100644 index 0000000000..f8f3468155 --- /dev/null +++ b/res/img/feather-customised/bridge.svg @@ -0,0 +1,50 @@ + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/res/img/feather-customised/chevron-down.svg b/res/img/feather-customised/chevron-down.svg new file mode 100644 index 0000000000..bcb185ede7 --- /dev/null +++ b/res/img/feather-customised/chevron-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/feather-customised/edit.svg b/res/img/feather-customised/edit.svg new file mode 100644 index 0000000000..f511aa1477 --- /dev/null +++ b/res/img/feather-customised/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/feather-customised/eye.svg b/res/img/feather-customised/eye.svg new file mode 100644 index 0000000000..fd06bf7b21 --- /dev/null +++ b/res/img/feather-customised/eye.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/feather-customised/image.svg b/res/img/feather-customised/image.svg new file mode 100644 index 0000000000..9690aecf36 --- /dev/null +++ b/res/img/feather-customised/image.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/img/feather-customised/lock-solid.svg b/res/img/feather-customised/lock-solid.svg new file mode 100644 index 0000000000..9eb8b6a4c5 --- /dev/null +++ b/res/img/feather-customised/lock-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/feather-customised/plus.svg b/res/img/feather-customised/plus.svg new file mode 100644 index 0000000000..c747253139 --- /dev/null +++ b/res/img/feather-customised/plus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/feather-customised/widget/bin.svg b/res/img/feather-customised/widget/bin.svg deleted file mode 100644 index 7616d8931b..0000000000 --- a/res/img/feather-customised/widget/bin.svg +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - diff --git a/res/img/feather-customised/widget/camera.svg b/res/img/feather-customised/widget/camera.svg deleted file mode 100644 index 5502493068..0000000000 --- a/res/img/feather-customised/widget/camera.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/feather-customised/widget/edit.svg b/res/img/feather-customised/widget/edit.svg deleted file mode 100644 index 749e83f982..0000000000 --- a/res/img/feather-customised/widget/edit.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/feather-customised/widget/refresh.svg b/res/img/feather-customised/widget/refresh.svg deleted file mode 100644 index 0994bbdd52..0000000000 --- a/res/img/feather-customised/widget/refresh.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/feather-customised/widget/x-circle.svg b/res/img/feather-customised/widget/x-circle.svg deleted file mode 100644 index 951407b39c..0000000000 --- a/res/img/feather-customised/widget/x-circle.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/format/bold.svg b/res/img/format/bold.svg new file mode 100644 index 0000000000..634d735031 --- /dev/null +++ b/res/img/format/bold.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/format/code.svg b/res/img/format/code.svg new file mode 100644 index 0000000000..0a29bcd7bd --- /dev/null +++ b/res/img/format/code.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/res/img/format/italics.svg b/res/img/format/italics.svg new file mode 100644 index 0000000000..841afadffd --- /dev/null +++ b/res/img/format/italics.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/format/quote.svg b/res/img/format/quote.svg new file mode 100644 index 0000000000..82d3403314 --- /dev/null +++ b/res/img/format/quote.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/img/format/strikethrough.svg b/res/img/format/strikethrough.svg new file mode 100644 index 0000000000..fc02b0aae2 --- /dev/null +++ b/res/img/format/strikethrough.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/icon-email-pill-avatar.svg b/res/img/icon-email-pill-avatar.svg new file mode 100644 index 0000000000..6b0ac200a5 --- /dev/null +++ b/res/img/icon-email-pill-avatar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/icon-pill-remove.svg b/res/img/icon-pill-remove.svg new file mode 100644 index 0000000000..adf6fd4771 --- /dev/null +++ b/res/img/icon-pill-remove.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/icons-settings-room.svg b/res/img/icons-settings-room.svg deleted file mode 100644 index 421eefdefa..0000000000 --- a/res/img/icons-settings-room.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/mod.svg b/res/img/mod.svg deleted file mode 100644 index 847baf98f9..0000000000 --- a/res/img/mod.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - icons_admin - Created with sketchtool. - - - - - - - - - - - diff --git a/res/img/react.svg b/res/img/react.svg new file mode 100644 index 0000000000..dd23c41c2c --- /dev/null +++ b/res/img/react.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/res/themes/dark-custom/css/dark-custom.scss b/res/themes/dark-custom/css/dark-custom.scss new file mode 100644 index 0000000000..aff647ce26 --- /dev/null +++ b/res/themes/dark-custom/css/dark-custom.scss @@ -0,0 +1,6 @@ +@import "../../light/css/_paths.scss"; +@import "../../light/css/_fonts.scss"; +@import "../../light/css/_light.scss"; +@import "../../dark/css/_dark.scss"; +@import "../../light-custom/css/_custom.scss"; +@import "../../../../res/css/_components.scss"; diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index bdccf71540..bfa2272283 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -16,6 +16,7 @@ $room-highlight-color: #343a46; // typical text (dark-on-white in light skin) $primary-fg-color: $text-primary-color; $primary-bg-color: $bg-color; +$muted-fg-color: $header-panel-text-primary-color; // used for dialog box text $light-fg-color: $header-panel-text-secondary-color; @@ -121,6 +122,9 @@ $event-sending-color: $text-secondary-color; $event-redacted-fg-color: #606060; $event-redacted-border-color: #000000; +$event-highlight-fg-color: $warning-color; +$event-highlight-bg-color: #25271F; + // event timestamp $event-timestamp-color: $text-secondary-color; @@ -140,6 +144,10 @@ $button-danger-fg-color: #ffffff; $button-danger-bg-color: $notice-primary-color; $button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color +$button-link-fg-color: $accent-color; +$button-link-bg-color: transparent; + +$visual-bell-bg-color: #800; $room-warning-bg-color: $header-panel-bg-color; @@ -157,9 +165,18 @@ $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; +$interactive-tooltip-bg-color: $base-color; +$interactive-tooltip-fg-color: #ffffff; + +$breadcrumb-placeholder-bg-color: #272c35; + +$user-tile-hover-bg-color: $header-panel-bg-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { @@ -192,6 +209,11 @@ $tooltip-timeline-fg-color: #ffffff; background-color: $button-secondary-bg-color; } +@define-mixin mx_Dialog_link { + color: $accent-color; + text-decoration: none; +} + // Nasty hacks to apply a filter to arbitrary monochrome artwork to make it // better match the theme. Typically applied to dark grey 'off' buttons or // light grey 'on' buttons. @@ -199,10 +221,6 @@ $tooltip-timeline-fg-color: #ffffff; 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 @@ -226,3 +244,13 @@ $tooltip-timeline-fg-color: #ffffff; } } } + +// diff highlight colors +// intentionally swapped to avoid inversion +.hljs-addition { + background: #fdd; +} + +.hljs-deletion { + background: #dfd; +} diff --git a/res/themes/light-custom/css/_custom.scss b/res/themes/light-custom/css/_custom.scss new file mode 100644 index 0000000000..e4a08277f9 --- /dev/null +++ b/res/themes/light-custom/css/_custom.scss @@ -0,0 +1,126 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +// --accent-color +$accent-color: var(--accent-color); +$button-bg-color: var(--accent-color); +$button-link-fg-color: var(--accent-color); +$button-primary-bg-color: var(--accent-color); +$input-valid-border-color: var(--accent-color); +$reaction-row-button-selected-border-color: var(--accent-color); +$roomsublist-chevron-color: var(--accent-color); +$tab-label-active-bg-color: var(--accent-color); +$togglesw-on-color: var(--accent-color); +$username-variant3-color: var(--accent-color); +$accent-color-50pct: var(--accent-color-50pct); //still needs alpha at .5 +// +// --timeline-background-color +$authpage-body-bg-color: var(--timeline-background-color); +$button-secondary-bg-color: var(--timeline-background-color); +$field-focused-label-bg-color: var(--timeline-background-color); +$lightbox-border-color: var(--timeline-background-color); +$menu-bg-color: var(--timeline-background-color); +$avatar-bg-color: var(--timeline-background-color); +$message-action-bar-bg-color: var(--timeline-background-color); +$primary-bg-color: var(--timeline-background-color); +$roomtile-focused-bg-color: var(--timeline-background-color); +$togglesw-ball-color: var(--timeline-background-color); +$droptarget-bg-color: var(--timeline-background-color-50pct); //still needs alpha at .5 +$authpage-modal-bg-color: var(--timeline-background-color-50pct); //still needs alpha at .59 +// +// --roomlist-highlights-color +$roomtile-selected-bg-color: var(--roomlist-highlights-color); +// +// --sidebar-color +$interactive-tooltip-bg-color: var(--sidebar-color); +$tagpanel-bg-color: var(--sidebar-color); +$tooltip-timeline-bg-color: var(--sidebar-color); +$dialog-backdrop-color: var(--sidebar-color-50pct); +// +// --roomlist-background-color +$event-selected-color: var(--roomlist-background-color); +$header-panel-bg-color: var(--roomlist-background-color); +$reaction-row-button-bg-color: var(--roomlist-background-color); +$panel-gradient: var(--roomlist-background-color-0pct), var(--roomlist-background-color); +// these were #f2f5f8 instead of #f3f8fd, but close enough +$dark-panel-bg-color: var(--roomlist-background-color); +$input-lighter-bg-color: var(--roomlist-background-color); +$plinth-bg-color: var(--roomlist-background-color); +$roomsublist-background: var(--roomlist-background-color); +$secondary-accent-color: var(--roomlist-background-color); +$selected-color: var(--roomlist-background-color); +$widget-menu-bar-bg-color: var(--roomlist-background-color); +$roomtile-badge-fg-color: var(--roomlist-background-color); +// +// --timeline-text-color +$message-action-bar-fg-color: var(--timeline-text-color); +$primary-fg-color: var(--timeline-text-color); +$settings-profile-overlay-placeholder-fg-color: var(--timeline-text-color); +$roomtopic-color: var(--timeline-text-color-50pct); +$tab-label-fg-color: var(--timeline-text-color); +$tab-label-icon-bg-color: var(--timeline-text-color); //was #454545 +// was #212121 +$topleftmenu-color: var(--timeline-text-color); +// was #45474a +$dialog-title-fg-color: var(--timeline-text-color); +$tab-label-fg-color: var(--timeline-text-color); +// was #4e5054 +$authpage-lang-color: var(--timeline-text-color); +$roomheader-color: var(--timeline-text-color); +// +// --roomlist-text-color +$roomtile-notified-color: var(--roomlist-text-color); +$roomtile-selected-color: var(--roomlist-text-color); +// +// --roomlist-text-secondary-color +$roomsublist-label-fg-color: var(--roomlist-text-secondary-color); +$roomtile-name-color: var(--roomlist-text-secondary-color); +// +// --roomlist-separator-color +$input-darker-bg-color: var(--roomlist-separator-color); +$panel-divider-color: var(--roomlist-separator-color);// originally #dee1f3, but close enough +$primary-hairline-color: var(--roomlist-separator-color);// originally #e5e5e5, but close enough +// +// --timeline-text-secondary-color +$authpage-secondary-color: var(--timeline-text-secondary-color); +$memberstatus-placeholder-color: var(--timeline-text-secondary-color); +$notice-secondary-color: var(--timeline-text-secondary-color); +$pinned-color: var(--timeline-text-secondary-color); +$settings-subsection-fg-color: var(--timeline-text-secondary-color); +$roomheader-addroom-bg-color: var(--timeline-text-secondary-color); +// was #747474 +$light-fg-color: var(--timeline-text-secondary-color); +// was #777777 +$blockquote-fg-color: var(--timeline-text-secondary-color); +// was #888888 +$greyed-fg-color: var(--timeline-text-secondary-color); +$info-plinth-fg-color: var(--timeline-text-secondary-color); +$preview-widget-fg-color: var(--timeline-text-secondary-color); +// +// --primary-color +$accent-color-alt: var(--primary-color); +$input-focused-border-color: var(--primary-color); +// +// --warning-color +$button-danger-bg-color: var(--warning-color); +$event-highlight-fg-color: var(--warning-color); +$input-invalid-border-color: var(--warning-color); +$mention-user-pill-bg-color: var(--warning-color); +$notice-primary-color: var(--warning-color); +$pinned-unread-color: var(--warning-color); +$warning-color: var(--warning-color); +$button-danger-disabled-bg-color: var(--warning-color-50pct); // still needs alpha at 0.5 diff --git a/res/themes/light-custom/css/light-custom.scss b/res/themes/light-custom/css/light-custom.scss new file mode 100644 index 0000000000..278ca5f0b1 --- /dev/null +++ b/res/themes/light-custom/css/light-custom.scss @@ -0,0 +1,5 @@ +@import "../../light/css/_paths.scss"; +@import "../../light/css/_fonts.scss"; +@import "../../light/css/_light.scss"; +@import "_custom.scss"; +@import "../../../../res/css/_components.scss"; diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 712d905b43..9bdd712e07 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -5,20 +5,26 @@ 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 $accent-color: #03b381; +$accent-bg-color: rgba(3, 179, 129, 0.16); $notice-primary-color: #ff4b55; +$notice-primary-bg-color: rgba(255, 75, 85, 0.16); $notice-secondary-color: #61708b; $header-panel-bg-color: #f3f8fd; // typical text (dark-on-white in light skin) $primary-fg-color: #2e2f32; $primary-bg-color: #ffffff; +$muted-fg-color: #61708b; // Commonly used in headings and relevant alt text // used for dialog box text $light-fg-color: #747474; @@ -28,15 +34,17 @@ $focus-bg-color: #dddddd; // button UI (white-on-green in light skin) $accent-fg-color: #ffffff; -$accent-color-50pct: #92caad; +$accent-color-50pct: rgba(3, 179, 129, 0.5); //#03b381 in rgb +$accent-color-darker: #92caad; $accent-color-alt: #238CF5; $selection-fg-color: $primary-bg-color; $focus-brightness: 105%; -// red warning colour -$warning-color: $notice-primary-color; +// warning colours +$warning-color: $notice-primary-color; // red +$orange-warning-color: #ff8d13; // used for true warnings // background colour for warnings $warning-bg-color: #DF2A8B; $info-bg-color: #2A9EDF; @@ -165,6 +173,7 @@ $header-divider-color: #91A1C0; // ******************** $roomtile-name-color: #61708b; +$roomtile-badge-fg-color: $accent-fg-color; $roomtile-selected-color: #212121; $roomtile-notified-color: #212121; $roomtile-selected-bg-color: #fff; @@ -194,11 +203,19 @@ $widget-menu-bar-bg-color: $secondary-accent-color; // ******************** +// both $event-highlight-bg-color and $room-warning-bg-color share this value, +// so to not make their order dependent on who depends on who, have a shared value +// defined before both +$yellow-background: #fff8e3; + // event tile lifecycle $event-encrypting-color: #abddbc; $event-sending-color: #ddd; $event-notsent-color: #f44; +$event-highlight-fg-color: $warning-color; +$event-highlight-bg-color: $yellow-background; + // event redaction $event-redacted-fg-color: #e2e2e2; $event-redacted-border-color: #cccccc; @@ -210,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; @@ -224,7 +242,7 @@ $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; $tab-label-active-bg-color: $accent-color; $tab-label-icon-bg-color: #454545; -$tab-label-active-icon-bg-color: #ffffff; +$tab-label-active-icon-bg-color: $tab-label-active-fg-color; // Buttons $button-primary-fg-color: #ffffff; @@ -234,6 +252,10 @@ $button-danger-fg-color: #ffffff; $button-danger-bg-color: $notice-primary-color; $button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color +$button-link-fg-color: $accent-color; +$button-link-bg-color: transparent; + +$visual-bell-bg-color: #faa; // Toggle switch $togglesw-off-color: #c1c9d6; @@ -242,7 +264,7 @@ $togglesw-ball-color: #fff; $progressbar-color: #000; -$room-warning-bg-color: #fff8e3; +$room-warning-bg-color: $yellow-background; $memberstatus-placeholder-color: $roomtile-name-color; @@ -268,9 +290,18 @@ $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; +$interactive-tooltip-bg-color: #27303a; +$interactive-tooltip-fg-color: #ffffff; + +$breadcrumb-placeholder-bg-color: #e8eef5; + +$user-tile-hover-bg-color: $header-panel-bg-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { @@ -311,3 +342,17 @@ $tooltip-timeline-fg-color: #ffffff; color: $accent-color; background-color: $button-secondary-bg-color; } + +@define-mixin mx_Dialog_link { + color: $accent-color; + text-decoration: none; +} + +// diff highlight colors +.hljs-addition { + background: #dfd; +} + +.hljs-deletion { + background: #fdd; +} diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh deleted file mode 100644 index 0b1fa23093..0000000000 --- a/scripts/ci/build.sh +++ /dev/null @@ -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 diff --git a/scripts/ci/end-to-end-tests.sh b/scripts/ci/end-to-end-tests.sh old mode 100644 new mode 100755 index 0ec26df450..2f907dffa2 --- a/scripts/ci/end-to-end-tests.sh +++ b/scripts/ci/end-to-end-tests.sh @@ -6,36 +6,30 @@ 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 -echo "--- Fetching end-to-end tests from master" -scripts/fetchdep.sh matrix-org matrix-react-end-to-end-tests master -pushd matrix-react-end-to-end-tests -ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web +pushd test/end-to-end-tests +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" ./install.sh +# install static webserver to server symlinked local copy of riot +./riot/install-webserver.sh +rm -r logs || true mkdir logs echo "+++ Running end-to-end tests" TESTS_STARTED=1 diff --git a/scripts/ci/install-deps.sh b/scripts/ci/install-deps.sh old mode 100644 new mode 100755 index 6484ebab29..14b5fc5393 --- a/scripts/ci/install-deps.sh +++ b/scripts/ci/install-deps.sh @@ -6,8 +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 $@ diff --git a/scripts/ci/layered-riot-web.sh b/scripts/ci/layered-riot-web.sh new file mode 100755 index 0000000000..f58794b451 --- /dev/null +++ b/scripts/ci/layered-riot-web.sh @@ -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 diff --git a/scripts/ci/riot-unit-tests.sh b/scripts/ci/riot-unit-tests.sh old mode 100644 new mode 100755 index 215af13030..337c0fe6c3 --- a/scripts/ci/riot-unit-tests.sh +++ b/scripts/ci/riot-unit-tests.sh @@ -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 diff --git a/scripts/ci/unit-tests.sh b/scripts/ci/unit-tests.sh deleted file mode 100644 index 5b86190963..0000000000 --- a/scripts/ci/unit-tests.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# -# script which is run by the CI build (after `yarn test`). -# -# clones riot-web develop and runs the tests against our version of react-sdk. - -set -ev - -scripts/ci/build.sh -yarn test diff --git a/scripts/compare-file.js b/scripts/compare-file.js new file mode 100644 index 0000000000..f53275ebfa --- /dev/null +++ b/scripts/compare-file.js @@ -0,0 +1,10 @@ +const fs = require("fs"); + +if (process.argv.length < 4) throw new Error("Missing source and target file arguments"); + +const sourceFile = fs.readFileSync(process.argv[2], 'utf8'); +const targetFile = fs.readFileSync(process.argv[3], 'utf8'); + +if (sourceFile !== targetFile) { + throw new Error("Files do not match"); +} diff --git a/scripts/emoji-data-strip.js b/scripts/emoji-data-strip.js deleted file mode 100644 index 1c3738cab1..0000000000 --- a/scripts/emoji-data-strip.js +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env node - -// This generates src/stripped-emoji.json as used by the EmojiProvider autocomplete -// provider. - -const EMOJIBASE = require('emojibase-data/en/compact.json'); - -const fs = require('fs'); - -const output = EMOJIBASE.map( - (datum) => { - const newDatum = { - name: datum.annotation, - shortname: `:${datum.shortcodes[0]}:`, - category: datum.group, - emoji_order: datum.order, - }; - if (datum.shortcodes.length > 1) { - newDatum.aliases = datum.shortcodes.slice(1).map(s => `:${s}:`); - } - if (datum.emoticon) { - newDatum.aliases_ascii = [ datum.emoticon ]; - } - return newDatum; - } -); - -// Write to a file in src. Changes should be checked into git. This file is copied by -// babel using --copy-files -fs.writeFileSync('./src/stripped-emoji.json', JSON.stringify(output)); diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index f82752bfc5..0142305797 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -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 } diff --git a/scripts/gen-i18n.js b/scripts/gen-i18n.js index 3d3d5af116..a4d53aea2f 100755 --- a/scripts/gen-i18n.js +++ b/scripts/gen-i18n.js @@ -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); diff --git a/scripts/generate-eslint-error-ignore-file b/scripts/generate-eslint-error-ignore-file index 3a635f5a7d..54aacfc9fa 100755 --- a/scripts/generate-eslint-error-ignore-file +++ b/scripts/generate-eslint-error-ignore-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 diff --git a/scripts/reskindex.js b/scripts/reskindex.js index 833151a298..9fb0e1a7c0 100755 --- a/scripts/reskindex.js +++ b/scripts/reskindex.js @@ -2,24 +2,26 @@ 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; } prevFiles = files; var header = args.h || args.header; - var packageJson = JSON.parse(fs.readFileSync('./package.json')); var strm = fs.createWriteStream(componentIndexTmp); @@ -34,22 +36,10 @@ function reskindex() { strm.write(" * so you'd just be trying to swim upstream like a salmon.\n"); strm.write(" * You are not a salmon.\n"); strm.write(" */\n\n"); - - if (packageJson['matrix-react-parent']) { - const parentIndex = packageJson['matrix-react-parent'] + - '/lib/component-index'; - strm.write( -`let components = require('${parentIndex}').components; -if (!components) { - throw new Error("'${parentIndex}' didn't export components"); -} -`); - } else { - strm.write("let components = {};\n"); - } + strm.write("let components = {};\n"); for (var i = 0; i < files.length; ++i) { - var file = files[i].replace('.js', ''); + var file = files[i].replace('.js', '').replace('.tsx', ''); var moduleName = (file.replace(/\//g, '.')); var importName = moduleName.replace(/\./g, "$"); @@ -92,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); diff --git a/src/AddThreepid.js b/src/AddThreepid.js index adeaf84a69..7a3250d0ca 100644 --- a/src/AddThreepid.js +++ b/src/AddThreepid.js @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,8 +16,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import MatrixClientPeg from './MatrixClientPeg'; +import {MatrixClientPeg} from './MatrixClientPeg'; +import * as sdk from './index'; +import Modal from './Modal'; import { _t } from './languageHandler'; +import IdentityAuthClient from './IdentityAuthClient'; + +function getIdServerDomain() { + return MatrixClientPeg.get().idBaseUrl.split("://")[1]; +} /** * Allows a user to add a third party identifier to their homeserver and, @@ -25,21 +33,25 @@ import { _t } from './languageHandler'; * This involves getting an email token from the identity server to "prove" that * the client owns the given email address, which is then passed to the * add threepid API on the homeserver. + * + * Diagrams of the intended API flows here are available at: + * + * https://gist.github.com/jryans/839a09bf0c5a70e2f36ed990d50ed928 */ export default class AddThreepid { constructor() { this.clientSecret = MatrixClientPeg.get().generateClientSecret(); + this.sessionId = null; + this.submitUrl = null; } /** - * Attempt to add an email threepid. This will trigger a side-effect of - * sending an email to the provided email address. + * Attempt to add an email threepid to the homeserver. + * This will trigger a side-effect of sending an email to the provided email address. * @param {string} emailAddress The email address to add - * @param {boolean} bind If True, bind this email to this mxid on the Identity Server * @return {Promise} Resolves when the email has been sent. Then call checkEmailLinkClicked(). */ - addEmailAddress(emailAddress, bind) { - this.bind = bind; + addEmailAddress(emailAddress) { return MatrixClientPeg.get().requestAdd3pidEmailToken(emailAddress, this.clientSecret, 1).then((res) => { this.sessionId = res.sid; return res; @@ -54,19 +66,50 @@ export default class AddThreepid { } /** - * Attempt to add a msisdn threepid. This will trigger a side-effect of - * sending a test message to the provided phone number. + * Attempt to bind an email threepid on the identity server via the homeserver. + * This will trigger a side-effect of sending an email to the provided email address. + * @param {string} emailAddress The email address to add + * @return {Promise} Resolves when the email has been sent. Then call checkEmailLinkClicked(). + */ + async bindEmailAddress(emailAddress) { + this.bind = true; + if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) { + // For separate bind, request a token directly from the IS. + const authClient = new IdentityAuthClient(); + const identityAccessToken = await authClient.getAccessToken(); + return MatrixClientPeg.get().requestEmailToken( + emailAddress, this.clientSecret, 1, + undefined, undefined, identityAccessToken, + ).then((res) => { + this.sessionId = res.sid; + return res; + }, function(err) { + if (err.errcode === 'M_THREEPID_IN_USE') { + err.message = _t('This email address is already in use'); + } else if (err.httpStatus) { + err.message = err.message + ` (Status ${err.httpStatus})`; + } + throw err; + }); + } else { + // For tangled bind, request a token via the HS. + return this.addEmailAddress(emailAddress); + } + } + + /** + * Attempt to add a MSISDN threepid to the homeserver. + * This will trigger a side-effect of sending an SMS to the provided phone number. * @param {string} phoneCountry The ISO 2 letter code of the country to resolve phoneNumber in * @param {string} phoneNumber The national or international formatted phone number to add - * @param {boolean} bind If True, bind this phone number to this mxid on the Identity Server * @return {Promise} Resolves when the text message has been sent. Then call haveMsisdnToken(). */ - addMsisdn(phoneCountry, phoneNumber, bind) { - this.bind = bind; + addMsisdn(phoneCountry, phoneNumber) { return MatrixClientPeg.get().requestAdd3pidMsisdnToken( phoneCountry, phoneNumber, this.clientSecret, 1, ).then((res) => { this.sessionId = res.sid; + this.submitUrl = res.submit_url; return res; }, function(err) { if (err.errcode === 'M_THREEPID_IN_USE') { @@ -78,49 +121,185 @@ export default class AddThreepid { }); } + /** + * Attempt to bind a MSISDN threepid on the identity server via the homeserver. + * This will trigger a side-effect of sending an SMS to the provided phone number. + * @param {string} phoneCountry The ISO 2 letter code of the country to resolve phoneNumber in + * @param {string} phoneNumber The national or international formatted phone number to add + * @return {Promise} Resolves when the text message has been sent. Then call haveMsisdnToken(). + */ + async bindMsisdn(phoneCountry, phoneNumber) { + this.bind = true; + if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) { + // For separate bind, request a token directly from the IS. + const authClient = new IdentityAuthClient(); + const identityAccessToken = await authClient.getAccessToken(); + return MatrixClientPeg.get().requestMsisdnToken( + phoneCountry, phoneNumber, this.clientSecret, 1, + undefined, undefined, identityAccessToken, + ).then((res) => { + this.sessionId = res.sid; + return res; + }, function(err) { + if (err.errcode === 'M_THREEPID_IN_USE') { + err.message = _t('This phone number is already in use'); + } else if (err.httpStatus) { + err.message = err.message + ` (Status ${err.httpStatus})`; + } + throw err; + }); + } else { + // For tangled bind, request a token via the HS. + return this.addMsisdn(phoneCountry, phoneNumber); + } + } + /** * Checks if the email link has been clicked by attempting to add the threepid * @return {Promise} Resolves if the email address was added. Rejects with an object * with a "message" property which contains a human-readable message detailing why * the request failed. */ - checkEmailLinkClicked() { - const identityServerDomain = MatrixClientPeg.get().idBaseUrl.split("://")[1]; - return MatrixClientPeg.get().addThreePid({ - sid: this.sessionId, - client_secret: this.clientSecret, - id_server: identityServerDomain, - }, this.bind).catch(function(err) { + async checkEmailLinkClicked() { + try { + if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) { + if (this.bind) { + const authClient = new IdentityAuthClient(); + const identityAccessToken = await authClient.getAccessToken(); + await MatrixClientPeg.get().bindThreePid({ + sid: this.sessionId, + client_secret: this.clientSecret, + id_server: getIdServerDomain(), + id_access_token: identityAccessToken, + }); + } else { + try { + await this._makeAddThreepidOnlyRequest(); + + // The spec has always required this to use UI auth but synapse briefly + // implemented it without, so this may just succeed and that's OK. + return; + } catch (e) { + if (e.httpStatus !== 401 || !e.data || !e.data.flows) { + // doesn't look like an interactive-auth failure + throw e; + } + + // pop up an interactive auth dialog + const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog"); + + const { finished } = Modal.createTrackedDialog('Add Email', '', InteractiveAuthDialog, { + title: _t("Add Email Address"), + matrixClient: MatrixClientPeg.get(), + authData: e.data, + makeRequest: this._makeAddThreepidOnlyRequest, + }); + return finished; + } + } + } else { + await MatrixClientPeg.get().addThreePid({ + sid: this.sessionId, + client_secret: this.clientSecret, + id_server: getIdServerDomain(), + }, this.bind); + } + } catch (err) { if (err.httpStatus === 401) { err.message = _t('Failed to verify email address: make sure you clicked the link in the email'); } else if (err.httpStatus) { err.message += ` (Status ${err.httpStatus})`; } throw err; + } + } + + /** + * @param {Object} auth UI auth object + * @return {Promise} Response from /3pid/add call (in current spec, an empty object) + */ + _makeAddThreepidOnlyRequest = (auth) => { + return MatrixClientPeg.get().addThreePidOnly({ + sid: this.sessionId, + client_secret: this.clientSecret, + auth, }); } /** * Takes a phone number verification code as entered by the user and validates * it with the ID server, then if successful, adds the phone number. - * @param {string} token phone number verification code as entered by the user + * @param {string} msisdnToken phone number verification code as entered by the user * @return {Promise} Resolves if the phone number was added. Rejects with an object * with a "message" property which contains a human-readable message detailing why * the request failed. */ - haveMsisdnToken(token) { - return MatrixClientPeg.get().submitMsisdnToken( - this.sessionId, this.clientSecret, token, - ).then((result) => { - if (result.errcode) { - throw result; + async haveMsisdnToken(msisdnToken) { + const authClient = new IdentityAuthClient(); + const supportsSeparateAddAndBind = + await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind(); + + let result; + if (this.submitUrl) { + result = await MatrixClientPeg.get().submitMsisdnTokenOtherUrl( + this.submitUrl, + this.sessionId, + this.clientSecret, + msisdnToken, + ); + } else if (this.bind || !supportsSeparateAddAndBind) { + result = await MatrixClientPeg.get().submitMsisdnToken( + this.sessionId, + this.clientSecret, + msisdnToken, + await authClient.getAccessToken(), + ); + } else { + throw new Error("The add / bind with MSISDN flow is misconfigured"); + } + if (result.errcode) { + throw result; + } + + if (supportsSeparateAddAndBind) { + if (this.bind) { + await MatrixClientPeg.get().bindThreePid({ + sid: this.sessionId, + client_secret: this.clientSecret, + id_server: getIdServerDomain(), + id_access_token: await authClient.getAccessToken(), + }); + } else { + try { + await this._makeAddThreepidOnlyRequest(); + + // The spec has always required this to use UI auth but synapse briefly + // implemented it without, so this may just succeed and that's OK. + return; + } catch (e) { + if (e.httpStatus !== 401 || !e.data || !e.data.flows) { + // doesn't look like an interactive-auth failure + throw e; + } + + // pop up an interactive auth dialog + const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog"); + + const { finished } = Modal.createTrackedDialog('Add MSISDN', '', InteractiveAuthDialog, { + title: _t("Add Phone Number"), + matrixClient: MatrixClientPeg.get(), + authData: e.data, + makeRequest: this._makeAddThreepidOnlyRequest, + }); + return finished; + } } - const identityServerDomain = MatrixClientPeg.get().idBaseUrl.split("://")[1]; - return MatrixClientPeg.get().addThreePid({ + } else { + await MatrixClientPeg.get().addThreePid({ sid: this.sessionId, client_secret: this.clientSecret, - id_server: identityServerDomain, + id_server: getIdServerDomain(), }, this.bind); - }); + } } } diff --git a/src/Analytics.js b/src/Analytics.js index 3e208ad6bd..c96cfdefee 100644 --- a/src/Analytics.js +++ b/src/Analytics.js @@ -1,24 +1,27 @@ /* - 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'; import SdkConfig from './SdkConfig'; import Modal from './Modal'; -import sdk from './index'; +import * as sdk from './index'; const hashRegex = /#\/(groups?|room|user|settings|register|login|forgot_password|home|directory)/; const hashVarRegex = /#\/(group|room|user)\/.*$/; @@ -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 ''; } +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:

- { _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:') }
{ rows.map((row) => @@ -300,10 +375,10 @@ class Analytics { , }); - } + }; } if (!global.mxAnalytics) { global.mxAnalytics = new Analytics(); } -module.exports = global.mxAnalytics; +export default global.mxAnalytics; diff --git a/src/AsyncWrapper.js b/src/AsyncWrapper.js new file mode 100644 index 0000000000..b7b81688e1 --- /dev/null +++ b/src/AsyncWrapper.js @@ -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 ; + } else if (this.state.error) { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); + return + {_t("Unable to load! Check your network connectivity and try again.")} + + ; + } else { + // show a spinner until the component is loaded. + const Spinner = sdk.getComponent("elements.Spinner"); + return ; + } + }, +}); + diff --git a/src/Avatar.js b/src/Avatar.js index 2e15874b4e..217b196348 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -15,13 +15,14 @@ limitations under the License. */ 'use strict'; -import {ContentRepo} from 'matrix-js-sdk'; -import MatrixClientPeg from './MatrixClientPeg'; +import {MatrixClientPeg} from './MatrixClientPeg'; import DMRoomMap from './utils/DMRoomMap'; +import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; -module.exports = { - avatarUrlForMember: function(member, width, height, resizeMethod) { - let url = member.getAvatarUrl( +export function avatarUrlForMember(member, width, height, resizeMethod) { + let url; + if (member && member.getAvatarUrl) { + url = member.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), Math.floor(width * window.devicePixelRatio), Math.floor(height * window.devicePixelRatio), @@ -29,101 +30,108 @@ module.exports = { false, false, ); - if (!url) { - // member can be null here currently since on invites, the JS SDK - // does not have enough info to build a RoomMember object for - // the inviter. - url = this.defaultAvatarUrlForString(member ? member.userId : ''); + } + if (!url) { + // member can be null here currently since on invites, the JS SDK + // does not have enough info to build a RoomMember object for + // the inviter. + url = defaultAvatarUrlForString(member ? member.userId : ''); + } + return url; +} + +export function avatarUrlForUser(user, width, height, resizeMethod) { + const url = getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl, + Math.floor(width * window.devicePixelRatio), + Math.floor(height * window.devicePixelRatio), + resizeMethod, + ); + if (!url || url.length === 0) { + return null; + } + return url; +} + +export function defaultAvatarUrlForString(s) { + const images = ['03b381', '368bd6', 'ac3ba8']; + let total = 0; + for (let i = 0; i < s.length; ++i) { + total += s.charCodeAt(i); + } + return require('../res/img/' + images[total % images.length] + '.png'); +} + +/** + * returns the first (non-sigil) character of 'name', + * converted to uppercase + * @param {string} name + * @return {string} the first letter + */ +export function getInitialLetter(name) { + if (!name) { + // XXX: We should find out what causes the name to sometimes be falsy. + console.trace("`name` argument to `getInitialLetter` not supplied"); + return undefined; + } + if (name.length < 1) { + return undefined; + } + + let idx = 0; + const initial = name[0]; + if ((initial === '@' || initial === '#' || initial === '+') && name[1]) { + idx++; + } + + // string.codePointAt(0) would do this, but that isn't supported by + // some browsers (notably PhantomJS). + let chars = 1; + const first = name.charCodeAt(idx); + + // check if it’s the start of a surrogate pair + if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) { + const second = name.charCodeAt(idx+1); + if (second >= 0xDC00 && second <= 0xDFFF) { + chars++; } - return url; - }, + } - avatarUrlForUser: function(user, width, height, resizeMethod) { - const url = ContentRepo.getHttpUriForMxc( - MatrixClientPeg.get().getHomeserverUrl(), user.avatarUrl, - Math.floor(width * window.devicePixelRatio), - Math.floor(height * window.devicePixelRatio), - resizeMethod, - ); - if (!url || url.length === 0) { - return null; - } - return url; - }, + const firstChar = name.substring(idx, idx+chars); + return firstChar.toUpperCase(); +} - defaultAvatarUrlForString: function(s) { - const images = ['03b381', '368bd6', 'ac3ba8']; - let total = 0; - for (let i = 0; i < s.length; ++i) { - total += s.charCodeAt(i); - } - return require('../res/img/' + images[total % images.length] + '.png'); - }, +export function avatarUrlForRoom(room, width, height, resizeMethod) { + if (!room) return null; // null-guard - /** - * returns the first (non-sigil) character of 'name', - * converted to uppercase - * @param {string} name - * @return {string} the first letter - */ - getInitialLetter(name) { - if (name.length < 1) { - return undefined; - } + const explicitRoomAvatar = room.getAvatarUrl( + MatrixClientPeg.get().getHomeserverUrl(), + width, + height, + resizeMethod, + false, + ); + if (explicitRoomAvatar) { + return explicitRoomAvatar; + } - let idx = 0; - const initial = name[0]; - if ((initial === '@' || initial === '#' || initial === '+') && name[1]) { - idx++; - } - - // string.codePointAt(0) would do this, but that isn't supported by - // some browsers (notably PhantomJS). - let chars = 1; - const first = name.charCodeAt(idx); - - // check if it’s the start of a surrogate pair - if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) { - const second = name.charCodeAt(idx+1); - if (second >= 0xDC00 && second <= 0xDFFF) { - chars++; - } - } - - const firstChar = name.substring(idx, idx+chars); - return firstChar.toUpperCase(); - }, - - avatarUrlForRoom(room, width, height, resizeMethod) { - const explicitRoomAvatar = room.getAvatarUrl( + let otherMember = null; + const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); + if (otherUserId) { + otherMember = room.getMember(otherUserId); + } else { + // if the room is not marked as a 1:1, but only has max 2 members + // then still try to show any avatar (pref. other member) + otherMember = room.getAvatarFallbackMember(); + } + if (otherMember) { + return otherMember.getAvatarUrl( MatrixClientPeg.get().getHomeserverUrl(), width, height, resizeMethod, false, ); - if (explicitRoomAvatar) { - return explicitRoomAvatar; - } - - let otherMember = null; - const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); - if (otherUserId) { - otherMember = room.getMember(otherUserId); - } else { - // if the room is not marked as a 1:1, but only has max 2 members - // then still try to show any avatar (pref. other member) - otherMember = room.getAvatarFallbackMember(); - } - if (otherMember) { - return otherMember.getAvatarUrl( - MatrixClientPeg.get().getHomeserverUrl(), - width, - height, - resizeMethod, - false, - ); - } - return null; - }, -}; + } + return null; +} diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 54310d1849..5d809eb28f 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -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,7 +19,9 @@ 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'; /** * Base class for classes that provide platform-specific functionality @@ -36,6 +39,7 @@ export default class BasePlatform { _onAction(payload: Object) { switch (payload.action) { + case 'on_client_not_viable': case 'on_logged_out': this.setNotificationCount(0); break; @@ -127,6 +131,18 @@ export default class BasePlatform { throw new Error("Unimplemented"); } + supportsAutoHideMenuBar(): boolean { + return false; + } + + async getAutoHideMenuBarEnabled(): boolean { + return false; + } + + async setAutoHideMenuBarEnabled(enabled: boolean): void { + throw new Error("Unimplemented"); + } + supportsMinimizeToTray(): boolean { return false; } @@ -138,4 +154,38 @@ export default class BasePlatform { async setMinimizeToTrayEnabled(enabled: boolean): void { throw new Error("Unimplemented"); } + + /** + * Get our platform specific EventIndexManager. + * + * @return {BaseEventIndexManager} The EventIndex manager for our platform, + * can be null if the platform doesn't support event indexing. + */ + 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 + } } diff --git a/src/CallHandler.js b/src/CallHandler.js index e47209eebe..8284e788b4 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017, 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -52,10 +53,10 @@ limitations under the License. * } */ -import MatrixClientPeg from './MatrixClientPeg'; +import {MatrixClientPeg} from './MatrixClientPeg'; import PlatformPeg from './PlatformPeg'; import Modal from './Modal'; -import sdk from './index'; +import * as sdk from './index'; import { _t } from './languageHandler'; import Matrix from 'matrix-js-sdk'; import dis from './dispatcher'; @@ -63,7 +64,8 @@ import SdkConfig from './SdkConfig'; import { showUnknownDeviceDialogForCalls } from './cryptodevices'; import WidgetUtils from './utils/WidgetUtils'; import WidgetEchoStore from './stores/WidgetEchoStore'; -import ScalarAuthClient from './ScalarAuthClient'; +import SettingsStore, { SettingLevel } from './settings/SettingsStore'; +import {generateHumanReadableId} from "./utils/NamingUtils"; global.mxCalls = { //room_id: MatrixCall @@ -78,13 +80,26 @@ function play(audioId) { // which listens? const audio = document.getElementById(audioId); if (audio) { + const playAudio = async () => { + try { + // This still causes the chrome debugger to break on promise rejection if + // the promise is rejected, even though we're catching the exception. + await audio.play(); + } catch (e) { + // This is usually because the user hasn't interacted with the document, + // or chrome doesn't think so and is denying the request. Not sure what + // we can really do here... + // https://github.com/vector-im/riot-web/issues/7657 + console.log("Unable to play audio clip", e); + } + }; if (audioPromises[audioId]) { audioPromises[audioId] = audioPromises[audioId].then(()=>{ audio.load(); - return audio.play(); + return playAudio(); }); } else { - audioPromises[audioId] = audio.play(); + audioPromises[audioId] = playAudio(); } } } @@ -117,19 +132,18 @@ function _reAttemptCall(call) { function _setCallListeners(call) { call.on("error", function(err) { - console.error("Call error: %s", err); - console.error(err.stack); + console.error("Call error:", err); if (err.code === 'unknown_devices') { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); 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); @@ -146,8 +160,15 @@ function _setCallListeners(call) { }, }); } else { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + if ( + MatrixClientPeg.get().getTurnServers().length === 0 && + SettingsStore.getValue("fallbackICEServerAllowed") === null + ) { + _showICEFallbackPrompt(); + return; + } + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Call Failed', '', ErrorDialog, { title: _t('Call Failed'), description: err.message, @@ -197,7 +218,7 @@ function _setCallListeners(call) { function _setCallState(call, roomId, status) { console.log( - "Call state in %s changed to %s (%s)", roomId, status, (call ? call.call_state : "-"), + `Call state in ${roomId} changed to ${status} (${call ? call.call_state : "-"})`, ); calls[roomId] = call; @@ -217,6 +238,36 @@ function _setCallState(call, roomId, status) { }); } +function _showICEFallbackPrompt() { + const cli = MatrixClientPeg.get(); + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + const code = sub => {sub}; + Modal.createTrackedDialog('No TURN servers', '', QuestionDialog, { + title: _t("Call failed due to misconfigured server"), + description:
+

{_t( + "Please ask the administrator of your homeserver " + + "(%(homeserverDomain)s) to configure a TURN server in " + + "order for calls to work reliably.", + { homeserverDomain: cli.getDomain() }, { code }, + )}

+

{_t( + "Alternatively, you can try to use the public server at " + + "turn.matrix.org, but this will not be as reliable, and " + + "it will share your IP address with that server. You can also manage " + + "this in Settings.", + null, { code }, + )}

+
, + button: _t('Try using turn.matrix.org'), + cancelButton: _t('OK'), + onFinished: (allow) => { + SettingsStore.setValue("fallbackICEServerAllowed", null, SettingLevel.DEVICE, allow); + cli.setFallbackICEServerAllowed(allow); + }, + }, null, true); +} + function _onAction(payload) { function placeCall(newCall) { _setCallListeners(newCall); @@ -251,7 +302,7 @@ function _onAction(payload) { switch (payload.action) { case 'place_call': { - if (module.exports.getAnyActiveCall()) { + if (callHandler.getAnyActiveCall()) { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Call Handler', 'Existing Call', ErrorDialog, { title: _t('Existing Call'), @@ -284,7 +335,7 @@ function _onAction(payload) { }); return; } else if (members.length === 2) { - console.log("Place %s call in %s", payload.type, payload.room_id); + console.info("Place %s call in %s", payload.type, payload.room_id); const call = Matrix.createNewMatrixCall(MatrixClientPeg.get(), payload.room_id); placeCall(call); } else { // > 2 @@ -299,12 +350,12 @@ function _onAction(payload) { } break; case 'place_conference_call': - console.log("Place conference call in %s", payload.room_id); + console.info("Place conference call in %s", payload.room_id); _startCallApp(payload.room_id, payload.type); break; case 'incoming_call': { - if (module.exports.getAnyActiveCall()) { + if (callHandler.getAnyActiveCall()) { // ignore multiple incoming calls. in future, we may want a line-1/line-2 setup. // we avoid rejecting with "busy" in case the user wants to answer it on a different device. // in future we could signal a "local busy" as a warning to the caller. @@ -344,28 +395,6 @@ function _onAction(payload) { } async function _startCallApp(roomId, type) { - // check for a working intgrations 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 scalarClient = new ScalarAuthClient(); - let haveScalar = false; - try { - await scalarClient.connect(); - haveScalar = scalarClient.hasCredentials(); - } catch (e) { - // fall through - } - 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, @@ -401,30 +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 { - widgetUrl = SdkConfig.get().integrations_rest_url + '/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_' + @@ -452,11 +473,22 @@ async function _startCallApp(roomId, type) { // with the dispatcher once if (!global.mxCallHandler) { dis.register(_onAction); + // add empty handlers for media actions, otherwise the media keys + // end up causing the audio elements with our ring/ringback etc + // audio clips in to play. + if (navigator.mediaSession) { + navigator.mediaSession.setActionHandler('play', function() {}); + navigator.mediaSession.setActionHandler('pause', function() {}); + navigator.mediaSession.setActionHandler('seekbackward', function() {}); + navigator.mediaSession.setActionHandler('seekforward', function() {}); + navigator.mediaSession.setActionHandler('previoustrack', function() {}); + navigator.mediaSession.setActionHandler('nexttrack', function() {}); + } } const callHandler = { getCallForRoom: function(roomId) { - let call = module.exports.getCall(roomId); + let call = callHandler.getCall(roomId); if (call) return call; if (ConferenceHandler) { @@ -516,4 +548,4 @@ if (global.mxCallHandler === undefined) { global.mxCallHandler = callHandler; } -module.exports = global.mxCallHandler; +export default global.mxCallHandler; diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 9a1c9d70b8..a0364f798a 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -18,6 +18,11 @@ import * as Matrix from 'matrix-js-sdk'; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; export default { + hasAnyLabeledDevices: async function() { + const devices = await navigator.mediaDevices.enumerateDevices(); + return devices.some(d => !!d.label); + }, + getDevices: function() { // Only needed for Electron atm, though should work in modern browsers // once permission has been granted to the webapp @@ -26,8 +31,6 @@ export default { const audioinput = []; const videoinput = []; - if (devices.some((device) => !device.label)) return false; - devices.forEach((device) => { switch (device.kind) { case 'audiooutput': audiooutput.push(device); break; diff --git a/src/ContentMessages.js b/src/ContentMessages.js index ee3e8f1390..34379c029b 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -17,11 +17,10 @@ limitations under the License. 'use strict'; -import Promise from 'bluebird'; import extend from './extend'; import dis from './dispatcher'; -import MatrixClientPeg from './MatrixClientPeg'; -import sdk from './index'; +import {MatrixClientPeg} from './MatrixClientPeg'; +import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; import RoomViewStore from './stores/RoomViewStore'; @@ -59,40 +58,38 @@ export class UploadCanceledError extends Error {} * and a thumbnail key. */ function createThumbnail(element, inputWidth, inputHeight, mimeType) { - const deferred = Promise.defer(); + return new Promise((resolve) => { + let targetWidth = inputWidth; + let targetHeight = inputHeight; + if (targetHeight > MAX_HEIGHT) { + targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight)); + targetHeight = MAX_HEIGHT; + } + if (targetWidth > MAX_WIDTH) { + targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth)); + targetWidth = MAX_WIDTH; + } - let targetWidth = inputWidth; - let targetHeight = inputHeight; - if (targetHeight > MAX_HEIGHT) { - targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight)); - targetHeight = MAX_HEIGHT; - } - if (targetWidth > MAX_WIDTH) { - targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth)); - targetWidth = MAX_WIDTH; - } - - const canvas = document.createElement("canvas"); - canvas.width = targetWidth; - canvas.height = targetHeight; - canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight); - canvas.toBlob(function(thumbnail) { - deferred.resolve({ - info: { - thumbnail_info: { - w: targetWidth, - h: targetHeight, - mimetype: thumbnail.type, - size: thumbnail.size, + const canvas = document.createElement("canvas"); + canvas.width = targetWidth; + canvas.height = targetHeight; + canvas.getContext("2d").drawImage(element, 0, 0, targetWidth, targetHeight); + canvas.toBlob(function(thumbnail) { + resolve({ + info: { + thumbnail_info: { + w: targetWidth, + h: targetHeight, + mimetype: thumbnail.type, + size: thumbnail.size, + }, + w: inputWidth, + h: inputHeight, }, - w: inputWidth, - h: inputHeight, - }, - thumbnail: thumbnail, - }); - }, mimeType); - - return deferred.promise; + thumbnail: thumbnail, + }); + }, mimeType); + }); } /** @@ -179,30 +176,29 @@ function infoForImageFile(matrixClient, roomId, imageFile) { * @return {Promise} A promise that resolves with the video image element. */ function loadVideoElement(videoFile) { - const deferred = Promise.defer(); + return new Promise((resolve, reject) => { + // Load the file into an html element + const video = document.createElement("video"); - // Load the file into an html element - const video = document.createElement("video"); + const reader = new FileReader(); - const reader = new FileReader(); - reader.onload = function(e) { - video.src = e.target.result; + reader.onload = function(e) { + video.src = e.target.result; - // Once ready, returns its size - // Wait until we have enough data to thumbnail the first frame. - video.onloadeddata = function() { - deferred.resolve(video); + // Once ready, returns its size + // Wait until we have enough data to thumbnail the first frame. + video.onloadeddata = function() { + resolve(video); + }; + video.onerror = function(e) { + reject(e); + }; }; - video.onerror = function(e) { - deferred.reject(e); + reader.onerror = function(e) { + reject(e); }; - }; - reader.onerror = function(e) { - deferred.reject(e); - }; - reader.readAsDataURL(videoFile); - - return deferred.promise; + reader.readAsDataURL(videoFile); + }); } /** @@ -236,16 +232,16 @@ function infoForVideoFile(matrixClient, roomId, videoFile) { * is read. */ function readFileAsArrayBuffer(file) { - const deferred = Promise.defer(); - const reader = new FileReader(); - reader.onload = function(e) { - deferred.resolve(e.target.result); - }; - reader.onerror = function(e) { - deferred.reject(e); - }; - reader.readAsArrayBuffer(file); - return deferred.promise; + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = function(e) { + resolve(e.target.result); + }; + reader.onerror = function(e) { + reject(e); + }; + reader.readAsArrayBuffer(file); + }); } /** @@ -425,24 +421,33 @@ export default class ContentMessages { } const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); + let uploadAll = false; + // Promise to complete before sending next file into room, used for synchronisation of file-sending + // to match the order the files were specified in + let promBefore = Promise.resolve(); for (let i = 0; i < okFiles.length; ++i) { const file = okFiles[i]; - const shouldContinue = await new Promise((resolve) => { - Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { - file, - currentIndex: i, - totalFiles: okFiles.length, - onFinished: (shouldContinue) => { - resolve(shouldContinue); - }, + if (!uploadAll) { + const shouldContinue = await new Promise((resolve) => { + Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { + file, + currentIndex: i, + totalFiles: okFiles.length, + onFinished: (shouldContinue, shouldUploadAll) => { + if (shouldUploadAll) { + uploadAll = true; + } + resolve(shouldContinue); + }, + }); }); - }); - if (!shouldContinue) break; - this._sendContentToRoom(file, roomId, matrixClient); + if (!shouldContinue) break; + } + promBefore = this._sendContentToRoom(file, roomId, matrixClient, promBefore); } } - _sendContentToRoom(file, roomId, matrixClient) { + _sendContentToRoom(file, roomId, matrixClient, promBefore) { const content = { body: file.name || 'Attachment', info: { @@ -455,33 +460,34 @@ export default class ContentMessages { content.info.mimetype = file.type; } - const def = Promise.defer(); - if (file.type.indexOf('image/') == 0) { - content.msgtype = 'm.image'; - infoForImageFile(matrixClient, roomId, file).then((imageInfo)=>{ - extend(content.info, imageInfo); - def.resolve(); - }, (error)=>{ - console.error(error); + const prom = new Promise((resolve) => { + if (file.type.indexOf('image/') == 0) { + content.msgtype = 'm.image'; + infoForImageFile(matrixClient, roomId, file).then((imageInfo)=>{ + extend(content.info, imageInfo); + resolve(); + }, (error)=>{ + console.error(error); + content.msgtype = 'm.file'; + resolve(); + }); + } else if (file.type.indexOf('audio/') == 0) { + content.msgtype = 'm.audio'; + resolve(); + } else if (file.type.indexOf('video/') == 0) { + content.msgtype = 'm.video'; + infoForVideoFile(matrixClient, roomId, file).then((videoInfo)=>{ + extend(content.info, videoInfo); + resolve(); + }, (error)=>{ + content.msgtype = 'm.file'; + resolve(); + }); + } else { content.msgtype = 'm.file'; - def.resolve(); - }); - } else if (file.type.indexOf('audio/') == 0) { - content.msgtype = 'm.audio'; - def.resolve(); - } else if (file.type.indexOf('video/') == 0) { - content.msgtype = 'm.video'; - infoForVideoFile(matrixClient, roomId, file).then((videoInfo)=>{ - extend(content.info, videoInfo); - def.resolve(); - }, (error)=>{ - content.msgtype = 'm.file'; - def.resolve(); - }); - } else { - content.msgtype = 'm.file'; - def.resolve(); - } + resolve(); + } + }); const upload = { fileName: file.name || 'Attachment', @@ -503,7 +509,7 @@ export default class ContentMessages { dis.dispatch({action: 'upload_progress', upload: upload}); } - return def.promise.then(function() { + return prom.then(function() { // XXX: upload.promise must be the promise that // is returned by uploadFile as it has an abort() // method hacked onto it. @@ -514,7 +520,10 @@ export default class ContentMessages { content.file = result.file; content.url = result.url; }); - }).then(function(url) { + }).then((url) => { + // Await previous message being sent into the room + return promBefore; + }).then(function() { return matrixClient.sendMessage(roomId, content); }, function(err) { error = err; diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js new file mode 100644 index 0000000000..da7c0673d2 --- /dev/null +++ b/src/CrossSigningManager.js @@ -0,0 +1,270 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import Modal from './Modal'; +import * as sdk from './index'; +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 +// during the same single operation. Use `accessSecretStorage` below to scope a +// single secret storage operation, as it will clear the cached keys once the +// operation ends. +let secretStorageKeys = {}; +let secretStorageBeingAccessed = false; + +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"); + } + const [name, info] = keyInfoEntries[0]; + + // Check the in-memory cache + if (isCachingAllowed() && secretStorageKeys[name]) { + return [name, secretStorageKeys[name]]; + } + + const inputToKey = async ({ passphrase, recoveryKey }) => { + if (passphrase) { + return deriveKey( + passphrase, + info.passphrase.salt, + info.passphrase.iterations, + ); + } else { + return decodeRecoveryKey(recoveryKey); + } + }; + const AccessSecretStorageDialog = + 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 AccessCancelledError(); + } + const key = await inputToKey(input); + + // Save to cache to avoid future prompts in the current session + 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 + * each other in a cycle of sorts) have been bootstrapped before running the + * provided function. + * + * Bootstrapping secret storage may take one of these paths: + * 1. Create secret storage from a passphrase and store cross-signing keys + * in secret storage. + * 2. Access existing secret storage by requesting passphrase and accessing + * cross-signing keys as needed. + * 3. All keys are loaded and there's nothing to do. + * + * Additionally, the secret storage keys are cached during the scope of this function + * to ensure the user is prompted only once for their secret storage + * passphrase. The cache is then cleared once the provided function completes. + * + * @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 () => { }, forceReset = false) { + const cli = MatrixClientPeg.get(); + secretStorageBeingAccessed = true; + try { + 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"), + { + force: forceReset, + }, + null, /* priority = */ false, /* static = */ true, + ); + const [confirmed] = await finished; + if (!confirmed) { + throw new Error("Secret storage creation canceled"); + } + } else { + const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog"); + await cli.bootstrapSecretStorage({ + authUploadDeviceSigningKeys: async (makeRequest) => { + const { finished } = Modal.createTrackedDialog( + 'Cross-signing keys dialog', '', InteractiveAuthDialog, + { + title: _t("Setting up keys"), + matrixClient: MatrixClientPeg.get(), + makeRequest, + }, + ); + const [confirmed] = await finished; + if (!confirmed) { + throw new Error("Cross-signing key upload auth canceled"); + } + }, + getBackupPassphrase: promptForBackupPassphrase, + }); + } + + // `return await` needed here to ensure `finally` block runs after the + // inner operation completes. + return await func(); + } finally { + // Clear secret storage key cache now that work is complete + secretStorageBeingAccessed = false; + if (!isCachingAllowed()) { + secretStorageKeys = {}; + } + } +} diff --git a/src/DeviceListener.js b/src/DeviceListener.js new file mode 100644 index 0000000000..f8555c7602 --- /dev/null +++ b/src/DeviceListener.js @@ -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; + } + } +} diff --git a/src/Entities.js b/src/Entities.js deleted file mode 100644 index 8be1da0db8..0000000000 --- a/src/Entities.js +++ /dev/null @@ -1,140 +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 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 ( - - ); - } - - 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 ( - - ); - } - - matches(queryString) { - const name = this.model.displayName || this.model.userId; - return isMatch(queryString, name, this.model.userId); - } -} - - -module.exports = { - newEntity: function(jsx, matchFn) { - const entity = new Entity(); - entity.getJsx = function() { - return jsx; - }; - entity.matches = matchFn; - return entity; - }, - - /** - * @param {RoomMember[]} members - * @return {Entity[]} - */ - fromRoomMembers: function(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[]} - */ - fromUsers: function(users, showInviteButton, inviteFn) { - return users.map(function(u) { - return new UserEntity(u, showInviteButton, inviteFn); - }); - }, -}; diff --git a/src/FromWidgetPostMessageApi.js b/src/FromWidgetPostMessageApi.js index 61c51d4a20..ea76c85643 100644 --- a/src/FromWidgetPostMessageApi.js +++ b/src/FromWidgetPostMessageApi.js @@ -1,6 +1,7 @@ /* Copyright 2018 New Vector Ltd Copyright 2019 Travis Ralston +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the 'License'); you may not use this file except in compliance with the License. @@ -17,9 +18,14 @@ limitations under the License. import URL from 'url'; import dis from './dispatcher'; -import IntegrationManager from './IntegrationManager'; import WidgetMessagingEndpoint from './WidgetMessagingEndpoint'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; +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 = [ @@ -189,17 +195,38 @@ export default class FromWidgetPostMessageApi { const data = event.data.data || event.data.widgetData; const integType = (data && data.integType) ? data.integType : null; const integId = (data && data.integId) ? data.integId : null; - IntegrationManager.open(integType, integId); + + // TODO: Open the right integration manager for the widget + if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) { + IntegrationManagers.sharedInstance().openAll( + MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()), + `type_${integType}`, + integId, + ); + } else { + IntegrationManagers.sharedInstance().getPrimaryManager().open( + MatrixClientPeg.get().getRoom(RoomViewStore.getRoomId()), + `type_${integType}`, + integId, + ); + } } else if (action === 'set_always_on_screen') { // This is a new message: there is no reason to support the deprecated widgetData here 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'}); diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js index cd5ecc790d..9131a89e5d 100644 --- a/src/GroupAddressPicker.js +++ b/src/GroupAddressPicker.js @@ -16,11 +16,12 @@ limitations under the License. import React from 'react'; import Modal from './Modal'; -import sdk from './'; +import * as sdk from './'; import MultiInviter from './utils/MultiInviter'; import { _t } from './languageHandler'; -import MatrixClientPeg from './MatrixClientPeg'; +import {MatrixClientPeg} from './MatrixClientPeg'; import GroupStore from './stores/GroupStore'; +import {allSettled} from "./utils/promise"; export function showGroupInviteDialog(groupId) { return new Promise((resolve, reject) => { @@ -46,7 +47,7 @@ export function showGroupInviteDialog(groupId) { _onGroupInviteFinished(groupId, addrs).then(resolve, reject); }, - }); + }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); }); } @@ -61,7 +62,7 @@ export function showGroupAddRoomDialog(groupId) { ; const checkboxContainer =
{ev.preventDefault();}} - > - - - - , + if (room.world_readable && !hasJoinedRoom) { + previewButton = ( + this.onPreviewClick(ev, room)}>{_t("Preview")} ); } - return rows; + if (hasJoinedRoom) { + joinOrViewButton = ( + this.onViewClick(ev, room)}>{_t("View")} + ); + } else if (!isGuest || room.guest_can_join) { + joinOrViewButton = ( + this.onJoinClick(ev, room)}>{_t("Join")} + ); + } + + let name = room.name || get_display_alias_for_room(room) || _t('Unnamed room'); + if (name.length > MAX_NAME_LENGTH) { + name = `${name.substring(0, MAX_NAME_LENGTH)}...`; + } + + let topic = room.topic || ''; + if (topic.length > MAX_TOPIC_LENGTH) { + topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`; + } + topic = linkifyAndSanitizeHtml(topic); + const avatarUrl = getHttpUriForMxc( + MatrixClientPeg.get().getHomeserverUrl(), + room.avatar_url, 32, 32, "crop", + ); + return ( + this.onRoomClicked(room, ev)} + // cancel onMouseDown otherwise shift-clicking highlights text + onMouseDown={(ev) => {ev.preventDefault();}} + > + + + + + + + ); }, collectScrollPanel: function(element) { @@ -526,20 +535,26 @@ module.exports = React.createClass({ let content; if (this.state.error) { content = this.state.error; - } else if (this.state.protocolsLoading || this.state.loading) { + } else if (this.state.protocolsLoading) { content = ; } else { - const rows = this.getRows(); + const rows = (this.state.publicRooms || []).map(room => this.getRow(room)); // we still show the scrollpanel, at least for now, because // otherwise we don't fetch more because we don't get a fill // request from the scrollpanel because there isn't one + + let spinner; + if (this.state.loading) { + spinner = ; + } + let scrollpanel_content; - if (rows.length == 0) { + if (rows.length === 0 && !this.state.loading) { scrollpanel_content = { _t('No rooms to show') }; } else { - scrollpanel_content =
- - -
{ name }
  - { perms } -
-
{ get_display_alias_for_room(rooms[i]) }
-
- { rooms[i].num_joined_members } -
+ + +
{ name }
  +
{ ev.stopPropagation(); } } + dangerouslySetInnerHTML={{ __html: topic }} /> +
{ get_display_alias_for_room(room) }
+
+ { room.num_joined_members } + {previewButton}{joinOrViewButton}
+ scrollpanel_content =
- { this.getRows() } + { rows }
; } @@ -551,6 +566,7 @@ module.exports = React.createClass({ startAtBottom={false} > { scrollpanel_content } + { spinner } ; } @@ -572,10 +588,9 @@ module.exports = React.createClass({ instance_expected_field_type = this.protocols[protocolName].field_types[last_field]; } - - let placeholder = _t('Search for a room'); - if (!this.state.instanceId) { - placeholder = _t('Search for a room like #example') + ':' + this.state.roomServer; + let placeholder = _t('Find a room…'); + if (!this.state.instanceId || this.state.instanceId === ALL_ROOMS) { + placeholder = _t("Find a room… (e.g. %(exampleRoom)s)", {exampleRoom: "#example:" + this.state.roomServer}); } else if (instance_expected_field_type) { placeholder = instance_expected_field_type.placeholder; } @@ -591,27 +606,39 @@ module.exports = React.createClass({ listHeader =
+ -
; } - - const createRoomButton = ({_t("Create new room")}); + const explanation = + _t("If you can't find the room you're looking for, ask for an invite or Create a new room.", null, + {a: sub => { + return ({sub}); + }}, + ); return (
+ {explanation}
{listHeader} {content} diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 7ef080e235..13b73ec02b 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017, 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,17 +17,16 @@ limitations under the License. */ import React from 'react'; +import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import Matrix from 'matrix-js-sdk'; import { _t, _td } from '../../languageHandler'; -import sdk from '../../index'; -import WhoIsTyping from '../../WhoIsTyping'; -import MatrixClientPeg from '../../MatrixClientPeg'; -import MemberAvatar from '../views/avatars/MemberAvatar'; +import * as sdk from '../../index'; +import {MatrixClientPeg} from '../../MatrixClientPeg'; import Resend from '../../Resend'; import * as cryptodevices from '../../cryptodevices'; import dis from '../../dispatcher'; -import { messageForResourceLimitError } from '../../utils/ErrorUtils'; +import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils'; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; @@ -39,7 +39,7 @@ function getUnsentMessages(room) { }); } -module.exports = React.createClass({ +export default createReactClass({ displayName: 'RoomStatusBar', propTypes: { @@ -220,12 +220,12 @@ module.exports = React.createClass({ }); if (hasUDE) { - title = _t("Message not sent due to unknown devices being present"); + title = _t("Message not sent due to unknown sessions being present"); content = _t( - "Show devices, send anyway or cancel.", + "Show sessions, send anyway or cancel.", {}, { - 'showDevicesText': (sub) => { sub }, + 'showSessionsText': (sub) => { sub }, 'sendAnywayText': (sub) => { sub }, 'cancelText': (sub) => { sub }, }, @@ -273,7 +273,7 @@ module.exports = React.createClass({ unsentMessages[0].error.data && unsentMessages[0].error.data.error ) { - title = unsentMessages[0].error.data.error; + title = messageForSendError(unsentMessages[0].error.data) || unsentMessages[0].error.data.error; } else { title = _t('%(count)s of your messages have not been sent.', { count: unsentMessages.length }); } @@ -290,7 +290,7 @@ module.exports = React.createClass({ } return
- +
{ title } @@ -307,7 +307,7 @@ module.exports = React.createClass({ if (this._shouldShowConnectionError()) { return (
- /!\ + /!\
{ _t('Connectivity to the server has been lost.') } diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 9ca9d3261d..9428de3e22 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -2,6 +2,7 @@ Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd Copyright 2018, 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,37 +17,35 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import classNames from 'classnames'; -import sdk from '../../index'; +import * as sdk from '../../index'; import dis from '../../dispatcher'; -import Unread from '../../Unread'; +import * as Unread from '../../Unread'; import * as RoomNotifs from '../../RoomNotifs'; import * as FormattingUtils from '../../utils/FormattingUtils'; import IndicatorScrollbar from './IndicatorScrollbar'; -import { KeyCode } from '../../Keyboard'; +import {Key} from '../../Keyboard'; import { Group } from 'matrix-js-sdk'; import PropTypes from 'prop-types'; import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; +import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; // turn this on for drop & drag console debugging galore const debug = false; -const RoomSubList = React.createClass({ - displayName: 'RoomSubList', +export default class RoomSubList extends React.PureComponent { + static displayName = 'RoomSubList'; + static debug = debug; - debug: debug, - - propTypes: { + static propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired, label: PropTypes.string.isRequired, tagName: PropTypes.string, addRoomLabel: PropTypes.string, - order: PropTypes.string.isRequired, - // passed through to RoomTile and used to highlight room with `!` regardless of notifications count isInvite: PropTypes.bool, @@ -55,92 +54,158 @@ const RoomSubList = React.createClass({ collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed? onHeaderClick: PropTypes.func, incomingCall: PropTypes.object, - isFiltered: PropTypes.bool, - headerItems: PropTypes.node, // content shown in the sublist header extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles - }, + forceExpand: PropTypes.bool, + }; - getInitialState: function() { + static defaultProps = { + onHeaderClick: function() { + }, // NOP + extraTiles: [], + isInvite: false, + }; + + static getDerivedStateFromProps(props, state) { return { + listLength: props.list.length, + scrollTop: props.list.length === state.listLength ? state.scrollTop : 0, + }; + } + + constructor(props) { + super(props); + + this.state = { hidden: this.props.startAsHidden || false, // some values to get LazyRenderList starting scrollerHeight: 800, scrollTop: 0, + // React 16's getDerivedStateFromProps(props, state) doesn't give the previous props so + // we have to store the length of the list here so we can see if it's changed or not... + listLength: null, }; - }, - getDefaultProps: function() { - return { - onHeaderClick: function() { - }, // NOP - extraTiles: [], - isInvite: false, - }; - }, + this._header = createRef(); + this._subList = createRef(); + this._scroller = createRef(); + this._headerButton = createRef(); + } - componentWillMount: function() { + componentDidMount() { this.dispatcherRef = dis.register(this.onAction); - }, + } - componentWillUnmount: function() { + componentWillUnmount() { dis.unregister(this.dispatcherRef); - }, + } - // The header is collapsable if it is hidden or not stuck + // The header is collapsible if it is hidden or not stuck // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method - isCollapsableOnClick: function() { - const stuck = this.refs.header.dataset.stuck; + isCollapsibleOnClick() { + const stuck = this._header.current.dataset.stuck; if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) { return true; } else { return false; } - }, + } - onAction: function(payload) { - // XXX: Previously RoomList would forceUpdate whenever on_room_read is dispatched, - // but this is no longer true, so we must do it here (and can apply the small - // optimisation of checking that we care about the room being read). - // - // Ultimately we need to transition to a state pushing flow where something - // explicitly notifies the components concerned that the notif count for a room - // has change (e.g. a Flux store). - if (payload.action === 'on_room_read' && - this.props.list.some((r) => r.roomId === payload.roomId) - ) { - this.forceUpdate(); + onAction = (payload) => { + switch (payload.action) { + case 'on_room_read': + // XXX: Previously RoomList would forceUpdate whenever on_room_read is dispatched, + // but this is no longer true, so we must do it here (and can apply the small + // optimisation of checking that we care about the room being read). + // + // Ultimately we need to transition to a state pushing flow where something + // explicitly notifies the components concerned that the notif count for a room + // has change (e.g. a Flux store). + if (this.props.list.some((r) => r.roomId === payload.roomId)) { + this.forceUpdate(); + } + break; + + case 'view_room': + if (this.state.hidden && !this.props.forceExpand && + this.props.list.some((r) => r.roomId === payload.room_id) + ) { + this.toggle(); + } } - }, + }; - onClick: function(ev) { - if (this.isCollapsableOnClick()) { - // The header isCollapsable, so the click is to be interpreted as collapse and truncation logic + toggle = () => { + if (this.isCollapsibleOnClick()) { + // The header isCollapsible, so the click is to be interpreted as collapse and truncation logic const isHidden = !this.state.hidden; this.setState({hidden: isHidden}, () => { this.props.onHeaderClick(isHidden); }); } else { // The header is stuck, so the click is to be interpreted as a scroll to the header - this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition); + this.props.onHeaderClick(this.state.hidden, this._header.current.dataset.originalPosition); } - }, + }; - onRoomTileClick(roomId, ev) { + onClick = (ev) => { + this.toggle(); + }; + + onHeaderKeyDown = (ev) => { + switch (ev.key) { + case Key.ARROW_LEFT: + // On ARROW_LEFT collapse the room sublist + if (!this.state.hidden && !this.props.forceExpand) { + this.onClick(); + } + ev.stopPropagation(); + break; + case Key.ARROW_RIGHT: { + ev.stopPropagation(); + if (this.state.hidden && !this.props.forceExpand) { + // sublist is collapsed, expand it + this.onClick(); + } else if (!this.props.forceExpand) { + // sublist is expanded, go to first room + const element = this._subList.current && this._subList.current.querySelector(".mx_RoomTile"); + if (element) { + element.focus(); + } + } + break; + } + } + }; + + onKeyDown = (ev) => { + switch (ev.key) { + // On ARROW_LEFT go to the sublist header + case Key.ARROW_LEFT: + ev.stopPropagation(); + this._headerButton.current.focus(); + break; + // Consume ARROW_RIGHT so it doesn't cause focus to get sent to composer + case Key.ARROW_RIGHT: + ev.stopPropagation(); + } + }; + + onRoomTileClick = (roomId, ev) => { dis.dispatch({ action: 'view_room', room_id: roomId, - clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)), + clear_search: (ev && (ev.key === Key.ENTER || ev.key === Key.SPACE)), }); - }, + }; - _updateSubListCount: function() { + _updateSubListCount = () => { // Force an update by setting the state to the current state // Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate() // method is honoured this.setState(this.state); - }, + }; - makeRoomTile: function(room) { + makeRoomTile = (room) => { return ; - }, + }; - _onNotifBadgeClick: function(e) { + _onNotifBadgeClick = (e) => { // prevent the roomsublist collapsing e.preventDefault(); e.stopPropagation(); @@ -168,9 +233,9 @@ const RoomSubList = React.createClass({ room_id: room.roomId, }); } - }, + }; - _onInviteBadgeClick: function(e) { + _onInviteBadgeClick = (e) => { // prevent the roomsublist collapsing e.preventDefault(); e.stopPropagation(); @@ -190,32 +255,22 @@ const RoomSubList = React.createClass({ }); } } - }, + }; - _getHeaderJsx: function(isCollapsed) { + onAddRoom = (e) => { + e.stopPropagation(); + if (this.props.onAddRoom) this.props.onAddRoom(); + }; + + _getHeaderJsx(isCollapsed) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + const AccessibleTooltipButton = sdk.getComponent('elements.AccessibleTooltipButton'); const subListNotifications = !this.props.isInvite ? RoomNotifs.aggregateNotificationCount(this.props.list) : {count: 0, highlight: true}; const subListNotifCount = subListNotifications.count; const subListNotifHighlight = subListNotifications.highlight; - let badge; - if (!this.props.collapsed) { - const badgeClasses = classNames({ - 'mx_RoomSubList_badge': true, - 'mx_RoomSubList_badgeHighlight': subListNotifHighlight, - }); - if (subListNotifCount > 0) { - badge =
- { FormattingUtils.formatCount(subListNotifCount) } -
; - } else if (this.props.isInvite && this.props.list.length) { - // no notifications but highlight anyway because this is an invite badge - badge =
{this.props.list.length}
; - } - } - // When collapsed, allow a long hover on the header to show user // the full tag name and room count let title; @@ -231,17 +286,6 @@ const RoomSubList = React.createClass({ ; } - let addRoomButton; - if (this.props.onAddRoom) { - addRoomButton = ( - - ); - } - const len = this.props.list.length + this.props.extraTiles.length; let chevron; if (len) { @@ -250,68 +294,130 @@ const RoomSubList = React.createClass({ 'mx_RoomSubList_chevronRight': isCollapsed, 'mx_RoomSubList_chevronDown': !isCollapsed, }); - chevron = (
); + chevron = (
); } - const tabindex = this.props.isFiltered ? "0" : "-1"; - return ( -
- - { chevron } - {this.props.label} - { incomingCall } - - { badge } - { addRoomButton } -
- ); - }, + return + {({onFocus, isActive, ref}) => { + const tabIndex = isActive ? 0 : -1; - checkOverflow: function() { - if (this.refs.scroller) { - this.refs.scroller.checkOverflow(); + let badge; + if (!this.props.collapsed) { + const badgeClasses = classNames({ + 'mx_RoomSubList_badge': true, + 'mx_RoomSubList_badgeHighlight': subListNotifHighlight, + }); + // Wrap the contents in a div and apply styles to the child div so that the browser default outline works + if (subListNotifCount > 0) { + badge = ( + +
+ { FormattingUtils.formatCount(subListNotifCount) } +
+
+ ); + } else if (this.props.isInvite && this.props.list.length) { + // no notifications but highlight anyway because this is an invite badge + badge = ( + +
+ { this.props.list.length } +
+
+ ); + } + } + + let addRoomButton; + if (this.props.onAddRoom) { + addRoomButton = ( + + ); + } + + return ( +
+ + { chevron } + {this.props.label} + { incomingCall } + + { badge } + { addRoomButton } +
+ ); + } } +
; + } + + checkOverflow = () => { + if (this._scroller.current) { + this._scroller.current.checkOverflow(); } - }, + }; - setHeight: function(height) { - if (this.refs.subList) { - this.refs.subList.style.height = `${height}px`; + setHeight = (height) => { + if (this._subList.current) { + this._subList.current.style.height = `${height}px`; } this._updateLazyRenderHeight(height); - }, + }; - _updateLazyRenderHeight: function(height) { + _updateLazyRenderHeight(height) { this.setState({scrollerHeight: height}); - }, + } - _onScroll: function() { - this.setState({scrollTop: this.refs.scroller.getScrollTop()}); - }, + _onScroll = () => { + this.setState({scrollTop: this._scroller.current.getScrollTop()}); + }; _canUseLazyListRendering() { // for now disable lazy rendering as they are already rendered tiles // not rooms like props.list we pass to LazyRenderList return !this.props.extraTiles || !this.props.extraTiles.length; - }, + } - render: function() { + render() { const len = this.props.list.length + this.props.extraTiles.length; const isCollapsed = this.state.hidden && !this.props.forceExpand; - if (len) { - const subListClasses = classNames({ - "mx_RoomSubList": true, - "mx_RoomSubList_hidden": isCollapsed, - "mx_RoomSubList_nonEmpty": len && !isCollapsed, - }); + const subListClasses = classNames({ + "mx_RoomSubList": true, + "mx_RoomSubList_hidden": len && isCollapsed, + "mx_RoomSubList_nonEmpty": len && !isCollapsed, + }); + + let content; + if (len) { if (isCollapsed) { - return
- {this._getHeaderJsx(isCollapsed)} -
; + // no body } else if (this._canUseLazyListRendering()) { - return
- {this._getHeaderJsx(isCollapsed)} - + content = ( + -
; + ); } else { const roomTiles = this.props.list.map(r => this.makeRoomTile(r)); const tiles = roomTiles.concat(this.props.extraTiles); - return
- {this._getHeaderJsx(isCollapsed)} - + content = ( + { tiles } -
; + ); } } else { - const Loader = sdk.getComponent("elements.Spinner"); - let content; if (this.props.showSpinner && !isCollapsed) { + const Loader = sdk.getComponent("elements.Spinner"); content = ; } - - return ( -
- { this._getHeaderJsx(isCollapsed) } - { content } -
- ); } - }, -}); -module.exports = RoomSubList; + return ( +
+ { this._getHeaderJsx(isCollapsed) } + { content } +
+ ); + } +} diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7c0710a18d..2c9e798bd8 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -2,6 +2,7 @@ Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd Copyright 2018, 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,27 +23,25 @@ limitations under the License. import shouldHideEvent from '../../shouldHideEvent'; -import React from 'react'; -import ReactDOM from 'react-dom'; +import React, {createRef} from 'react'; +import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; -import Promise from 'bluebird'; -import filesize from 'filesize'; import classNames from 'classnames'; import { _t } from '../../languageHandler'; -import {RoomPermalinkCreator} from '../../matrix-to'; +import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks'; -import MatrixClientPeg from '../../MatrixClientPeg'; import ContentMessages from '../../ContentMessages'; import Modal from '../../Modal'; -import sdk from '../../index'; +import * as sdk from '../../index'; import CallHandler from '../../CallHandler'; import dis from '../../dispatcher'; import Tinter from '../../Tinter'; import rate_limited_func from '../../ratelimitedfunc'; -import ObjectUtils from '../../ObjectUtils'; +import * as ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; +import eventSearch from '../../Searching'; -import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard'; +import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard'; import MainSplit from './MainSplit'; import RightPanel from './RightPanel'; @@ -52,6 +51,11 @@ import WidgetEchoStore from '../../stores/WidgetEchoStore'; import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; import WidgetUtils from '../../utils/WidgetUtils'; import AccessibleButton from "../views/elements/AccessibleButton"; +import RightPanelStore from "../../stores/RightPanelStore"; +import {haveTileForEvent} from "../views/rooms/EventTile"; +import RoomContext from "../../contexts/RoomContext"; +import MatrixClientContext from "../../contexts/MatrixClientContext"; +import { shieldStatusForRoom } from '../../utils/ShieldUtils'; const DEBUG = false; let debuglog = function() {}; @@ -63,7 +67,7 @@ if (DEBUG) { debuglog = console.log.bind(console); } -module.exports = React.createClass({ +export default createReactClass({ displayName: 'RoomView', propTypes: { ConferenceHandler: PropTypes.any, @@ -87,18 +91,19 @@ module.exports = React.createClass({ // * name (string) The room's name // * avatarUrl (string) The mxc:// avatar URL for the room // * inviterName (string) The display name of the person who - // * invited us tovthe room + // * invited us to the room oobData: PropTypes.object, - // is the RightPanel collapsed? - collapsedRhs: PropTypes.bool, - // Servers the RoomView can use to try and assist joins viaServers: PropTypes.arrayOf(PropTypes.string), }, + statics: { + contextType: MatrixClientContext, + }, + getInitialState: function() { - const llMembers = MatrixClientPeg.get().hasLazyLoadMembersEnabled(); + const llMembers = this.context.hasLazyLoadMembersEnabled(); return { room: null, roomId: null, @@ -131,6 +136,8 @@ module.exports = React.createClass({ isAlone: false, isPeeking: false, showingPinned: false, + showReadReceipts: true, + showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom, // error object, as from the matrix client/server API // If we failed to load information about the room, @@ -155,25 +162,42 @@ module.exports = React.createClass({ // We load this later by asking the js-sdk to suggest a version for us. // This object is the result of Room#getRecommendedVersion() upgradeRecommendation: null, + + canReact: false, + canReply: false, }; }, componentWillMount: function() { this.dispatcherRef = dis.register(this.onAction); - MatrixClientPeg.get().on("Room", this.onRoom); - MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); - MatrixClientPeg.get().on("Room.name", this.onRoomName); - MatrixClientPeg.get().on("Room.accountData", this.onRoomAccountData); - MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember); - MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership); - MatrixClientPeg.get().on("accountData", this.onAccountData); - MatrixClientPeg.get().on("crypto.keyBackupStatus", this.onKeyBackupStatus); - MatrixClientPeg.get().on("deviceVerificationChanged", this.onDeviceVerificationChanged); + this.context.on("Room", this.onRoom); + this.context.on("Room.timeline", this.onRoomTimeline); + this.context.on("Room.name", this.onRoomName); + this.context.on("Room.accountData", this.onRoomAccountData); + this.context.on("RoomState.events", this.onRoomStateEvents); + this.context.on("RoomState.members", this.onRoomStateMember); + this.context.on("Room.myMembership", this.onMyMembership); + this.context.on("accountData", this.onAccountData); + this.context.on("crypto.keyBackupStatus", this.onKeyBackupStatus); + this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); + this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); // Start listening for RoomViewStore updates this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); + this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate); this._onRoomViewStoreUpdate(true); WidgetEchoStore.on('update', this._onWidgetEchoStoreUpdate); + this._showReadReceiptsWatchRef = SettingsStore.watchSetting("showReadReceipts", null, + this._onReadReceiptsChange); + + this._roomView = createRef(); + this._searchResultsPanel = createRef(); + }, + + _onReadReceiptsChange: function() { + this.setState({ + showReadReceipts: SettingsStore.getValue("showReadReceipts", this.state.roomId), + }); }, _onRoomViewStoreUpdate: function(initial) { @@ -196,8 +220,10 @@ module.exports = React.createClass({ return; } + const roomId = RoomViewStore.getRoomId(); + const newState = { - roomId: RoomViewStore.getRoomId(), + roomId, roomAlias: RoomViewStore.getRoomAlias(), roomLoading: RoomViewStore.isRoomLoading(), roomLoadError: RoomViewStore.getRoomLoadError(), @@ -206,9 +232,15 @@ module.exports = React.createClass({ isInitialEventHighlighted: RoomViewStore.isInitialEventHighlighted(), forwardingEvent: RoomViewStore.getForwardingEvent(), shouldPeek: RoomViewStore.shouldPeek(), - showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", RoomViewStore.getRoomId()), + showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", roomId), + showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), }; + if (!initial && this.state.shouldPeek && !newState.shouldPeek) { + // Stop peeking because we have joined this room now + this.context.stopPeeking(); + } + // Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307 console.log( 'RVS update:', @@ -223,7 +255,7 @@ module.exports = React.createClass({ // NB: This does assume that the roomID will not change for the lifetime of // the RoomView instance if (initial) { - newState.room = MatrixClientPeg.get().getRoom(newState.roomId); + newState.room = this.context.getRoom(newState.roomId); if (newState.room) { newState.showApps = this._shouldShowApps(newState.room); this._onRoomLoaded(newState.room); @@ -320,12 +352,12 @@ module.exports = React.createClass({ if (this.props.autoJoin) { this.onJoinButtonClicked(); } else if (!room && shouldPeek) { - console.log("Attempting to peek into room %s", roomId); + console.info("Attempting to peek into room %s", roomId); this.setState({ peekLoading: true, isPeeking: true, // this will change to false if peeking fails }); - MatrixClientPeg.get().peekInRoom(roomId).then((room) => { + this.context.peekInRoom(roomId).then((room) => { if (this.unmounted) { return; } @@ -334,7 +366,7 @@ module.exports = React.createClass({ peekLoading: false, }); this._onRoomLoaded(room); - }, (err) => { + }).catch((err) => { if (this.unmounted) { return; } @@ -347,7 +379,7 @@ module.exports = React.createClass({ // This won't necessarily be a MatrixError, but we duck-type // here and say if it's got an 'errcode' key with the right value, // it means we can't peek. - if (err.errcode == "M_GUEST_ACCESS_FORBIDDEN") { + if (err.errcode === "M_GUEST_ACCESS_FORBIDDEN" || err.errcode === 'M_FORBIDDEN') { // This is fine: the room just isn't peekable (we assume). this.setState({ peekLoading: false, @@ -357,10 +389,8 @@ module.exports = React.createClass({ } }); } else if (room) { - //viewing a previously joined room, try to lazy load members - // Stop peeking because we have joined this room previously - MatrixClientPeg.get().stopPeeking(); + this.context.stopPeeking(); this.setState({isPeeking: false}); } } @@ -399,21 +429,6 @@ module.exports = React.createClass({ this.onResize(); document.addEventListener("keydown", this.onKeyDown); - - // XXX: EVIL HACK to autofocus inviting on empty rooms. - // We use the setTimeout to avoid racing with focus_composer. - if (this.state.room && - this.state.room.getJoinedMemberCount() == 1 && - this.state.room.getLiveTimeline() && - this.state.room.getLiveTimeline().getEvents() && - this.state.room.getLiveTimeline().getEvents().length <= 6) { - const inviteBox = document.getElementById("mx_SearchableEntityList_query"); - setTimeout(function() { - if (inviteBox) { - inviteBox.focus(); - } - }, 50); - } }, shouldComponentUpdate: function(nextProps, nextState) { @@ -422,8 +437,8 @@ module.exports = React.createClass({ }, componentDidUpdate: function() { - if (this.refs.roomView) { - const roomView = ReactDOM.findDOMNode(this.refs.roomView); + if (this._roomView.current) { + const roomView = this._roomView.current; if (!roomView.ondrop) { roomView.addEventListener('drop', this.onDrop); roomView.addEventListener('dragover', this.onDragOver); @@ -437,10 +452,10 @@ module.exports = React.createClass({ // in render() prevents the ref from being set on first mount, so we try and // catch the messagePanel when it does mount. Because we only want the ref once, // we use a boolean flag to avoid duplicate work. - if (this.refs.messagePanel && !this.state.atEndOfLiveTimelineInit) { + if (this._messagePanel && !this.state.atEndOfLiveTimelineInit) { this.setState({ atEndOfLiveTimelineInit: true, - atEndOfLiveTimeline: this.refs.messagePanel.isAtEndOfLiveTimeline(), + atEndOfLiveTimeline: this._messagePanel.isAtEndOfLiveTimeline(), }); } }, @@ -457,31 +472,37 @@ module.exports = React.createClass({ RoomScrollStateStore.setScrollState(this.state.roomId, this._getScrollState()); } + if (this.state.shouldPeek) { + this.context.stopPeeking(); + } + // stop tracking room changes to format permalinks this._stopAllPermalinkCreators(); - if (this.refs.roomView) { + if (this._roomView.current) { // disconnect the D&D event listeners from the room view. This // is really just for hygiene - we're going to be // deleted anyway, so it doesn't matter if the event listeners // don't get cleaned up. - const roomView = ReactDOM.findDOMNode(this.refs.roomView); + const roomView = this._roomView.current; roomView.removeEventListener('drop', this.onDrop); roomView.removeEventListener('dragover', this.onDragOver); roomView.removeEventListener('dragleave', this.onDragLeaveOrEnd); roomView.removeEventListener('dragend', this.onDragLeaveOrEnd); } dis.unregister(this.dispatcherRef); - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("Room", this.onRoom); - MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); - MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); - MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData); - MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership); - MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); - MatrixClientPeg.get().removeListener("accountData", this.onAccountData); - MatrixClientPeg.get().removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus); - MatrixClientPeg.get().removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); + if (this.context) { + this.context.removeListener("Room", this.onRoom); + this.context.removeListener("Room.timeline", this.onRoomTimeline); + this.context.removeListener("Room.name", this.onRoomName); + this.context.removeListener("Room.accountData", this.onRoomAccountData); + this.context.removeListener("RoomState.events", this.onRoomStateEvents); + this.context.removeListener("Room.myMembership", this.onMyMembership); + this.context.removeListener("RoomState.members", this.onRoomStateMember); + this.context.removeListener("accountData", this.onAccountData); + this.context.removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus); + this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); + this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -495,9 +516,18 @@ module.exports = React.createClass({ if (this._roomStoreToken) { this._roomStoreToken.remove(); } + // Remove RightPanelStore listener + if (this._rightPanelStoreToken) { + this._rightPanelStoreToken.remove(); + } WidgetEchoStore.removeListener('update', this._onWidgetEchoStoreUpdate); + if (this._showReadReceiptsWatchRef) { + SettingsStore.unwatchSetting(this._showReadReceiptsWatchRef); + this._showReadReceiptsWatchRef = null; + } + // cancel any pending calls to the rate_limited_funcs this._updateRoomMembers.cancelPendingCall(); @@ -506,6 +536,12 @@ module.exports = React.createClass({ // Tinter.tint(); // reset colourscheme }, + _onRightPanelStoreUpdate: function() { + this.setState({ + showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom, + }); + }, + onPageUnload(event) { if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) { return event.returnValue = @@ -516,20 +552,19 @@ module.exports = React.createClass({ } }, - onKeyDown: function(ev) { let handled = false; const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev); - switch (ev.keyCode) { - case KeyCode.KEY_D: + switch (ev.key) { + case Key.D: if (ctrlCmdOnly) { this.onMuteAudioClick(); handled = true; } break; - case KeyCode.KEY_E: + case Key.E: if (ctrlCmdOnly) { this.onMuteVideoClick(); handled = true; @@ -556,9 +591,7 @@ module.exports = React.createClass({ payload.data.description || payload.data.name); break; case 'picture_snapshot': - return ContentMessages.sharedInstance().sendContentListToRoom( - [payload.file], this.state.room.roomId, MatrixClientPeg.get(), - ); + ContentMessages.sharedInstance().sendContentListToRoom([payload.file], this.state.room.roomId, this.context); break; case 'notifier_enabled': case 'upload_started': @@ -597,6 +630,27 @@ module.exports = React.createClass({ showApps: payload.show, }); break; + case 'reply_to_event': + if (this.state.searchResults && payload.event.getRoomId() === this.state.roomId && !this.unmounted) { + this.onCancelSearchClick(); + } + break; + case 'quote': + if (this.state.searchResults) { + const roomId = payload.event.getRoomId(); + if (roomId === this.state.roomId) { + this.onCancelSearchClick(); + } + + setImmediate(() => { + dis.dispatch({ + action: 'view_room', + room_id: roomId, + deferred_action: payload, + }); + }); + } + break; } }, @@ -626,7 +680,7 @@ module.exports = React.createClass({ // we'll only be showing a spinner. if (this.state.joining) return; - if (ev.getSender() !== MatrixClientPeg.get().credentials.userId) { + if (ev.getSender() !== this.context.credentials.userId) { // update unread count when scrolled up if (!this.state.searchResults && this.state.atEndOfLiveTimeline) { // no change @@ -657,10 +711,10 @@ module.exports = React.createClass({ }, canResetTimeline: function() { - if (!this.refs.messagePanel) { + if (!this._messagePanel) { return true; } - return this.refs.messagePanel.canResetTimeline(); + return this._messagePanel.canResetTimeline(); }, // called when state.room is first initialised (either at initial load, @@ -671,6 +725,7 @@ module.exports = React.createClass({ this._loadMembersIfJoined(room); this._calculateRecommendedVersion(room); this._updateE2EStatus(room); + this._updatePermissions(room); }, _calculateRecommendedVersion: async function(room) { @@ -681,8 +736,7 @@ module.exports = React.createClass({ _loadMembersIfJoined: async function(room) { // lazy load members if enabled - const cli = MatrixClientPeg.get(); - if (cli.hasLazyLoadMembersEnabled()) { + if (this.context.hasLazyLoadMembersEnabled()) { if (room && room.getMyMembership() === 'join') { try { await room.loadMembersIfNeeded(); @@ -717,7 +771,7 @@ module.exports = React.createClass({ _updatePreviewUrlVisibility: function({roomId}) { // URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit - const key = MatrixClientPeg.get().isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled'; + const key = this.context.isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled'; this.setState({ showUrlPreview: SettingsStore.getValue(key, roomId), }); @@ -742,11 +796,19 @@ module.exports = React.createClass({ this._updateE2EStatus(room); }, - _updateE2EStatus: function(room) { - if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) { + onUserVerificationChanged: function(userId, _trustStatus) { + const room = this.state.room; + if (!room || !room.currentState.getMember(userId)) { return; } - if (!MatrixClientPeg.get().isCryptoEnabled()) { + this._updateE2EStatus(room); + }, + + _updateE2EStatus: async function(room) { + if (!this.context.isRoomEncrypted(room.roomId)) { + return; + } + if (!this.context.isCryptoEnabled()) { // If crypto is not currently enabled, we aren't tracking devices at all, // so we don't know what the answer is. Let's error on the safe side and show // a warning for this case. @@ -755,10 +817,19 @@ module.exports = React.createClass({ }); return; } - room.hasUnverifiedDevices().then((hasUnverifiedDevices) => { - this.setState({ - e2eStatus: hasUnverifiedDevices ? "warning" : "verified", + if (!SettingsStore.isFeatureEnabled("feature_cross_signing")) { + room.hasUnverifiedDevices().then((hasUnverifiedDevices) => { + this.setState({ + e2eStatus: hasUnverifiedDevices ? "warning" : "verified", + }); }); + debuglog("e2e check is warning/verified only as cross-signing is off"); + return; + } + + /* At this point, the user has encryption on and cross-signing on */ + this.setState({ + e2eStatus: await shieldStatusForRoom(this.context, room), }); }, @@ -794,6 +865,15 @@ module.exports = React.createClass({ } }, + onRoomStateEvents: function(ev, state) { + // ignore if we don't have a room yet + if (!this.state.room || this.state.room.roomId !== state.roomId) { + return; + } + + this._updatePermissions(this.state.room); + }, + onRoomStateMember: function(ev, state, member) { // ignore if we don't have a room yet if (!this.state.room) { @@ -812,12 +892,23 @@ module.exports = React.createClass({ if (room.roomId === this.state.roomId) { this.forceUpdate(); this._loadMembersIfJoined(room); + this._updatePermissions(room); + } + }, + + _updatePermissions: function(room) { + if (room) { + const me = this.context.getUserId(); + const canReact = room.getMyMembership() === "join" && room.currentState.maySendEvent("m.reaction", me); + const canReply = room.maySendMessage(); + + this.setState({canReact, canReply}); } }, // rate limited because a power level change will emit an event for every // member in the room. - _updateRoomMembers: new rate_limited_func(function(dueToMember) { + _updateRoomMembers: rate_limited_func(function(dueToMember) { // a member state changed in this room // refresh the conf call notification state this._updateConfCallNotification(); @@ -891,7 +982,7 @@ module.exports = React.createClass({ if (this.state.searchResults.next_batch) { debuglog("requesting more search results"); - const searchPromise = MatrixClientPeg.get().backPaginateRoomEventsSearch( + const searchPromise = this.context.backPaginateRoomEventsSearch( this.state.searchResults); return this._handleSearchResult(searchPromise); } else { @@ -917,10 +1008,8 @@ module.exports = React.createClass({ }, onJoinButtonClicked: function(ev) { - const cli = MatrixClientPeg.get(); - // If the user is a ROU, allow them to transition to a PWLU - if (cli && cli.isGuest()) { + if (this.context && this.context.isGuest()) { // Join this room once the user has registered and logged in // (If we failed to peek, we may not have a valid room object.) dis.dispatch({ @@ -981,7 +1070,7 @@ module.exports = React.createClass({ }, onMessageListScroll: function(ev) { - if (this.refs.messagePanel.isAtEndOfLiveTimeline()) { + if (this._messagePanel.isAtEndOfLiveTimeline()) { this.setState({ numUnreadMessages: 0, atEndOfLiveTimeline: true, @@ -1017,7 +1106,7 @@ module.exports = React.createClass({ ev.stopPropagation(); ev.preventDefault(); ContentMessages.sharedInstance().sendContentListToRoom( - ev.dataTransfer.files, this.state.room.roomId, MatrixClientPeg.get(), + ev.dataTransfer.files, this.state.room.roomId, this.context, ); this.setState({ draggingFile: false }); dis.dispatch({action: 'focus_composer'}); @@ -1030,13 +1119,13 @@ module.exports = React.createClass({ }, injectSticker: function(url, info, text) { - if (MatrixClientPeg.get().isGuest()) { + if (this.context.isGuest()) { dis.dispatch({action: 'require_registration'}); return; } - ContentMessages.sharedInstance().sendStickerContentToRoom(url, this.state.room.roomId, info, text, MatrixClientPeg.get()) - .done(undefined, (error) => { + ContentMessages.sharedInstance().sendStickerContentToRoom(url, this.state.room.roomId, info, text, this.context) + .then(undefined, (error) => { if (error.name === "UnknownDeviceError") { // Let the staus bar handle this return; @@ -1054,8 +1143,8 @@ module.exports = React.createClass({ // if we already have a search panel, we need to tell it to forget // about its scroll state. - if (this.refs.searchResultsPanel) { - this.refs.searchResultsPanel.resetScrollState(); + if (this._searchResultsPanel.current) { + this._searchResultsPanel.current.resetScrollState(); } // make sure that we don't end up showing results from @@ -1064,23 +1153,12 @@ module.exports = React.createClass({ // todo: should cancel any previous search requests. this.searchId = new Date().getTime(); - let filter; - if (scope === "Room") { - filter = { - // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :( - rooms: [ - this.state.room.roomId, - ], - }; - } + let roomId; + if (scope === "Room") roomId = this.state.room.roomId; debuglog("sending search request"); - - const searchPromise = MatrixClientPeg.get().searchRoomEvents({ - filter: filter, - term: term, - }); - this._handleSearchResult(searchPromise).done(); + const searchPromise = eventSearch(term, roomId); + this._handleSearchResult(searchPromise); }, _handleSearchResult: function(searchPromise) { @@ -1137,12 +1215,9 @@ module.exports = React.createClass({ }, getSearchResultTiles: function() { - const EventTile = sdk.getComponent('rooms.EventTile'); const SearchResultTile = sdk.getComponent('rooms.SearchResultTile'); const Spinner = sdk.getComponent("elements.Spinner"); - const cli = MatrixClientPeg.get(); - // XXX: todo: merge overlapping results somehow? // XXX: why doesn't searching on name work? @@ -1150,28 +1225,28 @@ module.exports = React.createClass({ if (this.state.searchInProgress) { ret.push(
  • - -
  • ); + + ); } if (!this.state.searchResults.next_batch) { if (this.state.searchResults.results.length == 0) { ret.push(
  • -

    { _t("No results") }

    -
  • , - ); +

    { _t("No results") }

    + , + ); } else { ret.push(
  • -

    { _t("No more results") }

    -
  • , - ); +

    { _t("No more results") }

    + , + ); } } // once dynamic content in the search results load, make the scrollPanel check // the scroll offsets. const onHeightChanged = () => { - const scrollPanel = this.refs.searchResultsPanel; + const scrollPanel = this._searchResultsPanel.current; if (scrollPanel) { scrollPanel.checkScroll(); } @@ -1184,9 +1259,9 @@ module.exports = React.createClass({ const mxEv = result.context.getEvent(); const roomId = mxEv.getRoomId(); - const room = cli.getRoom(roomId); + const room = this.context.getRoom(roomId); - if (!EventTile.haveTileForEvent(mxEv)) { + if (!haveTileForEvent(mxEv)) { // XXX: can this ever happen? It will make the result count // not match the displayed count. continue; @@ -1251,7 +1326,7 @@ module.exports = React.createClass({ }, onForgetClick: function() { - MatrixClientPeg.get().forget(this.state.room.roomId).done(function() { + this.context.forget(this.state.room.roomId).then(function() { dis.dispatch({ action: 'view_next_room' }); }, function(err) { const errCode = err.errcode || _t("unknown error code"); @@ -1268,7 +1343,7 @@ module.exports = React.createClass({ this.setState({ rejecting: true, }); - MatrixClientPeg.get().leave(this.state.roomId).done(function() { + this.context.leave(this.state.roomId).then(function() { dis.dispatch({ action: 'view_next_room' }); self.setState({ rejecting: false, @@ -1290,6 +1365,40 @@ module.exports = React.createClass({ }); }, + onRejectAndIgnoreClick: async function() { + this.setState({ + rejecting: true, + }); + + try { + const myMember = this.state.room.getMember(this.context.getUserId()); + const inviteEvent = myMember.events.member; + const ignoredUsers = this.context.getIgnoredUsers(); + ignoredUsers.push(inviteEvent.getSender()); // de-duped internally in the js-sdk + await this.context.setIgnoredUsers(ignoredUsers); + + await this.context.leave(this.state.roomId); + dis.dispatch({ action: 'view_next_room' }); + this.setState({ + rejecting: false, + }); + } catch (error) { + console.error("Failed to reject invite: %s", error); + + const msg = error.message ? error.message : JSON.stringify(error); + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, { + title: _t("Failed to reject invite"), + description: msg, + }); + + self.setState({ + rejecting: false, + rejectError: error, + }); + } + }, + onRejectThreepidInviteButtonClicked: function(ev) { // We can reject 3pid invites in the same way that we accept them, // using /leave rather than /join. In the short term though, we @@ -1316,28 +1425,28 @@ module.exports = React.createClass({ // jump down to the bottom of this room, where new events are arriving jumpToLiveTimeline: function() { - this.refs.messagePanel.jumpToLiveTimeline(); + this._messagePanel.jumpToLiveTimeline(); dis.dispatch({action: 'focus_composer'}); }, // jump up to wherever our read marker is jumpToReadMarker: function() { - this.refs.messagePanel.jumpToReadMarker(); + this._messagePanel.jumpToReadMarker(); }, // update the read marker to match the read-receipt forgetReadMarker: function(ev) { ev.stopPropagation(); - this.refs.messagePanel.forgetReadMarker(); + this._messagePanel.forgetReadMarker(); }, // decide whether or not the top 'unread messages' bar should be shown _updateTopUnreadMessagesBar: function() { - if (!this.refs.messagePanel) { + if (!this._messagePanel) { return; } - const showBar = this.refs.messagePanel.canJumpToReadMarker(); + const showBar = this._messagePanel.canJumpToReadMarker(); if (this.state.showTopUnreadMessagesBar != showBar) { this.setState({showTopUnreadMessagesBar: showBar}); } @@ -1347,7 +1456,7 @@ module.exports = React.createClass({ // restored when we switch back to it. // _getScrollState: function() { - const messagePanel = this.refs.messagePanel; + const messagePanel = this._messagePanel; if (!messagePanel) return null; // if we're following the live timeline, we want to return null; that @@ -1364,7 +1473,8 @@ module.exports = React.createClass({ const scrollState = messagePanel.getScrollState(); - if (scrollState.stuckAtBottom) { + // getScrollState on TimelinePanel *may* return null, so guard against that + if (!scrollState || scrollState.stuckAtBottom) { // we don't really expect to be in this state, but it will // occasionally happen when no scroll state has been set on the // messagePanel (ie, we didn't have an initial event (so it's @@ -1451,10 +1561,10 @@ module.exports = React.createClass({ */ handleScrollKey: function(ev) { let panel; - if (this.refs.searchResultsPanel) { - panel = this.refs.searchResultsPanel; - } else if (this.refs.messagePanel) { - panel = this.refs.messagePanel; + if (this._searchResultsPanel.current) { + panel = this._searchResultsPanel.current; + } else if (this._messagePanel) { + panel = this._messagePanel; } if (panel) { @@ -1475,7 +1585,7 @@ module.exports = React.createClass({ // this has to be a proper method rather than an unnamed function, // otherwise react calls it with null on each update. _gatherTimelinePanelRef: function(r) { - this.refs.messagePanel = r; + this._messagePanel = r; if (r) { console.log("updateTint from RoomView._gatherTimelinePanelRef"); this.updateTint(); @@ -1486,7 +1596,7 @@ module.exports = React.createClass({ const createEvent = this.state.room.currentState.getStateEvents("m.room.create", ""); if (!createEvent || !createEvent.getContent()['predecessor']) return null; - return MatrixClientPeg.get().getRoom(createEvent.getContent()['predecessor']['room_id']); + return this.context.getRoom(createEvent.getContent()['predecessor']['room_id']); }, _getHiddenHighlightCount: function() { @@ -1503,7 +1613,6 @@ module.exports = React.createClass({ render: function() { const RoomHeader = sdk.getComponent('rooms.RoomHeader'); - const MessageComposer = sdk.getComponent('rooms.MessageComposer'); const ForwardMessage = sdk.getComponent("rooms.ForwardMessage"); const AuxPanel = sdk.getComponent("rooms.AuxPanel"); const SearchBar = sdk.getComponent("rooms.SearchBar"); @@ -1514,18 +1623,23 @@ module.exports = React.createClass({ const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); const RoomUpgradeWarningBar = sdk.getComponent("rooms.RoomUpgradeWarningBar"); const RoomRecoveryReminder = sdk.getComponent("rooms.RoomRecoveryReminder"); + const ErrorBoundary = sdk.getComponent("elements.ErrorBoundary"); if (!this.state.room) { const loading = this.state.roomLoading || this.state.peekLoading; if (loading) { return (
    - + + +
    ); } else { @@ -1543,16 +1657,20 @@ module.exports = React.createClass({ const roomAlias = this.state.roomAlias; return (
    - + + +
    ); } @@ -1562,15 +1680,17 @@ module.exports = React.createClass({ if (myMembership == 'invite') { if (this.state.joining || this.state.rejecting) { return ( - + + ); } else { - const myUserId = MatrixClientPeg.get().credentials.userId; + const myUserId = this.context.credentials.userId; const myMember = this.state.room.getMember(myUserId); const inviteEvent = myMember.events.member; var inviterName = inviteEvent.sender ? inviteEvent.sender.name : inviteEvent.getSender(); @@ -1582,14 +1702,18 @@ module.exports = React.createClass({ // We have a regular invite for this room. return (
    - + + +
    ); } @@ -1633,13 +1757,13 @@ module.exports = React.createClass({ const showRoomUpgradeBar = ( roomVersionRecommendation && roomVersionRecommendation.needsUpgrade && - this.state.room.userMayUpgradeRoom(MatrixClientPeg.get().credentials.userId) + this.state.room.userMayUpgradeRoom(this.context.credentials.userId) ); const showRoomRecoveryReminder = ( SettingsStore.getValue("showRoomRecoveryReminder") && - MatrixClientPeg.get().isRoomEncrypted(this.state.room.roomId) && - !MatrixClientPeg.get().getKeyBackupEnabled() + this.context.isRoomEncrypted(this.state.room.roomId) && + !this.context.getKeyBackupEnabled() ); const hiddenHighlightCount = this._getHiddenHighlightCount(); @@ -1647,12 +1771,12 @@ module.exports = React.createClass({ let aux = null; let previewBar; let hideCancel = false; - let hideRightPanel = false; + let forceHideRightPanel = false; if (this.state.forwardingEvent !== null) { aux = ; } else if (this.state.searching) { hideCancel = true; // has own cancel - aux = ; + aux = ; } else if (showRoomUpgradeBar) { aux = ; hideCancel = true; @@ -1681,6 +1805,7 @@ module.exports = React.createClass({ joining={this.state.joining} inviterName={inviterName} invitedEmail={invitedEmail} + oobData={this.props.oobData} canPreview={this.state.canPeek} room={this.state.room} /> @@ -1692,7 +1817,7 @@ module.exports = React.createClass({
    ); } else { - hideRightPanel = true; + forceHideRightPanel = true; } } else if (hiddenHighlightCount > 0) { aux = ( @@ -1707,9 +1832,9 @@ module.exports = React.createClass({ } const auxPanel = ( - ); } else { searchResultsPanel = ( -