diff --git a/.eslintrc.js b/.eslintrc.js index 6a0576c58a..069a67e511 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -11,111 +11,36 @@ const path = require('path'); const matrixJsSdkPath = path.join(path.dirname(require.resolve('matrix-js-sdk')), '..'); module.exports = { + extends: ["matrix-org", "matrix-org/react-legacy"], parser: "babel-eslint", - extends: [matrixJsSdkPath + "/.eslintrc.js"], - plugins: [ - "react", - "react-hooks", - "flowtype", - "babel" - ], + + env: { + browser: true, + node: true, + }, globals: { LANGUAGES_FILE: "readonly", }, - env: { - es6: true, - }, - parserOptions: { - ecmaFeatures: { - jsx: true, - legacyDecorators: true, - } - }, rules: { - // eslint's built in no-invalid-this rule breaks with class properties - "no-invalid-this": "off", - // so we replace it with a version that is class property aware - "babel/no-invalid-this": "error", - - // We appear to follow this most of the time, so let's enforce it instead - // of occasionally following it (or catching it in review) - "keyword-spacing": "error", - - /** react **/ - // This just uses the react plugin to help eslint known when - // variables have been used in JSX - "react/jsx-uses-vars": "error", - // Don't mark React as unused if we're using JSX - "react/jsx-uses-react": "error", - - // bind or arrow function in props causes performance issues - // (but we currently use them in some places) - // It's disabled here, but we should using it sparingly. - "react/jsx-no-bind": "off", - "react/jsx-key": ["error"], - - // Components in JSX should always be defined. - "react/jsx-no-undef": "error", - - // Assert no spacing in JSX curly brackets - // - // - // https://github.com/yannickcr/eslint-plugin-react/blob/HEAD/docs/rules/jsx-curly-spacing.md - // - // Disabled for now - if anything we'd like to *enforce* spacing in JSX - // curly brackets for legibility, but in practice it's not clear that the - // consistency particularly improves legibility here. --Matthew - // - // "react/jsx-curly-spacing": ["error", {"when": "never", "children": {"when": "always"}}], - - // Assert spacing before self-closing JSX tags, and no spacing before or - // after the closing slash, and no spacing after the opening bracket of - // the opening tag or closing tag. - // - // https://github.com/yannickcr/eslint-plugin-react/blob/HEAD/docs/rules/jsx-tag-spacing.md - "react/jsx-tag-spacing": ["error"], - - /** flowtype **/ - "flowtype/require-parameter-type": ["warn", { - "excludeArrowFunctions": true, - }], - "flowtype/define-flow-type": "warn", - "flowtype/require-return-type": ["warn", - "always", - { - "annotateUndefined": "never", - "excludeArrowFunctions": true, - } - ], - "flowtype/space-after-type-colon": ["warn", "always"], - "flowtype/space-before-type-colon": ["warn", "never"], - - /* - * things that are errors in the js-sdk config that the current - * code does not adhere to, turned down to warn - */ - "max-len": ["warn", { - // apparently people believe the length limit shouldn't apply - // to JSX. - ignorePattern: '^\\s*<', - ignoreComments: true, - ignoreRegExpLiterals: true, - code: 120, - }], - "valid-jsdoc": ["warn"], - "new-cap": ["warn"], - "key-spacing": ["warn"], - "prefer-const": ["warn"], - - // 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: { - onlyFilesWithFlowAnnotation: true - }, + // Things we do that break the ideal style + "no-constant-condition": "off", + "prefer-promise-reject-errors": "off", + "no-async-promise-executor": "off", + "quotes": "off", + "indent": "off", }, + + overrides: [{ + files: ["src/**/*.{ts, tsx}"], + "extends": ["matrix-org/ts"], + "rules": { + // We disable this while we're transitioning + "@typescript-eslint/no-explicit-any": "off", + // We'd rather not do this but we do + "@typescript-eslint/ban-ts-comment": "off", + + "quotes": "off", + "no-extra-boolean-cast": "off", + } + }], }; diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ad99ca49..e08b2ad612 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,40 @@ +Changes in [2.10.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.10.1) (2020-07-16) +===================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.10.0...v2.10.1) + + * Post-launch Element Web polish + [\#5002](https://github.com/matrix-org/matrix-react-sdk/pull/5002) + * Move e2e icon + [\#4992](https://github.com/matrix-org/matrix-react-sdk/pull/4992) + * Wire up new room list breadcrumbs as an ARIA Toolbar + [\#4976](https://github.com/matrix-org/matrix-react-sdk/pull/4976) + * Fix Room Tile Icon to not ignore DMs in other tags + [\#4999](https://github.com/matrix-org/matrix-react-sdk/pull/4999) + * Fix filtering by community not showing DM rooms with community members + [\#4997](https://github.com/matrix-org/matrix-react-sdk/pull/4997) + * Fix enter in new room list filter breaking things + [\#4996](https://github.com/matrix-org/matrix-react-sdk/pull/4996) + * Notify left panel of resizing when it is collapsed&expanded + [\#4995](https://github.com/matrix-org/matrix-react-sdk/pull/4995) + * When removing a filter condition, try recalculate in case it wasn't last + [\#4994](https://github.com/matrix-org/matrix-react-sdk/pull/4994) + * Create a generic ARIA toolbar component + [\#4975](https://github.com/matrix-org/matrix-react-sdk/pull/4975) + * Fix /op Slash Command + [\#4604](https://github.com/matrix-org/matrix-react-sdk/pull/4604) + * Fix copy button in share dialog + [\#4998](https://github.com/matrix-org/matrix-react-sdk/pull/4998) + * Add tooltip to Room Tile Icon + [\#4987](https://github.com/matrix-org/matrix-react-sdk/pull/4987) + * Fix names jumping on hover in irc layout + [\#4991](https://github.com/matrix-org/matrix-react-sdk/pull/4991) + * check that encryptionInfo.sender is set + [\#4988](https://github.com/matrix-org/matrix-react-sdk/pull/4988) + * Update help link + [\#4986](https://github.com/matrix-org/matrix-react-sdk/pull/4986) + * Update cover photo link + [\#4985](https://github.com/matrix-org/matrix-react-sdk/pull/4985) + Changes in [2.10.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.10.0) (2020-07-15) ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.9.0...v2.10.0) diff --git a/package.json b/package.json index c000466778..d136129180 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "2.10.0", + "version": "2.10.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -45,126 +45,127 @@ "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": "yarn lint:types && 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/runtime": "^7.8.3", + "@babel/runtime": "^7.10.5", "await-lock": "^2.0.1", - "blueimp-canvas-to-blob": "^3.5.0", + "blueimp-canvas-to-blob": "^3.27.0", "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", - "classnames": "^2.1.2", - "commonmark": "^0.28.1", - "counterpart": "^0.18.0", - "create-react-class": "^15.6.0", - "diff-dom": "^4.1.3", - "diff-match-patch": "^1.0.4", + "classnames": "^2.2.6", + "commonmark": "^0.29.1", + "counterpart": "^0.18.6", + "create-react-class": "^15.6.3", + "diff-dom": "^4.1.6", + "diff-match-patch": "^1.0.5", "emojibase-data": "^5.0.1", "emojibase-regex": "^4.0.1", "escape-html": "^1.0.3", - "file-saver": "^1.3.3", - "filesize": "3.5.6", + "file-saver": "^1.3.8", + "filesize": "3.6.1", "flux": "2.1.1", - "focus-visible": "^5.0.2", - "fuse.js": "^2.2.0", - "gfm.css": "^1.1.1", + "focus-visible": "^5.1.0", + "fuse.js": "^2.7.4", + "gfm.css": "^1.1.2", "glob-to-regexp": "^0.4.1", - "highlight.js": "^9.15.8", - "html-entities": "^1.2.1", + "highlight.js": "^10.1.2", + "html-entities": "^1.3.1", "is-ip": "^2.0.0", - "linkifyjs": "^2.1.6", - "lodash": "^4.17.14", + "linkifyjs": "^2.1.9", + "lodash": "^4.17.19", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", - "minimist": "^1.2.0", - "pako": "^1.0.5", + "minimist": "^1.2.5", + "pako": "^1.0.11", "parse5": "^5.1.1", "png-chunks-extract": "^1.0.0", "project-name-generator": "^2.1.7", - "prop-types": "^15.5.8", + "prop-types": "^15.7.2", "qrcode": "^1.4.4", - "qs": "^6.6.0", - "re-resizable": "^6.5.2", - "react": "^16.9.0", + "qs": "^6.9.4", + "re-resizable": "^6.5.4", + "react": "^16.13.1", "react-beautiful-dnd": "^4.0.1", - "react-dom": "^16.9.0", - "react-focus-lock": "^2.2.1", + "react-dom": "^16.13.1", + "react-focus-lock": "^2.4.1", "react-transition-group": "^4.4.1", - "resize-observer-polyfill": "^1.5.0", - "sanitize-html": "^1.18.4", - "text-encoding-utf-8": "^1.0.1", + "resize-observer-polyfill": "^1.5.1", + "sanitize-html": "^1.27.1", + "text-encoding-utf-8": "^1.0.2", "url": "^0.11.0", "velocity-animate": "^1.5.2", - "what-input": "^5.2.6", + "what-input": "^5.2.10", "zxcvbn": "^4.4.2" }, "devDependencies": { - "@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", + "@babel/cli": "^7.10.5", + "@babel/core": "^7.10.5", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-decorators": "^7.10.5", + "@babel/plugin-proposal-export-default-from": "^7.10.4", + "@babel/plugin-proposal-numeric-separator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.10.4", + "@babel/plugin-transform-flow-comments": "^7.10.4", + "@babel/plugin-transform-runtime": "^7.10.5", + "@babel/preset-env": "^7.10.4", + "@babel/preset-flow": "^7.10.4", + "@babel/preset-react": "^7.10.4", + "@babel/preset-typescript": "^7.10.4", + "@babel/register": "^7.10.5", + "@peculiar/webcrypto": "^1.1.2", "@types/classnames": "^2.2.10", "@types/counterpart": "^0.18.1", "@types/flux": "^3.1.9", "@types/linkifyjs": "^2.1.3", - "@types/lodash": "^4.14.152", + "@types/lodash": "^4.14.158", "@types/modernizr": "^3.5.3", - "@types/node": "^12.12.41", + "@types/node": "^12.12.51", "@types/qrcode": "^1.3.4", "@types/react": "^16.9", "@types/react-dom": "^16.9.8", "@types/react-transition-group": "^4.4.0", "@types/sanitize-html": "^1.23.3", "@types/zxcvbn": "^4.4.0", - "babel-eslint": "^10.0.3", + "@typescript-eslint/eslint-plugin": "^3.7.0", + "@typescript-eslint/parser": "^3.7.0", + "babel-eslint": "^10.1.0", "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", + "chokidar": "^3.4.1", + "concurrently": "^4.1.2", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.2", + "eslint": "7.5.0", + "eslint-config-google": "^0.14.0", + "eslint-config-matrix-org": "^0.1.2", + "eslint-plugin-babel": "^5.3.1", + "eslint-plugin-flowtype": "^2.50.3", + "eslint-plugin-jest": "^23.18.0", + "eslint-plugin-react": "^7.20.3", + "eslint-plugin-react-hooks": "^2.5.1", + "estree-walker": "^0.9.0", "file-loader": "^3.0.1", - "flow-parser": "^0.57.3", - "glob": "^5.0.14", + "flow-parser": "0.57.3", + "glob": "^5.0.15", "jest": "^24.9.0", "jest-canvas-mock": "^2.2.0", "lolex": "^5.1.2", "matrix-mock-request": "^1.2.3", "matrix-react-test-utils": "^0.2.2", - "react-test-renderer": "^16.9.0", - "rimraf": "^2.4.3", - "source-map-loader": "^0.2.3", + "react-test-renderer": "^16.13.1", + "rimraf": "^2.7.1", + "source-map-loader": "^0.2.4", "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" + "stylelint-config-standard": "^18.3.0", + "stylelint-scss": "^3.18.0", + "typescript": "^3.9.7", + "walk": "^2.3.14", + "webpack": "^4.43.0", + "webpack-cli": "^3.3.12" }, "jest": { "testMatch": [ diff --git a/res/css/_common.scss b/res/css/_common.scss index 7fead4317e..f2d3a0e54b 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -174,7 +174,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text]::placeholder, :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search]::placeholder, .mx_textinput input::placeholder { - color: $roomsublist-label-fg-color; + color: rgba($input-darker-fg-color, .75); } } @@ -226,7 +226,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { } #mx_theme_tertiaryAccentColor { - color: $roomsublist-label-bg-color; + color: $tertiary-accent-color; } /* Expected z-indexes for dialogs: diff --git a/res/css/_components.scss b/res/css/_components.scss index 8f3c187ff8..23e4af780a 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -12,7 +12,6 @@ @import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; -@import "./structures/_LeftPanel2.scss"; @import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; @@ -21,14 +20,12 @@ @import "./structures/_RoomDirectory.scss"; @import "./structures/_RoomSearch.scss"; @import "./structures/_RoomStatusBar.scss"; -@import "./structures/_RoomSubList.scss"; @import "./structures/_RoomView.scss"; @import "./structures/_ScrollPanel.scss"; @import "./structures/_SearchBox.scss"; @import "./structures/_TabbedView.scss"; @import "./structures/_TagPanel.scss"; @import "./structures/_ToastContainer.scss"; -@import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; @import "./structures/_UserMenu.scss"; @import "./structures/_ViewSource.scss"; @@ -108,7 +105,6 @@ @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/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; @@ -146,7 +142,6 @@ @import "./views/messages/_MjolnirBody.scss"; @import "./views/messages/_ReactionsRow.scss"; @import "./views/messages/_ReactionsRowButton.scss"; -@import "./views/messages/_ReactionsRowButtonTooltip.scss"; @import "./views/messages/_RedactedBody.scss"; @import "./views/messages/_RoomAvatarEvent.scss"; @import "./views/messages/_SenderProfile.scss"; @@ -169,7 +164,6 @@ @import "./views/rooms/_EventTile.scss"; @import "./views/rooms/_GroupLayout.scss"; @import "./views/rooms/_IRCLayout.scss"; -@import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; @import "./views/rooms/_MemberInfo.scss"; @@ -182,23 +176,18 @@ @import "./views/rooms/_PresenceLabel.scss"; @import "./views/rooms/_ReplyPreview.scss"; @import "./views/rooms/_RoomBreadcrumbs.scss"; -@import "./views/rooms/_RoomBreadcrumbs2.scss"; -@import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; @import "./views/rooms/_RoomList.scss"; -@import "./views/rooms/_RoomList2.scss"; @import "./views/rooms/_RoomPreviewBar.scss"; @import "./views/rooms/_RoomRecoveryReminder.scss"; -@import "./views/rooms/_RoomSublist2.scss"; +@import "./views/rooms/_RoomSublist.scss"; @import "./views/rooms/_RoomTile.scss"; -@import "./views/rooms/_RoomTile2.scss"; @import "./views/rooms/_RoomTileIcon.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.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"; @@ -229,6 +218,4 @@ @import "./views/verification/_VerificationShowSas.scss"; @import "./views/voip/_CallContainer.scss"; @import "./views/voip/_CallView.scss"; -@import "./views/voip/_CallView2.scss"; -@import "./views/voip/_IncomingCallbox.scss"; @import "./views/voip/_VideoView.scss"; diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index 5b876ab11d..caa3a452b0 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -68,5 +68,6 @@ $font-49px: 4.9rem; $font-50px: 5.0rem; $font-51px: 5.1rem; $font-52px: 5.2rem; +$font-78px: 7.8rem; $font-88px: 8.8rem; $font-400px: 40rem; diff --git a/res/css/structures/_CustomRoomTagPanel.scss b/res/css/structures/_CustomRoomTagPanel.scss index 1fb18ec41e..135a51c7cd 100644 --- a/res/css/structures/_CustomRoomTagPanel.scss +++ b/res/css/structures/_CustomRoomTagPanel.scss @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +// TODO: Update design for custom tags to match new designs + .mx_LeftPanel_tagPanelContainer { display: flex; flex-direction: column; @@ -50,7 +52,7 @@ limitations under the License. background-color: $accent-color-alt; width: 5px; position: absolute; - left: -15px; + left: -9px; border-radius: 0 3px 3px 0; - top: 2px; // 10 [padding-top] - (56 - 40)/2 + top: 12px; // just feels right (see comment above about designs needing to be updated) } diff --git a/res/css/structures/_HeaderButtons.scss b/res/css/structures/_HeaderButtons.scss index eef7653b24..9ef40e9d6a 100644 --- a/res/css/structures/_HeaderButtons.scss +++ b/res/css/structures/_HeaderButtons.scss @@ -22,7 +22,7 @@ limitations under the License. content: ""; background-color: $header-divider-color; opacity: 0.5; - margin: 0 15px; + margin: 6px 8px; border-radius: 1px; width: 1px; } diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 35d9f0e7da..db531cf088 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -1,6 +1,5 @@ /* -Copyright 2015, 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. @@ -15,164 +14,171 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_LeftPanel_container { - display: flex; - /* LeftPanel 260px */ - min-width: 260px; - max-width: 50%; - flex: 0 0 auto; -} - -.mx_LeftPanel_container.collapsed { - min-width: unset; - /* Collapsed LeftPanel 50px */ - flex: 0 0 50px; -} - -.mx_LeftPanel_container.collapsed.mx_LeftPanel_container_hasTagPanel { - /* TagPanel 70px + Collapsed LeftPanel 50px */ - flex: 0 0 120px; -} - -.mx_LeftPanel_tagPanelContainer { - flex: 0 0 70px; - height: 100%; -} - -.mx_LeftPanel_hideButton { - position: absolute; - top: 10px; - right: 0px; - padding: 8px; - cursor: pointer; -} +$tagPanelWidth: 56px; // only applies in this file, used for calculations .mx_LeftPanel { - flex: 1; - overflow-x: hidden; - display: flex; - flex-direction: column; - min-height: 0; -} + background-color: $roomlist-bg-color; + min-width: 260px; + max-width: 50%; -.mx_LeftPanel .mx_AppTile_mini { - height: 132px; -} - -.mx_LeftPanel .mx_RoomList_scrollbar { - order: 1; - - flex: 1 1 0; - - overflow-y: auto; - z-index: 6; -} - -.mx_LeftPanel .mx_BottomLeftMenu { - order: 3; - - border-top: 1px solid $panel-divider-color; - margin-left: 16px; /* gutter */ - margin-right: 16px; /* gutter */ - flex: 0 0 60px; - z-index: 1; -} - -.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu { - flex: 0 0 160px; - margin-bottom: 9px; -} - -.mx_LeftPanel .mx_BottomLeftMenu_options { - margin-top: 18px; -} - -.mx_BottomLeftMenu_options object { - pointer-events: none; -} - -.mx_BottomLeftMenu_options > div { - display: inline-block; -} - -.mx_BottomLeftMenu_options .mx_RoleButton { - margin-left: 0px; - margin-right: 10px; - height: 30px; -} - -.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings { - float: right; -} - -.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings .mx_RoleButton { - margin-right: 0px; -} - -.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu_settings { - float: none; -} - -.mx_MatrixChat_useCompactLayout { - .mx_LeftPanel .mx_BottomLeftMenu { - flex: 0 0 50px; - } - - .mx_LeftPanel_container.collapsed .mx_BottomLeftMenu { - flex: 0 0 160px; - } - - .mx_LeftPanel .mx_BottomLeftMenu_options { - margin-top: 12px; - } -} - -.mx_LeftPanel_exploreAndFilterRow { + // Create a row-based flexbox for the TagPanel and the room list display: flex; - .mx_SearchBox { - flex: 1 1 0; - min-width: 0; - margin: 4px 9px 1px 9px; - } -} + .mx_LeftPanel_tagPanelContainer { + flex-grow: 0; + flex-shrink: 0; + flex-basis: $tagPanelWidth; + height: 100%; -.mx_LeftPanel_explore { - flex: 0 0 50%; - overflow: hidden; - transition: flex-basis 0.2s; - box-sizing: border-box; + // Create another flexbox so the TagPanel fills the container + display: flex; - &.mx_LeftPanel_explore_hidden { - flex-basis: 0; + // TagPanel handles its own CSS } - .mx_AccessibleButton { - font-size: $font-14px; - margin: 4px 0 1px 9px; - padding: 9px; - padding-left: 42px; - font-weight: 600; - color: $notice-secondary-color; - position: relative; - border-radius: 4px; + &:not(.mx_LeftPanel_hasTagPanel) { + .mx_LeftPanel_roomListContainer { + width: 100%; + } + } - &:hover { - background-color: $primary-bg-color; + // Note: The 'room list' in this context is actually everything that isn't the tag + // panel, such as the menu options, breadcrumbs, filtering, etc + .mx_LeftPanel_roomListContainer { + width: calc(100% - $tagPanelWidth); + background-color: $roomlist-bg-color; + + // Create another flexbox (this time a column) for the room list components + display: flex; + flex-direction: column; + + .mx_LeftPanel_userHeader { + /* 12px top, 12px sides, 20px bottom (using 13px bottom to account + * for internal whitespace in the breadcrumbs) + */ + padding: 12px; + flex-shrink: 0; // to convince safari's layout engine the flexbox is fine + + // Create another flexbox column for the rows to stack within + display: flex; + flex-direction: column; } - &::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; + .mx_LeftPanel_breadcrumbsContainer { + overflow-y: hidden; + overflow-x: scroll; + margin: 12px 12px 0 12px; + flex: 0 0 auto; + // Create yet another flexbox, this time within the row, to ensure items stay + // aligned correctly. This is also a row-based flexbox. + display: flex; + align-items: center; + + &.mx_IndicatorScrollbar_leftOverflow { + mask-image: linear-gradient(90deg, transparent, black 5%); + } + + &.mx_IndicatorScrollbar_rightOverflow { + mask-image: linear-gradient(90deg, black, black 95%, transparent); + } + + &.mx_IndicatorScrollbar_rightOverflow.mx_IndicatorScrollbar_leftOverflow { + mask-image: linear-gradient(90deg, transparent, black 5%, black 95%, transparent); + } + } + + .mx_LeftPanel_filterContainer { + margin-left: 12px; + margin-right: 12px; + + flex-shrink: 0; // to convince safari's layout engine the flexbox is fine + + // Create a flexbox to organize the inputs + display: flex; + align-items: center; + + .mx_RoomSearch_expanded + .mx_LeftPanel_exploreButton { + // Cheaty way to return the occupied space to the filter input + flex-basis: 0; + margin: 0; + width: 0; + + // Don't forget to hide the masked ::before icon, + // using display:none or visibility:hidden would break accessibility + &::before { + content: none; + } + } + + .mx_LeftPanel_exploreButton { + width: 28px; + height: 28px; + border-radius: 20px; + background-color: $roomlist-button-bg-color; + position: relative; + margin-left: 8px; + + &::before { + content: ''; + position: absolute; + top: 6px; + left: 6px; + width: 16px; + height: 16px; + mask-image: url('$(res)/img/feather-customised/compass.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } + } + + .mx_LeftPanel_roomListWrapper { + overflow: hidden; + margin-top: 10px; // so we're not up against the search/filter + + &.mx_LeftPanel_roomListWrapper_stickyBottom { + padding-bottom: 32px; + } + + &.mx_LeftPanel_roomListWrapper_stickyTop { + padding-top: 32px; + } + } + + .mx_LeftPanel_actualRoomListContainer { + position: relative; // for sticky headers + height: 100%; // ensure scrolling still works + } + } + + // These styles override the defaults for the minimized (66px) layout + &.mx_LeftPanel_minimized { + min-width: unset; + + // We have to forcefully set the width to override the resizer's style attribute. + &.mx_LeftPanel_hasTagPanel { + width: calc(68px + $tagPanelWidth) !important; + } + &:not(.mx_LeftPanel_hasTagPanel) { + width: 68px !important; + } + + .mx_LeftPanel_roomListContainer { + width: 68px; + + .mx_LeftPanel_filterContainer { + // Organize the flexbox into a centered column layout + flex-direction: column; + justify-content: center; + + .mx_LeftPanel_exploreButton { + margin-left: 0; + margin-top: 8px; + background-color: transparent; + } + } } } } diff --git a/res/css/structures/_LeftPanel2.scss b/res/css/structures/_LeftPanel2.scss deleted file mode 100644 index 9603731dd5..0000000000 --- a/res/css/structures/_LeftPanel2.scss +++ /dev/null @@ -1,197 +0,0 @@ -/* -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. -*/ - -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 - -$tagPanelWidth: 56px; // only applies in this file, used for calculations - -.mx_LeftPanel2 { - background-color: $roomlist2-bg-color; - min-width: 260px; - max-width: 50%; - - // Create a row-based flexbox for the TagPanel and the room list - display: flex; - - .mx_LeftPanel2_tagPanelContainer { - flex-grow: 0; - flex-shrink: 0; - flex-basis: $tagPanelWidth; - height: 100%; - - // Create another flexbox so the TagPanel fills the container - display: flex; - - // TagPanel handles its own CSS - } - - &:not(.mx_LeftPanel2_hasTagPanel) { - .mx_LeftPanel2_roomListContainer { - width: 100%; - } - } - - // Note: The 'room list' in this context is actually everything that isn't the tag - // panel, such as the menu options, breadcrumbs, filtering, etc - .mx_LeftPanel2_roomListContainer { - width: calc(100% - $tagPanelWidth); - background-color: $roomlist2-bg-color; - - // Create another flexbox (this time a column) for the room list components - display: flex; - flex-direction: column; - - .mx_LeftPanel2_userHeader { - /* 12px top, 12px sides, 20px bottom (using 13px bottom to account - * for internal whitespace in the breadcrumbs) - */ - padding: 12px; - flex-shrink: 0; // to convince safari's layout engine the flexbox is fine - - // Create another flexbox column for the rows to stack within - display: flex; - flex-direction: column; - } - - .mx_LeftPanel2_breadcrumbsContainer { - overflow-y: hidden; - overflow-x: scroll; - margin: 12px 12px 0 12px; - flex: 0 0 auto; - // Create yet another flexbox, this time within the row, to ensure items stay - // aligned correctly. This is also a row-based flexbox. - display: flex; - align-items: center; - - &.mx_IndicatorScrollbar_leftOverflow { - mask-image: linear-gradient(90deg, transparent, black 5%); - } - - &.mx_IndicatorScrollbar_rightOverflow { - mask-image: linear-gradient(90deg, black, black 95%, transparent); - } - - &.mx_IndicatorScrollbar_rightOverflow.mx_IndicatorScrollbar_leftOverflow { - mask-image: linear-gradient(90deg, transparent, black 5%, black 95%, transparent); - } - } - - .mx_LeftPanel2_filterContainer { - margin-left: 12px; - margin-right: 12px; - - flex-shrink: 0; // to convince safari's layout engine the flexbox is fine - - // Create a flexbox to organize the inputs - display: flex; - align-items: center; - - .mx_RoomSearch_expanded + .mx_LeftPanel2_exploreButton { - // Cheaty way to return the occupied space to the filter input - flex-basis: 0; - margin: 0; - width: 0; - - // Don't forget to hide the masked ::before icon, - // using display:none or visibility:hidden would break accessibility - &::before { - content: none; - } - } - - .mx_LeftPanel2_exploreButton { - width: 28px; - height: 28px; - border-radius: 20px; - background-color: $roomlist2-button-bg-color; - position: relative; - margin-left: 8px; - - &::before { - content: ''; - position: absolute; - top: 6px; - left: 6px; - width: 16px; - height: 16px; - mask-image: url('$(res)/img/feather-customised/compass.svg'); - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background: $primary-fg-color; - } - } - } - - .mx_LeftPanel2_roomListWrapper { - // Create a flexbox to ensure the containing items cause appropriate overflow. - display: flex; - - flex-grow: 1; - overflow: hidden; - min-height: 0; - margin-top: 10px; // so we're not up against the search/filter - - &.mx_LeftPanel2_roomListWrapper_stickyBottom { - padding-bottom: 32px; - } - - &.mx_LeftPanel2_roomListWrapper_stickyTop { - padding-top: 32px; - } - } - - .mx_LeftPanel2_actualRoomListContainer { - flex-grow: 1; // fill the available space - overflow-y: auto; - width: 100%; - max-width: 100%; - position: relative; // for sticky headers - - // Create a flexbox to trick the layout engine - display: flex; - } - } - - // These styles override the defaults for the minimized (66px) layout - &.mx_LeftPanel2_minimized { - min-width: unset; - - // We have to forcefully set the width to override the resizer's style attribute. - &.mx_LeftPanel2_hasTagPanel { - width: calc(68px + $tagPanelWidth) !important; - } - &:not(.mx_LeftPanel2_hasTagPanel) { - width: 68px !important; - } - - .mx_LeftPanel2_roomListContainer { - width: 68px; - - .mx_LeftPanel2_filterContainer { - // Organize the flexbox into a centered column layout - flex-direction: column; - justify-content: center; - - .mx_LeftPanel2_exploreButton { - margin-left: 0; - margin-top: 8px; - background-color: transparent; - } - } - } - } -} diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss index 387879ea7b..25e1153fce 100644 --- a/res/css/structures/_MainSplit.scss +++ b/res/css/structures/_MainSplit.scss @@ -26,23 +26,3 @@ limitations under the License. margin: 0 -10px 0 0; padding: 0 10px 0 0; } - -.mx_MainSplit > .mx_ResizeHandle_horizontal:hover { - position: relative; - - &::before { - position: absolute; - left: 4px; - top: 50%; - transform: translate(0, -50%); - - height: 30%; - width: 4px; - border-radius: 4px; - - content: ' '; - - background-color: $primary-fg-color; - opacity: 0.8; - } -} diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index 926d10ee04..88b29a96e8 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -66,7 +66,7 @@ limitations under the License. } /* not the left panel, and not the resize handle, so the roomview/groupview/... */ -.mx_MatrixChat > :not(.mx_LeftPanel_container):not(.mx_LeftPanel2):not(.mx_ResizeHandle) { +.mx_MatrixChat > :not(.mx_LeftPanel):not(.mx_ResizeHandle) { background-color: $primary-bg-color; flex: 1 1 0; @@ -78,23 +78,3 @@ limitations under the License. */ height: 100%; } - -.mx_MatrixChat > .mx_ResizeHandle_horizontal:hover { - position: relative; - - &::before { - position: absolute; - left: -2px; - top: 50%; - transform: translate(0, -50%); - - height: 30%; - width: 4px; - border-radius: 4px; - - content: ' '; - - background-color: $primary-fg-color; - opacity: 0.8; - } -} diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index 77114954eb..2fe7aac3b2 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -61,10 +61,10 @@ limitations under the License. &::before { content: ''; position: absolute; - top: 6px; // center with parent of 32px - left: 6px; // center with parent of 32px - height: 20px; - width: 20px; + top: 4px; // center with parent of 32px + left: 4px; // center with parent of 32px + height: 24px; + width: 24px; background-color: $rightpanel-button-color; mask-repeat: no-repeat; mask-size: contain; diff --git a/res/css/structures/_RoomSearch.scss b/res/css/structures/_RoomSearch.scss index 371bab1611..39a3dee30b 100644 --- a/res/css/structures/_RoomSearch.scss +++ b/res/css/structures/_RoomSearch.scss @@ -18,7 +18,7 @@ limitations under the License. .mx_RoomSearch { flex: 1; border-radius: 20px; - background-color: $roomlist2-button-bg-color; + background-color: $roomlist-button-bg-color; height: 28px; padding: 2px; diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss deleted file mode 100644 index 2c53258b08..0000000000 --- a/res/css/structures/_RoomSubList.scss +++ /dev/null @@ -1,187 +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. -*/ - -/* a word of explanation about the flex-shrink values employed here: - there are 3 priotized categories of screen real-estate grabbing, - each with a flex-shrink difference of 4 order of magnitude, - so they ideally wouldn't affect each other. - lowest category: .mx_RoomSubList - flex-shrink: 10000000 - 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, - to limit the size - highest category: .mx_RoomSubList.resized-all - flex-shrink: 1 - small flex-shrink value (1), is only added if you can drag the resizer so far - so in practice you can only assign this category if there is enough space. -*/ - -.mx_RoomSubList { - display: flex; - flex-direction: column; -} - - -.mx_RoomSubList_nonEmpty .mx_AutoHideScrollbar_offset { - padding-bottom: 4px; -} - -.mx_RoomSubList_labelContainer { - display: flex; - flex-direction: row; - align-items: center; - flex: 0 0 auto; - 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; - display: flex; - align-items: center; - padding: 0 6px; -} - -.mx_RoomSubList_label > span { - flex: 1 1 auto; - text-transform: uppercase; - color: $roomsublist-label-fg-color; - font-weight: 700; - font-size: $font-12px; - margin-left: 8px; -} - -.mx_RoomSubList_badge > div { - flex: 0 0 auto; - border-radius: $font-16px; - font-weight: 600; - font-size: $font-12px; - padding: 0 5px; - color: $roomtile-badge-fg-color; - background-color: $roomtile-name-color; - cursor: pointer; -} - -.mx_RoomSubList_addRoom, .mx_RoomSubList_badge { - margin-left: 7px; -} - -.mx_RoomSubList_addRoom { - background-color: $roomheader-addroom-bg-color; - border-radius: 10px; // 16/2 + 2 padding - height: 16px; - flex: 0 0 16px; - position: relative; - - &::before { - background-color: $roomheader-addroom-fg-color; - mask: url('$(res)/img/icons-room-add.svg'); - mask-repeat: no-repeat; - mask-position: center; - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - } -} - -.mx_RoomSubList_badgeHighlight > div { - color: $accent-fg-color; - background-color: $warning-color; -} - -.mx_RoomSubList_chevron { - pointer-events: none; - mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); - mask-repeat: no-repeat; - transition: transform 0.2s ease-in; - width: 10px; - height: 6px; - margin-left: 2px; - background-color: $roomsublist-label-fg-color; -} - -.mx_RoomSubList_chevronDown { - transform: rotateZ(0deg); -} - -.mx_RoomSubList_chevronUp { - transform: rotateZ(180deg); -} - -.mx_RoomSubList_chevronRight { - transform: rotateZ(-90deg); -} - -.mx_RoomSubList_scroll { - /* let rooms list grab as much space as it needs (auto), - potentially overflowing and showing a scrollbar */ - flex: 0 1 auto; - padding: 0 8px; -} - -.collapsed { - .mx_RoomSubList_scroll { - padding: 0; - } - - .mx_RoomSubList_labelContainer { - margin-right: 8px; - margin-left: 2px; - padding: 0; - } - - .mx_RoomSubList_addRoom { - margin-left: 3px; - margin-right: 10px; - } - - .mx_RoomSubList_label > span { - display: none; - } -} - -// overflow indicators -.mx_RoomSubList:not(.resized-all) > .mx_RoomSubList_scroll { - &.mx_IndicatorScrollbar_topOverflow::before { - position: sticky; - content: ""; - top: 0; - left: 0; - right: 0; - height: 8px; - z-index: 100; - display: block; - pointer-events: none; - transition: background-image 0.1s ease-in; - background: linear-gradient(to top, $panel-gradient); - } - - - &.mx_IndicatorScrollbar_topOverflow { - margin-top: -8px; - } -} diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 505d6c348f..78e8326772 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -157,7 +157,7 @@ limitations under the License. font-weight: 600; font-size: $font-14px; padding: 0 5px; - background-color: $roomtile-name-color; + background-color: $muted-fg-color; } .mx_TagTile_badgeHighlight { diff --git a/res/css/structures/_TopLeftMenuButton.scss b/res/css/structures/_TopLeftMenuButton.scss deleted file mode 100644 index 8d2e36bcd6..0000000000 --- a/res/css/structures/_TopLeftMenuButton.scss +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_TopLeftMenuButton { - flex: 0 0 52px; - border-bottom: 1px solid $panel-divider-color; - color: $topleftmenu-color; - background-color: $primary-bg-color; - display: flex; - align-items: center; - min-width: 0; - padding: 0 4px; - overflow: hidden; -} - -.mx_TopLeftMenuButton .mx_BaseAvatar { - margin: 0 7px; -} - -.mx_TopLeftMenuButton_name { - margin: 0 7px; - font-size: $font-18px; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - font-weight: 600; -} - -.mx_TopLeftMenuButton_chevron { - margin: 0 7px; - mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); - mask-repeat: no-repeat; - width: $font-22px; - height: 6px; - background-color: $roomsublist-label-fg-color; -} diff --git a/res/css/views/avatars/_DecoratedRoomAvatar.scss b/res/css/views/avatars/_DecoratedRoomAvatar.scss index 900f351074..48d72131b5 100644 --- a/res/css/views/avatars/_DecoratedRoomAvatar.scss +++ b/res/css/views/avatars/_DecoratedRoomAvatar.scss @@ -24,7 +24,7 @@ limitations under the License. right: 0; } - .mx_NotificationBadge, .mx_RoomTile2_badgeContainer { + .mx_NotificationBadge, .mx_RoomTile_badgeContainer { position: absolute; top: 0; right: 0; diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index e3d2ae8306..d2fe98e8f9 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -55,7 +55,7 @@ limitations under the License. margin-left: 5px; width: 20px; height: 20px; - background-repeat: none; + background-repeat: no-repeat; } .mx_ShareDialog_split { diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index cf5bc7ab41..f67da6477b 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -191,5 +191,5 @@ limitations under the License. } .mx_Field .mx_CountryDropdown { - width: 67px; + width: $font-78px; } diff --git a/res/css/views/elements/_InteractiveTooltip.scss b/res/css/views/elements/_InteractiveTooltip.scss deleted file mode 100644 index db98d95709..0000000000 --- a/res/css/views/elements/_InteractiveTooltip.scss +++ /dev/null @@ -1,91 +0,0 @@ -/* -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: $font-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/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss index 7efea2c3f7..d90c818f94 100644 --- a/res/css/views/elements/_Tooltip.scss +++ b/res/css/views/elements/_Tooltip.scss @@ -51,21 +51,27 @@ limitations under the License. .mx_Tooltip { display: none; position: fixed; - border: 1px solid $menu-border-color; - border-radius: 4px; + border-radius: 8px; box-shadow: 4px 4px 12px 0 $menu-box-shadow-color; - background-color: $menu-bg-color; z-index: 6000; // Higher than context menu so tooltips can be used everywhere padding: 10px; pointer-events: none; line-height: $font-14px; font-size: $font-12px; - font-weight: 600; - color: $primary-fg-color; + font-weight: 500; max-width: 200px; word-break: break-word; margin-right: 50px; + background-color: $inverted-bg-color; + color: $accent-fg-color; + border: 0; + text-align: center; + + .mx_Tooltip_chevron { + display: none; + } + &.mx_Tooltip_visible { animation: mx_fadein 0.2s forwards; } @@ -75,15 +81,23 @@ limitations under the License. } } -.mx_Tooltip_timeline { - &.mx_Tooltip { - background-color: $inverted-bg-color; - color: $accent-fg-color; - border: 0; - text-align: center; +// These tooltips use an older style with a chevron +.mx_Field_tooltip { + background-color: $menu-bg-color; + color: $primary-fg-color; + border: 1px solid $menu-border-color; + text-align: unset; - .mx_Tooltip_chevron { - display: none; - } + .mx_Tooltip_chevron { + display: unset; } } + +.mx_Tooltip_title { + font-weight: 600; +} + +.mx_Tooltip_sub { + opacity: 0.7; + margin-top: 4px; +} diff --git a/res/css/views/messages/_MessageTimestamp.scss b/res/css/views/messages/_MessageTimestamp.scss index f8d91cc083..85c910296a 100644 --- a/res/css/views/messages/_MessageTimestamp.scss +++ b/res/css/views/messages/_MessageTimestamp.scss @@ -17,4 +17,5 @@ limitations under the License. .mx_MessageTimestamp { color: $event-timestamp-color; font-size: $font-10px; + font-variant-numeric: tabular-nums; } diff --git a/res/css/views/messages/_ReactionsRowButton.scss b/res/css/views/messages/_ReactionsRowButton.scss index fe5b081042..7158ffc027 100644 --- a/res/css/views/messages/_ReactionsRowButton.scss +++ b/res/css/views/messages/_ReactionsRowButton.scss @@ -16,7 +16,6 @@ limitations under the License. .mx_ReactionsRowButton { display: inline-flex; - height: 20px; line-height: $font-21px; margin-right: 6px; padding: 0 6px; @@ -35,11 +34,6 @@ limitations under the License. border-color: $reaction-row-button-selected-border-color; } - // ignore mouse events for all children, treat it as one entire hoverable entity - * { - pointer-events: none; - } - .mx_ReactionsRowButton_content { max-width: 100px; overflow: hidden; diff --git a/res/css/views/messages/_common_CryptoEvent.scss b/res/css/views/messages/_common_CryptoEvent.scss index 637d25d7a1..09c78ae5b4 100644 --- a/res/css/views/messages/_common_CryptoEvent.scss +++ b/res/css/views/messages/_common_CryptoEvent.scss @@ -15,28 +15,45 @@ limitations under the License. */ .mx_cryptoEvent { - display: grid; grid-template-columns: 24px minmax(0, 1fr) min-content; + &.mx_cryptoEvent_icon::before, &.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%; + top: 0; + bottom: 0; + left: 0; + right: 0; + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + mask-image: url('$(res)/img/e2e/normal.svg'); + background-color: $composer-e2e-icon-color; margin-top: 4px; } + // white infill for the transparency + &.mx_cryptoEvent_icon::before { + background-color: #ffffff; + mask-image: url('$(res)/img/e2e/normal.svg'); + mask-repeat: no-repeat; + mask-position: center; + mask-size: 90%; + } + &.mx_cryptoEvent_icon_verified::after { - background-image: url("$(res)/img/e2e/verified.svg"); + mask-image: url("$(res)/img/e2e/verified.svg"); + background-color: $accent-color; } &.mx_cryptoEvent_icon_warning::after { - background-image: url("$(res)/img/e2e/warning.svg"); + mask-image: url("$(res)/img/e2e/warning.svg"); + background-color: $notice-primary-color; } .mx_cryptoEvent_title, .mx_cryptoEvent_subtitle, .mx_cryptoEvent_state { diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 6d5ef5c14f..6be417f631 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -98,6 +98,7 @@ $AppsDrawerBodyHeight: 273px; .mx_AppTile_mini .mx_AppTile_persistedWrapper { height: 114px; + min-width: 300px; } .mx_AppTileMenuBar { diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index a4aebdb708..f8e0a382b1 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -6,9 +6,10 @@ border: 1px solid $primary-hairline-color; background: $primary-bg-color; border-bottom: none; - border-radius: 4px 4px 0 0; + border-radius: 8px 8px 0 0; max-height: 50vh; overflow: auto; + box-shadow: 0px -16px 32px $composer-shadow-color; } .mx_Autocomplete_ProviderSection { diff --git a/res/css/views/rooms/_E2EIcon.scss b/res/css/views/rooms/_E2EIcon.scss index ac47e3aedf..a3473dedec 100644 --- a/res/css/views/rooms/_E2EIcon.scss +++ b/res/css/views/rooms/_E2EIcon.scss @@ -42,7 +42,7 @@ limitations under the License. // white infill for the transparency .mx_E2EIcon::before { background-color: #ffffff; - mask: url('$(res)/img/e2e/normal.svg'); + mask-image: url('$(res)/img/e2e/normal.svg'); mask-repeat: no-repeat; mask-position: center; mask-size: 90%; diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 844ba1981e..2a2191b799 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -355,7 +355,7 @@ $left-gutter: 64px; &::before { background-color: #ffffff; - mask: url('$(res)/img/e2e/normal.svg'); + mask-image: url('$(res)/img/e2e/normal.svg'); mask-repeat: no-repeat; mask-position: center; mask-size: 90%; diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 94753f9473..ed60c220e7 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -97,7 +97,7 @@ $irc-line-height: $font-18px; } > .mx_EventTile_e2eIcon { - position: relative; + position: absolute; right: unset; left: unset; top: 0; @@ -181,9 +181,11 @@ $irc-line-height: $font-18px; > span { display: flex; - > .mx_SenderProfile_name { + > .mx_SenderProfile_name, + > .mx_SenderProfile_aux { overflow: hidden; text-overflow: ellipsis; + min-width: var(--name-width); } } } diff --git a/res/css/views/rooms/_InviteOnlyIcon.scss b/res/css/views/rooms/_InviteOnlyIcon.scss deleted file mode 100644 index b71fd6348d..0000000000 --- a/res/css/views/rooms/_InviteOnlyIcon.scss +++ /dev/null @@ -1,58 +0,0 @@ -/* -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 23018df8da..cb3803fe5b 100644 --- a/res/css/views/rooms/_JumpToBottomButton.scss +++ b/res/css/views/rooms/_JumpToBottomButton.scss @@ -41,8 +41,8 @@ limitations under the License. // with text-align in parent display: inline-block; padding: 0 4px; - color: $roomtile-badge-fg-color; - background-color: $roomtile-name-color; + color: $accent-fg-color; + background-color: $muted-fg-color; } .mx_JumpToBottomButton_highlight .mx_JumpToBottomButton_badge { @@ -56,7 +56,7 @@ limitations under the License. border-radius: 19px; box-sizing: border-box; background: $primary-bg-color; - border: 1.3px solid $roomtile-name-color; + border: 1.3px solid $muted-fg-color; cursor: pointer; } @@ -70,5 +70,5 @@ limitations under the License. mask: url('$(res)/img/icon-jump-to-bottom.svg'); mask-repeat: no-repeat; mask-position: 9px 14px; - background: $roomtile-name-color; + background: $muted-fg-color; } diff --git a/res/css/views/rooms/_MemberList.scss b/res/css/views/rooms/_MemberList.scss index 718164589c..90667d41b4 100644 --- a/res/css/views/rooms/_MemberList.scss +++ b/res/css/views/rooms/_MemberList.scss @@ -63,17 +63,18 @@ limitations under the License. .mx_GroupMemberList_query, .mx_GroupRoomList_query { flex: 1 1 0; + + // stricter rule to override the one in _common.scss + &[type="text"] { + font-size: $font-12px; + } } - - .mx_MemberList_wrapper { padding: 10px; } - -.mx_MemberList_invite, -.mx_RightPanel_invite { +.mx_MemberList_invite { flex: 0 0 auto; position: relative; background-color: $button-bg-color; @@ -83,11 +84,6 @@ limitations under the License. justify-content: center; color: $button-fg-color; font-weight: 600; - - .mx_RightPanel_icon { - padding-right: 5px; - padding-top: 2px; - } } .mx_MemberList_invite.mx_AccessibleButton_disabled { @@ -102,3 +98,11 @@ limitations under the License. background-size: 20px; padding: 8px 0 8px 25px; } + +.mx_MemberList_inviteCommunity span { + background-image: url('$(res)/img/icon-invite-people.svg'); +} + +.mx_MemberList_addRoomToCommunity span { + background-image: url('$(res)/img/icons-room-add.svg'); +} diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 9f6d2ec590..ec95403262 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -20,7 +20,7 @@ limitations under the License. margin: auto; border-top: 1px solid $primary-hairline-color; position: relative; - padding-left: 83px; + padding-left: 82px; } .mx_MessageComposer_replaced_wrapper { diff --git a/res/css/views/rooms/_NotificationBadge.scss b/res/css/views/rooms/_NotificationBadge.scss index 0e6d442cc1..64b2623238 100644 --- a/res/css/views/rooms/_NotificationBadge.scss +++ b/res/css/views/rooms/_NotificationBadge.scss @@ -27,7 +27,7 @@ limitations under the License. // ^- The count is an element floating within that. &.mx_NotificationBadge_visible { - background-color: $roomtile2-default-badge-bg-color; + background-color: $roomtile-default-badge-bg-color; // Create a flexbox to order the count a bit easier display: flex; @@ -42,6 +42,8 @@ limitations under the License. // These are the 3 background types &.mx_NotificationBadge_dot { + background-color: $primary-fg-color; // increased visibility + width: 6px; height: 6px; border-radius: 6px; diff --git a/res/css/views/rooms/_ReplyPreview.scss b/res/css/views/rooms/_ReplyPreview.scss index 4dc4cb2c40..9feb337042 100644 --- a/res/css/views/rooms/_ReplyPreview.scss +++ b/res/css/views/rooms/_ReplyPreview.scss @@ -22,9 +22,10 @@ limitations under the License. border: 1px solid $primary-hairline-color; background: $primary-bg-color; border-bottom: none; - border-radius: 4px 4px 0 0; + border-radius: 8px 8px 0 0; max-height: 50vh; overflow: auto; + box-shadow: 0px -16px 32px $composer-shadow-color; } .mx_ReplyPreview_section { diff --git a/res/css/views/rooms/_RoomBreadcrumbs.scss b/res/css/views/rooms/_RoomBreadcrumbs.scss index 3858d836e6..6512797401 100644 --- a/res/css/views/rooms/_RoomBreadcrumbs.scss +++ b/res/css/views/rooms/_RoomBreadcrumbs.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. @@ -15,98 +15,42 @@ limitations under the License. */ .mx_RoomBreadcrumbs { - position: relative; - height: 42px; - padding: 8px; - padding-bottom: 0; + width: 100%; + + // Create a flexbox for the crumbs 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 { - display: flex; - flex-direction: row; - height: 100%; - } + align-items: flex-start; .mx_RoomBreadcrumbs_crumb { - margin-left: 4px; - height: 32px; - display: inline-block; - transition: transform 0.3s, width 0.3s; - position: relative; - - .mx_RoomTile_badge { - position: absolute; - top: -3px; - right: -4px; - } - - .mx_RoomBreadcrumbs_dmIndicator { - position: absolute; - bottom: 0; - right: -4px; - } - } - - .mx_RoomBreadcrumbs_animate { - margin-left: 0; + margin-right: 8px; width: 32px; - transform: scale(1); } - .mx_RoomBreadcrumbs_preAnimate { - width: 0; - transform: scale(0); + // These classes come from the CSSTransition component. There's many more classes we + // could care about, but this is all we worried about for now. The animation works by + // first triggering the enter state with the newest breadcrumb off screen (-40px) then + // sliding it into view. + &.mx_RoomBreadcrumbs-enter { + margin-left: -40px; // 32px for the avatar, 8px for the margin + } + &.mx_RoomBreadcrumbs-enter-active { + margin-left: 0; + + // Timing function is as-requested by design. + // NOTE: The transition time MUST match the value passed to CSSTransition! + transition: margin-left 640ms cubic-bezier(0.66, 0.02, 0.36, 1); } - .mx_RoomBreadcrumbs_left { - opacity: 0.5; - } - - // Note: we have to manually control the gradient and stuff, but the IndicatorScrollbar - // will deal with left/right positioning for us. Normally we'd use position:sticky on - // a few key elements, however that doesn't work in horizontal scrolling scenarios. - - .mx_IndicatorScrollbar_leftOverflowIndicator, - .mx_IndicatorScrollbar_rightOverflowIndicator { - 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; - top: 0; - bottom: 0; - width: 15px; - display: block; - pointer-events: none; - z-index: 100; + .mx_RoomBreadcrumbs_placeholder { + font-weight: 600; + font-size: $font-14px; + line-height: 32px; // specifically to match the height this is not scaled + height: 32px; } } + +.mx_RoomBreadcrumbs_Tooltip { + margin-left: -42px; + margin-top: -42px; +} diff --git a/res/css/views/rooms/_RoomBreadcrumbs2.scss b/res/css/views/rooms/_RoomBreadcrumbs2.scss deleted file mode 100644 index fd050cfd7c..0000000000 --- a/res/css/views/rooms/_RoomBreadcrumbs2.scss +++ /dev/null @@ -1,68 +0,0 @@ -/* -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. -*/ - -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 - -.mx_RoomBreadcrumbs2 { - width: 100%; - - // Create a flexbox for the crumbs - display: flex; - flex-direction: row; - align-items: flex-start; - - .mx_RoomBreadcrumbs2_crumb { - margin-right: 8px; - width: 32px; - } - - // These classes come from the CSSTransition component. There's many more classes we - // could care about, but this is all we worried about for now. The animation works by - // first triggering the enter state with the newest breadcrumb off screen (-40px) then - // sliding it into view. - &.mx_RoomBreadcrumbs2-enter { - margin-left: -40px; // 32px for the avatar, 8px for the margin - } - &.mx_RoomBreadcrumbs2-enter-active { - margin-left: 0; - - // Timing function is as-requested by design. - // NOTE: The transition time MUST match the value passed to CSSTransition! - transition: margin-left 640ms cubic-bezier(0.66, 0.02, 0.36, 1); - } - - .mx_RoomBreadcrumbs2_placeholder { - font-weight: 600; - font-size: $font-14px; - line-height: 32px; // specifically to match the height this is not scaled - height: 32px; - } -} - -.mx_RoomBreadcrumbs2_Tooltip { - margin-left: -42px; - margin-top: -42px; - - &.mx_Tooltip { - background-color: $inverted-bg-color; - color: $accent-fg-color; - border: 0; - - .mx_Tooltip_chevron { - display: none; - } - } -} diff --git a/res/css/views/rooms/_RoomDropTarget.scss b/res/css/views/rooms/_RoomDropTarget.scss deleted file mode 100644 index 2e8145c2c9..0000000000 --- a/res/css/views/rooms/_RoomDropTarget.scss +++ /dev/null @@ -1,55 +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. -*/ - -.mx_RoomDropTarget_container { - background-color: $secondary-accent-color; - padding-left: 18px; - padding-right: 18px; - padding-top: 8px; - padding-bottom: 7px; -} - -.collapsed .mx_RoomDropTarget_container { - padding-right: 10px; - padding-left: 10px; -} - -.mx_RoomDropTarget { - font-size: $font-13px; - padding-top: 5px; - padding-bottom: 5px; - border: 1px dashed $accent-color; - color: $primary-fg-color; - background-color: $droptarget-bg-color; - border-radius: 4px; -} - - -.mx_RoomDropTarget_label { - position: relative; - margin-top: 3px; - line-height: $font-21px; - z-index: 1; - text-align: center; -} - -.collapsed .mx_RoomDropTarget_avatar { - float: none; -} - -.collapsed .mx_RoomDropTarget_label { - display: none; -} diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index 46993bb644..ba46100ea6 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -75,7 +75,6 @@ limitations under the License. .mx_RoomHeader_buttons { display: flex; background-color: $primary-bg-color; - padding-right: 5px; } .mx_RoomHeader_info { @@ -209,20 +208,32 @@ limitations under the License. .mx_RoomHeader_button { position: relative; - margin-left: 10px; + margin-left: 1px; + margin-right: 1px; cursor: pointer; - height: 20px; - width: 20px; + height: 32px; + width: 32px; + border-radius: 100%; &::before { content: ''; position: absolute; - height: 20px; - width: 20px; + top: 4px; // center with parent of 32px + left: 4px; // center with parent of 32px + height: 24px; + width: 24px; background-color: $roomheader-button-color; mask-repeat: no-repeat; mask-size: contain; } + + &:hover { + background: rgba($accent-color, 0.1); + + &::before { + background-color: $accent-color; + } + } } .mx_RoomHeader_settingsButton::before { diff --git a/res/css/views/rooms/_RoomList.scss b/res/css/views/rooms/_RoomList.scss index c23c19699d..89ab85e146 100644 --- a/res/css/views/rooms/_RoomList.scss +++ b/res/css/views/rooms/_RoomList.scss @@ -1,6 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,56 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_RoomList.mx_RoomList2 { - overflow-y: auto; -} - .mx_RoomList { - /* take up remaining space below TopLeftMenu */ - flex: 1; - min-height: 0; - overflow-y: hidden; -} - -.mx_RoomList .mx_ResizeHandle { - // needed so the z-index takes effect - position: relative; -} - -/* hide resize handles next to collapsed / empty sublists */ -.mx_RoomList .mx_RoomSubList:not(.mx_RoomSubList_nonEmpty) + .mx_ResizeHandle { - display: none; -} - -.mx_RoomList_expandButton { - margin-left: 8px; - cursor: pointer; - padding-left: 12px; - padding-right: 12px; -} - -.mx_RoomList_emptySubListTip_container { - padding-left: 18px; - padding-right: 18px; - padding-top: 8px; - padding-bottom: 7px; -} - -.mx_RoomList_emptySubListTip { - font-size: $font-13px; - padding: 5px; - border: 1px dashed $accent-color; - color: $primary-fg-color; - background-color: $droptarget-bg-color; - border-radius: 4px; - line-height: $font-16px; -} - -.mx_RoomList_emptySubListTip .mx_RoleButton { - vertical-align: -2px; -} - -.mx_RoomList_headerButtons { - position: absolute; - right: 60px; + padding-right: 7px; // width of the scrollbar, to line things up } diff --git a/res/css/views/rooms/_RoomList2.scss b/res/css/views/rooms/_RoomList2.scss deleted file mode 100644 index 5b78020626..0000000000 --- a/res/css/views/rooms/_RoomList2.scss +++ /dev/null @@ -1,27 +0,0 @@ -/* -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. -*/ - -// TODO: Rename to mx_RoomList during replacement of old component - -.mx_RoomList2 { - width: calc(100% - 16px); // 16px of artificial right-side margin (8px is overflowed from the sublists) - - // Create a column-based flexbox for the sublists. That's pretty much all we have to - // worry about in this stylesheet. - display: flex; - flex-direction: column; - flex-wrap: nowrap; // let the column overflow -} diff --git a/res/css/views/rooms/_RoomSublist2.scss b/res/css/views/rooms/_RoomSublist.scss similarity index 77% rename from res/css/views/rooms/_RoomSublist2.scss rename to res/css/views/rooms/_RoomSublist.scss index 77a762b4d8..b907d06d36 100644 --- a/res/css/views/rooms/_RoomSublist2.scss +++ b/res/css/views/rooms/_RoomSublist.scss @@ -14,20 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 - -.mx_RoomSublist2 { - // The sublist is a column of rows, essentially - display: flex; - flex-direction: column; - +.mx_RoomSublist { margin-left: 8px; margin-bottom: 4px; - width: 100%; - flex-shrink: 0; // to convince safari's layout engine the flexbox is fine - - .mx_RoomSublist2_headerContainer { + .mx_RoomSublist_headerContainer { // Create a flexbox to make alignment easy display: flex; align-items: center; @@ -43,13 +34,13 @@ limitations under the License. // all works by ensuring the header text has a fixed height when sticky so the // fixed height of the container can maintain the scroll position. - // The combined height must be set in the LeftPanel2 component for sticky headers + // The combined height must be set in the LeftPanel component for sticky headers // to work correctly. padding-bottom: 8px; height: 24px; - color: $roomlist2-header-color; + color: $roomlist-header-color; - .mx_RoomSublist2_stickable { + .mx_RoomSublist_stickable { flex: 1; max-width: 100%; @@ -61,26 +52,26 @@ limitations under the License. // to identify when a header is sticky. If we didn't have a consistent sticky class, // we'd have to do the "is sticky" checks again on click, as clicking the header // when sticky scrolls instead of collapses the list. - &.mx_RoomSublist2_headerContainer_sticky { + &.mx_RoomSublist_headerContainer_sticky { position: fixed; height: 32px; // to match the header container // width set by JS width: calc(100% - 22px); } - &.mx_RoomSublist2_headerContainer_stickyBottom { + &.mx_RoomSublist_headerContainer_stickyBottom { bottom: 0; } // We don't have a top style because the top is dependent on the room list header's // height, and is therefore calculated in JS. - // The class, mx_RoomSublist2_headerContainer_stickyTop, is applied though. + // The class, mx_RoomSublist_headerContainer_stickyTop, is applied though. } // Sticky Headers End // *************************** - .mx_RoomSublist2_badgeContainer { + .mx_RoomSublist_badgeContainer { // Create another flexbox row because it's super easy to position the badge this way. display: flex; align-items: center; @@ -93,14 +84,14 @@ limitations under the License. } } - &:not(.mx_RoomSublist2_headerContainer_withAux) { + &:not(.mx_RoomSublist_headerContainer_withAux) { .mx_NotificationBadge { margin-right: 4px; // just to push it over a bit, aligning it with the other elements } } - .mx_RoomSublist2_auxButton, - .mx_RoomSublist2_menuButton { + .mx_RoomSublist_auxButton, + .mx_RoomSublist_menuButton { margin-left: 8px; // should be the same as the notification badge position: relative; width: 24px; @@ -122,21 +113,21 @@ limitations under the License. } // Hide the menu button by default - .mx_RoomSublist2_menuButton { + .mx_RoomSublist_menuButton { visibility: hidden; width: 0; margin: 0; } - .mx_RoomSublist2_auxButton::before { + .mx_RoomSublist_auxButton::before { mask-image: url('$(res)/img/feather-customised/plus.svg'); } - .mx_RoomSublist2_menuButton::before { + .mx_RoomSublist_menuButton::before { mask-image: url('$(res)/img/element-icons/context-menu.svg'); } - .mx_RoomSublist2_headerText { + .mx_RoomSublist_headerText { flex: 1; max-width: calc(100% - 16px); // 16px is the badge width line-height: $font-16px; @@ -148,7 +139,7 @@ limitations under the License. overflow: hidden; white-space: nowrap; - .mx_RoomSublist2_collapseBtn { + .mx_RoomSublist_collapseBtn { display: inline-block; position: relative; width: 12px; @@ -169,7 +160,7 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); } - &.mx_RoomSublist2_collapseBtn_collapsed::before { + &.mx_RoomSublist_collapseBtn_collapsed::before { mask-image: url('$(res)/img/feather-customised/chevron-right.svg'); } } @@ -181,12 +172,12 @@ limitations under the License. // when scrolled to the top above the first sublist (whose header can only // ever stick to top), so we force height to 0 for only that first header. // See also https://github.com/vector-im/riot-web/issues/14429. - &:first-child .mx_RoomSublist2_headerContainer { + &:first-child .mx_RoomSublist_headerContainer { height: 0; padding-bottom: 4px; } - .mx_RoomSublist2_resizeBox { + .mx_RoomSublist_resizeBox { position: relative; // Create another flexbox column for the tiles @@ -194,7 +185,7 @@ limitations under the License. flex-direction: column; overflow: hidden; - .mx_RoomSublist2_tiles { + .mx_RoomSublist_tiles { flex: 1 0 0; overflow: hidden; // need this to be flex otherwise the overflow hidden from above @@ -206,18 +197,18 @@ limitations under the License. mask-image: linear-gradient(0deg, transparent, black 4px); } - .mx_RoomSublist2_resizerHandles_showNButton { + .mx_RoomSublist_resizerHandles_showNButton { flex: 0 0 32px; } - .mx_RoomSublist2_resizerHandles { + .mx_RoomSublist_resizerHandles { flex: 0 0 4px; } // Class name comes from the ResizableBox component // The hover state needs to use the whole sublist, not just the resizable box, // so that selector is below and one level higher. - .mx_RoomSublist2_resizerHandle { + .mx_RoomSublist_resizerHandle { cursor: ns-resize; border-radius: 3px; @@ -235,21 +226,21 @@ limitations under the License. right: calc(50% - 32px) !important; } - &:hover, &.mx_RoomSublist2_hasMenuOpen { - .mx_RoomSublist2_resizerHandle { + &:hover, &.mx_RoomSublist_hasMenuOpen { + .mx_RoomSublist_resizerHandle { opacity: 0.8; background-color: $primary-fg-color; } } } - .mx_RoomSublist2_showNButton { + .mx_RoomSublist_showNButton { cursor: pointer; font-size: $font-13px; line-height: $font-18px; - color: $roomtile2-preview-color; + color: $roomtile-preview-color; - // Update the render() function for RoomSublist2 if these change + // Update the render() function for RoomSublist if these change // Update the ListLayout class for minVisibleTiles if these change. height: 24px; padding-bottom: 4px; @@ -258,7 +249,7 @@ limitations under the License. display: flex; align-items: center; - .mx_RoomSublist2_showNButtonChevron { + .mx_RoomSublist_showNButtonChevron { position: relative; width: 16px; height: 16px; @@ -267,52 +258,52 @@ limitations under the License. mask-position: center; mask-size: contain; mask-repeat: no-repeat; - background: $roomtile2-preview-color; + background: $roomtile-preview-color; } - .mx_RoomSublist2_showMoreButtonChevron { + .mx_RoomSublist_showMoreButtonChevron { mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); } - .mx_RoomSublist2_showLessButtonChevron { + .mx_RoomSublist_showLessButtonChevron { mask-image: url('$(res)/img/feather-customised/chevron-up.svg'); } } - &.mx_RoomSublist2_hasMenuOpen, - &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer:focus-within, - &:not(.mx_RoomSublist2_minimized) > .mx_RoomSublist2_headerContainer:hover { - .mx_RoomSublist2_menuButton { + &.mx_RoomSublist_hasMenuOpen, + &:not(.mx_RoomSublist_minimized) > .mx_RoomSublist_headerContainer:focus-within, + &:not(.mx_RoomSublist_minimized) > .mx_RoomSublist_headerContainer:hover { + .mx_RoomSublist_menuButton { visibility: visible; width: 24px; margin-left: 8px; } } - &.mx_RoomSublist2_minimized { - .mx_RoomSublist2_headerContainer { + &.mx_RoomSublist_minimized { + .mx_RoomSublist_headerContainer { height: auto; flex-direction: column; position: relative; - .mx_RoomSublist2_badgeContainer { + .mx_RoomSublist_badgeContainer { order: 0; align-self: flex-end; margin-right: 0; } - .mx_RoomSublist2_stickable { + .mx_RoomSublist_stickable { order: 1; max-width: 100%; } - .mx_RoomSublist2_auxButton { + .mx_RoomSublist_auxButton { order: 2; visibility: visible; width: 32px !important; // !important to override hover styles height: 32px !important; // !important to override hover styles margin-left: 0 !important; // !important to override hover styles - background-color: $roomlist2-button-bg-color; + background-color: $roomlist-button-bg-color; margin-top: 8px; &::before { @@ -322,25 +313,25 @@ limitations under the License. } } - .mx_RoomSublist2_resizeBox { + .mx_RoomSublist_resizeBox { align-items: center; } - .mx_RoomSublist2_showNButton { + .mx_RoomSublist_showNButton { flex-direction: column; - .mx_RoomSublist2_showNButtonChevron { + .mx_RoomSublist_showNButtonChevron { margin-right: 12px; // to center } } - .mx_RoomSublist2_menuButton { + .mx_RoomSublist_menuButton { height: 16px; } - &.mx_RoomSublist2_hasMenuOpen, - & > .mx_RoomSublist2_headerContainer:hover { - .mx_RoomSublist2_menuButton { + &.mx_RoomSublist_hasMenuOpen, + & > .mx_RoomSublist_headerContainer:hover { + .mx_RoomSublist_menuButton { visibility: visible; position: absolute; bottom: 48px; // align to middle of name, 40px for aux button (with padding) and 8px for alignment @@ -352,7 +343,7 @@ limitations under the License. // This is the same color as the left panel background because it needs // to occlude the sublist title - background-color: $roomlist2-bg-color; + background-color: $roomlist-bg-color; &::before { top: 0; @@ -360,8 +351,8 @@ limitations under the License. } } - &.mx_RoomSublist2_headerContainer:not(.mx_RoomSublist2_headerContainer_withAux) { - .mx_RoomSublist2_menuButton { + &.mx_RoomSublist_headerContainer:not(.mx_RoomSublist_headerContainer_withAux) { + .mx_RoomSublist_menuButton { bottom: 8px; // align to the middle of name, 40px less than the `bottom` above. } } @@ -369,7 +360,7 @@ limitations under the License. } } -.mx_RoomSublist2_contextMenu { +.mx_RoomSublist_contextMenu { padding: 20px 16px; width: 250px; @@ -377,11 +368,11 @@ limitations under the License. margin-top: 16px; margin-bottom: 16px; margin-right: 16px; // additional 16px - border: 1px solid $roomsublist2-divider-color; + border: 1px solid $roomsublist-divider-color; opacity: 0.1; } - .mx_RoomSublist2_contextMenu_title { + .mx_RoomSublist_contextMenu_title { font-size: $font-15px; line-height: $font-20px; font-weight: 600; @@ -393,6 +384,6 @@ limitations under the License. } } -.mx_RoomSublist2_addRoomTooltip { +.mx_RoomSublist_addRoomTooltip { margin-top: -3px; } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 7f93da0bbf..f22228602d 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -1,6 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +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,214 +14,222 @@ See the License for the specific language governing permissions and limitations under the License. */ +// Note: the room tile expects to be in a flexbox column container .mx_RoomTile { - display: flex; - flex-direction: row; - align-items: center; - cursor: pointer; - height: 34px; - margin: 0; - padding: 0 8px 0 10px; - position: relative; - - .mx_RoomTile_menuButton { - display: none; - flex: 0 0 16px; - height: 16px; - background-image: url('$(res)/img/icon_context.svg'); - background-repeat: no-repeat; - background-position: center; - } - - .mx_UserOnlineDot { - display: block; - margin-right: 5px; - } -} - -.mx_RoomTile:focus { - filter: none !important; - background-color: $roomtile-focused-bg-color; -} - -.mx_RoomTile_tooltip { - display: inline-block; - position: relative; - top: -54px; - left: -12px; -} - -.mx_RoomTile_nameContainer { - display: flex; - align-items: center; - flex: 1; - vertical-align: middle; - min-width: 0; -} - -.mx_RoomTile_labelContainer { - display: flex; - flex-direction: column; - flex: 1; - min-width: 0; -} - -.mx_RoomTile_subtext { - display: inline-block; - font-size: $font-11px; - padding: 0 0 0 7px; - margin: 0; - overflow: hidden; - white-space: nowrap; - text-overflow: clip; - position: relative; - bottom: 4px; -} - -.mx_RoomTile_avatar_container { - position: relative; - display: flex; -} - -.mx_RoomTile_avatar { - flex: 0; + margin-bottom: 4px; padding: 4px; - width: 24px; - vertical-align: middle; -} -.mx_RoomTile_hasSubtext .mx_RoomTile_avatar { - padding-top: 0; - vertical-align: super; -} + // The tile is also a flexbox row itself + display: flex; -.mx_RoomTile_dm { - display: block; - position: absolute; - bottom: 0; - right: -5px; - z-index: 2; -} + &.mx_RoomTile_selected, + &:hover, + &:focus-within, + &.mx_RoomTile_hasMenuOpen { + background-color: $roomtile-selected-bg-color; + border-radius: 8px; + } -// 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_DecoratedRoomAvatar, .mx_RoomTile_avatarContainer { + margin-right: 8px; + } -.mx_RoomTile_name { - font-size: $font-14px; - padding: 0 4px; - color: $roomtile-name-color; - white-space: nowrap; - overflow-x: hidden; - text-overflow: ellipsis; -} + .mx_RoomTile_nameContainer { + flex-grow: 1; + min-width: 0; // allow flex to shrink it + margin-right: 8px; // spacing to buttons/badges -.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: $font-12px; -} - -.collapsed { - .mx_RoomTile { - margin: 0 6px; - padding: 0 2px; - position: relative; + // Create a new column layout flexbox for the name parts + display: flex; + flex-direction: column; justify-content: center; + + .mx_RoomTile_name, + .mx_RoomTile_messagePreview { + margin: 0 2px; + width: 100%; + + // Ellipsize any text overflow + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .mx_RoomTile_name { + font-size: $font-14px; + line-height: $font-18px; + } + + .mx_RoomTile_name.mx_RoomTile_nameHasUnreadEvents { + font-weight: 600; + } + + .mx_RoomTile_messagePreview { + font-size: $font-13px; + line-height: $font-18px; + color: $roomtile-preview-color; + } + + .mx_RoomTile_nameWithPreview { + margin-top: -4px; // shift the name up a bit more + } } - .mx_RoomTile_name { + .mx_RoomTile_notificationsButton { + margin-left: 4px; // spacing between buttons + } + + .mx_RoomTile_badgeContainer { + height: 16px; + // don't set width so that it takes no space when there is no badge to show + margin: auto 0; // vertically align + + // Create a flexbox to make aligning dot badges easier + display: flex; + align-items: center; + + .mx_NotificationBadge { + margin-right: 2px; // centering + } + + .mx_NotificationBadge_dot { + // make the smaller dot occupy the same width for centering + margin-left: 5px; + margin-right: 7px; + } + } + + // The context menu buttons are hidden by default + .mx_RoomTile_menuButton, + .mx_RoomTile_notificationsButton { + width: 20px; + min-width: 20px; // yay flex + height: 20px; + margin-top: auto; + margin-bottom: auto; + position: relative; display: none; + + &::before { + top: 2px; + left: 2px; + content: ''; + width: 16px; + height: 16px; + position: absolute; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } } - .mx_RoomTile_badge { - position: absolute; - right: 6px; - top: 0px; - border-radius: 16px; - z-index: 3; - border: 0.18em solid $secondary-accent-color; - } - - .mx_RoomTile_menuButton { - display: none; // no design for this for now - } - .mx_UserOnlineDot { - display: none; // no design for this for now - } -} - -// 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 { + // If the room has an overriden notification setting then we always show the notifications menu button + .mx_RoomTile_notificationsButton.mx_RoomTile_notificationsButton_show { display: block; } - .mx_UserOnlineDot { - display: none; + + .mx_RoomTile_menuButton::before { + mask-image: url('$(res)/img/element-icons/context-menu.svg'); + } + + &:not(.mx_RoomTile_minimized) { + &:hover, + &:focus-within, + &.mx_RoomTile_hasMenuOpen { + // Hide the badge container on hover because it'll be a menu button + .mx_RoomTile_badgeContainer { + width: 0; + height: 0; + display: none; + } + + .mx_RoomTile_notificationsButton, + .mx_RoomTile_menuButton { + display: block; + } + } + } + + &.mx_RoomTile_minimized { + flex-direction: column; + align-items: center; + position: relative; + + .mx_DecoratedRoomAvatar, .mx_RoomTile_avatarContainer { + margin-right: 0; + } } } -.mx_RoomTile_unreadNotify .mx_RoomTile_badge, -.mx_RoomTile_badge.mx_RoomTile_badgeUnread { - background-color: $roomtile-name-color; +// We use these both in context menus and the room tiles +.mx_RoomTile_iconBell::before { + mask-image: url('$(res)/img/element-icons/notifications.svg'); +} +.mx_RoomTile_iconBellDot::before { + mask-image: url('$(res)/img/element-icons/roomlist/notifications-default.svg'); +} +.mx_RoomTile_iconBellCrossed::before { + mask-image: url('$(res)/img/element-icons/roomlist/notifications-off.svg'); +} +.mx_RoomTile_iconBellMentions::before { + mask-image: url('$(res)/img/element-icons/roomlist/notifications-dm.svg'); +} +.mx_RoomTile_iconCheck::before { + mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg'); } -.mx_RoomTile_highlight .mx_RoomTile_badge, -.mx_RoomTile_badge.mx_RoomTile_badgeRed { - color: $accent-fg-color; - background-color: $warning-color; -} +.mx_RoomTile_contextMenu { + .mx_RoomTile_contextMenu_redRow { + .mx_AccessibleButton { + color: $warning-color !important; // !important to override styles from context menu + } -.mx_RoomTile_unread, .mx_RoomTile_highlight { - .mx_RoomTile_name { - font-weight: 600; - color: $roomtile-selected-color; + .mx_IconizedContextMenu_icon::before { + background-color: $warning-color; + } + } + + .mx_RoomTile_contextMenu_activeRow { + &.mx_AccessibleButton, .mx_AccessibleButton { + color: $accent-color !important; // !important to override styles from context menu + } + + .mx_IconizedContextMenu_icon::before { + background-color: $accent-color; + } + } + + .mx_IconizedContextMenu_icon { + position: relative; + width: 16px; + height: 16px; + + &::before { + content: ''; + width: 16px; + height: 16px; + position: absolute; + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $primary-fg-color; + } + } + + .mx_RoomTile_iconStar::before { + mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg'); + } + + .mx_RoomTile_iconArrowDown::before { + mask-image: url('$(res)/img/element-icons/roomlist/low-priority.svg'); + } + + .mx_RoomTile_iconSettings::before { + mask-image: url('$(res)/img/element-icons/settings.svg'); + } + + .mx_RoomTile_iconSignOut::before { + mask-image: url('$(res)/img/element-icons/leave.svg'); } } - -.mx_RoomTile_selected { - border-radius: 4px; - background-color: $roomtile-selected-bg-color; -} - -.mx_DNDRoomTile { - transform: none; - transition: transform 0.2s; -} - -.mx_DNDRoomTile_dragging { - transform: scale(1.05, 1.05); -} - -.mx_RoomTile_arrow { - position: absolute; - right: 0px; -} - -.mx_RoomTile.mx_RoomTile_transparent { - background-color: transparent; -} - -.mx_RoomTile.mx_RoomTile_transparent:focus { - background-color: $roomtile-transparent-focused-color; -} - -.mx_GroupInviteTile .mx_RoomTile_name { - flex: 1; -} diff --git a/res/css/views/rooms/_RoomTile2.scss b/res/css/views/rooms/_RoomTile2.scss deleted file mode 100644 index 79b28598bd..0000000000 --- a/res/css/views/rooms/_RoomTile2.scss +++ /dev/null @@ -1,241 +0,0 @@ -/* -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. -*/ - -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 - -// Note: the room tile expects to be in a flexbox column container -.mx_RoomTile2 { - margin-bottom: 4px; - padding: 4px; - - // The tile is also a flexbox row itself - display: flex; - - &.mx_RoomTile2_selected, - &:hover, - &:focus-within, - &.mx_RoomTile2_hasMenuOpen { - background-color: $roomtile2-selected-bg-color; - border-radius: 8px; - } - - .mx_DecoratedRoomAvatar, .mx_RoomTile2_avatarContainer { - margin-right: 8px; - } - - .mx_RoomTile2_nameContainer { - flex-grow: 1; - min-width: 0; // allow flex to shrink it - margin-right: 8px; // spacing to buttons/badges - - // Create a new column layout flexbox for the name parts - display: flex; - flex-direction: column; - justify-content: center; - - .mx_RoomTile2_name, - .mx_RoomTile2_messagePreview { - margin: 0 2px; - width: 100%; - - // Ellipsize any text overflow - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - - .mx_RoomTile2_name { - font-size: $font-14px; - line-height: $font-18px; - } - - .mx_RoomTile2_name.mx_RoomTile2_nameHasUnreadEvents { - font-weight: 600; - } - - .mx_RoomTile2_messagePreview { - font-size: $font-13px; - line-height: $font-18px; - color: $roomtile2-preview-color; - } - - .mx_RoomTile2_nameWithPreview { - margin-top: -4px; // shift the name up a bit more - } - } - - .mx_RoomTile2_notificationsButton { - margin-left: 4px; // spacing between buttons - } - - .mx_RoomTile2_badgeContainer { - height: 16px; - // don't set width so that it takes no space when there is no badge to show - margin: auto 0; // vertically align - - // Create a flexbox to make aligning dot badges easier - display: flex; - align-items: center; - - .mx_NotificationBadge { - margin-right: 2px; // centering - } - - .mx_NotificationBadge_dot { - // make the smaller dot occupy the same width for centering - margin-left: 5px; - margin-right: 7px; - } - } - - // The context menu buttons are hidden by default - .mx_RoomTile2_menuButton, - .mx_RoomTile2_notificationsButton { - width: 20px; - min-width: 20px; // yay flex - height: 20px; - margin-top: auto; - margin-bottom: auto; - position: relative; - display: none; - - &::before { - top: 2px; - left: 2px; - content: ''; - width: 16px; - height: 16px; - position: absolute; - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background: $primary-fg-color; - } - } - - // If the room has an overriden notification setting then we always show the notifications menu button - .mx_RoomTile2_notificationsButton.mx_RoomTile2_notificationsButton_show { - display: block; - } - - .mx_RoomTile2_menuButton::before { - mask-image: url('$(res)/img/element-icons/context-menu.svg'); - } - - &:not(.mx_RoomTile2_minimized) { - &:hover, - &:focus-within, - &.mx_RoomTile2_hasMenuOpen { - // Hide the badge container on hover because it'll be a menu button - .mx_RoomTile2_badgeContainer { - width: 0; - height: 0; - display: none; - } - - .mx_RoomTile2_notificationsButton, - .mx_RoomTile2_menuButton { - display: block; - } - } - } - - &.mx_RoomTile2_minimized { - flex-direction: column; - align-items: center; - position: relative; - - .mx_DecoratedRoomAvatar, .mx_RoomTile2_avatarContainer { - margin-right: 0; - } - } -} - -// We use these both in context menus and the room tiles -.mx_RoomTile2_iconBell::before { - mask-image: url('$(res)/img/element-icons/notifications.svg'); -} -.mx_RoomTile2_iconBellDot::before { - mask-image: url('$(res)/img/element-icons/roomlist/notifications-default.svg'); -} -.mx_RoomTile2_iconBellCrossed::before { - mask-image: url('$(res)/img/element-icons/roomlist/notifications-off.svg'); -} -.mx_RoomTile2_iconBellMentions::before { - mask-image: url('$(res)/img/element-icons/roomlist/notifications-dm.svg'); -} -.mx_RoomTile2_iconCheck::before { - mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg'); -} - -.mx_RoomTile2_contextMenu { - .mx_RoomTile2_contextMenu_redRow { - .mx_AccessibleButton { - color: $warning-color !important; // !important to override styles from context menu - } - - .mx_IconizedContextMenu_icon::before { - background-color: $warning-color; - } - } - - .mx_RoomTile2_contextMenu_activeRow { - &.mx_AccessibleButton, .mx_AccessibleButton { - color: $accent-color !important; // !important to override styles from context menu - } - - .mx_IconizedContextMenu_icon::before { - background-color: $accent-color; - } - } - - .mx_IconizedContextMenu_icon { - position: relative; - width: 16px; - height: 16px; - - &::before { - content: ''; - width: 16px; - height: 16px; - position: absolute; - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background: $primary-fg-color; - } - } - - .mx_RoomTile2_iconStar::before { - mask-image: url('$(res)/img/element-icons/roomlist/favorite.svg'); - } - - .mx_RoomTile2_iconFavorite::before { - mask-image: url('$(res)/img/feather-customised/favourites.svg'); - } - - .mx_RoomTile2_iconArrowDown::before { - mask-image: url('$(res)/img/element-icons/roomlist/low-priority.svg'); - } - - .mx_RoomTile2_iconSettings::before { - mask-image: url('$(res)/img/element-icons/settings.svg'); - } - - .mx_RoomTile2_iconSignOut::before { - mask-image: url('$(res)/img/element-icons/leave.svg'); - } -} diff --git a/res/css/views/rooms/_RoomTileIcon.scss b/res/css/views/rooms/_RoomTileIcon.scss index adc8ea2994..2f3afdd446 100644 --- a/res/css/views/rooms/_RoomTileIcon.scss +++ b/res/css/views/rooms/_RoomTileIcon.scss @@ -18,7 +18,7 @@ limitations under the License. width: 12px; height: 12px; border-radius: 12px; - background-color: $roomlist2-bg-color; // to match the room list itself + background-color: $roomlist-bg-color; // to match the room list itself } .mx_RoomTileIcon_globe::before { diff --git a/res/css/views/rooms/_TopUnreadMessagesBar.scss b/res/css/views/rooms/_TopUnreadMessagesBar.scss index 28eddf1fa2..c0545660c2 100644 --- a/res/css/views/rooms/_TopUnreadMessagesBar.scss +++ b/res/css/views/rooms/_TopUnreadMessagesBar.scss @@ -42,7 +42,7 @@ limitations under the License. border-radius: 19px; box-sizing: border-box; background: $primary-bg-color; - border: 1.3px solid $roomtile-name-color; + border: 1.3px solid $muted-fg-color; cursor: pointer; } @@ -54,7 +54,7 @@ limitations under the License. mask-image: url('$(res)/img/icon-jump-to-first-unread.svg'); mask-repeat: no-repeat; mask-position: 9px 13px; - background: $roomtile-name-color; + background: $muted-fg-color; } .mx_TopUnreadMessagesBar_markAsRead { @@ -62,7 +62,7 @@ limitations under the License. width: 18px; height: 18px; background: $primary-bg-color; - border: 1.3px solid $roomtile-name-color; + border: 1.3px solid $muted-fg-color; border-radius: 10px; margin: 5px auto; } @@ -76,5 +76,5 @@ limitations under the License. mask-repeat: no-repeat; mask-size: 10px; mask-position: 4px 4px; - background: $roomtile-name-color; + background: $muted-fg-color; } diff --git a/res/css/views/rooms/_UserOnlineDot.scss b/res/css/views/rooms/_UserOnlineDot.scss deleted file mode 100644 index f9da8648ed..0000000000 --- a/res/css/views/rooms/_UserOnlineDot.scss +++ /dev/null @@ -1,23 +0,0 @@ -/* -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: 6px; - width: 6px; - display: inline-block; -} diff --git a/res/css/views/rooms/_WhoIsTypingTile.scss b/res/css/views/rooms/_WhoIsTypingTile.scss index 02d779a99c..1c0dabbeb5 100644 --- a/res/css/views/rooms/_WhoIsTypingTile.scss +++ b/res/css/views/rooms/_WhoIsTypingTile.scss @@ -59,7 +59,7 @@ limitations under the License. flex: 1; font-size: $font-14px; font-weight: 600; - color: $composer-button-color; + color: $roomtopic-color; } .mx_WhoIsTypingTile_label > span { diff --git a/res/css/views/voip/_CallContainer.scss b/res/css/views/voip/_CallContainer.scss index e13c851716..8d1b68dd99 100644 --- a/res/css/views/voip/_CallContainer.scss +++ b/res/css/views/voip/_CallContainer.scss @@ -36,12 +36,12 @@ limitations under the License. } } - .mx_IncomingCallBox2 { + .mx_IncomingCallBox { min-width: 250px; background-color: $primary-bg-color; padding: 8px; - .mx_IncomingCallBox2_CallerInfo { + .mx_IncomingCallBox_CallerInfo { display: flex; direction: row; @@ -68,12 +68,12 @@ limitations under the License. } } - .mx_IncomingCallBox2_buttons { + .mx_IncomingCallBox_buttons { padding: 8px; display: flex; flex-direction: row; - > .mx_IncomingCallBox2_spacer { + > .mx_IncomingCallBox_spacer { width: 8px; } diff --git a/res/css/views/voip/_CallView.scss b/res/css/views/voip/_CallView.scss index 4650f30c1d..f6f3d40308 100644 --- a/res/css/views/voip/_CallView.scss +++ b/res/css/views/voip/_CallView.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,8 +19,76 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; cursor: pointer; - text-align: center; padding: 6px; font-weight: bold; - font-size: $font-13px; + + border-radius: 8px; + min-width: 200px; + + display: flex; + align-items: center; + + img { + margin: 4px; + margin-right: 10px; + } + + > div { + display: flex; + flex-direction: column; + // Hacky vertical align + padding-top: 3px; + } + + > div > p, + > div > h1 { + padding: 0; + margin: 0; + font-size: $font-13px; + line-height: $font-15px; + } + + > div > p { + font-weight: bold; + } + + > * { + flex-grow: 0; + flex-shrink: 0; + } +} + +.mx_CallView_hangup { + position: absolute; + + right: 8px; + bottom: 10px; + + height: 35px; + width: 35px; + + border-radius: 35px; + + background-color: $notice-primary-color; + + z-index: 101; + + cursor: pointer; + + &::before { + content: ''; + position: absolute; + + height: 20px; + width: 20px; + + top: 6.5px; + left: 7.5px; + + mask: url('$(res)/img/hangup.svg'); + mask-size: contain; + background-size: contain; + + background-color: $primary-fg-color; + } } diff --git a/res/css/views/voip/_CallView2.scss b/res/css/views/voip/_CallView2.scss deleted file mode 100644 index 3b66e7a175..0000000000 --- a/res/css/views/voip/_CallView2.scss +++ /dev/null @@ -1,96 +0,0 @@ -/* -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. -*/ - -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14231 - -.mx_CallView2_voice { - background-color: $accent-color; - color: $accent-fg-color; - cursor: pointer; - padding: 6px; - font-weight: bold; - - border-radius: 8px; - min-width: 200px; - - display: flex; - align-items: center; - - img { - margin: 4px; - margin-right: 10px; - } - - > div { - display: flex; - flex-direction: column; - // Hacky vertical align - padding-top: 3px; - } - - > div > p, - > div > h1 { - padding: 0; - margin: 0; - font-size: $font-13px; - line-height: $font-15px; - } - - > div > p { - font-weight: bold; - } - - > * { - flex-grow: 0; - flex-shrink: 0; - } -} - -.mx_CallView2_hangup { - position: absolute; - - right: 8px; - bottom: 10px; - - height: 35px; - width: 35px; - - border-radius: 35px; - - background-color: $notice-primary-color; - - z-index: 101; - - cursor: pointer; - - &::before { - content: ''; - position: absolute; - - height: 20px; - width: 20px; - - top: 6.5px; - left: 7.5px; - - mask: url('$(res)/img/hangup.svg'); - mask-size: contain; - background-size: contain; - - background-color: $primary-fg-color; - } -} diff --git a/res/css/views/voip/_IncomingCallbox.scss b/res/css/views/voip/_IncomingCallbox.scss deleted file mode 100644 index ed33de470d..0000000000 --- a/res/css/views/voip/_IncomingCallbox.scss +++ /dev/null @@ -1,69 +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. -*/ - -.mx_IncomingCallBox { - text-align: center; - border: 1px solid #a4a4a4; - border-radius: 8px; - background-color: $primary-bg-color; - position: fixed; - z-index: 1000; - padding: 6px; - margin-top: -3px; - margin-left: -20px; - width: 200px; -} - -.mx_IncomingCallBox_chevron { - padding: 12px; - position: absolute; - left: -21px; - top: 0px; -} - -.mx_IncomingCallBox_title { - padding: 6px; - font-weight: bold; -} - -.mx_IncomingCallBox_buttons { - display: flex; -} - -.mx_IncomingCallBox_buttons_cell { - vertical-align: middle; - padding: 6px; - flex: 1; -} - -.mx_IncomingCallBox_buttons_decline, -.mx_IncomingCallBox_buttons_accept { - vertical-align: middle; - width: 80px; - height: 36px; - line-height: $font-36px; - border-radius: 36px; - color: $accent-fg-color; - margin: auto; -} - -.mx_IncomingCallBox_buttons_decline { - background-color: $voip-decline-color; -} - -.mx_IncomingCallBox_buttons_accept { - background-color: $voip-accept-color; -} diff --git a/res/img/element-icons/community-members.svg b/res/img/element-icons/community-members.svg index 3131075c7b..553ba3b1af 100644 --- a/res/img/element-icons/community-members.svg +++ b/res/img/element-icons/community-members.svg @@ -1,8 +1,8 @@ - + - + - - - + + + diff --git a/res/img/element-icons/community-rooms.svg b/res/img/element-icons/community-rooms.svg index c46cedc74f..570b45a488 100644 --- a/res/img/element-icons/community-rooms.svg +++ b/res/img/element-icons/community-rooms.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/img/element-icons/leave.svg b/res/img/element-icons/leave.svg index 8a96160afd..773e27d4ce 100644 --- a/res/img/element-icons/leave.svg +++ b/res/img/element-icons/leave.svg @@ -1,7 +1,7 @@ - - + + - + - + diff --git a/res/img/element-icons/notifications.svg b/res/img/element-icons/notifications.svg index c86a7a3b98..7002782129 100644 --- a/res/img/element-icons/notifications.svg +++ b/res/img/element-icons/notifications.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/res/img/element-icons/room/files.svg b/res/img/element-icons/room/files.svg index 6dfd6856d6..6648ab00a5 100644 --- a/res/img/element-icons/room/files.svg +++ b/res/img/element-icons/room/files.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/img/element-icons/room/integrations.svg b/res/img/element-icons/room/integrations.svg index 57937cd929..3a39506411 100644 --- a/res/img/element-icons/room/integrations.svg +++ b/res/img/element-icons/room/integrations.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/img/element-icons/room/members.svg b/res/img/element-icons/room/members.svg index e73834bfe5..03aba81ad4 100644 --- a/res/img/element-icons/room/members.svg +++ b/res/img/element-icons/room/members.svg @@ -1,7 +1,7 @@ - + - + - - + + diff --git a/res/img/element-icons/room/pin.svg b/res/img/element-icons/room/pin.svg index d2e9a2c2eb..16941b329b 100644 --- a/res/img/element-icons/room/pin.svg +++ b/res/img/element-icons/room/pin.svg @@ -1,7 +1,7 @@ - - - - - - + + + + + + diff --git a/res/img/element-icons/room/search-inset.svg b/res/img/element-icons/room/search-inset.svg index 2a837f5106..699cdd1d00 100644 --- a/res/img/element-icons/room/search-inset.svg +++ b/res/img/element-icons/room/search-inset.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/img/element-icons/room/share.svg b/res/img/element-icons/room/share.svg index 5accc0a849..dac35ae5a7 100644 --- a/res/img/element-icons/room/share.svg +++ b/res/img/element-icons/room/share.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/img/element-icons/settings.svg b/res/img/element-icons/settings.svg index e6e2aef54c..05d640df27 100644 --- a/res/img/element-icons/settings.svg +++ b/res/img/element-icons/settings.svg @@ -1,3 +1,3 @@ - - + + diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 990debb0a4..15155ba854 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -108,32 +108,20 @@ $composer-e2e-icon-color: $header-panel-text-primary-color; // ******************** -// V2 Room List -// TODO: Remove the 2 from all of these when the new list takes over - $theme-button-bg-color: #e3e8f0; +$roomlist-button-bg-color: rgba(141, 151, 165, 0.2); // Buttons include the filter box, explore button, and sublist buttons +$roomlist-bg-color: rgba(33, 38, 44, 0.90); +$roomlist-header-color: #8E99A4; +$roomsublist-divider-color: $primary-fg-color; -$roomlist2-button-bg-color: rgba(141, 151, 165, 0.2); // Buttons include the filter box, explore button, and sublist buttons -$roomlist2-bg-color: rgba(33, 38, 44, 0.90); -$roomlist2-header-color: #8E99A4; -$roomsublist2-divider-color: $primary-fg-color; - -$roomtile2-preview-color: #A9B2BC; -$roomtile2-default-badge-bg-color: #61708b; -$roomtile2-selected-bg-color: rgba(141, 151, 165, 0.2); +$roomtile-preview-color: #A9B2BC; +$roomtile-default-badge-bg-color: #61708b; +$roomtile-selected-bg-color: rgba(141, 151, 165, 0.2); // ******************** -$notice-secondary-color: $roomlist2-header-color; - -$roomtile-name-color: $header-panel-text-primary-color; -$roomtile-selected-color: $text-primary-color; -$roomtile-notified-color: $text-primary-color; -$roomtile-selected-bg-color: $room-highlight-color; -$roomtile-focused-bg-color: $room-highlight-color; - -$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); +$notice-secondary-color: $roomlist-header-color; $panel-divider-color: transparent; @@ -211,6 +199,8 @@ $appearance-tab-border-color: $room-highlight-color; $roomlist-background-blur-amount: 60px; $tagpanel-background-blur-amount: 30px; +$composer-shadow-color: rgba(0, 0, 0, 0.28); + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index a6a85edfe1..7ecfcf13d9 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -107,30 +107,19 @@ $composer-e2e-icon-color: $header-panel-text-primary-color; // ******************** -// V2 Room List -// TODO: Remove the 2 from all of these when the new list takes over - $theme-button-bg-color: #e3e8f0; -$roomlist2-button-bg-color: #1A1D23; // Buttons include the filter box, explore button, and sublist buttons -$roomlist2-bg-color: $header-panel-bg-color; +$roomlist-button-bg-color: #1A1D23; // Buttons include the filter box, explore button, and sublist buttons +$roomlist-bg-color: $header-panel-bg-color; -$roomsublist2-divider-color: $primary-fg-color; +$roomsublist-divider-color: $primary-fg-color; -$roomtile2-preview-color: #9e9e9e; -$roomtile2-default-badge-bg-color: #61708b; -$roomtile2-selected-bg-color: #1A1D23; +$roomtile-preview-color: #9e9e9e; +$roomtile-default-badge-bg-color: #61708b; +$roomtile-selected-bg-color: #1A1D23; // ******************** -$roomtile-name-color: $header-panel-text-primary-color; -$roomtile-selected-color: $text-primary-color; -$roomtile-notified-color: $text-primary-color; -$roomtile-selected-bg-color: $room-highlight-color; -$roomtile-focused-bg-color: $room-highlight-color; - -$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); - $panel-divider-color: $header-panel-border-color; $widget-menu-bar-bg-color: $header-panel-bg-color; @@ -203,6 +192,8 @@ $user-tile-hover-bg-color: $header-panel-bg-color; // Appearance tab colors $appearance-tab-border-color: $room-highlight-color; +$composer-shadow-color: tranparent; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index 991abdefff..3465aa307e 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -174,19 +174,16 @@ $header-divider-color: #91a1c0; // ******************** -// V2 Room List -// TODO: Remove the 2 from all of these when the new list takes over - $theme-button-bg-color: #e3e8f0; -$roomlist2-button-bg-color: #fff; // Buttons include the filter box, explore button, and sublist buttons -$roomlist2-bg-color: $header-panel-bg-color; -$roomlist2-header-color: $primary-fg-color; -$roomsublist2-divider-color: $primary-fg-color; +$roomlist-button-bg-color: #fff; // Buttons include the filter box, explore button, and sublist buttons +$roomlist-bg-color: $header-panel-bg-color; +$roomlist-header-color: $primary-fg-color; +$roomsublist-divider-color: $primary-fg-color; -$roomtile2-preview-color: #9e9e9e; -$roomtile2-default-badge-bg-color: #61708b; -$roomtile2-selected-bg-color: #fff; +$roomtile-preview-color: #9e9e9e; +$roomtile-default-badge-bg-color: #61708b; +$roomtile-selected-bg-color: #fff; $presence-online: $accent-color; $presence-away: #d9b072; @@ -194,13 +191,6 @@ $presence-offline: #e3e8f0; // ******************** -$roomtile-name-color: #61708b; -$roomtile-badge-fg-color: $accent-fg-color; -$roomtile-selected-color: #212121; -$roomtile-notified-color: #212121; -$roomtile-selected-bg-color: #fff; -$roomtile-focused-bg-color: #fff; - $username-variant1-color: #368bd6; $username-variant2-color: #ac3ba8; $username-variant3-color: #03b381; @@ -210,13 +200,6 @@ $username-variant6-color: #2dc2c5; $username-variant7-color: #5c56f5; $username-variant8-color: #74d12c; -$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); - -$roomsublist-background: $secondary-accent-color; -$roomsublist-label-fg-color: $roomtile-name-color; -$roomsublist-label-bg-color: $tertiary-accent-color; -$roomsublist-chevron-color: $accent-color; - $panel-divider-color: #dee1f3; // ******************** @@ -292,7 +275,7 @@ $progressbar-color: #000; $room-warning-bg-color: $yellow-background; -$memberstatus-placeholder-color: $roomtile-name-color; +$memberstatus-placeholder-color: $muted-fg-color; $authpage-bg-color: #2e3649; $authpage-modal-bg-color: rgba(255, 255, 255, 0.59); @@ -331,6 +314,8 @@ $user-tile-hover-bg-color: $header-panel-bg-color; // FontSlider colors $appearance-tab-border-color: $input-darker-bg-color; +$composer-shadow-color: tranparent; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/res/themes/light-custom/css/_custom.scss b/res/themes/light-custom/css/_custom.scss index b492006247..b830e86e02 100644 --- a/res/themes/light-custom/css/_custom.scss +++ b/res/themes/light-custom/css/_custom.scss @@ -25,7 +25,6 @@ $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); @@ -40,7 +39,6 @@ $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 @@ -48,14 +46,13 @@ $roomheader-bg-color: var(--timeline-background-color); // // --roomlist-highlights-color $roomtile-selected-bg-color: var(--roomlist-highlights-color); -$roomtile2-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); -$roomlist2-button-bg-color: var(--sidebar-color-15pct); +$roomlist-button-bg-color: var(--sidebar-color-15pct); // // --roomlist-background-color $header-panel-bg-color: var(--roomlist-background-color); @@ -65,12 +62,10 @@ $panel-gradient: var(--roomlist-background-color-0pct), var(--roomlist-backgroun $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); -$roomlist2-bg-color: var(--roomlist-background-color); +$roomlist-bg-color: var(--roomlist-background-color); // // --timeline-text-color $message-action-bar-fg-color: var(--timeline-text-color); @@ -87,23 +82,17 @@ $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); -$roomtile2-preview-color: var(--roomlist-text-secondary-color); -$roomlist2-header-color: var(--roomlist-text-secondary-color); -$roomtile2-default-badge-bg-color: var(--roomlist-text-secondary-color); +$roomtile-preview-color: var(--roomlist-text-secondary-color); +$roomlist-header-color: var(--roomlist-text-secondary-color); +$roomtile-default-badge-bg-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 -$roomsublist2-divider-color: var(--roomlist-separator-color); +$roomsublist-divider-color: var(--roomlist-separator-color); // // --timeline-text-secondary-color $authpage-secondary-color: var(--timeline-text-secondary-color); diff --git a/res/themes/light/css/_fonts.scss b/res/themes/light/css/_fonts.scss index 4f133bb015..ba64830f15 100644 --- a/res/themes/light/css/_fonts.scss +++ b/res/themes/light/css/_fonts.scss @@ -1,25 +1,20 @@ -/* - * Nunito. - * Includes extended Latin and Vietnamese character sets - * Current URLs are taken from - * https://github.com/alexeiva/NunitoFont/releases/tag/v3.500 - * ...in order to include cyrillic. - * - * Previously, they were - * https://fonts.googleapis.com/css?family=Nunito:400,400i,600,600i,700,700i&subset=latin-ext,vietnamese - * - * We explicitly do not include Nunito's italic variants, as they are not italic enough - * and it's better to rely on the browser's built-in obliquing behaviour. - */ - /* the 'src' links are relative to the bundle.css, which is in a subdirectory. */ +/* Inter unexpectedly contains various codepoints which collide with emoji, even + when variation-16 is applied to request the emoji variant. From eyeballing + the emoji picker, these are: 20e3, 23cf, 24c2, 25a0-25c1, 2665, 2764, 2b06, 2b1c. + Therefore we define a unicode-range to load which excludes the glyphs + (to avoid having to maintain a fork of Inter). */ + +$inter-unicode-range: U+0000-20e2,U+20e4-23ce,U+23d0-24c1,U+24c3-259f,U+25c2-2664,U+2666-2763,U+2765-2b05,U+2b07-2b1b,U+2b1d-10FFFF; + @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-Regular.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-Regular.woff?v=3.13") format("woff"); } @@ -28,6 +23,7 @@ font-style: italic; font-weight: 400; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-Italic.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-Italic.woff?v=3.13") format("woff"); } @@ -37,6 +33,7 @@ font-style: normal; font-weight: 500; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-Medium.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-Medium.woff?v=3.13") format("woff"); } @@ -45,6 +42,7 @@ font-style: italic; font-weight: 500; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-MediumItalic.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-MediumItalic.woff?v=3.13") format("woff"); } @@ -54,6 +52,7 @@ font-style: normal; font-weight: 600; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-SemiBold.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-SemiBold.woff?v=3.13") format("woff"); } @@ -62,6 +61,7 @@ font-style: italic; font-weight: 600; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-SemiBoldItalic.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-SemiBoldItalic.woff?v=3.13") format("woff"); } @@ -71,6 +71,7 @@ font-style: normal; font-weight: 700; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-Bold.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-Bold.woff?v=3.13") format("woff"); } @@ -79,6 +80,7 @@ font-style: italic; font-weight: 700; font-display: swap; + unicode-range: $inter-unicode-range; src: url("$(res)/fonts/Inter/Inter-BoldItalic.woff2?v=3.13") format("woff2"), url("$(res)/fonts/Inter/Inter-BoldItalic.woff?v=3.13") format("woff"); } @@ -118,17 +120,3 @@ src: local('Inconsolata Bold'), local('Inconsolata-Bold'), url('$(res)/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71p5_zaDpwm.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } - -/* a COLR/CPAL version of Twemoji used for consistent cross-browser emoji - * taken from https://github.com/mozilla/twemoji-colr - * using the fix from https://github.com/mozilla/twemoji-colr/issues/50 to - * work on macOS - */ -/* -// except we now load it dynamically via FontManager to handle browsers -// which can't render COLR/CPAL still -@font-face { - font-family: "Twemoji Mozilla"; - src: url('$(res)/fonts/Twemoji_Mozilla/TwemojiMozilla.woff2') format('woff2'); -} -*/ diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index 3737d21a0f..e317683963 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -19,8 +19,8 @@ $accent-bg-color: rgba(3, 179, 129, 0.16); $notice-primary-color: #ff4b55; $notice-primary-bg-color: rgba(255, 75, 85, 0.16); $primary-fg-color: #2e2f32; -$roomlist2-header-color: $primary-fg-color; -$notice-secondary-color: $roomlist2-header-color; +$roomlist-header-color: $primary-fg-color; +$notice-secondary-color: $roomlist-header-color; $header-panel-bg-color: #f3f8fd; // typical text (dark-on-white in light skin) @@ -174,32 +174,22 @@ $header-divider-color: #91A1C0; // ******************** -// V2 Room List -// TODO: Remove the 2 from all of these when the new list takes over - $theme-button-bg-color: #e3e8f0; -$roomlist2-button-bg-color: #fff; // Buttons include the filter box, explore button, and sublist buttons -$roomlist2-bg-color: rgba(245, 245, 245, 0.90); -$roomsublist2-divider-color: $primary-fg-color; +$roomlist-button-bg-color: #fff; // Buttons include the filter box, explore button, and sublist buttons +$roomlist-bg-color: rgba(245, 245, 245, 0.90); +$roomsublist-divider-color: $primary-fg-color; -$roomtile2-preview-color: #737D8C; -$roomtile2-default-badge-bg-color: #61708b; -$roomtile2-selected-bg-color: #FFF; +$roomtile-preview-color: #737D8C; +$roomtile-default-badge-bg-color: #61708b; +$roomtile-selected-bg-color: #FFF; $presence-online: $accent-color; -$presence-away: orange; // TODO: Get color +$presence-away: #d9b072; $presence-offline: #E3E8F0; // ******************** -$roomtile-name-color: #61708b; -$roomtile-badge-fg-color: $accent-fg-color; -$roomtile-selected-color: #212121; -$roomtile-notified-color: #212121; -$roomtile-selected-bg-color: #fff; -$roomtile-focused-bg-color: #fff; - $username-variant1-color: #368bd6; $username-variant2-color: #ac3ba8; $username-variant3-color: #0DBD8B; @@ -209,13 +199,6 @@ $username-variant6-color: #2dc2c5; $username-variant7-color: #5c56f5; $username-variant8-color: #74d12c; -$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); - -$roomsublist-background: $secondary-accent-color; -$roomsublist-label-fg-color: $roomtile-name-color; -$roomsublist-label-bg-color: $tertiary-accent-color; -$roomsublist-chevron-color: $accent-color; - $panel-divider-color: transparent; // ******************** @@ -291,7 +274,7 @@ $progressbar-color: #000; $room-warning-bg-color: $yellow-background; -$memberstatus-placeholder-color: $roomtile-name-color; +$memberstatus-placeholder-color: $muted-fg-color; $authpage-bg-color: #2e3649; $authpage-modal-bg-color: rgba(245, 245, 245, 0.90); @@ -335,6 +318,7 @@ $appearance-tab-border-color: $input-darker-bg-color; $roomlist-background-blur-amount: 40px; $tagpanel-background-blur-amount: 20px; +$composer-shadow-color: rgba(0, 0, 0, 0.04); // ***** Mixins! ***** diff --git a/res/themes/light/css/_mods.scss b/res/themes/light/css/_mods.scss index 810e0375ba..9a59acba8e 100644 --- a/res/themes/light/css/_mods.scss +++ b/res/themes/light/css/_mods.scss @@ -5,7 +5,7 @@ // it can be blurred by the tag panel and room list @supports (backdrop-filter: none) { - .mx_LeftPanel2 { + .mx_LeftPanel { background-image: var(--avatar-url); background-repeat: no-repeat; background-size: cover; @@ -16,12 +16,12 @@ backdrop-filter: blur($tagpanel-background-blur-amount); } - .mx_LeftPanel2 .mx_LeftPanel2_roomListContainer { + .mx_LeftPanel .mx_LeftPanel_roomListContainer { backdrop-filter: blur($roomlist-background-blur-amount); } } -.mx_RoomSublist2_showNButton { +.mx_RoomSublist_showNButton { background-color: transparent !important; } diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 3e4eb13766..080cdacafd 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -20,9 +20,11 @@ import { IMatrixClientPeg } from "../MatrixClientPeg"; import ToastStore from "../stores/ToastStore"; import DeviceListener from "../DeviceListener"; import RebrandListener from "../RebrandListener"; -import { RoomListStore2 } from "../stores/room-list/RoomListStore2"; +import { RoomListStoreClass } from "../stores/room-list/RoomListStore"; import { PlatformPeg } from "../PlatformPeg"; import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore"; +import {IntegrationManagers} from "../integrations/IntegrationManagers"; +import {ModalManager} from "../Modal"; declare global { interface Window { @@ -32,21 +34,20 @@ declare global { init: () => Promise; }; - mx_ContentMessages: ContentMessages; - mx_ToastStore: ToastStore; - mx_DeviceListener: DeviceListener; - mx_RebrandListener: RebrandListener; - mx_RoomListStore2: RoomListStore2; - mx_RoomListLayoutStore: RoomListLayoutStore; + mxContentMessages: ContentMessages; + mxToastStore: ToastStore; + mxDeviceListener: DeviceListener; + mxRebrandListener: RebrandListener; + mxRoomListStore: RoomListStoreClass; + mxRoomListLayoutStore: RoomListLayoutStore; mxPlatformPeg: PlatformPeg; - - // TODO: Remove flag before launch: https://github.com/vector-im/riot-web/issues/14231 - mx_LoudRoomListLogging: boolean; + mxIntegrationManagers: typeof IntegrationManagers; + singletonModalManager: ModalManager; } // workaround for https://github.com/microsoft/TypeScript/issues/30933 interface ObjectConstructor { - fromEntries?(xs: [string|number|symbol, any][]): object + fromEntries?(xs: [string|number|symbol, any][]): object; } interface Document { diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index 25445b1c74..6f55a75d0c 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -386,7 +386,7 @@ export default class ContentMessages { const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); if (isQuoting) { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - const {finished} = Modal.createTrackedDialog('Upload Reply Warning', '', QuestionDialog, { + const {finished} = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, { title: _t('Replying With Files'), description: (
{_t( @@ -397,7 +397,7 @@ export default class ContentMessages { hasCancelButton: true, button: _t("Continue"), }); - const [shouldUpload]: [boolean] = await finished; + const [shouldUpload] = await finished; if (!shouldUpload) return; } @@ -420,12 +420,12 @@ export default class ContentMessages { if (tooBigFiles.length > 0) { const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog"); - const {finished} = Modal.createTrackedDialog('Upload Failure', '', UploadFailureDialog, { + const {finished} = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, { badFiles: tooBigFiles, totalFiles: files.length, contentMessages: this, }); - const [shouldContinue]: [boolean] = await finished; + const [shouldContinue] = await finished; if (!shouldContinue) return; } @@ -437,12 +437,12 @@ export default class ContentMessages { for (let i = 0; i < okFiles.length; ++i) { const file = okFiles[i]; if (!uploadAll) { - const {finished} = Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { + const {finished} = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation', '', UploadConfirmDialog, { file, currentIndex: i, totalFiles: okFiles.length, }); - const [shouldContinue, shouldUploadAll]: [boolean, boolean] = await finished; + const [shouldContinue, shouldUploadAll] = await finished; if (!shouldContinue) break; if (shouldUploadAll) { uploadAll = true; @@ -621,9 +621,9 @@ export default class ContentMessages { } static sharedInstance() { - if (window.mx_ContentMessages === undefined) { - window.mx_ContentMessages = new ContentMessages(); + if (window.mxContentMessages === undefined) { + window.mxContentMessages = new ContentMessages(); } - return window.mx_ContentMessages; + return window.mxContentMessages; } } diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js index a80c91a59a..a584a69d35 100644 --- a/src/CrossSigningManager.js +++ b/src/CrossSigningManager.js @@ -40,25 +40,16 @@ export class AccessCancelledError extends Error { } } -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 operation."); - } - +async function confirmToDismiss() { 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"), + description: _t("Are you sure you want to cancel entering passphrase?"), + danger: false, + button: _t("Go Back"), + cancelButton: _t("Cancel"), }).finished; - return sure; + return !sure; } async function getSecretStorageKey({ keys: keyInfos }, ssssItemName) { @@ -102,7 +93,7 @@ async function getSecretStorageKey({ keys: keyInfos }, ssssItemName) { /* options= */ { onBeforeClose: async (reason) => { if (reason === "backgroundClick") { - return confirmToDismiss(ssssItemName); + return confirmToDismiss(); } return true; }, diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index cfec2890d2..a37521118f 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -17,16 +17,16 @@ limitations under the License. import {MatrixClientPeg} from './MatrixClientPeg'; import { hideToast as hideBulkUnverifiedSessionsToast, - showToast as showBulkUnverifiedSessionsToast + showToast as showBulkUnverifiedSessionsToast, } from "./toasts/BulkUnverifiedSessionsToast"; import { hideToast as hideSetupEncryptionToast, Kind as SetupKind, - showToast as showSetupEncryptionToast + showToast as showSetupEncryptionToast, } from "./toasts/SetupEncryptionToast"; import { hideToast as hideUnverifiedSessionsToast, - showToast as showUnverifiedSessionsToast + showToast as showUnverifiedSessionsToast, } from "./toasts/UnverifiedSessionToast"; import {privateShouldBeEncrypted} from "./createRoom"; @@ -48,8 +48,8 @@ export default class DeviceListener { private displayingToastsForDeviceIds = new Set(); static sharedInstance() { - if (!window.mx_DeviceListener) window.mx_DeviceListener = new DeviceListener(); - return window.mx_DeviceListener; + if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener(); + return window.mxDeviceListener; } start() { diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 6dba041685..77a9579f2c 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -184,7 +184,7 @@ const transformTags: sanitizeHtml.IOptions["transformTags"] = { // custom to mat if (typeof attribs.class !== 'undefined') { // Filter out all classes other than ones starting with language- for syntax highlighting. const classes = attribs.class.split(/\s/).filter(function(cl) { - return cl.startsWith('language-'); + return cl.startsWith('language-') && !cl.startsWith('language-_'); }); attribs.class = classes.join(' '); } diff --git a/src/Modal.js b/src/Modal.tsx similarity index 50% rename from src/Modal.js rename to src/Modal.tsx index 9b9f190d58..b744dbacf4 100644 --- a/src/Modal.js +++ b/src/Modal.tsx @@ -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,6 +18,8 @@ limitations under the License. import React from 'react'; import ReactDOM from 'react-dom'; +import classNames from 'classnames'; + import Analytics from './Analytics'; import dis from './dispatcher/dispatcher'; import {defer} from './utils/promise'; @@ -25,36 +28,48 @@ import AsyncWrapper from './AsyncWrapper'; const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer"; -class ModalManager { - constructor() { - this._counter = 0; +interface IModal { + elem: React.ReactNode; + className?: string; + beforeClosePromise?: Promise; + closeReason?: string; + onBeforeClose?(reason?: string): Promise; + onFinished(...args: T): void; + close(...args: T): void; +} - // The modal to prioritise over all others. If this is set, only show - // this modal. Remove all other modals from the stack when this modal - // is closed. - this._priorityModal = null; - // The modal to keep open underneath other modals if possible. Useful - // for cases like Settings where the modal should remain open while the - // user is prompted for more information/errors. - this._staticModal = null; - // A list of the modals we have stacked up, with the most recent at [0] - // Neither the static nor priority modal will be in this list. - this._modals = [ - /* { - elem: React component for this dialog - onFinished: caller-supplied onFinished callback - className: CSS class for the dialog wrapper div - } */ - ]; +interface IHandle { + finished: Promise; + close(...args: T): void; +} - this.onBackgroundClick = this.onBackgroundClick.bind(this); - } +interface IProps { + onFinished?(...args: T): void; + // TODO improve typing here once all Modals are TS and we can exhaustively check the props + [key: string]: any; +} - hasDialogs() { - return this._priorityModal || this._staticModal || this._modals.length > 0; - } +interface IOptions { + onBeforeClose?: IModal["onBeforeClose"]; +} - getOrCreateContainer() { +type ParametersWithoutFirst any> = T extends (a: any, ...args: infer P) => any ? P : never; + +export class ModalManager { + private counter = 0; + // The modal to prioritise over all others. If this is set, only show + // this modal. Remove all other modals from the stack when this modal + // is closed. + private priorityModal: IModal = null; + // The modal to keep open underneath other modals if possible. Useful + // for cases like Settings where the modal should remain open while the + // user is prompted for more information/errors. + private staticModal: IModal = null; + // A list of the modals we have stacked up, with the most recent at [0] + // Neither the static nor priority modal will be in this list. + private modals: IModal[] = []; + + private static getOrCreateContainer() { let container = document.getElementById(DIALOG_CONTAINER_ID); if (!container) { @@ -66,7 +81,7 @@ class ModalManager { return container; } - getOrCreateStaticContainer() { + private static getOrCreateStaticContainer() { let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID); if (!container) { @@ -78,63 +93,99 @@ class ModalManager { return container; } - createTrackedDialog(analyticsAction, analyticsInfo, ...rest) { + public hasDialogs() { + return this.priorityModal || this.staticModal || this.modals.length > 0; + } + + public createTrackedDialog( + analyticsAction: string, + analyticsInfo: string, + ...rest: Parameters + ) { Analytics.trackEvent('Modal', analyticsAction, analyticsInfo); - return this.createDialog(...rest); + return this.createDialog(...rest); } - appendTrackedDialog(analyticsAction, analyticsInfo, ...rest) { + public appendTrackedDialog( + analyticsAction: string, + analyticsInfo: string, + ...rest: Parameters + ) { Analytics.trackEvent('Modal', analyticsAction, analyticsInfo); - return this.appendDialog(...rest); + return this.appendDialog(...rest); } - createDialog(Element, ...rest) { - return this.createDialogAsync(Promise.resolve(Element), ...rest); + public createDialog( + Element: React.ComponentType, + ...rest: ParametersWithoutFirst + ) { + return this.createDialogAsync(Promise.resolve(Element), ...rest); } - appendDialog(Element, ...rest) { - return this.appendDialogAsync(Promise.resolve(Element), ...rest); + public appendDialog( + Element: React.ComponentType, + ...rest: ParametersWithoutFirst + ) { + return this.appendDialogAsync(Promise.resolve(Element), ...rest); } - createTrackedDialogAsync(analyticsAction, analyticsInfo, ...rest) { + public createTrackedDialogAsync( + analyticsAction: string, + analyticsInfo: string, + ...rest: Parameters + ) { Analytics.trackEvent('Modal', analyticsAction, analyticsInfo); - return this.createDialogAsync(...rest); + return this.createDialogAsync(...rest); } - appendTrackedDialogAsync(analyticsAction, analyticsInfo, ...rest) { + public appendTrackedDialogAsync( + analyticsAction: string, + analyticsInfo: string, + ...rest: Parameters + ) { Analytics.trackEvent('Modal', analyticsAction, analyticsInfo); - return this.appendDialogAsync(...rest); + return this.appendDialogAsync(...rest); } - _buildModal(prom, props, className, options) { - const modal = {}; + private buildModal( + prom: Promise, + props?: IProps, + className?: string, + options?: IOptions + ) { + const modal: IModal = { + onFinished: props ? props.onFinished : null, + onBeforeClose: options.onBeforeClose, + beforeClosePromise: null, + closeReason: null, + className, + + // these will be set below but we need an object reference to pass to getCloseFn before we can do that + elem: null, + close: null, + }; // never call this from onFinished() otherwise it will loop - const [closeDialog, onFinishedProm] = this._getCloseFn(modal, props); + const [closeDialog, onFinishedProm] = this.getCloseFn(modal, props); // don't attempt to reuse the same AsyncWrapper for different dialogs, // otherwise we'll get confused. - const modalCount = this._counter++; + const modalCount = this.counter++; // FIXME: If a dialog uses getDefaultProps it clobbers the onFinished // property set here so you can't close the dialog from a button click! - modal.elem = ( - - ); - modal.onFinished = props ? props.onFinished : null; - modal.className = className; - modal.onBeforeClose = options.onBeforeClose; - modal.beforeClosePromise = null; + modal.elem = ; modal.close = closeDialog; - modal.closeReason = null; return {modal, closeDialog, onFinishedProm}; } - _getCloseFn(modal, props) { - const deferred = defer(); - return [async (...args) => { + private getCloseFn( + modal: IModal, + props: IProps + ): [IHandle["close"], IHandle["finished"]] { + const deferred = defer(); + return [async (...args: T) => { if (modal.beforeClosePromise) { await modal.beforeClosePromise; } else if (modal.onBeforeClose) { @@ -147,26 +198,26 @@ class ModalManager { } deferred.resolve(args); if (props && props.onFinished) props.onFinished.apply(null, args); - const i = this._modals.indexOf(modal); + const i = this.modals.indexOf(modal); if (i >= 0) { - this._modals.splice(i, 1); + this.modals.splice(i, 1); } - if (this._priorityModal === modal) { - this._priorityModal = null; + if (this.priorityModal === modal) { + this.priorityModal = null; // XXX: This is destructive - this._modals = []; + this.modals = []; } - if (this._staticModal === modal) { - this._staticModal = null; + if (this.staticModal === modal) { + this.staticModal = null; // XXX: This is destructive - this._modals = []; + this.modals = []; } - this._reRender(); + this.reRender(); }, deferred.promise]; } @@ -207,38 +258,49 @@ class ModalManager { * @param {onBeforeClose} options.onBeforeClose a callback to decide whether to close the dialog * @returns {object} Object with 'close' parameter being a function that will close the dialog */ - createDialogAsync(prom, props, className, isPriorityModal, isStaticModal, options = {}) { - const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className, options); + private createDialogAsync( + prom: Promise, + props?: IProps, + className?: string, + isPriorityModal = false, + isStaticModal = false, + options: IOptions = {} + ): IHandle { + const {modal, closeDialog, onFinishedProm} = this.buildModal(prom, props, className, options); if (isPriorityModal) { // XXX: This is destructive - this._priorityModal = modal; + this.priorityModal = modal; } else if (isStaticModal) { // This is intentionally destructive - this._staticModal = modal; + this.staticModal = modal; } else { - this._modals.unshift(modal); + this.modals.unshift(modal); } - this._reRender(); + this.reRender(); return { close: closeDialog, finished: onFinishedProm, }; } - appendDialogAsync(prom, props, className) { - const {modal, closeDialog, onFinishedProm} = this._buildModal(prom, props, className, {}); + private appendDialogAsync( + prom: Promise, + props?: IProps, + className?: string + ): IHandle { + const {modal, closeDialog, onFinishedProm} = this.buildModal(prom, props, className, {}); - this._modals.push(modal); - this._reRender(); + this.modals.push(modal); + this.reRender(); return { close: closeDialog, finished: onFinishedProm, }; } - onBackgroundClick() { - const modal = this._getCurrentModal(); + private onBackgroundClick = () => { + const modal = this.getCurrentModal(); if (!modal) { return; } @@ -249,21 +311,21 @@ class ModalManager { modal.closeReason = "backgroundClick"; modal.close(); modal.closeReason = null; + }; + + private getCurrentModal(): IModal { + return this.priorityModal ? this.priorityModal : (this.modals[0] || this.staticModal); } - _getCurrentModal() { - return this._priorityModal ? this._priorityModal : (this._modals[0] || this._staticModal); - } - - _reRender() { - if (this._modals.length === 0 && !this._priorityModal && !this._staticModal) { + private reRender() { + if (this.modals.length === 0 && !this.priorityModal && !this.staticModal) { // If there is no modal to render, make all of Riot available // to screen reader users again dis.dispatch({ action: 'aria_unhide_main_app', }); - ReactDOM.unmountComponentAtNode(this.getOrCreateContainer()); - ReactDOM.unmountComponentAtNode(this.getOrCreateStaticContainer()); + ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer()); + ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateStaticContainer()); return; } @@ -274,49 +336,48 @@ class ModalManager { action: 'aria_hide_main_app', }); - if (this._staticModal) { - const classes = "mx_Dialog_wrapper mx_Dialog_staticWrapper " - + (this._staticModal.className ? this._staticModal.className : ''); + if (this.staticModal) { + const classes = classNames("mx_Dialog_wrapper mx_Dialog_staticWrapper", this.staticModal.className); const staticDialog = (
- { this._staticModal.elem } + { this.staticModal.elem }
-
+
); - ReactDOM.render(staticDialog, this.getOrCreateStaticContainer()); + ReactDOM.render(staticDialog, ModalManager.getOrCreateStaticContainer()); } else { // This is safe to call repeatedly if we happen to do that - ReactDOM.unmountComponentAtNode(this.getOrCreateStaticContainer()); + ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateStaticContainer()); } - const modal = this._getCurrentModal(); - if (modal !== this._staticModal) { - const classes = "mx_Dialog_wrapper " - + (this._staticModal ? "mx_Dialog_wrapperWithStaticUnder " : '') - + (modal.className ? modal.className : ''); + const modal = this.getCurrentModal(); + if (modal !== this.staticModal) { + const classes = classNames("mx_Dialog_wrapper", modal.className, { + mx_Dialog_wrapperWithStaticUnder: this.staticModal, + }); const dialog = (
{modal.elem}
-
+
); - ReactDOM.render(dialog, this.getOrCreateContainer()); + ReactDOM.render(dialog, ModalManager.getOrCreateContainer()); } else { // This is safe to call repeatedly if we happen to do that - ReactDOM.unmountComponentAtNode(this.getOrCreateContainer()); + ReactDOM.unmountComponentAtNode(ModalManager.getOrCreateContainer()); } } } -if (!global.singletonModalManager) { - global.singletonModalManager = new ModalManager(); +if (!window.singletonModalManager) { + window.singletonModalManager = new ModalManager(); } -export default global.singletonModalManager; +export default window.singletonModalManager; diff --git a/src/RebrandListener.tsx b/src/RebrandListener.tsx index a93ce9e3a5..47b883cf35 100644 --- a/src/RebrandListener.tsx +++ b/src/RebrandListener.tsx @@ -67,8 +67,8 @@ export default class RebrandListener { private nagAgainAt?: number = null; static sharedInstance() { - if (!window.mx_RebrandListener) window.mx_RebrandListener = new RebrandListener(); - return window.mx_RebrandListener; + if (!window.mxRebrandListener) window.mxRebrandListener = new RebrandListener(); + return window.mxRebrandListener; } constructor() { @@ -114,6 +114,11 @@ export default class RebrandListener { } }; + onOneTimeToastDismiss = async () => { + localStorage.setItem('mx_rename_dialog_dismissed', 'true'); + this.recheck(); + }; + onNagTimerFired = () => { this._reshowTimer = null; this.nagAgainAt = null; @@ -143,10 +148,14 @@ export default class RebrandListener { if (nagToast || oneTimeToast) { let description; + let rejectLabel = null; + let onReject = null; if (nagToast) { description = _t("Use your account to sign in to the latest version"); } else { description = _t("We’re excited to announce Riot is now Element"); + rejectLabel = _t("Dismiss"); + onReject = this.onOneTimeToastDismiss; } ToastStore.sharedInstance().addOrReplaceToast({ @@ -157,6 +166,8 @@ export default class RebrandListener { description, acceptLabel: _t("Learn More"), onAccept: nagToast ? this.onNagToastLearnMore : this.onOneTimeToastLearnMore, + rejectLabel, + onReject, }, component: GenericToast, priority: 20, diff --git a/src/RoomNotifs.js b/src/RoomNotifs.js index 4614bef378..a86c521ac4 100644 --- a/src/RoomNotifs.js +++ b/src/RoomNotifs.js @@ -34,27 +34,6 @@ export function shouldShowMentionBadge(roomNotifState) { return MENTION_BADGE_STATES.includes(roomNotifState); } -export function countRoomsWithNotif(rooms) { - return rooms.reduce((result, room, index) => { - const roomNotifState = getRoomNotifsState(room.roomId); - const highlight = room.getUnreadNotificationCount('highlight') > 0; - const notificationCount = room.getUnreadNotificationCount(); - - const notifBadges = notificationCount > 0 && shouldShowNotifBadge(roomNotifState); - const mentionBadges = highlight && shouldShowMentionBadge(roomNotifState); - const isInvite = room.hasMembershipState(MatrixClientPeg.get().credentials.userId, 'invite'); - const badges = notifBadges || mentionBadges || isInvite; - - if (badges) { - result.count++; - if (highlight) { - result.highlight = true; - } - } - return result; - }, {count: 0, highlight: false}); -} - export function aggregateNotificationCount(rooms) { return rooms.reduce((result, room) => { const roomNotifState = getRoomNotifsState(room.roomId); diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 11c955749d..ad3dc7002a 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -43,6 +43,7 @@ import SdkConfig from "./SdkConfig"; import { ensureDMExists } from "./createRoom"; import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; import { Action } from "./dispatcher/actions"; +import { EffectiveMembership, getEffectiveMembership } from "./utils/membership"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -400,14 +401,16 @@ export const Commands = [ // If we need an identity server but don't have one, things // get a bit more complex here, but we try to show something // meaningful. - let finished = Promise.resolve(); + let prom = Promise.resolve(); if ( getAddressType(address) === 'email' && !MatrixClientPeg.get().getIdentityServerUrl() ) { const defaultIdentityServerUrl = getDefaultIdentityServerUrl(); if (defaultIdentityServerUrl) { - ({ finished } = Modal.createTrackedDialog('Slash Commands', 'Identity server', + const { finished } = Modal.createTrackedDialog<[boolean]>( + 'Slash Commands', + 'Identity server', QuestionDialog, { title: _t("Use an identity server"), description:

{_t( @@ -420,9 +423,9 @@ export const Commands = [ )}

, button: _t("Continue"), }, - )); + ); - finished = finished.then(([useDefault]: any) => { + prom = finished.then(([useDefault]) => { if (useDefault) { useDefaultIdentityServer(); return; @@ -434,7 +437,7 @@ export const Commands = [ } } const inviter = new MultiInviter(roomId); - return success(finished.then(() => { + return success(prom.then(() => { return inviter.invite([address]); }).then(() => { if (inviter.getCompletionState(address) !== "invited") { @@ -730,9 +733,11 @@ export const Commands = [ const cli = MatrixClientPeg.get(); const room = cli.getRoom(roomId); if (!room) return reject(_t("Command failed")); - + const member = room.getMember(args); + if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) { + return reject(_t("Could not find user in room")); + } const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', ''); - if (!powerLevelEvent.getContent().users[args]) return reject(_t("Could not find user in room")); return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent)); } } @@ -1046,7 +1051,7 @@ export function parseCommandString(input) { // trim any trailing whitespace, as it can confuse the parser for // IRC-style commands input = input.replace(/\s+$/, ''); - if (input[0] !== '/') return null; // not a command + if (input[0] !== '/') return {}; // not a command const bits = input.match(/^(\S+?)(?: +((.|\n)*))?$/); let cmd; diff --git a/src/accessibility/RovingTabIndex.tsx b/src/accessibility/RovingTabIndex.tsx index 388d67d9f3..5a650d4b6e 100644 --- a/src/accessibility/RovingTabIndex.tsx +++ b/src/accessibility/RovingTabIndex.tsx @@ -23,12 +23,11 @@ import React, { useRef, useReducer, Reducer, - RefObject, Dispatch, } from "react"; import {Key} from "../Keyboard"; -import AccessibleButton from "../components/views/elements/AccessibleButton"; +import {FocusHandler, Ref} from "./roving/types"; /** * Module to simplify implementing the Roving TabIndex accessibility technique @@ -45,9 +44,7 @@ import AccessibleButton from "../components/views/elements/AccessibleButton"; const DOCUMENT_POSITION_PRECEDING = 2; -type Ref = RefObject; - -interface IState { +export interface IState { activeRef: Ref; refs: Ref[]; } @@ -156,7 +153,7 @@ interface IProps { children(renderProps: { onKeyDownHandler(ev: React.KeyboardEvent); }); - onKeyDown?(ev: React.KeyboardEvent); + onKeyDown?(ev: React.KeyboardEvent, state: IState); } export const RovingTabIndexProvider: React.FC = ({children, handleHomeEnd, onKeyDown}) => { @@ -193,7 +190,7 @@ export const RovingTabIndexProvider: React.FC = ({children, handleHomeEn ev.preventDefault(); ev.stopPropagation(); } else if (onKeyDown) { - return onKeyDown(ev); + return onKeyDown(ev, state); } }, [context.state, onKeyDown, handleHomeEnd]); @@ -202,8 +199,6 @@ export const RovingTabIndexProvider: React.FC = ({children, handleHomeEn ; }; -type FocusHandler = () => void; - // Hook to register a roving tab index // inputRef parameter specifies the ref to use // onFocus should be called when the index gained focus in any manner @@ -244,28 +239,7 @@ export const useRovingTabIndex = (inputRef: Ref): [FocusHandler, boolean, Ref] = return [onFocus, isActive, ref]; }; -interface IRovingTabIndexWrapperProps { - inputRef?: Ref; - children(renderProps: { - onFocus: FocusHandler; - isActive: boolean; - ref: Ref; - }); -} - -// Wrapper to allow use of useRovingTabIndex outside of React Functional Components. -export const RovingTabIndexWrapper: React.FC = ({children, inputRef}) => { - const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); - return children({onFocus, isActive, ref}); -}; - -interface IRovingAccessibleButtonProps extends React.ComponentProps { - inputRef?: Ref; -} - -// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. -export const RovingAccessibleButton: React.FC = ({inputRef, ...props}) => { - const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); - return ; -}; - +// re-export the semantic helper components for simplicity +export {RovingTabIndexWrapper} from "./roving/RovingTabIndexWrapper"; +export {RovingAccessibleButton} from "./roving/RovingAccessibleButton"; +export {RovingAccessibleTooltipButton} from "./roving/RovingAccessibleTooltipButton"; diff --git a/src/accessibility/Toolbar.tsx b/src/accessibility/Toolbar.tsx new file mode 100644 index 0000000000..0e968461a8 --- /dev/null +++ b/src/accessibility/Toolbar.tsx @@ -0,0 +1,69 @@ +/* +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 React from "react"; + +import {IState, RovingTabIndexProvider} from "./RovingTabIndex"; +import {Key} from "../Keyboard"; + +interface IProps extends Omit, "onKeyDown"> { +} + +// This component implements the Toolbar design pattern from the WAI-ARIA Authoring Practices guidelines. +// https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar +// All buttons passed in children must use RovingTabIndex to set `onFocus`, `isActive`, `ref` +const Toolbar: React.FC = ({children, ...props}) => { + const onKeyDown = (ev: React.KeyboardEvent, state: IState) => { + const target = ev.target as HTMLElement; + let handled = true; + + switch (ev.key) { + case Key.ARROW_UP: + case Key.ARROW_DOWN: + if (target.hasAttribute('aria-haspopup')) { + target.click(); + } + break; + + case Key.ARROW_LEFT: + case Key.ARROW_RIGHT: + if (state.refs.length > 0) { + const i = state.refs.findIndex(r => r === state.activeRef); + const delta = ev.key === Key.ARROW_RIGHT ? 1 : -1; + state.refs.slice((i + delta) % state.refs.length)[0].current.focus(); + } + break; + + // HOME and END are handled by RovingTabIndexProvider + + default: + handled = false; + } + + if (handled) { + ev.preventDefault(); + ev.stopPropagation(); + } + }; + + return + {({onKeyDownHandler}) =>
+ { children } +
} +
; +}; + +export default Toolbar; diff --git a/src/accessibility/context_menu/ContextMenuButton.tsx b/src/accessibility/context_menu/ContextMenuButton.tsx index c358155e10..e211a4c933 100644 --- a/src/accessibility/context_menu/ContextMenuButton.tsx +++ b/src/accessibility/context_menu/ContextMenuButton.tsx @@ -18,9 +18,9 @@ limitations under the License. import React from "react"; -import AccessibleButton, {IProps as IAccessibleButtonProps} from "../../components/views/elements/AccessibleButton"; +import AccessibleButton from "../../components/views/elements/AccessibleButton"; -interface IProps extends IAccessibleButtonProps { +interface IProps extends React.ComponentProps { label?: string; // whether or not the context menu is currently open isExpanded: boolean; diff --git a/src/accessibility/context_menu/ContextMenuTooltipButton.tsx b/src/accessibility/context_menu/ContextMenuTooltipButton.tsx new file mode 100644 index 0000000000..abc5412100 --- /dev/null +++ b/src/accessibility/context_menu/ContextMenuTooltipButton.tsx @@ -0,0 +1,47 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2018 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; + +import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton"; + +interface IProps extends React.ComponentProps { + // whether or not the context menu is currently open + isExpanded: boolean; +} + +// Semantic component for representing the AccessibleButton which launches a +export const ContextMenuTooltipButton: React.FC = ({ + isExpanded, + children, + onClick, + onContextMenu, + ...props +}) => { + return ( + + { children } + + ); +}; diff --git a/src/accessibility/roving/RovingAccessibleButton.tsx b/src/accessibility/roving/RovingAccessibleButton.tsx new file mode 100644 index 0000000000..3473ef1bc9 --- /dev/null +++ b/src/accessibility/roving/RovingAccessibleButton.tsx @@ -0,0 +1,32 @@ +/* +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 React from "react"; + +import AccessibleButton from "../../components/views/elements/AccessibleButton"; +import {useRovingTabIndex} from "../RovingTabIndex"; +import {Ref} from "./types"; + +interface IProps extends Omit, "onFocus" | "inputRef" | "tabIndex"> { + inputRef?: Ref; +} + +// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. +export const RovingAccessibleButton: React.FC = ({inputRef, ...props}) => { + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + return ; +}; + diff --git a/src/accessibility/roving/RovingAccessibleTooltipButton.tsx b/src/accessibility/roving/RovingAccessibleTooltipButton.tsx new file mode 100644 index 0000000000..cc824fef22 --- /dev/null +++ b/src/accessibility/roving/RovingAccessibleTooltipButton.tsx @@ -0,0 +1,32 @@ +/* +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 React from "react"; + +import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton"; +import {useRovingTabIndex} from "../RovingTabIndex"; +import {Ref} from "./types"; + +interface IProps extends Omit, "onFocus" | "inputRef" | "tabIndex"> { + inputRef?: Ref; +} + +// Wrapper to allow use of useRovingTabIndex for simple AccessibleTooltipButtons outside of React Functional Components. +export const RovingAccessibleTooltipButton: React.FC = ({inputRef, ...props}) => { + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + return ; +}; + diff --git a/src/accessibility/roving/RovingTabIndexWrapper.tsx b/src/accessibility/roving/RovingTabIndexWrapper.tsx new file mode 100644 index 0000000000..c826b74497 --- /dev/null +++ b/src/accessibility/roving/RovingTabIndexWrapper.tsx @@ -0,0 +1,36 @@ +/* +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 React from "react"; + +import AccessibleButton from "../../components/views/elements/AccessibleButton"; +import {useRovingTabIndex} from "../RovingTabIndex"; +import {FocusHandler, Ref} from "./types"; + +interface IProps { + inputRef?: Ref; + children(renderProps: { + onFocus: FocusHandler; + isActive: boolean; + ref: Ref; + }); +} + +// Wrapper to allow use of useRovingTabIndex outside of React Functional Components. +export const RovingTabIndexWrapper: React.FC = ({children, inputRef}) => { + const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); + return children({onFocus, isActive, ref}); +}; diff --git a/res/css/views/messages/_ReactionsRowButtonTooltip.scss b/src/accessibility/roving/types.ts similarity index 76% rename from res/css/views/messages/_ReactionsRowButtonTooltip.scss rename to src/accessibility/roving/types.ts index cf4219fcec..f0a43e5fb8 100644 --- a/res/css/views/messages/_ReactionsRowButtonTooltip.scss +++ b/src/accessibility/roving/types.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +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,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_ReactionsRowButtonTooltip_reactedWith { - opacity: 0.7; -} +import {RefObject} from "react"; + +export type Ref = RefObject; + +export type FocusHandler = () => void; diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts index e15e1b0c65..88946ee26f 100644 --- a/src/actions/RoomListActions.ts +++ b/src/actions/RoomListActions.ts @@ -16,7 +16,6 @@ limitations under the License. */ import { asyncAction } from './actionCreators'; -import { TAG_DM } from '../stores/RoomListStore'; import Modal from '../Modal'; import * as Rooms from '../Rooms'; import { _t } from '../languageHandler'; @@ -24,7 +23,9 @@ import * as sdk from '../index'; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import { AsyncActionPayload } from "../dispatcher/payloads"; -import { RoomListStoreTempProxy } from "../stores/room-list/RoomListStoreTempProxy"; +import RoomListStore from "../stores/room-list/RoomListStore"; +import { SortAlgorithm } from "../stores/room-list/algorithms/models"; +import { DefaultTagID } from "../stores/room-list/models"; export default class RoomListActions { /** @@ -51,9 +52,9 @@ export default class RoomListActions { let metaData = null; // Is the tag ordered manually? - if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { - const lists = RoomListStoreTempProxy.getRoomLists(); - const newList = [...lists[newTag]]; + const store = RoomListStore.instance; + if (newTag && store.getTagSorting(newTag) === SortAlgorithm.Manual) { + const newList = [...store.orderedLists[newTag]]; newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); @@ -81,11 +82,11 @@ export default class RoomListActions { const roomId = room.roomId; // Evil hack to get DMs behaving - if ((oldTag === undefined && newTag === TAG_DM) || - (oldTag === TAG_DM && newTag === undefined) + if ((oldTag === undefined && newTag === DefaultTagID.DM) || + (oldTag === DefaultTagID.DM && newTag === undefined) ) { return Rooms.guessAndSetDMRoom( - room, newTag === TAG_DM, + room, newTag === DefaultTagID.DM, ).catch((err) => { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to set direct chat tag " + err); @@ -102,12 +103,12 @@ export default class RoomListActions { // but we avoid ever doing a request with TAG_DM. // // if we moved lists, remove the old tag - if (oldTag && oldTag !== TAG_DM && + if (oldTag && oldTag !== DefaultTagID.DM && hasChangedSubLists ) { const promiseToDelete = matrixClient.deleteRoomTag( roomId, oldTag, - ).catch(function (err) { + ).catch(function(err) { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to remove tag " + oldTag + " from room: " + err); Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, { @@ -120,14 +121,14 @@ export default class RoomListActions { } // if we moved lists or the ordering changed, add the new tag - if (newTag && newTag !== TAG_DM && + if (newTag && newTag !== DefaultTagID.DM && (hasChangedSubLists || metaData) ) { // metaData is the body of the PUT to set the tag, so it must // at least be an empty object. metaData = metaData || {}; - const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function (err) { + const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function(err) { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to add tag " + newTag + " to room: " + err); Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { diff --git a/src/actions/TagOrderActions.ts b/src/actions/TagOrderActions.ts index 75097952c0..c203172874 100644 --- a/src/actions/TagOrderActions.ts +++ b/src/actions/TagOrderActions.ts @@ -22,7 +22,6 @@ import { AsyncActionPayload } from "../dispatcher/payloads"; import { MatrixClient } from "matrix-js-sdk/src/client"; export default class TagOrderActions { - /** * Creates an action thunk that will do an asynchronous request to * move a tag in TagOrderStore to destinationIx. diff --git a/src/autocomplete/QueryMatcher.ts b/src/autocomplete/QueryMatcher.ts index 7a0219e264..f717fb11f6 100644 --- a/src/autocomplete/QueryMatcher.ts +++ b/src/autocomplete/QueryMatcher.ts @@ -18,16 +18,15 @@ limitations under the License. import _at from 'lodash/at'; import _uniq from 'lodash/uniq'; - -function stripDiacritics(str: string): string { - return str.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); -} +import {removeHiddenChars} from "matrix-js-sdk/src/utils"; interface IOptions { keys: Array; funcs?: Array<(T) => string>; shouldMatchWordsOnly?: boolean; shouldMatchPrefix?: boolean; + // whether to apply unhomoglyph and strip diacritics to fuzz up the search. Defaults to true + fuzzy?: boolean; } /** @@ -46,14 +45,10 @@ interface IOptions { */ export default class QueryMatcher { private _options: IOptions; - private _keys: IOptions["keys"]; - private _funcs: Required["funcs"]>; private _items: Map; constructor(objects: T[], options: IOptions = { keys: [] }) { this._options = options; - this._keys = options.keys; - this._funcs = options.funcs || []; this.setObjects(objects); @@ -78,15 +73,17 @@ export default class QueryMatcher { // type for their values. We assume that those values who's keys have // been specified will be string. Also, we cannot infer all the // types of the keys of the objects at compile. - const keyValues = _at(object, this._keys); + const keyValues = _at(object, this._options.keys); - for (const f of this._funcs) { - keyValues.push(f(object)); + if (this._options.funcs) { + for (const f of this._options.funcs) { + keyValues.push(f(object)); + } } for (const [index, keyValue] of Object.entries(keyValues)) { if (!keyValue) continue; // skip falsy keyValues - const key = stripDiacritics(keyValue).toLowerCase(); + const key = this.processQuery(keyValue); if (!this._items.has(key)) { this._items.set(key, []); } @@ -99,7 +96,7 @@ export default class QueryMatcher { } match(query: string): T[] { - query = stripDiacritics(query).toLowerCase(); + query = this.processQuery(query); if (this._options.shouldMatchWordsOnly) { query = query.replace(/[^\w]/g, ''); } @@ -118,7 +115,7 @@ export default class QueryMatcher { const index = resultKey.indexOf(query); if (index !== -1 && (!this._options.shouldMatchPrefix || index === 0)) { matches.push( - ...candidates.map((candidate) => ({index, ...candidate})) + ...candidates.map((candidate) => ({index, ...candidate})), ); } } @@ -142,4 +139,11 @@ export default class QueryMatcher { // Now map the keys to the result objects. Also remove any duplicates. return _uniq(matches.map((match) => match.object)); } + + private processQuery(query: string): string { + if (this._options.fuzzy !== false) { + return removeHiddenChars(query).toLowerCase(); + } + return query.toLowerCase(); + } } diff --git a/src/components/structures/AutoHideScrollbar.js b/src/components/structures/AutoHideScrollbar.js index 04323bb548..14f7c9ca83 100644 --- a/src/components/structures/AutoHideScrollbar.js +++ b/src/components/structures/AutoHideScrollbar.js @@ -38,12 +38,13 @@ export default class AutoHideScrollbar extends React.Component { render() { return (
+ ref={this._collectContainerRef} + style={this.props.style} + className={["mx_AutoHideScrollbar", this.props.className].join(" ")} + onScroll={this.props.onScroll} + onWheel={this.props.onWheel} + tabIndex={this.props.tabIndex} + > { this.props.children }
); } diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index cb1349da4b..f1bd297730 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -233,6 +233,9 @@ export class ContextMenu extends React.PureComponent { switch (ev.key) { case Key.TAB: case Key.ESCAPE: + // close on left and right arrows too for when it is a context menu on a + case Key.ARROW_LEFT: + case Key.ARROW_RIGHT: this.props.onFinished(); break; case Key.ARROW_UP: @@ -458,6 +461,7 @@ export function createMenu(ElementClass, props) { // re-export the semantic helper components for simplicity export {ContextMenuButton} from "../../accessibility/context_menu/ContextMenuButton"; +export {ContextMenuTooltipButton} from "../../accessibility/context_menu/ContextMenuTooltipButton"; export {MenuGroup} from "../../accessibility/context_menu/MenuGroup"; export {MenuItem} from "../../accessibility/context_menu/MenuItem"; export {MenuItemCheckbox} from "../../accessibility/context_menu/MenuItemCheckbox"; diff --git a/src/components/structures/CustomRoomTagPanel.js b/src/components/structures/CustomRoomTagPanel.js index 2753d5c4da..a79bdafeb5 100644 --- a/src/components/structures/CustomRoomTagPanel.js +++ b/src/components/structures/CustomRoomTagPanel.js @@ -72,17 +72,17 @@ class CustomRoomTagTile extends React.Component { const tag = this.props.tag; const avatarHeight = 40; const className = classNames({ - CustomRoomTagPanel_tileSelected: tag.selected, + "CustomRoomTagPanel_tileSelected": tag.selected, }); const name = tag.name; - const badge = tag.badge; + const badgeNotifState = tag.badgeNotifState; let badgeElement; - if (badge) { + if (badgeNotifState) { const badgeClasses = classNames({ "mx_TagTile_badge": true, - "mx_TagTile_badgeHighlight": badge.highlight, + "mx_TagTile_badgeHighlight": badgeNotifState.hasMentions, }); - badgeElement = (
{FormattingUtils.formatCount(badge.count)}
); + badgeElement = (
{FormattingUtils.formatCount(badgeNotifState.count)}
); } return ( diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js index 05ad4f7561..27f7fbb301 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.js @@ -192,7 +192,7 @@ export default class IndicatorScrollbar extends React.Component { ref={this._collectScrollerComponent} wrappedRef={this._collectScroller} onWheel={this.onMouseWheel} - {... this.props} + {...this.props} > { leftOverflowIndicator } { this.props.children } diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js deleted file mode 100644 index bae69b5631..0000000000 --- a/src/components/structures/LeftPanel.js +++ /dev/null @@ -1,305 +0,0 @@ -/* -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. -*/ - -import React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { Key } from '../../Keyboard'; -import * as sdk from '../../index'; -import dis from '../../dispatcher/dispatcher'; -import * as VectorConferenceHandler from '../../VectorConferenceHandler'; -import SettingsStore from '../../settings/SettingsStore'; -import {_t} from "../../languageHandler"; -import Analytics from "../../Analytics"; -import {Action} from "../../dispatcher/actions"; - - -const LeftPanel = createReactClass({ - displayName: 'LeftPanel', - - // NB. If you add props, don't forget to update - // shouldComponentUpdate! - propTypes: { - collapsed: PropTypes.bool.isRequired, - }, - - getInitialState: function() { - return { - searchFilter: '', - breadcrumbs: false, - }; - }, - - // TODO: [REACT-WARNING] Move this to constructor - UNSAFE_componentWillMount: function() { - this.focusedElement = null; - - this._breadcrumbsWatcherRef = SettingsStore.watchSetting( - "breadcrumbs", null, this._onBreadcrumbsChanged); - this._tagPanelWatcherRef = SettingsStore.watchSetting( - "TagPanel.enableTagPanel", null, () => this.forceUpdate()); - - const useBreadcrumbs = !!SettingsStore.getValue("breadcrumbs"); - Analytics.setBreadcrumbs(useBreadcrumbs); - this.setState({breadcrumbs: useBreadcrumbs}); - }, - - componentWillUnmount: function() { - SettingsStore.unwatchSetting(this._breadcrumbsWatcherRef); - SettingsStore.unwatchSetting(this._tagPanelWatcherRef); - }, - - shouldComponentUpdate: function(nextProps, nextState) { - // MatrixChat will update whenever the user switches - // rooms, but propagating this change all the way down - // the react tree is quite slow, so we cut this off - // here. The RoomTiles listen for the room change - // events themselves to know when to update. - // We just need to update if any of these things change. - if ( - this.props.collapsed !== nextProps.collapsed || - this.props.disabled !== nextProps.disabled - ) { - return true; - } - - if (this.state.searchFilter !== nextState.searchFilter) { - return true; - } - if (this.state.searchExpanded !== nextState.searchExpanded) { - return true; - } - - return false; - }, - - componentDidUpdate(prevProps, prevState) { - if (prevState.breadcrumbs !== this.state.breadcrumbs) { - Analytics.setBreadcrumbs(this.state.breadcrumbs); - } - }, - - _onBreadcrumbsChanged: function(settingName, roomId, level, valueAtLevel, value) { - // Features are only possible at a single level, so we can get away with using valueAtLevel. - // The SettingsStore runs on the same tick as the update, so `value` will be wrong. - this.setState({breadcrumbs: valueAtLevel}); - - // For some reason the setState doesn't trigger a render of the component, so force one. - // Probably has to do with the change happening outside of a change detector cycle. - this.forceUpdate(); - }, - - _onFocus: function(ev) { - this.focusedElement = ev.target; - }, - - _onBlur: function(ev) { - this.focusedElement = null; - }, - - _onFilterKeyDown: function(ev) { - if (!this.focusedElement) return; - - switch (ev.key) { - // On enter of rooms filter select and activate first room if such one exists - case Key.ENTER: { - const firstRoom = ev.target.closest(".mx_LeftPanel").querySelector(".mx_RoomTile"); - if (firstRoom) { - firstRoom.click(); - } - break; - } - } - }, - - _onKeyDown: function(ev) { - if (!this.focusedElement) return; - - switch (ev.key) { - case Key.ARROW_UP: - this._onMoveFocus(ev, true, true); - break; - case Key.ARROW_DOWN: - this._onMoveFocus(ev, false, true); - break; - } - }, - - _onMoveFocus: function(ev, up, trap) { - let element = this.focusedElement; - - // unclear why this isn't needed - // var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending; - // this.focusDirection = up; - - let descending = false; // are we currently descending or ascending through the DOM tree? - let classes; - - do { - const child = up ? element.lastElementChild : element.firstElementChild; - const sibling = up ? element.previousElementSibling : element.nextElementSibling; - - if (descending) { - if (child) { - element = child; - } else if (sibling) { - element = sibling; - } else { - descending = false; - element = element.parentElement; - } - } else { - if (sibling) { - element = sibling; - descending = true; - } else { - element = element.parentElement; - } - } - - if (element) { - classes = element.classList; - } - } while (element && !( - classes.contains("mx_RoomTile") || - classes.contains("mx_RoomSubList_label") || - classes.contains("mx_LeftPanel_filterRooms"))); - - if (element) { - ev.stopPropagation(); - ev.preventDefault(); - element.focus(); - this.focusedElement = element; - } else if (trap) { - // if navigation is via up/down arrow-keys, trap in the widget so it doesn't send to composer - ev.stopPropagation(); - ev.preventDefault(); - } - }, - - onSearch: function(term) { - this.setState({ searchFilter: term }); - }, - - onSearchCleared: function(source) { - if (source === "keyboard") { - dis.fire(Action.FocusComposer); - } - this.setState({searchExpanded: false}); - }, - - collectRoomList: function(ref) { - this._roomList = ref; - }, - - _onSearchFocus: function() { - this.setState({searchExpanded: true}); - }, - - _onSearchBlur: function(event) { - if (event.target.value.length === 0) { - this.setState({searchExpanded: false}); - } - }, - - render: function() { - const RoomList = sdk.getComponent('rooms.RoomList'); - const RoomBreadcrumbs = sdk.getComponent('rooms.RoomBreadcrumbs'); - const TagPanel = sdk.getComponent('structures.TagPanel'); - const CustomRoomTagPanel = sdk.getComponent('structures.CustomRoomTagPanel'); - const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton'); - const SearchBox = sdk.getComponent('structures.SearchBox'); - const CallPreview = sdk.getComponent('voip.CallPreview'); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - - const tagPanelEnabled = SettingsStore.getValue("TagPanel.enableTagPanel"); - let tagPanelContainer; - - const isCustomTagsEnabled = SettingsStore.isFeatureEnabled("feature_custom_tags"); - - if (tagPanelEnabled) { - tagPanelContainer = (
- - { isCustomTagsEnabled ? : undefined } -
); - } - - const containerClasses = classNames( - "mx_LeftPanel_container", "mx_fadable", - { - "collapsed": this.props.collapsed, - "mx_LeftPanel_container_hasTagPanel": tagPanelEnabled, - "mx_fadable_faded": this.props.disabled, - }, - ); - - let exploreButton; - if (!this.props.collapsed) { - exploreButton = ( -
- dis.fire(Action.ViewRoomDirectory)}>{_t("Explore")} -
- ); - } - - const searchBox = (); - - let breadcrumbs; - if (this.state.breadcrumbs) { - breadcrumbs = (); - } - - const roomList = ; - - return ( -
- { tagPanelContainer } - -
- ); - }, -}); - -export default LeftPanel; diff --git a/src/components/structures/LeftPanel2.tsx b/src/components/structures/LeftPanel.tsx similarity index 75% rename from src/components/structures/LeftPanel2.tsx rename to src/components/structures/LeftPanel.tsx index 012b518093..86136233d8 100644 --- a/src/components/structures/LeftPanel2.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -17,25 +17,26 @@ limitations under the License. import * as React from "react"; import { createRef } from "react"; import TagPanel from "./TagPanel"; +import CustomRoomTagPanel from "./CustomRoomTagPanel"; import classNames from "classnames"; import dis from "../../dispatcher/dispatcher"; import { _t } from "../../languageHandler"; -import RoomList2 from "../views/rooms/RoomList2"; -import { HEADER_HEIGHT } from "../views/rooms/RoomSublist2"; +import RoomList from "../views/rooms/RoomList"; +import { HEADER_HEIGHT } from "../views/rooms/RoomSublist"; import { Action } from "../../dispatcher/actions"; import UserMenu from "./UserMenu"; import RoomSearch from "./RoomSearch"; -import AccessibleButton from "../views/elements/AccessibleButton"; -import RoomBreadcrumbs2 from "../views/rooms/RoomBreadcrumbs2"; +import RoomBreadcrumbs from "../views/rooms/RoomBreadcrumbs"; import { BreadcrumbsStore } from "../../stores/BreadcrumbsStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore"; import ResizeNotifier from "../../utils/ResizeNotifier"; import SettingsStore from "../../settings/SettingsStore"; -import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore2"; +import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore"; import {Key} from "../../Keyboard"; import IndicatorScrollbar from "../structures/IndicatorScrollbar"; - -// TODO: Rename on launch: https://github.com/vector-im/riot-web/issues/14367 +import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; +import { OwnProfileStore } from "../../stores/OwnProfileStore"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; interface IProps { isMinimized: boolean; @@ -52,14 +53,15 @@ interface IState { const cssClasses = [ "mx_RoomSearch_input", "mx_RoomSearch_icon", // minimized - "mx_RoomSublist2_headerText", - "mx_RoomTile2", - "mx_RoomSublist2_showNButton", + "mx_RoomSublist_headerText", + "mx_RoomTile", + "mx_RoomSublist_showNButton", ]; -export default class LeftPanel2 extends React.Component { +export default class LeftPanel extends React.Component { private listContainerRef: React.RefObject = createRef(); private tagPanelWatcherRef: string; + private bgImageWatcherRef: string; private focusedElement = null; private isDoingStickyHeaders = false; @@ -74,6 +76,9 @@ export default class LeftPanel2 extends React.Component { BreadcrumbsStore.instance.on(UPDATE_EVENT, this.onBreadcrumbsUpdate); RoomListStore.instance.on(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate); + OwnProfileStore.instance.on(UPDATE_EVENT, this.onBackgroundImageUpdate); + this.bgImageWatcherRef = SettingsStore.watchSetting( + "RoomList.backgroundImage", null, this.onBackgroundImageUpdate); this.tagPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => { this.setState({showTagPanel: SettingsStore.getValue("TagPanel.enableTagPanel")}); }); @@ -85,8 +90,10 @@ export default class LeftPanel2 extends React.Component { public componentWillUnmount() { SettingsStore.unwatchSetting(this.tagPanelWatcherRef); + SettingsStore.unwatchSetting(this.bgImageWatcherRef); BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate); + OwnProfileStore.instance.off(UPDATE_EVENT, this.onBackgroundImageUpdate); this.props.resizeNotifier.off("middlePanelResizedNoisy", this.onResize); } @@ -109,6 +116,20 @@ export default class LeftPanel2 extends React.Component { } }; + private onBackgroundImageUpdate = () => { + // Note: we do this in the LeftPanel as it uses this variable most prominently. + const avatarSize = 32; // arbitrary + let avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize); + const settingBgMxc = SettingsStore.getValue("RoomList.backgroundImage"); + if (settingBgMxc) { + avatarUrl = MatrixClientPeg.get().mxcUrlToHttp(settingBgMxc, avatarSize, avatarSize); + } + const avatarUrlProp = `url(${avatarUrl})`; + if (document.body.style.getPropertyValue("--avatar-url") !== avatarUrlProp) { + document.body.style.setProperty("--avatar-url", avatarUrlProp); + } + }; + private handleStickyHeaders(list: HTMLDivElement) { if (this.isDoingStickyHeaders) return; this.isDoingStickyHeaders = true; @@ -121,7 +142,7 @@ export default class LeftPanel2 extends React.Component { private doStickyHeaders(list: HTMLDivElement) { const topEdge = list.scrollTop; const bottomEdge = list.offsetHeight + list.scrollTop; - const sublists = list.querySelectorAll(".mx_RoomSublist2"); + const sublists = list.querySelectorAll(".mx_RoomSublist"); const headerRightMargin = 16; // calculated from margins and widths to align with non-sticky tiles const headerStickyWidth = list.clientWidth - headerRightMargin; @@ -137,7 +158,7 @@ export default class LeftPanel2 extends React.Component { let lastTopHeader; let firstBottomHeader; for (const sublist of sublists) { - const header = sublist.querySelector(".mx_RoomSublist2_stickable"); + const header = sublist.querySelector(".mx_RoomSublist_stickable"); header.style.removeProperty("display"); // always clear display:none first // When an element is <=40% off screen, make it take over @@ -173,8 +194,8 @@ export default class LeftPanel2 extends React.Component { } if (style.stickyTop) { - if (!header.classList.contains("mx_RoomSublist2_headerContainer_stickyTop")) { - header.classList.add("mx_RoomSublist2_headerContainer_stickyTop"); + if (!header.classList.contains("mx_RoomSublist_headerContainer_stickyTop")) { + header.classList.add("mx_RoomSublist_headerContainer_stickyTop"); } const newTop = `${list.parentElement.offsetTop}px`; @@ -182,8 +203,8 @@ export default class LeftPanel2 extends React.Component { header.style.top = newTop; } } else { - if (header.classList.contains("mx_RoomSublist2_headerContainer_stickyTop")) { - header.classList.remove("mx_RoomSublist2_headerContainer_stickyTop"); + if (header.classList.contains("mx_RoomSublist_headerContainer_stickyTop")) { + header.classList.remove("mx_RoomSublist_headerContainer_stickyTop"); } if (header.style.top) { header.style.removeProperty('top'); @@ -191,18 +212,18 @@ export default class LeftPanel2 extends React.Component { } if (style.stickyBottom) { - if (!header.classList.contains("mx_RoomSublist2_headerContainer_stickyBottom")) { - header.classList.add("mx_RoomSublist2_headerContainer_stickyBottom"); + if (!header.classList.contains("mx_RoomSublist_headerContainer_stickyBottom")) { + header.classList.add("mx_RoomSublist_headerContainer_stickyBottom"); } } else { - if (header.classList.contains("mx_RoomSublist2_headerContainer_stickyBottom")) { - header.classList.remove("mx_RoomSublist2_headerContainer_stickyBottom"); + if (header.classList.contains("mx_RoomSublist_headerContainer_stickyBottom")) { + header.classList.remove("mx_RoomSublist_headerContainer_stickyBottom"); } } if (style.stickyTop || style.stickyBottom) { - if (!header.classList.contains("mx_RoomSublist2_headerContainer_sticky")) { - header.classList.add("mx_RoomSublist2_headerContainer_sticky"); + if (!header.classList.contains("mx_RoomSublist_headerContainer_sticky")) { + header.classList.add("mx_RoomSublist_headerContainer_sticky"); } const newWidth = `${headerStickyWidth}px`; @@ -210,8 +231,8 @@ export default class LeftPanel2 extends React.Component { header.style.width = newWidth; } } else if (!style.stickyTop && !style.stickyBottom) { - if (header.classList.contains("mx_RoomSublist2_headerContainer_sticky")) { - header.classList.remove("mx_RoomSublist2_headerContainer_sticky"); + if (header.classList.contains("mx_RoomSublist_headerContainer_sticky")) { + header.classList.remove("mx_RoomSublist_headerContainer_sticky"); } if (header.style.width) { header.style.removeProperty('width'); @@ -221,16 +242,16 @@ export default class LeftPanel2 extends React.Component { // add appropriate sticky classes to wrapper so it has // the necessary top/bottom padding to put the sticky header in - const listWrapper = list.parentElement; // .mx_LeftPanel2_roomListWrapper + const listWrapper = list.parentElement; // .mx_LeftPanel_roomListWrapper if (lastTopHeader) { - listWrapper.classList.add("mx_LeftPanel2_roomListWrapper_stickyTop"); + listWrapper.classList.add("mx_LeftPanel_roomListWrapper_stickyTop"); } else { - listWrapper.classList.remove("mx_LeftPanel2_roomListWrapper_stickyTop"); + listWrapper.classList.remove("mx_LeftPanel_roomListWrapper_stickyTop"); } if (firstBottomHeader) { - listWrapper.classList.add("mx_LeftPanel2_roomListWrapper_stickyBottom"); + listWrapper.classList.add("mx_LeftPanel_roomListWrapper_stickyBottom"); } else { - listWrapper.classList.remove("mx_LeftPanel2_roomListWrapper_stickyBottom"); + listWrapper.classList.remove("mx_LeftPanel_roomListWrapper_stickyBottom"); } } @@ -266,10 +287,10 @@ export default class LeftPanel2 extends React.Component { }; private onEnter = () => { - const firstRoom = this.listContainerRef.current.querySelector(".mx_RoomTile2"); + const firstRoom = this.listContainerRef.current.querySelector(".mx_RoomTile"); if (firstRoom) { firstRoom.click(); - this.onSearch(""); // clear the search field + return true; // to get the field to clear } }; @@ -314,7 +335,7 @@ export default class LeftPanel2 extends React.Component { private renderHeader(): React.ReactNode { return ( -
+
); @@ -324,10 +345,13 @@ export default class LeftPanel2 extends React.Component { if (this.state.showBreadcrumbs && !this.props.isMinimized) { return ( - + ); } @@ -336,7 +360,7 @@ export default class LeftPanel2 extends React.Component { private renderSearchExplore(): React.ReactNode { return (
{ onVerticalArrow={this.onKeyDown} onEnter={this.onEnter} /> - @@ -358,12 +382,13 @@ export default class LeftPanel2 extends React.Component { public render(): React.ReactNode { const tagPanel = !this.state.showTagPanel ? null : ( -
+
+ {SettingsStore.isFeatureEnabled("feature_custom_tags") ? : null}
); - const roomList = { />; const containerClasses = classNames({ - "mx_LeftPanel2": true, - "mx_LeftPanel2_hasTagPanel": !!tagPanel, - "mx_LeftPanel2_minimized": this.props.isMinimized, + "mx_LeftPanel": true, + "mx_LeftPanel_hasTagPanel": !!tagPanel, + "mx_LeftPanel_minimized": this.props.isMinimized, }); const roomListClasses = classNames( - "mx_LeftPanel2_actualRoomListContainer", + "mx_LeftPanel_actualRoomListContainer", "mx_AutoHideScrollbar", ); return (
{tagPanel} -