From 237084da7866432321ac52fcc7de74e8ec6c1075 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 11 Apr 2020 23:46:58 +0100 Subject: [PATCH 001/196] wrap node-qrcode in a React FC and use it for ShareDialog instead of qrcode-react Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 2 +- res/css/views/dialogs/_ShareDialog.scss | 9 +- src/components/views/dialogs/ShareDialog.js | 4 +- src/components/views/elements/QRCode.tsx | 50 +++++ yarn.lock | 214 ++------------------ 5 files changed, 77 insertions(+), 202 deletions(-) create mode 100644 src/components/views/elements/QRCode.tsx diff --git a/package.json b/package.json index 616f3f541e..02ec206ee3 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ "project-name-generator": "^2.1.7", "prop-types": "^15.5.8", "qrcode": "^1.4.4", - "qrcode-react": "^0.1.16", "qs": "^6.6.0", "react": "^16.9.0", "react-addons-css-transition-group": "15.6.2", @@ -118,6 +117,7 @@ "@babel/register": "^7.7.4", "@peculiar/webcrypto": "^1.0.22", "@types/classnames": "^2.2.10", + "@types/qrcode": "^1.3.4", "@types/react": "16.9", "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index 9a2f67dea3..3c984bbd1b 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -64,12 +64,15 @@ limitations under the License. .mx_ShareDialog_qrcode_container { float: left; - background-color: #ffffff; - padding: 5px; // makes qr code more readable in dark theme - border-radius: 5px; height: 256px; width: 256px; margin-right: 64px; + + img { + width: 100%; + height: 100%; + border-radius: 8px; + } } .mx_ShareDialog_social_container { diff --git a/src/components/views/dialogs/ShareDialog.js b/src/components/views/dialogs/ShareDialog.js index 1bc9decd39..a2554c3dcf 100644 --- a/src/components/views/dialogs/ShareDialog.js +++ b/src/components/views/dialogs/ShareDialog.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; -import QRCode from 'qrcode-react'; +import QRCode from "../elements/QRCode"; import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks"; import * as ContextMenu from "../../structures/ContextMenu"; import {toRightOf} from "../../structures/ContextMenu"; @@ -213,7 +213,7 @@ export default class ShareDialog extends React.Component { <div className="mx_ShareDialog_split"> <div className="mx_ShareDialog_qrcode_container"> - <QRCode value={matrixToUrl} size={256} logoWidth={48} logo={require("../../../../res/img/matrix-m.svg")} /> + <QRCode data={matrixToUrl} /> </div> <div className="mx_ShareDialog_social_container"> { diff --git a/src/components/views/elements/QRCode.tsx b/src/components/views/elements/QRCode.tsx new file mode 100644 index 0000000000..5e5db8cf93 --- /dev/null +++ b/src/components/views/elements/QRCode.tsx @@ -0,0 +1,50 @@ +/* +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 * as React from "react"; +import {toDataURL, QRCodeSegment, QRCodeToDataURLOptions} from "qrcode"; + +import {_t} from "../../../languageHandler"; +import Spinner from "./Spinner"; + +interface IProps extends QRCodeToDataURLOptions { + data: string | QRCodeSegment[]; +} + +const defaultOptions: QRCodeToDataURLOptions = { + errorCorrectionLevel: 'L', // we want it as trivial-looking as possible +}; + +const QRCode: React.FC<IProps> = ({data, ...options}) => { + const [dataUri, setUri] = React.useState<string>(null); + React.useEffect(() => { + let cancelled = false; + toDataURL(data, {...defaultOptions, ...options}).then(uri => { + if (cancelled) return; + setUri(uri); + }); + return () => { + cancelled = true; + }; + }, [data, options]); + + + return <div className="mx_QRCode"> + { dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} />: <Spinner /> } + </div>; +}; + +export default QRCode; diff --git a/yarn.lock b/yarn.lock index 538a049bff..05716ae985 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1267,6 +1267,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/qrcode@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b" + integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg== + dependencies: + "@types/node" "*" + "@types/react@16.9": version "16.9.32" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383" @@ -1496,11 +1503,6 @@ abab@^2.0.0: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - acorn-globals@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" @@ -1634,19 +1636,11 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.0.3, aproba@^1.1.1: +aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -2553,11 +2547,6 @@ console-browserify@^1.1.0: resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -2803,7 +2792,7 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^3.1.0, debug@^3.2.6: +debug@^3.1.0: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -2879,11 +2868,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - des.js@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" @@ -2897,11 +2881,6 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= - detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" @@ -3916,13 +3895,6 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - fs-readdir-recursive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" @@ -3985,20 +3957,6 @@ fuse.js@^2.2.0: resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-2.7.4.tgz#96e420fde7ef011ac49c258a621314fe576536f9" integrity sha1-luQg/efvARrEnCWKYhMU/ldlNvk= -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -4196,11 +4154,6 @@ has-symbols@^1.0.0, has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -4388,7 +4341,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -4405,13 +4358,6 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -ignore-walk@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== - dependencies: - minimatch "^3.0.4" - ignore@^4.0.3, ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -5975,21 +5921,6 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, "minimist@~ 1.2.0": resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -6014,7 +5945,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: +mkdirp@^0.5.1, mkdirp@^0.5.3: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -6091,15 +6022,6 @@ nearley@^2.7.10: randexp "0.4.6" semver "^5.4.1" -needle@^2.2.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" - integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - neo-async@^2.5.0, neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" @@ -6177,35 +6099,11 @@ node-notifier@^5.4.2: shellwords "^0.1.1" which "^1.3.0" -node-pre-gyp@*: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-releases@^1.1.53: version "1.1.53" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.53.tgz#2d821bfa499ed7c5dffc5e2f28c88e78a08ee3f4" integrity sha512-wp8zyQVwef2hpZ/dJH7SfSrIPD6YoJz6BDQDpGEkcA0s3LpAQoxBIYmfIq6QAhC1DhwsyCgTaTTcONwX8qzCuQ== -nopt@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== - dependencies: - abbrev "1" - osenv "^0.1.4" - normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -6238,27 +6136,6 @@ normalize-selector@^0.2.0: resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" integrity sha1-0LFF62kRicY6eNIB3E/bEpPvDAM= -npm-bundled@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-packlist@^1.1.6: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - npm-normalize-package-bin "^1.0.1" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -6266,16 +6143,6 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -6425,11 +6292,6 @@ os-browserify@^0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - os-locale@^3.0.0, os-locale@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" @@ -6439,19 +6301,11 @@ os-locale@^3.0.0, os-locale@^3.1.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -7006,18 +6860,6 @@ pvutils@latest: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.0.17.tgz#ade3c74dfe7178944fe44806626bd2e249d996bf" integrity sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ== -qr.js@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" - integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8= - -qrcode-react@^0.1.16: - version "0.1.16" - resolved "https://registry.yarnpkg.com/qrcode-react/-/qrcode-react-0.1.16.tgz#d064999d510ffc3e55a9ca3ffcf6c203c69f1517" - integrity sha512-FK+QCfFqCQMSxUE1byzglERJQkwKqXYvYMCS+/Ad2zACJOfoHkHHtRqsQQPji7lfb1y1qCXLvL+3eP1hAfg8Ng== - dependencies: - qr.js "0.0.0" - qrcode@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.4.4.tgz#f0c43568a7e7510a55efc3b88d9602f71963ea83" @@ -7096,7 +6938,7 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -rc@1.2.8, rc@^1.2.7, rc@^1.2.8: +rc@1.2.8, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7254,7 +7096,7 @@ read-pkg@^4.0.1: parse-json "^4.0.0" pify "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -7614,7 +7456,7 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.4.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: +rimraf@^2.4.3, rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== @@ -7776,7 +7618,7 @@ serialize-javascript@^2.1.2: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -8112,7 +7954,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: +string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -8393,19 +8235,6 @@ tapable@^1.0.0, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^4.4.2: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - terser-webpack-plugin@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" @@ -9128,13 +8957,6 @@ which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" @@ -9219,7 +9041,7 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 610fab7857d32ab9af557eae8fac1a1211d02713 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 11 Apr 2020 23:53:24 +0100 Subject: [PATCH 002/196] i18n and delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/QRCode.tsx | 2 +- src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/elements/QRCode.tsx b/src/components/views/elements/QRCode.tsx index 5e5db8cf93..9c867058c0 100644 --- a/src/components/views/elements/QRCode.tsx +++ b/src/components/views/elements/QRCode.tsx @@ -43,7 +43,7 @@ const QRCode: React.FC<IProps> = ({data, ...options}) => { return <div className="mx_QRCode"> - { dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} />: <Spinner /> } + { dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> } </div>; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ae8ed90402..832b53e67c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1482,6 +1482,7 @@ "%(oneUser)smade no changes %(count)s times|one": "%(oneUser)smade no changes", "Power level": "Power level", "Custom level": "Custom level", + "QR Code": "QR Code", "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.", "<a>In reply to</a> <pill>": "<a>In reply to</a> <pill>", "Room alias": "Room alias", From f10d9e47e7c9e4d4ee20175256af3a2616940877 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 31 Mar 2020 16:02:23 +0100 Subject: [PATCH 003/196] Fix event tiles to smoothly resize with font. --- res/css/views/rooms/_EventTile.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 0dc60226b8..a6cff9cfb0 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -114,10 +114,9 @@ limitations under the License. clear: both; position: relative; padding-left: 65px; /* left gutter */ - padding-top: 4px; - padding-bottom: 2px; + padding-top: 3px; + padding-bottom: 3px; border-radius: 4px; - min-height: 24px; line-height: $font-22px; } From 1ff0f3445a52806888d82a2f25d95a120b9114cb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Fri, 3 Apr 2020 17:53:49 +0100 Subject: [PATCH 004/196] Fix pills. This was a hard pill to swallow --- res/css/views/elements/_RichText.scss | 35 +++++++++---------- res/css/views/rooms/_Autocomplete.scss | 9 ++--- .../views/rooms/_BasicMessageComposer.scss | 13 +++---- src/components/views/avatars/BaseAvatar.js | 22 ++++++++---- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index e01b1f8938..cb0c2eacc1 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -6,11 +6,14 @@ .mx_RoomPill, .mx_GroupPill, .mx_AtRoomPill { - border-radius: 16px; - display: inline-block; - height: 20px; + display: inline-flex; + align-items: center; + vertical-align: middle; + border-radius: $font-16px; + height: $font-16px; line-height: $font-20px; - padding-left: 5px; + padding-left: 0; + padding-right: 0.5em; } a.mx_Pill { @@ -19,6 +22,11 @@ a.mx_Pill { overflow: hidden; vertical-align: text-bottom; max-width: calc(100% - 1ch); + height: $font-24px; +} + +.mx_Pill { + padding: 0.3rem; } /* More specific to override `.markdown-body a` text-decoration */ @@ -31,8 +39,7 @@ a.mx_Pill { .mx_UserPill { color: $primary-fg-color; background-color: $other-user-pill-bg-color; - padding-right: 5px; -} + } .mx_UserPill_selected { background-color: $accent-color !important; @@ -45,7 +52,6 @@ a.mx_Pill { .mx_MessageComposer_input .mx_AtRoomPill { color: $accent-fg-color; background-color: $mention-user-pill-bg-color; - padding-right: 5px; } /* More specific to override `.markdown-body a` color */ @@ -55,15 +61,6 @@ a.mx_Pill { .mx_GroupPill { color: $accent-fg-color; background-color: $rte-room-pill-color; - padding-right: 5px; -} - -/* More specific to override `.markdown-body a` color */ -.mx_EventTile_content .markdown-body a.mx_GroupPill, -.mx_GroupPill { - color: $accent-fg-color; - background-color: $rte-group-pill-color; - padding-right: 5px; } .mx_EventTile_body .mx_UserPill, @@ -77,8 +74,10 @@ a.mx_Pill { .mx_GroupPill .mx_BaseAvatar, .mx_AtRoomPill .mx_BaseAvatar { position: relative; - left: -3px; - top: 2px; + display: inline-flex; + align-items: center; + border-radius: 10rem; + margin-right: 0.24rem; } .mx_Markdown_BOLD { diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index e5316f5a46..a4aebdb708 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -31,9 +31,10 @@ } .mx_Autocomplete_Completion_pill { - border-radius: 17px; - height: 34px; - padding: 0px 5px; + box-sizing: border-box; + border-radius: 2rem; + height: $font-34px; + padding: 0.4rem; display: flex; user-select: none; cursor: pointer; @@ -42,7 +43,7 @@ } .mx_Autocomplete_Completion_pill > * { - margin: 0 3px; + margin-right: 0.3rem; } /* styling for common completion elements */ diff --git a/res/css/views/rooms/_BasicMessageComposer.scss b/res/css/views/rooms/_BasicMessageComposer.scss index e9013eb7b7..e126e523a6 100644 --- a/res/css/views/rooms/_BasicMessageComposer.scss +++ b/res/css/views/rooms/_BasicMessageComposer.scss @@ -46,22 +46,19 @@ limitations under the License. &.mx_BasicMessageComposer_input_shouldShowPillAvatar { span.mx_UserPill, span.mx_RoomPill { - padding-left: 21px; position: relative; // avatar psuedo element &::before { - position: absolute; - left: 2px; - top: 2px; content: var(--avatar-letter); - width: 16px; - height: 16px; + width: $font-16px; + height: $font-16px; + margin-right: 0.24rem; background: var(--avatar-background), $avatar-bg-color; color: $avatar-initial-color; background-repeat: no-repeat; - background-size: 16px; - border-radius: 8px; + background-size: $font-16px; + border-radius: $font-16px; text-align: center; font-weight: normal; line-height: $font-16px; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 3e3a2e6bd9..d9336a81e7 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -164,9 +164,9 @@ export default createReactClass({ const initialLetter = AvatarLogic.getInitialLetter(name); const textNode = ( <span className="mx_BaseAvatar_initial" aria-hidden="true" - style={{ fontSize: (width * 0.65) + "px", - width: width + "px", - lineHeight: height + "px" }} + style={{ fontSize: (width * 0.65) / 15 + "rem", + width: width / 15 + "rem", + lineHeight: height / 15 + "rem" }} > { initialLetter } </span> @@ -174,7 +174,11 @@ export default createReactClass({ const imgNode = ( <img className="mx_BaseAvatar_image" src={imageUrl} alt="" title={title} onError={this.onError} - width={width} height={height} aria-hidden="true" /> + aria-hidden="true" + style={{ + width: width/15 + "rem", + height: height/15 + "rem" + }} /> ); if (onClick != null) { return ( @@ -202,7 +206,10 @@ export default createReactClass({ src={imageUrl} onClick={onClick} onError={this.onError} - width={width} height={height} + style={{ + width: width/15 + "rem", + height: height/15 + "rem" + }} title={title} alt="" inputRef={inputRef} {...otherProps} /> @@ -213,7 +220,10 @@ export default createReactClass({ className="mx_BaseAvatar mx_BaseAvatar_image" src={imageUrl} onError={this.onError} - width={width} height={height} + style={{ + width: width/15 + "rem", + height: height/15 + "rem" + }} title={title} alt="" ref={inputRef} {...otherProps} /> From 27b1ce841f4397a0bf6c362c7bc718efdd4571dc Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 16:23:16 +0100 Subject: [PATCH 005/196] Scale room tile heights. --- res/css/views/rooms/_RoomTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index de018bf178..5bc7d5624d 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. flex-direction: row; align-items: center; cursor: pointer; - height: 34px; + height: $font-34px; margin: 0; padding: 0 8px 0 10px; position: relative; From f54ca219cf01a8c88f74abfb76b0f0cad7fae10c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 16:23:52 +0100 Subject: [PATCH 006/196] Scale read receipt images --- res/css/views/rooms/_EventTile.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index a6cff9cfb0..77bfa85862 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -310,6 +310,8 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody { .mx_EventTile_readAvatars .mx_BaseAvatar { position: absolute; display: inline-block; + height: $font-14px; + width: $font-14px; } .mx_EventTile_readAvatarRemainder { From d12ed333c1040d0bcd6fb4b00b7a40baeac433f4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 16:48:35 +0100 Subject: [PATCH 007/196] Fix avatar alignment for room state events --- res/css/views/rooms/_EventTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 77bfa85862..1c781eec20 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -45,7 +45,7 @@ limitations under the License. } .mx_EventTile.mx_EventTile_info .mx_EventTile_avatar { - top: 8px; + top: $font-8px; left: 65px; } From 05d11fea6900b18c7d4063668188bfdf1c648772 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 16:49:44 +0100 Subject: [PATCH 008/196] Use a function to convert to rem. --- src/components/views/avatars/BaseAvatar.js | 19 ++++++++++--------- src/utils/rem.js | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 src/utils/rem.js diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index d9336a81e7..a17124b9ad 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,6 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import toRem from "../../../utils/rem"; export default createReactClass({ displayName: 'BaseAvatar', @@ -164,9 +165,9 @@ export default createReactClass({ const initialLetter = AvatarLogic.getInitialLetter(name); const textNode = ( <span className="mx_BaseAvatar_initial" aria-hidden="true" - style={{ fontSize: (width * 0.65) / 15 + "rem", - width: width / 15 + "rem", - lineHeight: height / 15 + "rem" }} + style={{ fontSize: toRem(width * 0.65), + width: toRem(width), + lineHeight: toRem(height) }} > { initialLetter } </span> @@ -176,8 +177,8 @@ export default createReactClass({ alt="" title={title} onError={this.onError} aria-hidden="true" style={{ - width: width/15 + "rem", - height: height/15 + "rem" + width: toRem(width), + height: toRem(height) }} /> ); if (onClick != null) { @@ -207,8 +208,8 @@ export default createReactClass({ onClick={onClick} onError={this.onError} style={{ - width: width/15 + "rem", - height: height/15 + "rem" + width: toRem(width), + height: toRem(height) }} title={title} alt="" inputRef={inputRef} @@ -221,8 +222,8 @@ export default createReactClass({ src={imageUrl} onError={this.onError} style={{ - width: width/15 + "rem", - height: height/15 + "rem" + width: toRem(width), + height: toRem(height) }} title={title} alt="" ref={inputRef} diff --git a/src/utils/rem.js b/src/utils/rem.js new file mode 100644 index 0000000000..0ba430950c --- /dev/null +++ b/src/utils/rem.js @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// converts a pixel value to rem. +export default function(pixel_val) { + return pixel_val / 15 + "rem" +} From c6bc0c79147b51c737709a202a90ebb3033e5eed Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 16:51:07 +0100 Subject: [PATCH 009/196] Fix read receipt horizontal spacing at scale --- src/components/views/rooms/ReadReceiptMarker.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index be0ca1f8d6..85d443d55a 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -23,6 +23,7 @@ import { _t } from '../../../languageHandler'; import {formatDate} from '../../../DateUtils'; import Velociraptor from "../../../Velociraptor"; import * as sdk from "../../../index"; +import toRem from "../../../utils/rem"; let bounce = false; try { @@ -148,7 +149,7 @@ export default createReactClass({ // start at the old height and in the old h pos startStyles.push({ top: startTopOffset+"px", - left: oldInfo.left+"px" }); + left: toRem(oldInfo.left) }); const reorderTransitionOpts = { duration: 100, @@ -181,7 +182,7 @@ export default createReactClass({ } const style = { - left: this.props.leftOffset+'px', + left: toRem(this.props.leftOffset), top: '0px', visibility: this.props.hidden ? 'hidden' : 'visible', }; From f29717ee62aceb982afd573fc0f473efad8c4488 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 17:47:12 +0100 Subject: [PATCH 010/196] Fix room header vertical alignment --- res/css/views/rooms/_RoomHeader.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index 969106c9ea..80f6c40f39 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -173,8 +173,6 @@ limitations under the License. .mx_RoomHeader_avatar { flex: 0; - width: 28px; - height: 28px; margin: 0 7px; position: relative; } From 38919518da7bef490df539b08c2d72e4f23c5c22 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 6 Apr 2020 20:52:36 +0100 Subject: [PATCH 011/196] Resize room list scroll section. This fix isn't perfect. Currently the scroll view is slightly smaller than the list of rooms. I think it has something to do with the how the heigh is calculate in js, considering it has some assumptions about the height of each bar and the padding. However room items are the only things which change with respect to the root value. Therefore the item list is actually taller than the computed pixel value of the list converted to rems. I'll look into it. --- src/components/structures/RoomSubList.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 2ae2d71100..1e3e15b4ec 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,6 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; +import toRem from "../../utils/rem"; // turn this on for drop & drag console debugging galore const debug = false; @@ -383,7 +384,7 @@ export default class RoomSubList extends React.PureComponent { setHeight = (height) => { if (this._subList.current) { - this._subList.current.style.height = `${height}px`; + this._subList.current.style.height = toRem(height); } this._updateLazyRenderHeight(height); }; From cc601e1595e5bd4e571e74066e3b5cc1d3a8013f Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 14:21:21 +0100 Subject: [PATCH 012/196] Resize toggle switches with font --- res/css/views/elements/_ToggleSwitch.scss | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 1f4445b88c..9a07e01978 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -16,12 +16,19 @@ limitations under the License. .mx_ToggleSwitch { transition: background-color 0.20s ease-out 0.1s; - width: 48px; - height: 24px; - border-radius: 14px; + + width: $font-48px; + height: $font-20px; + border-radius: 1.5rem; + padding: 2px; + background-color: $togglesw-off-color; - position: relative; opacity: 0.5; + + display: flex; + flex-direction: row; + flex: 0 0 auto; + align-items: center; } .mx_ToggleSwitch_enabled { @@ -35,19 +42,12 @@ limitations under the License. .mx_ToggleSwitch_ball { transition: left 0.15s ease-out 0.1s; - margin: 2px; - width: 20px; - height: 20px; - border-radius: 20px; + width: $font-20px; + height: $font-20px; + border-radius: $font-20px; background-color: $togglesw-ball-color; - position: absolute; - top: 0; } -.mx_ToggleSwitch_on > .mx_ToggleSwitch_ball { - left: 23px; // 48px switch - 20px ball - 5px padding = 23px -} - -.mx_ToggleSwitch:not(.mx_ToggleSwitch_on) > .mx_ToggleSwitch_ball { - left: 2px; -} +.mx_ToggleSwitch_on { + flex-direction: row-reverse; +} \ No newline at end of file From 0182128189e433cd775f78a44f90ed0d523a961a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 15:07:34 +0100 Subject: [PATCH 013/196] Fix settings when scaling up --- res/css/views/elements/_ToggleSwitch.scss | 2 +- res/css/views/settings/tabs/_SettingsTab.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 9a07e01978..073bf5c00b 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -17,7 +17,7 @@ limitations under the License. .mx_ToggleSwitch { transition: background-color 0.20s ease-out 0.1s; - width: $font-48px; + width: $font-44px; height: $font-20px; border-radius: 1.5rem; padding: 2px; diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 1fbfb35927..e3a61e6825 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -63,7 +63,7 @@ limitations under the License. display: inline-block; font-size: $font-14px; color: $primary-fg-color; - max-width: calc(100% - 48px); // Force word wrap instead of colliding with the switch + max-width: calc(100% - $font-48px); // Force word wrap instead of colliding with the switch box-sizing: border-box; padding-right: 10px; } From 5caec2a289a2f2b1bfc75cfeae1b05a9d3479b34 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 15:23:19 +0100 Subject: [PATCH 014/196] lint --- res/css/views/elements/_RichText.scss | 2 +- res/css/views/elements/_ToggleSwitch.scss | 4 ++-- src/utils/rem.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index cb0c2eacc1..21d01fdd51 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -39,7 +39,7 @@ a.mx_Pill { .mx_UserPill { color: $primary-fg-color; background-color: $other-user-pill-bg-color; - } +} .mx_UserPill_selected { background-color: $accent-color !important; diff --git a/res/css/views/elements/_ToggleSwitch.scss b/res/css/views/elements/_ToggleSwitch.scss index 073bf5c00b..7c65fbc90d 100644 --- a/res/css/views/elements/_ToggleSwitch.scss +++ b/res/css/views/elements/_ToggleSwitch.scss @@ -21,7 +21,7 @@ limitations under the License. height: $font-20px; border-radius: 1.5rem; padding: 2px; - + background-color: $togglesw-off-color; opacity: 0.5; @@ -50,4 +50,4 @@ limitations under the License. .mx_ToggleSwitch_on { flex-direction: row-reverse; -} \ No newline at end of file +} diff --git a/src/utils/rem.js b/src/utils/rem.js index 0ba430950c..1f18c9de05 100644 --- a/src/utils/rem.js +++ b/src/utils/rem.js @@ -15,6 +15,6 @@ limitations under the License. */ // converts a pixel value to rem. -export default function(pixel_val) { - return pixel_val / 15 + "rem" +export default function(pixelVal) { + return pixelVal / 15 + "rem"; } From a58721047e6345ccd1b722c648314da7fbacac07 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 15:41:41 +0100 Subject: [PATCH 015/196] Room memebers avatars scale --- res/css/views/rooms/_EntityTile.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/rooms/_EntityTile.scss b/res/css/views/rooms/_EntityTile.scss index 966d2c4e70..8db71f297c 100644 --- a/res/css/views/rooms/_EntityTile.scss +++ b/res/css/views/rooms/_EntityTile.scss @@ -69,8 +69,6 @@ limitations under the License. padding-right: 12px; padding-top: 4px; padding-bottom: 4px; - width: 36px; - height: 36px; position: relative; } From e49c041df79cb8169b39c76de21c087c5e21caec Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 16:08:59 +0100 Subject: [PATCH 016/196] Commuity seleciton bar vertical alignment scale fix --- res/css/structures/_TagPanel.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 0065ffa502..f5c4c4c698 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -110,13 +110,13 @@ limitations under the License. .mx_TagPanel .mx_TagTile.mx_TagTile_selected::before { content: ''; - height: 56px; + height: calc(100% + 16px); background-color: $accent-color; width: 5px; position: absolute; left: -15px; border-radius: 0 3px 3px 0; - top: -8px; // (56 - 40)/2 + top: -8px; // (16px / 2) } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { From 121b53f99a5f1d17decdd296ff4b960ffc026c3a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 16:27:26 +0100 Subject: [PATCH 017/196] FIx lanugage selection alignment at scale. --- res/css/views/elements/_Dropdown.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/elements/_Dropdown.scss b/res/css/views/elements/_Dropdown.scss index 32a68d5252..dd8511cc42 100644 --- a/res/css/views/elements/_Dropdown.scss +++ b/res/css/views/elements/_Dropdown.scss @@ -67,6 +67,8 @@ limitations under the License. text-overflow: ellipsis; white-space: nowrap; flex: 1; + display: inline-flex; + align-items: center; } .mx_Dropdown_option div { From 3966e45b8fccf884850e68469a9443795b0e830e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 17:13:09 +0100 Subject: [PATCH 018/196] Fix test --- test/components/views/messages/TextualBody-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 4ad46586ae..7801995254 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -206,7 +206,8 @@ describe("<TextualBody />", () => { 'Hey <span>' + '<a class="mx_Pill mx_UserPill" title="@user:server">' + '<img class="mx_BaseAvatar mx_BaseAvatar_image" src="mxc://avatar.url/image.png" ' + - 'width="16" height="16" title="@member:domain.bla" alt="" aria-hidden="true">Member</a>' + + 'style=\"width: 1.0666666666666667rem; height: 1.0666666666666667rem;\" ' + + 'title="@member:domain.bla" alt="" aria-hidden="true">Member</a>' + '</span></span>'); }); }); From 2d2b39892a2ad349514754f42725e86f33b58a54 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 17:23:07 +0100 Subject: [PATCH 019/196] lint test --- test/components/views/messages/TextualBody-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 7801995254..59671327ce 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -206,7 +206,7 @@ describe("<TextualBody />", () => { 'Hey <span>' + '<a class="mx_Pill mx_UserPill" title="@user:server">' + '<img class="mx_BaseAvatar mx_BaseAvatar_image" src="mxc://avatar.url/image.png" ' + - 'style=\"width: 1.0666666666666667rem; height: 1.0666666666666667rem;\" ' + + 'style="width: 1.0666666666666667rem; height: 1.0666666666666667rem;" ' + 'title="@member:domain.bla" alt="" aria-hidden="true">Member</a>' + '</span></span>'); }); From c2a29087460969fd7368d84ff5112fb5b3b25d06 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 7 Apr 2020 17:41:18 +0100 Subject: [PATCH 020/196] Fix community line height --- res/css/structures/_TagPanel.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index f5c4c4c698..4a78c8df92 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -69,7 +69,7 @@ limitations under the License. height: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer > div { - height: 40px; + height: $font-40px; padding: 10px 0 9px 0; } From c921859067b25b4c86d33ec93f3d06d6a0889ef0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:35:38 +0100 Subject: [PATCH 021/196] Use rem instead of em. --- res/css/views/elements/_RichText.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 21d01fdd51..d46d07e82b 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -13,7 +13,7 @@ height: $font-16px; line-height: $font-20px; padding-left: 0; - padding-right: 0.5em; + padding-right: 0.5rem; } a.mx_Pill { From 07853f48bb882a55ec2fdab9255bf2a8366d4efd Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:45:21 +0100 Subject: [PATCH 022/196] Use font-variable instead of rem --- res/css/_font-sizes.scss | 7 +++++++ res/css/views/elements/_RichText.scss | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index ad9e2e7103..b37b2eb3c2 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -14,6 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ +$font-1px: 0.067rem; +$font-2px: 0.133rem; +$font-3px: 0.200rem; +$font-4px: 0.267rem; +$font-5px: 0.333rem; +$font-6px: 0.400rem; +$font-7px: 0.467rem; $font-8px: 0.533rem; $font-9px: 0.600rem; $font-10px: 0.667rem; diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index d46d07e82b..5b5759b794 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -26,7 +26,7 @@ a.mx_Pill { } .mx_Pill { - padding: 0.3rem; + padding: $font-5px; } /* More specific to override `.markdown-body a` text-decoration */ From 18efbcccebece7ebd4cf17d5e6e9073261b3cb2a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 17:07:59 +0100 Subject: [PATCH 023/196] Undo superfluous delete --- res/css/views/elements/_RichText.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 5b5759b794..b933b1b828 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -29,6 +29,13 @@ a.mx_Pill { padding: $font-5px; } +/* More specific to override `.markdown-body a` color */ +.mx_EventTile_content .markdown-body a.mx_GroupPill, +.mx_GroupPill { + color: $accent-fg-color; + background-color: $rte-group-pill-color; +} + /* More specific to override `.markdown-body a` text-decoration */ .mx_EventTile_content .markdown-body a.mx_Pill { text-decoration: none; From fc04266b4239fe7f1135e81bf8d4a38b771d095d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 17:32:24 +0100 Subject: [PATCH 024/196] Skinnier pills are easier to swallow --- res/css/views/elements/_RichText.scss | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index b933b1b828..1fa04595f9 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -10,10 +10,9 @@ align-items: center; vertical-align: middle; border-radius: $font-16px; - height: $font-16px; - line-height: $font-20px; + line-height: $font-15px; padding-left: 0; - padding-right: 0.5rem; + padding-right: $font-5px; } a.mx_Pill { @@ -22,11 +21,10 @@ a.mx_Pill { overflow: hidden; vertical-align: text-bottom; max-width: calc(100% - 1ch); - height: $font-24px; } .mx_Pill { - padding: $font-5px; + padding: $font-1px; } /* More specific to override `.markdown-body a` color */ From 7a0caafb77fadeabba717a78ae5ddd09e8fdaf33 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 19:15:34 +0100 Subject: [PATCH 025/196] FIx unread count --- res/css/structures/_RoomSubList.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 2e0c94263e..2c53258b08 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -74,7 +74,7 @@ limitations under the License. .mx_RoomSubList_badge > div { flex: 0 0 auto; - border-radius: 8px; + border-radius: $font-16px; font-weight: 600; font-size: $font-12px; padding: 0 5px; From 83609f0ab2bb1b75d0e0282eba5baee014e6f569 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 20:12:53 +0100 Subject: [PATCH 026/196] Scale user photo upload with font size --- res/css/_font-sizes.scss | 1 + res/css/views/settings/_AvatarSetting.scss | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/res/css/_font-sizes.scss b/res/css/_font-sizes.scss index b37b2eb3c2..5cd19ce620 100644 --- a/res/css/_font-sizes.scss +++ b/res/css/_font-sizes.scss @@ -67,4 +67,5 @@ $font-49px: 3.267rem; $font-50px: 3.333rem; $font-51px: 3.400rem; $font-52px: 3.467rem; +$font-88px: 5.887rem; $font-400px: 26.667rem; diff --git a/res/css/views/settings/_AvatarSetting.scss b/res/css/views/settings/_AvatarSetting.scss index 35dba90f85..9fa10907b4 100644 --- a/res/css/views/settings/_AvatarSetting.scss +++ b/res/css/views/settings/_AvatarSetting.scss @@ -15,13 +15,13 @@ limitations under the License. */ .mx_AvatarSetting_avatar { - width: 88px; - height: 88px; + width: $font-88px; + height: $font-88px; margin-left: 13px; position: relative; & > * { - width: 88px; + width: $font-88px; box-sizing: border-box; } @@ -63,7 +63,7 @@ limitations under the License. & > img, .mx_AvatarSetting_avatarPlaceholder { display: block; - height: 88px; + height: $font-88px; border-radius: 4px; } From 42ec21f5bb306c51ae21e4e2b3995bc757139db8 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 20:13:13 +0100 Subject: [PATCH 027/196] Tweak read receipt remainder text --- src/components/views/rooms/EventTile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index f67877373e..af14f6922c 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -34,6 +34,7 @@ import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; import * as ObjectUtils from "../../../ObjectUtils"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {E2E_STATE} from "./E2EIcon"; +import torem from "../../../utils/rem"; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', @@ -473,7 +474,7 @@ export default createReactClass({ if (remainder > 0) { remText = <span className="mx_EventTile_readAvatarRemainder" onClick={this.toggleAllReadAvatars} - style={{ right: -(left - receiptOffset) }}>{ remainder }+ + style={{ right: "calc(" + torem(-left) + " + " + receiptOffset + "px)" }}>{ remainder }+ </span>; } } From e10a511997ca6667bfb96b74938735c8c81447d5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 15:49:19 +0100 Subject: [PATCH 028/196] Extra right padding in user pills --- res/css/views/elements/_RichText.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index 1fa04595f9..b45b567db7 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -25,6 +25,7 @@ a.mx_Pill { .mx_Pill { padding: $font-1px; + padding-right: $font-6px; } /* More specific to override `.markdown-body a` color */ From 61f2e19d95b8f7ef7589de0461d1877b9e607a11 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:18:57 +0100 Subject: [PATCH 029/196] Basic font settings. Include a default value getter in the store in order to make the default value easy to work with. --- src/settings/Settings.js | 7 ++++ src/settings/SettingsStore.js | 14 ++++++++ .../controllers/FontSizeController.js | 33 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/settings/controllers/FontSizeController.js diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 928b1985fa..3b316e39d0 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -29,6 +29,7 @@ import ThemeController from './controllers/ThemeController'; import PushToMatrixClientController from './controllers/PushToMatrixClientController'; import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases"; +import FontSizeController from './controllers/FontSizeController'; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; @@ -94,6 +95,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "font_size": { + displayName: _td("Font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 16, + controller: new FontSizeController(), + }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 0122916bc3..70ea5ac57c 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -370,6 +370,20 @@ export default class SettingsStore { return SettingsStore._getFinalValue(setting, level, roomId, null, null); } + /** + * Gets the default value of a setting. + * @param {string} settingName The name of the setting to read the value of. + * @return {*} The value, or null if not found + */ + static getDefaultValue(settingName, roomId = null, excludeDefault = false) { + // Verify that the setting is actually a setting + if (!SETTINGS[settingName]) { + throw new Error("Setting '" + settingName + "' does not appear to be a setting."); + } + + return SETTINGS[settingName].default; + } + static _getFinalValue(setting, level, roomId, calculatedValue, calculatedAtLevel) { let resultingValue = calculatedValue; diff --git a/src/settings/controllers/FontSizeController.js b/src/settings/controllers/FontSizeController.js new file mode 100644 index 0000000000..4ab2f331f1 --- /dev/null +++ b/src/settings/controllers/FontSizeController.js @@ -0,0 +1,33 @@ +/* +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 SettingController from "./SettingController"; +import dis from "../../dispatcher"; + +export default class FontSizeController extends SettingController { + + constructor() { + super() + } + + onChange(level, roomId, newValue) { + // Dispatch font size change so that everything open responds to the change. + dis.dispatch({ + action: "update-font-size", + size: newValue, + }); + } +} From 269621ad2436f2181d44a10829cad1447249d8fd Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:19:50 +0100 Subject: [PATCH 030/196] Move theme settings to a new tab --- .../tabs/user/GeneralUserSettingsTab.js | 172 ------------- .../tabs/user/StyleUserSettingsTab.js | 233 ++++++++++++++++++ src/theme.js | 2 +- 3 files changed, 234 insertions(+), 173 deletions(-) create mode 100644 src/components/views/settings/tabs/user/StyleUserSettingsTab.js diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 801062bff4..4e57203ca2 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -19,7 +19,6 @@ limitations under the License. import React from 'react'; import {_t} from "../../../../../languageHandler"; import ProfileSettings from "../../ProfileSettings"; -import Field from "../../../elements/Field"; import * as languageHandler from "../../../../../languageHandler"; import {SettingLevel} from "../../../../../settings/SettingsStore"; import SettingsStore from "../../../../../settings/SettingsStore"; @@ -27,7 +26,6 @@ import LanguageDropdown from "../../../elements/LanguageDropdown"; import AccessibleButton from "../../../elements/AccessibleButton"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog"; import PropTypes from "prop-types"; -import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import PlatformPeg from "../../../../../PlatformPeg"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; @@ -60,9 +58,6 @@ export default class GeneralUserSettingsTab extends React.Component { }, emails: [], msisdns: [], - ...this._calculateThemeState(), - customThemeUrl: "", - customThemeMessage: {isError: false, text: ""}, }; this.dispatcherRef = dis.register(this._onAction); @@ -91,39 +86,6 @@ export default class GeneralUserSettingsTab extends React.Component { dis.unregister(this.dispatcherRef); } - _calculateThemeState() { - // We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we - // show the right values for things. - - const themeChoice = SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"); - const systemThemeExplicit = SettingsStore.getValueAt( - SettingLevel.DEVICE, "use_system_theme", null, false, true); - const themeExplicit = SettingsStore.getValueAt( - SettingLevel.DEVICE, "theme", null, false, true); - - // If the user has enabled system theme matching, use that. - if (systemThemeExplicit) { - return { - theme: themeChoice, - useSystemTheme: true, - }; - } - - // If the user has set a theme explicitly, use that (no system theme matching) - if (themeExplicit) { - return { - theme: themeChoice, - useSystemTheme: false, - }; - } - - // Otherwise assume the defaults for the settings - return { - theme: themeChoice, - useSystemTheme: SettingsStore.getValueAt(SettingLevel.DEVICE, "use_system_theme"), - }; - } - _onAction = (payload) => { if (payload.action === 'id_server_changed') { this.setState({haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl())}); @@ -214,33 +176,6 @@ export default class GeneralUserSettingsTab extends React.Component { PlatformPeg.get().reload(); }; - _onThemeChange = (e) => { - const newTheme = e.target.value; - if (this.state.theme === newTheme) return; - - // doing getValue in the .catch will still return the value we failed to set, - // so remember what the value was before we tried to set it so we can revert - const oldTheme = SettingsStore.getValue('theme'); - SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme).catch(() => { - dis.dispatch({action: 'recheck_theme'}); - this.setState({theme: oldTheme}); - }); - this.setState({theme: newTheme}); - // The settings watcher doesn't fire until the echo comes back from the - // server, so to make the theme change immediately we need to manually - // do the dispatch now - // XXX: The local echoed value appears to be unreliable, in particular - // when settings custom themes(!) so adding forceTheme to override - // the value from settings. - dis.dispatch({action: 'recheck_theme', forceTheme: newTheme}); - }; - - _onUseSystemThemeChanged = (checked) => { - this.setState({useSystemTheme: checked}); - SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, checked); - dis.dispatch({action: 'recheck_theme'}); - }; - _onPasswordChangeError = (err) => { // TODO: Figure out a design that doesn't involve replacing the current dialog let errMsg = err.error || ""; @@ -277,41 +212,6 @@ export default class GeneralUserSettingsTab extends React.Component { }); }; - _onAddCustomTheme = async () => { - let currentThemes = SettingsStore.getValue("custom_themes"); - if (!currentThemes) currentThemes = []; - currentThemes = currentThemes.map(c => c); // cheap clone - - if (this._themeTimer) { - clearTimeout(this._themeTimer); - } - - try { - const r = await fetch(this.state.customThemeUrl); - const themeInfo = await r.json(); - if (!themeInfo || typeof(themeInfo['name']) !== 'string' || typeof(themeInfo['colors']) !== 'object') { - this.setState({customThemeMessage: {text: _t("Invalid theme schema."), isError: true}}); - return; - } - currentThemes.push(themeInfo); - } catch (e) { - console.error(e); - this.setState({customThemeMessage: {text: _t("Error downloading theme information."), isError: true}}); - return; // Don't continue on error - } - - await SettingsStore.setValue("custom_themes", null, SettingLevel.ACCOUNT, currentThemes); - this.setState({customThemeUrl: "", customThemeMessage: {text: _t("Theme added!"), isError: false}}); - - this._themeTimer = setTimeout(() => { - this.setState({customThemeMessage: {text: "", isError: false}}); - }, 3000); - }; - - _onCustomThemeChange = (e) => { - this.setState({customThemeUrl: e.target.value}); - }; - _renderProfileSection() { return ( <div className="mx_SettingsTab_section"> @@ -391,77 +291,6 @@ export default class GeneralUserSettingsTab extends React.Component { ); } - _renderThemeSection() { - const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); - const LabelledToggleSwitch = sdk.getComponent("views.elements.LabelledToggleSwitch"); - - const themeWatcher = new ThemeWatcher(); - let systemThemeSection; - if (themeWatcher.isSystemThemeSupported()) { - systemThemeSection = <div> - <LabelledToggleSwitch - value={this.state.useSystemTheme} - label={SettingsStore.getDisplayName("use_system_theme")} - onChange={this._onUseSystemThemeChanged} - /> - </div>; - } - - let customThemeForm; - if (SettingsStore.isFeatureEnabled("feature_custom_themes")) { - let messageElement = null; - if (this.state.customThemeMessage.text) { - if (this.state.customThemeMessage.isError) { - messageElement = <div className='text-error'>{this.state.customThemeMessage.text}</div>; - } else { - messageElement = <div className='text-success'>{this.state.customThemeMessage.text}</div>; - } - } - customThemeForm = ( - <div className='mx_SettingsTab_section'> - <form onSubmit={this._onAddCustomTheme}> - <Field - label={_t("Custom theme URL")} - type='text' - autoComplete="off" - onChange={this._onCustomThemeChange} - value={this.state.customThemeUrl} - /> - <AccessibleButton - onClick={this._onAddCustomTheme} - type="submit" kind="primary_sm" - disabled={!this.state.customThemeUrl.trim()} - >{_t("Add theme")}</AccessibleButton> - {messageElement} - </form> - </div> - ); - } - - const themes = Object.entries(enumerateThemes()) - .map(p => ({id: p[0], name: p[1]})); // convert pairs to objects for code readability - const builtInThemes = themes.filter(p => !p.id.startsWith("custom-")); - const customThemes = themes.filter(p => !builtInThemes.includes(p)) - .sort((a, b) => a.name.localeCompare(b.name)); - const orderedThemes = [...builtInThemes, ...customThemes]; - return ( - <div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_themeSection"> - <span className="mx_SettingsTab_subheading">{_t("Theme")}</span> - {systemThemeSection} - <Field label={_t("Theme")} element="select" - value={this.state.theme} onChange={this._onThemeChange} - disabled={this.state.useSystemTheme} - > - {orderedThemes.map(theme => { - return <option key={theme.id} value={theme.id}>{theme.name}</option>; - })} - </Field> - {customThemeForm} - <SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} /> - </div> - ); - } - _renderDiscoverySection() { const SetIdServer = sdk.getComponent("views.settings.SetIdServer"); @@ -547,7 +376,6 @@ export default class GeneralUserSettingsTab extends React.Component { {this._renderProfileSection()} {this._renderAccountSection()} {this._renderLanguageSection()} - {this._renderThemeSection()} <div className="mx_SettingsTab_heading">{discoWarning} {_t("Discovery")}</div> {this._renderDiscoverySection()} {this._renderIntegrationManagerSection() /* Has its own title */} diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js new file mode 100644 index 0000000000..5c68f214d4 --- /dev/null +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -0,0 +1,233 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import {_t} from "../../../../../languageHandler"; +import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore"; +import * as sdk from "../../../../../index"; +import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; +import Field from "../../../elements/Field"; + +export default class StyleUserSettingsTab extends React.Component { + constructor() { + super(); + + this.state = { + fontSize: SettingsStore.getValue("font_size", null), + ...this._calculateThemeState(), + customThemeUrl: "", + customThemeMessage: {isError: false, text: ""}, + } + } + + _calculateThemeState() { + // We have to mirror the logic from ThemeWatcher.getEffectiveTheme so we + // show the right values for things. + + const themeChoice = SettingsStore.getValueAt(SettingLevel.ACCOUNT, "theme"); + const systemThemeExplicit = SettingsStore.getValueAt( + SettingLevel.DEVICE, "use_system_theme", null, false, true); + const themeExplicit = SettingsStore.getValueAt( + SettingLevel.DEVICE, "theme", null, false, true); + + // If the user has enabled system theme matching, use that. + if (systemThemeExplicit) { + return { + theme: themeChoice, + useSystemTheme: true, + }; + } + + // If the user has set a theme explicitly, use that (no system theme matching) + if (themeExplicit) { + return { + theme: themeChoice, + useSystemTheme: false, + }; + } + + // Otherwise assume the defaults for the settings + return { + theme: themeChoice, + useSystemTheme: SettingsStore.getValueAt(SettingLevel.DEVICE, "use_system_theme"), + }; + } + + _onThemeChange = (e) => { + const newTheme = e.target.value; + if (this.state.theme === newTheme) return; + + // doing getValue in the .catch will still return the value we failed to set, + // so remember what the value was before we tried to set it so we can revert + const oldTheme = SettingsStore.getValue('theme'); + SettingsStore.setValue("theme", null, SettingLevel.ACCOUNT, newTheme).catch(() => { + dis.dispatch({action: 'recheck_theme'}); + this.setState({theme: oldTheme}); + }); + this.setState({theme: newTheme}); + // The settings watcher doesn't fire until the echo comes back from the + // server, so to make the theme change immediately we need to manually + // do the dispatch now + // XXX: The local echoed value appears to be unreliable, in particular + // when settings custom themes(!) so adding forceTheme to override + // the value from settings. + dis.dispatch({action: 'recheck_theme', forceTheme: newTheme}); + }; + + _onUseSystemThemeChanged = (checked) => { + this.setState({useSystemTheme: checked}); + SettingsStore.setValue("use_system_theme", null, SettingLevel.DEVICE, checked); + dis.dispatch({action: 'recheck_theme'}); + }; + + _onFontSizeChanged = (size) => { + let parsed_size = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); + this.setState({fontSize: parsed_size}) + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsed_size) + }; + + _onAddCustomTheme = async () => { + let currentThemes = SettingsStore.getValue("custom_themes"); + if (!currentThemes) currentThemes = []; + currentThemes = currentThemes.map(c => c); // cheap clone + + if (this._themeTimer) { + clearTimeout(this._themeTimer); + } + + try { + const r = await fetch(this.state.customThemeUrl); + const themeInfo = await r.json(); + if (!themeInfo || typeof(themeInfo['name']) !== 'string' || typeof(themeInfo['colors']) !== 'object') { + this.setState({customThemeMessage: {text: _t("Invalid theme schema."), isError: true}}); + return; + } + currentThemes.push(themeInfo); + } catch (e) { + console.error(e); + this.setState({customThemeMessage: {text: _t("Error downloading theme information."), isError: true}}); + return; // Don't continue on error + } + + await SettingsStore.setValue("custom_themes", null, SettingLevel.ACCOUNT, currentThemes); + this.setState({customThemeUrl: "", customThemeMessage: {text: _t("Theme added!"), isError: false}}); + + this._themeTimer = setTimeout(() => { + this.setState({customThemeMessage: {text: "", isError: false}}); + }, 3000); + }; + + _onCustomThemeChange = (e) => { + this.setState({customThemeUrl: e.target.value}); + }; + + render() { + return ( + <div className="mx_SettingsTab"> + <div className="mx_SettingsTab_heading">{_t("Style")}</div> + {this._renderThemeSection()} + {this._renderFontSection()} + </div> + ); + } + + _renderThemeSection() { + const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); + const LabelledToggleSwitch = sdk.getComponent("views.elements.LabelledToggleSwitch"); + + const themeWatcher = new ThemeWatcher(); + let systemThemeSection; + if (themeWatcher.isSystemThemeSupported()) { + systemThemeSection = <div> + <LabelledToggleSwitch + value={this.state.useSystemTheme} + label={SettingsStore.getDisplayName("use_system_theme")} + onChange={this._onUseSystemThemeChanged} + /> + </div>; + } + + let customThemeForm; + if (SettingsStore.isFeatureEnabled("feature_custom_themes")) { + let messageElement = null; + if (this.state.customThemeMessage.text) { + if (this.state.customThemeMessage.isError) { + messageElement = <div className='text-error'>{this.state.customThemeMessage.text}</div>; + } else { + messageElement = <div className='text-success'>{this.state.customThemeMessage.text}</div>; + } + } + customThemeForm = ( + <div className='mx_SettingsTab_section'> + <form onSubmit={this._onAddCustomTheme}> + <Field + label={_t("Custom theme URL")} + type='text' + id='mx_GeneralUserSettingsTab_customThemeInput' + autoComplete="off" + onChange={this._onCustomThemeChange} + value={this.state.customThemeUrl} + /> + <AccessibleButton + onClick={this._onAddCustomTheme} + type="submit" kind="primary_sm" + disabled={!this.state.customThemeUrl.trim()} + >{_t("Add theme")}</AccessibleButton> + {messageElement} + </form> + </div> + ); + } + + const themes = Object.entries(enumerateThemes()) + .map(p => ({id: p[0], name: p[1]})); // convert pairs to objects for code readability + const builtInThemes = themes.filter(p => !p.id.startsWith("custom-")); + const customThemes = themes.filter(p => !builtInThemes.includes(p)) + .sort((a, b) => a.name.localeCompare(b.name)); + const orderedThemes = [...builtInThemes, ...customThemes]; + return ( + <div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_themeSection"> + <span className="mx_SettingsTab_subheading">{_t("Theme")}</span> + {systemThemeSection} + <Field id="theme" label={_t("Theme")} element="select" + value={this.state.theme} onChange={this._onThemeChange} + disabled={this.state.useSystemTheme} + > + {orderedThemes.map(theme => { + return <option key={theme.id} value={theme.id}>{theme.name}</option>; + })} + </Field> + {customThemeForm} + <SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} /> + </div> + ); + } + + _renderFontSection() { + return <div className="mx_SettingsTab_section"> + <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> + <Field + type="text" + label={_t("Font size")} + autoComplete="off" + placeholder={SettingsStore.getValue("font_size", null).toString()} + id="font_size_field" + onChange={(ev) => this._onFontSizeChanged(ev.target.value)} + /> + </div> + } + +} diff --git a/src/theme.js b/src/theme.js index 442a89e25f..3309acdd01 100644 --- a/src/theme.js +++ b/src/theme.js @@ -81,7 +81,7 @@ export class ThemeWatcher { } getEffectiveTheme() { - // Dev note: Much of this logic is replicated in the GeneralUserSettingsTab + // Dev note: Much of this logic is replicated in the StyleUserSettingsTab // XXX: checking the isLight flag here makes checking it in the ThemeController // itself completely redundant since we just override the result here and we're From bce1fa0c1a5277e87b46470055017f7f8a49d32e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:20:08 +0100 Subject: [PATCH 031/196] Simple translations --- src/i18n/strings/en_EN.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3eac055054..f10da54729 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2293,5 +2293,7 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End" + "End": "End", + "Font size": "Font size", + "Style": "Style" } From c1827925dad16eaac32c784dbbb13d7530dc7dd2 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:20:41 +0100 Subject: [PATCH 032/196] Use new style tab --- src/components/views/dialogs/UserSettingsDialog.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index b135d5f5f6..ee6f7e13ec 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -22,6 +22,7 @@ import {_t, _td} from "../../../languageHandler"; import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab"; import SettingsStore from "../../../settings/SettingsStore"; import LabsUserSettingsTab from "../settings/tabs/user/LabsUserSettingsTab"; +import StyleUserSettingsTab from "../settings/tabs/user/StyleUserSettingsTab"; import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab"; import NotificationUserSettingsTab from "../settings/tabs/user/NotificationUserSettingsTab"; import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSettingsTab"; @@ -66,6 +67,11 @@ export default class UserSettingsDialog extends React.Component { "mx_UserSettingsDialog_settingsIcon", <GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />, )); + tabs.push(new Tab( + _td("Style"), + "mx_userSettingsDialog_styleIcon", + <StyleUserSettingsTab />, + )); tabs.push(new Tab( _td("Flair"), "mx_UserSettingsDialog_flairIcon", From af4dd2770c4cc3da7ef20617c2bb4fdb3e174a52 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 14 Apr 2020 16:21:04 +0100 Subject: [PATCH 033/196] Respond to font size changes --- src/components/structures/MatrixChat.js | 4 ++ src/fontSize.js | 50 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/fontSize.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1293ccc7e9..1845e0011d 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -66,6 +66,7 @@ import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; +import { FontWatcher } from '../../fontSize'; /** constants for MatrixChat.state.view */ export const VIEWS = { @@ -265,7 +266,9 @@ export default createReactClass({ this.dispatcherRef = dis.register(this.onAction); this._themeWatcher = new ThemeWatcher(); + this._fontWatcher = new FontWatcher(10, 20); this._themeWatcher.start(); + this._fontWatcher.start(); this.focusComposer = false; @@ -353,6 +356,7 @@ export default createReactClass({ Lifecycle.stopMatrixClient(); dis.unregister(this.dispatcherRef); this._themeWatcher.stop(); + this._fontWatcher.stop(); window.removeEventListener("focus", this.onFocus); window.removeEventListener('resize', this.handleResize); this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); diff --git a/src/fontSize.js b/src/fontSize.js new file mode 100644 index 0000000000..c242fcc743 --- /dev/null +++ b/src/fontSize.js @@ -0,0 +1,50 @@ +/* +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 dis from './dispatcher'; +import SettingsStore from './settings/SettingsStore'; + +export class FontWatcher { + constructor(min_size, max_size) { + this._min_size = min_size; + this._max_size = max_size; + this._dispatcherRef = null; + } + + start() { + this._setRootFontSize(SettingsStore.getValue("font_size")); + this._dispatcherRef = dis.register(this._onAction); + } + + stop() { + dis.unregister(this._dispatcherRef); + } + + _onAction = (payload) => { + if (payload.action === 'update-font-size') { + this._setRootFontSize(payload.size); + } + }; + + _setRootFontSize = size => { + let fontSize = this._min_size < size?size:this._min_size; + fontSize = fontSize < this._max_size?fontSize:this._max_size; + if (fontSize != size) { + SettingsStore.setValue("font_size", null, fontSize) + } + document.querySelector(":root").style.fontSize = fontSize + "px"; + } +} \ No newline at end of file From 14551b1885bc0d309e284d0293b7ad0f042e9927 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 10:28:07 +0100 Subject: [PATCH 034/196] Hide font scaling behind labs --- .../views/settings/tabs/user/StyleUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index 5c68f214d4..ab9107eac0 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -139,7 +139,7 @@ export default class StyleUserSettingsTab extends React.Component { <div className="mx_SettingsTab"> <div className="mx_SettingsTab_heading">{_t("Style")}</div> {this._renderThemeSection()} - {this._renderFontSection()} + {SettingsStore.getValue("feature_font_scaling") ? this._renderFontSection() : null} </div> ); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f10da54729..bf2eaa4652 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2295,5 +2295,6 @@ "Space": "Space", "End": "End", "Font size": "Font size", + "Font scaling": "Font scaling", "Style": "Style" } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 3b316e39d0..23b73f740b 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -95,6 +95,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "feature_font_scaling": { + isFeature: true, + displayName: _td("Font scaling"), + supportedLevels: LEVELS_FEATURE, + default: false + }, "font_size": { displayName: _td("Font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 0faf7b865f47a4fc28a0339d999f6592c501e045 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 10:31:40 +0100 Subject: [PATCH 035/196] Set font option width --- .../tabs/user/_GeneralUserSettingsTab.scss | 3 ++- .../tabs/user/_StyleUserSettingsTab.scss | 20 +++++++++++++++++++ .../tabs/user/StyleUserSettingsTab.js | 4 ++-- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 62d230e752..45aecd032f 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -15,7 +15,8 @@ limitations under the License. */ .mx_GeneralUserSettingsTab_changePassword .mx_Field, -.mx_GeneralUserSettingsTab_themeSection .mx_Field { +.mx_StyleUserSettingsTab_themeSection .mx_Field, +.mx_StyleUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } diff --git a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss new file mode 100644 index 0000000000..dd9646bd5a --- /dev/null +++ b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss @@ -0,0 +1,20 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_StyleUserSettingsTab_themeSection .mx_Field, +.mx_StyleUserSettingsTab_fontScaling .mx_Field { + @mixin mx_Settings_fullWidthField; +} \ No newline at end of file diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index ab9107eac0..e7b7385f5a 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -199,7 +199,7 @@ export default class StyleUserSettingsTab extends React.Component { .sort((a, b) => a.name.localeCompare(b.name)); const orderedThemes = [...builtInThemes, ...customThemes]; return ( - <div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_themeSection"> + <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_themeSection"> <span className="mx_SettingsTab_subheading">{_t("Theme")}</span> {systemThemeSection} <Field id="theme" label={_t("Theme")} element="select" @@ -217,7 +217,7 @@ export default class StyleUserSettingsTab extends React.Component { } _renderFontSection() { - return <div className="mx_SettingsTab_section"> + return <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> <Field type="text" From df73f12320b50cc97f0e7a9535a422354dbce5c5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 10:33:23 +0100 Subject: [PATCH 036/196] Make a font slider --- res/css/_components.scss | 1 + res/css/structures/_FontSlider.scss | 88 +++++++++++++++++++++++++ res/themes/light/css/_light.scss | 4 ++ src/components/structures/FontSlider.js | 62 +++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 res/css/structures/_FontSlider.scss create mode 100644 src/components/structures/FontSlider.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 0ba2b609e8..9d6629e703 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,6 +7,7 @@ @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; +@import "./structures/_FontSlider.scss"; @import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_FontSlider.scss new file mode 100644 index 0000000000..94902f6fd9 --- /dev/null +++ b/res/css/structures/_FontSlider.scss @@ -0,0 +1,88 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_fontSlider { + position: relative; + margin: 0px; + @mixin mx_Settings_fullWidthField; +} + +.mx_fontSlider_dotContainer { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.mx_fontSlider_bar { + display: flex; + box-sizing: border-box; + position: absolute; + height: 1rem; + width: 100%; + padding: 0 1.1rem; + align-items: center; +} + +.mx_fontSlider_bar > hr { + width: 100%; + border: 0.2rem solid $fontSlider-background-color; +} + +.mx_fontSlider_selection { + display: flex; + align-items: center; + width: calc(100% - 2.2rem); + height: 1rem; + position: absolute; +} + +.mx_fontSlider_selectionDot { + transition: left 0.25s; + position: absolute; + width: 1.1rem; + height: 1.1rem; + background-color: $fontSlider-selection-color; + border-radius: 50%; + box-shadow: 0 0 6px lightgrey; + z-index: 10; +} + +.mx_fontSlider_selection > hr { + transition: width 0.25s; + margin: 0; + border: 0.2rem solid $fontSlider-selection-color; +} + +.mx_fontSlider_dot { + transition: background-color 0.2s ease-in; + height: 1rem; + width: 1rem; + border-radius: 50%; + background-color: $fontSlider-background-color; + margin-bottom: 5px; + z-index: 0; +} + +.mx_fontSlider_dotActive { + background-color: $fontSlider-selection-color; +} + +.mx_fontSlider_dotValue { + display: flex; + flex-direction: column; + align-items: center; + color: $fontSlider-background-color; +} \ No newline at end of file diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index f5f3013354..b457a8bccb 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -262,6 +262,10 @@ $togglesw-off-color: #c1c9d6; $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; +// FontSlider +$fontSlider-selection-color: $accent-color; +$fontSlider-background-color: #c1c9d6; + $progressbar-color: #000; $room-warning-bg-color: $yellow-background; diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js new file mode 100644 index 0000000000..e2e36c2c9f --- /dev/null +++ b/src/components/structures/FontSlider.js @@ -0,0 +1,62 @@ +/* +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'; + +export default class Slider extends React.Component { + render() { + let dots = this.props.values.map(v => + <Dot active={v<=this.props.value} + value={this.props.displayFunc(v)} + onClick={() => this.props.updateFontSize(v)} + key={v} + />); + + let offset = this.offset(this.props.values, this.props.value); + + return <div className="mx_fontSlider"> + <div> + <div className="mx_fontSlider_bar"> + <hr /> + <div className="mx_fontSlider_selection"> + <div className="mx_fontSlider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%"}} /> + <hr style={{width: offset + "%"}}/> + </div> + </div> + <div className="mx_fontSlider_dotContainer"> + {dots} + </div> + </div> + </div> + } + + offset(values, value) { + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + } +} + +class Dot extends React.Component { + render () { + let className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); + + return <span onClick={this.props.onClick} className="mx_fontSlider_dotValue"> + <div className={className} /> + <div> + {this.props.value} + </div> + </span> + } +} \ No newline at end of file From 8b72756b8d987bb7218867dcf690ead46c0dea14 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 10:33:44 +0100 Subject: [PATCH 037/196] Use the font slider --- .../views/settings/tabs/user/StyleUserSettingsTab.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index e7b7385f5a..d77822ceb7 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -20,6 +20,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; +import FontSlider from "../../../../structures/FontSlider"; export default class StyleUserSettingsTab extends React.Component { constructor() { @@ -219,11 +220,18 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> + <FontSlider + values={[12,14,16,18,20]} + value={this.state.fontSize} + updateFontSize={this._onFontSizeChanged} + displayFunc={value => value + 'px'} + /> <Field type="text" label={_t("Font size")} autoComplete="off" placeholder={SettingsStore.getValue("font_size", null).toString()} + value={this.state.fontSize} id="font_size_field" onChange={(ev) => this._onFontSizeChanged(ev.target.value)} /> From 647d99a17a6ff66816caee01f0258138a7a15493 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 10:33:59 +0100 Subject: [PATCH 038/196] Smooth font-size change transition --- res/css/_common.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 03442ca510..687a238c8e 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,6 +19,7 @@ limitations under the License. @import "./_font-sizes.scss"; :root { + transition: font-size 0.25s; font-size: 15px; } From 66fd0f707fc290db64ba3a729c96ceedd402258d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 10:53:23 +0100 Subject: [PATCH 039/196] Type enforcement and comments --- src/components/structures/FontSlider.js | 35 ++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js index e2e36c2c9f..0ae0092443 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.js @@ -15,12 +15,29 @@ limitations under the License. */ import React from 'react'; +import PropTypes from 'prop-types'; export default class Slider extends React.Component { + + static propTypes = { + // A callback for the new value onclick + updateFontSize: PropTypes.func, + + // The current value of the slider + value: PropTypes.number, + + // The range and values of the slider + // Currently only supports an ascending, constant interval range + values: PropTypes.arrayOf(PropTypes.number), + + // A function for formatting the the values + displayFunc: PropTypes.func, + }; + render() { let dots = this.props.values.map(v => <Dot active={v<=this.props.value} - value={this.props.displayFunc(v)} + label={this.props.displayFunc(v)} onClick={() => this.props.updateFontSize(v)} key={v} />); @@ -42,20 +59,32 @@ export default class Slider extends React.Component { </div> </div> } - + offset(values, value) { return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } } class Dot extends React.Component { + + static propTypes = { + // Callback for behaviour onclick + onClick: PropTypes.func, + + // Whether the dot should appear active + active: PropTypes.bool, + + // The label on the dot + label: PropTypes.string, + } + render () { let className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); return <span onClick={this.props.onClick} className="mx_fontSlider_dotValue"> <div className={className} /> <div> - {this.props.value} + {this.props.label} </div> </span> } From f1130ecba11be12bbc1e1efbf2823106501727e6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 11:56:43 +0100 Subject: [PATCH 040/196] Linting. Finally set up my linter properly --- src/components/structures/FontSlider.js | 28 +++++++++---------- .../tabs/user/StyleUserSettingsTab.js | 16 ++++++----- src/fontSize.js | 10 +++---- .../controllers/FontSizeController.js | 3 +- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js index 0ae0092443..68173345ff 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.js @@ -18,8 +18,8 @@ import React from 'react'; import PropTypes from 'prop-types'; export default class Slider extends React.Component { - static propTypes = { + // A callback for the new value onclick updateFontSize: PropTypes.func, @@ -32,41 +32,41 @@ export default class Slider extends React.Component { // A function for formatting the the values displayFunc: PropTypes.func, + }; render() { - let dots = this.props.values.map(v => - <Dot active={v<=this.props.value} - label={this.props.displayFunc(v)} + const dots = this.props.values.map(v => + <Dot active={v<=this.props.value} + label={this.props.displayFunc(v)} onClick={() => this.props.updateFontSize(v)} key={v} />); - let offset = this.offset(this.props.values, this.props.value); + const offset = this.offset(this.props.values, this.props.value); return <div className="mx_fontSlider"> <div> <div className="mx_fontSlider_bar"> <hr /> <div className="mx_fontSlider_selection"> - <div className="mx_fontSlider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%"}} /> - <hr style={{width: offset + "%"}}/> + <div className="mx_fontSlider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%)"}} /> + <hr style={{width: offset + "%"}} /> </div> </div> <div className="mx_fontSlider_dotContainer"> {dots} </div> </div> - </div> + </div>; } offset(values, value) { - return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } } class Dot extends React.Component { - static propTypes = { // Callback for behaviour onclick onClick: PropTypes.func, @@ -78,14 +78,14 @@ class Dot extends React.Component { label: PropTypes.string, } - render () { - let className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); + render() { + const className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); return <span onClick={this.props.onClick} className="mx_fontSlider_dotValue"> <div className={className} /> <div> {this.props.label} </div> - </span> + </span>; } -} \ No newline at end of file +} diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js index d77822ceb7..9cca0a2ce9 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/StyleUserSettingsTab.js @@ -21,6 +21,8 @@ import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; import FontSlider from "../../../../structures/FontSlider"; +import AccessibleButton from "../../../elements/AccessibleButton"; +import dis from "../../../../../dispatcher"; export default class StyleUserSettingsTab extends React.Component { constructor() { @@ -31,7 +33,8 @@ export default class StyleUserSettingsTab extends React.Component { ...this._calculateThemeState(), customThemeUrl: "", customThemeMessage: {isError: false, text: ""}, - } + + }; } _calculateThemeState() { @@ -95,9 +98,9 @@ export default class StyleUserSettingsTab extends React.Component { }; _onFontSizeChanged = (size) => { - let parsed_size = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); - this.setState({fontSize: parsed_size}) - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsed_size) + const parsedSize = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); + this.setState({fontSize: parsedSize}); + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsedSize); }; _onAddCustomTheme = async () => { @@ -221,7 +224,7 @@ export default class StyleUserSettingsTab extends React.Component { return <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> <FontSlider - values={[12,14,16,18,20]} + values={[12, 14, 16, 18, 20]} value={this.state.fontSize} updateFontSize={this._onFontSizeChanged} displayFunc={value => value + 'px'} @@ -235,7 +238,6 @@ export default class StyleUserSettingsTab extends React.Component { id="font_size_field" onChange={(ev) => this._onFontSizeChanged(ev.target.value)} /> - </div> + </div>; } - } diff --git a/src/fontSize.js b/src/fontSize.js index c242fcc743..8dbdb29102 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -18,9 +18,9 @@ import dis from './dispatcher'; import SettingsStore from './settings/SettingsStore'; export class FontWatcher { - constructor(min_size, max_size) { - this._min_size = min_size; - this._max_size = max_size; + constructor(minSize, maxSize) { + this._min_size = minSize; + this._max_size = maxSize; this._dispatcherRef = null; } @@ -43,8 +43,8 @@ export class FontWatcher { let fontSize = this._min_size < size?size:this._min_size; fontSize = fontSize < this._max_size?fontSize:this._max_size; if (fontSize != size) { - SettingsStore.setValue("font_size", null, fontSize) + SettingsStore.setValue("font_size", null, fontSize); } document.querySelector(":root").style.fontSize = fontSize + "px"; } -} \ No newline at end of file +} diff --git a/src/settings/controllers/FontSizeController.js b/src/settings/controllers/FontSizeController.js index 4ab2f331f1..8e855e31ec 100644 --- a/src/settings/controllers/FontSizeController.js +++ b/src/settings/controllers/FontSizeController.js @@ -18,9 +18,8 @@ import SettingController from "./SettingController"; import dis from "../../dispatcher"; export default class FontSizeController extends SettingController { - constructor() { - super() + super(); } onChange(level, roomId, newValue) { From af3858fa98ccc5ff93e2e63b372b095626126390 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 12:09:36 +0100 Subject: [PATCH 041/196] Style linting --- res/css/structures/_FontSlider.scss | 3 ++- res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_FontSlider.scss index 94902f6fd9..fc83bd91bc 100644 --- a/res/css/structures/_FontSlider.scss +++ b/res/css/structures/_FontSlider.scss @@ -17,6 +17,7 @@ limitations under the License. .mx_fontSlider { position: relative; margin: 0px; + @mixin mx_Settings_fullWidthField; } @@ -85,4 +86,4 @@ limitations under the License. flex-direction: column; align-items: center; color: $fontSlider-background-color; -} \ No newline at end of file +} diff --git a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss index dd9646bd5a..f2a98ac426 100644 --- a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss @@ -17,4 +17,4 @@ limitations under the License. .mx_StyleUserSettingsTab_themeSection .mx_Field, .mx_StyleUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; -} \ No newline at end of file +} From 7c9df04d427d419c8df747eaef69e11e988e27a6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 20:26:04 +0100 Subject: [PATCH 042/196] Use "Appearance" instead of "Style" --- src/components/views/dialogs/UserSettingsDialog.js | 6 +++--- ...StyleUserSettingsTab.js => AppearanceUserSettingsTab.js} | 2 +- src/i18n/strings/en_EN.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/components/views/settings/tabs/user/{StyleUserSettingsTab.js => AppearanceUserSettingsTab.js} (99%) diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index ee6f7e13ec..91ab203753 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -22,7 +22,7 @@ import {_t, _td} from "../../../languageHandler"; import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab"; import SettingsStore from "../../../settings/SettingsStore"; import LabsUserSettingsTab from "../settings/tabs/user/LabsUserSettingsTab"; -import StyleUserSettingsTab from "../settings/tabs/user/StyleUserSettingsTab"; +import AppearanceUserSettingsTab from "../settings/tabs/user/AppearanceUserSettingsTab"; import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab"; import NotificationUserSettingsTab from "../settings/tabs/user/NotificationUserSettingsTab"; import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSettingsTab"; @@ -68,9 +68,9 @@ export default class UserSettingsDialog extends React.Component { <GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />, )); tabs.push(new Tab( - _td("Style"), + _td("Appearance"), "mx_userSettingsDialog_styleIcon", - <StyleUserSettingsTab />, + <AppearanceUserSettingsTab />, )); tabs.push(new Tab( _td("Flair"), diff --git a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js similarity index 99% rename from src/components/views/settings/tabs/user/StyleUserSettingsTab.js rename to src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 9cca0a2ce9..d09f4b3e6a 100644 --- a/src/components/views/settings/tabs/user/StyleUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -141,7 +141,7 @@ export default class StyleUserSettingsTab extends React.Component { render() { return ( <div className="mx_SettingsTab"> - <div className="mx_SettingsTab_heading">{_t("Style")}</div> + <div className="mx_SettingsTab_heading">{_t("Appearance")}</div> {this._renderThemeSection()} {SettingsStore.getValue("feature_font_scaling") ? this._renderFontSection() : null} </div> diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bf2eaa4652..56798ff932 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2296,5 +2296,5 @@ "End": "End", "Font size": "Font size", "Font scaling": "Font scaling", - "Style": "Style" + "Appearance": "Appearance" } From b1452b5aa3bdd971aa2fa24c3bbd672ecd5d5255 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 20:47:29 +0100 Subject: [PATCH 043/196] Lint lint lint --- src/settings/Settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 23b73f740b..dd0103aa78 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -99,7 +99,7 @@ export const SETTINGS = { isFeature: true, displayName: _td("Font scaling"), supportedLevels: LEVELS_FEATURE, - default: false + default: false, }, "font_size": { displayName: _td("Font size"), From e455473d8fe1fba82c7c00603d746f8930b013e2 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 16 Apr 2020 20:54:11 +0100 Subject: [PATCH 044/196] i18n happy --- src/i18n/strings/en_EN.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 56798ff932..cff2b8cda4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -395,6 +395,8 @@ "Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.", "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", + "Font scaling": "Font scaling", + "Font size": "Font size", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", @@ -742,22 +744,23 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.", "Manage integrations": "Manage integrations", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", + "Invalid theme schema.": "Invalid theme schema.", + "Error downloading theme information.": "Error downloading theme information.", + "Theme added!": "Theme added!", + "Appearance": "Appearance", + "Custom theme URL": "Custom theme URL", + "Add theme": "Add theme", + "Theme": "Theme", "Flair": "Flair", "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Success": "Success", "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them", - "Invalid theme schema.": "Invalid theme schema.", - "Error downloading theme information.": "Error downloading theme information.", - "Theme added!": "Theme added!", "Profile": "Profile", "Email addresses": "Email addresses", "Phone numbers": "Phone numbers", "Set a new account password...": "Set a new account password...", "Account": "Account", "Language and region": "Language and region", - "Custom theme URL": "Custom theme URL", - "Add theme": "Add theme", - "Theme": "Theme", "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.", "Account management": "Account management", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", @@ -2293,8 +2296,5 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End", - "Font size": "Font size", - "Font scaling": "Font scaling", - "Appearance": "Appearance" + "End": "End" } From 787e408016e53a3293d1df336e0e2a5fc1ac50c5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 10:08:40 +0100 Subject: [PATCH 045/196] Explain origin of magic number Co-Authored-By: Travis Ralston <travpc@gmail.com> --- res/css/structures/_TagPanel.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 4a78c8df92..536c88be63 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -116,7 +116,7 @@ limitations under the License. position: absolute; left: -15px; border-radius: 0 3px 3px 0; - top: -8px; // (16px / 2) + top: -8px; // (16px from height / 2) } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { From 4d0cac1260af9dd9f4cdd930de47a202f0bee65b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 10:15:37 +0100 Subject: [PATCH 046/196] Render should be last method declared --- src/components/structures/FontSlider.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.js index 68173345ff..aa4bfe42f5 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.js @@ -35,6 +35,10 @@ export default class Slider extends React.Component { }; + _offset(values, value) { + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + } + render() { const dots = this.props.values.map(v => <Dot active={v<=this.props.value} @@ -43,7 +47,7 @@ export default class Slider extends React.Component { key={v} />); - const offset = this.offset(this.props.values, this.props.value); + const offset = this._offset(this.props.values, this.props.value); return <div className="mx_fontSlider"> <div> @@ -60,10 +64,6 @@ export default class Slider extends React.Component { </div> </div>; } - - offset(values, value) { - return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; - } } class Dot extends React.Component { From db1141b162e441ddf324c4e708f7c7c3c55297d9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 10:46:33 +0100 Subject: [PATCH 047/196] Move to typescript --- .../{FontSlider.js => FontSlider.tsx} | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) rename src/components/structures/{FontSlider.js => FontSlider.tsx} (73%) diff --git a/src/components/structures/FontSlider.js b/src/components/structures/FontSlider.tsx similarity index 73% rename from src/components/structures/FontSlider.js rename to src/components/structures/FontSlider.tsx index aa4bfe42f5..9048e7b37b 100644 --- a/src/components/structures/FontSlider.js +++ b/src/components/structures/FontSlider.tsx @@ -14,10 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; +import * as PropTypes from 'prop-types'; -export default class Slider extends React.Component { +type SliderProps = { + // A callback for the new value onclick + updateFontSize: (size: number) => null; + + // The current value of the slider + value: number; + + // The range and values of the slider + // Currently only supports an ascending, constant interval range + values: number[]; + + // A function for formatting the the values + displayFunc: (value: number) => string; + +} + +export default class Slider extends React.Component<SliderProps> { static propTypes = { // A callback for the new value onclick @@ -35,11 +51,11 @@ export default class Slider extends React.Component { }; - _offset(values, value) { + _offset(values: number[], value: number): number { return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } - render() { + render(): React.ReactNode { const dots = this.props.values.map(v => <Dot active={v<=this.props.value} label={this.props.displayFunc(v)} @@ -66,7 +82,18 @@ export default class Slider extends React.Component { } } -class Dot extends React.Component { +type DotProps = { + // Callback for behaviour onclick + onClick: () => null, + + // Whether the dot should appear active + active: boolean, + + // The label on the dot + label: string, +} + +class Dot extends React.Component<DotProps> { static propTypes = { // Callback for behaviour onclick onClick: PropTypes.func, @@ -78,7 +105,7 @@ class Dot extends React.Component { label: PropTypes.string, } - render() { + render(): React.ReactNode { const className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); return <span onClick={this.props.onClick} className="mx_fontSlider_dotValue"> From dd841fcde92f097a0cc056fa8f856d65c938620e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:01:52 +0100 Subject: [PATCH 048/196] Remove references to font --- res/css/structures/_FontSlider.scss | 20 ++++++++++---------- src/components/structures/FontSlider.tsx | 22 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_FontSlider.scss index fc83bd91bc..2112ac9a88 100644 --- a/res/css/structures/_FontSlider.scss +++ b/res/css/structures/_FontSlider.scss @@ -14,20 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_fontSlider { +.mx_Slider { position: relative; margin: 0px; @mixin mx_Settings_fullWidthField; } -.mx_fontSlider_dotContainer { +.mx_Slider_dotContainer { display: flex; flex-direction: row; justify-content: space-between; } -.mx_fontSlider_bar { +.mx_Slider_bar { display: flex; box-sizing: border-box; position: absolute; @@ -37,12 +37,12 @@ limitations under the License. align-items: center; } -.mx_fontSlider_bar > hr { +.mx_Slider_bar > hr { width: 100%; border: 0.2rem solid $fontSlider-background-color; } -.mx_fontSlider_selection { +.mx_Slider_selection { display: flex; align-items: center; width: calc(100% - 2.2rem); @@ -50,7 +50,7 @@ limitations under the License. position: absolute; } -.mx_fontSlider_selectionDot { +.mx_Slider_selectionDot { transition: left 0.25s; position: absolute; width: 1.1rem; @@ -61,13 +61,13 @@ limitations under the License. z-index: 10; } -.mx_fontSlider_selection > hr { +.mx_Slider_selection > hr { transition: width 0.25s; margin: 0; border: 0.2rem solid $fontSlider-selection-color; } -.mx_fontSlider_dot { +.mx_Slider_dot { transition: background-color 0.2s ease-in; height: 1rem; width: 1rem; @@ -77,11 +77,11 @@ limitations under the License. z-index: 0; } -.mx_fontSlider_dotActive { +.mx_Slider_dotActive { background-color: $fontSlider-selection-color; } -.mx_fontSlider_dotValue { +.mx_Slider_dotValue { display: flex; flex-direction: column; align-items: center; diff --git a/src/components/structures/FontSlider.tsx b/src/components/structures/FontSlider.tsx index 9048e7b37b..7985fa206a 100644 --- a/src/components/structures/FontSlider.tsx +++ b/src/components/structures/FontSlider.tsx @@ -18,8 +18,8 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; type SliderProps = { - // A callback for the new value onclick - updateFontSize: (size: number) => null; + // A callback for the selected value + onSelectionChange: (value: number) => null; // The current value of the slider value: number; @@ -37,7 +37,7 @@ export default class Slider extends React.Component<SliderProps> { static propTypes = { // A callback for the new value onclick - updateFontSize: PropTypes.func, + onSelectionChange: PropTypes.func, // The current value of the slider value: PropTypes.number, @@ -59,22 +59,22 @@ export default class Slider extends React.Component<SliderProps> { const dots = this.props.values.map(v => <Dot active={v<=this.props.value} label={this.props.displayFunc(v)} - onClick={() => this.props.updateFontSize(v)} + onClick={() => this.props.onSelectionChange(v)} key={v} />); const offset = this._offset(this.props.values, this.props.value); - return <div className="mx_fontSlider"> + return <div className="mx_Slider"> <div> - <div className="mx_fontSlider_bar"> + <div className="mx_Slider_bar"> <hr /> - <div className="mx_fontSlider_selection"> - <div className="mx_fontSlider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%)"}} /> + <div className="mx_Slider_selection"> + <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%)"}} /> <hr style={{width: offset + "%"}} /> </div> </div> - <div className="mx_fontSlider_dotContainer"> + <div className="mx_Slider_dotContainer"> {dots} </div> </div> @@ -106,9 +106,9 @@ class Dot extends React.Component<DotProps> { } render(): React.ReactNode { - const className = "mx_fontSlider_dot" + (this.props.active? " mx_fontSlider_dotActive": ""); + const className = "mx_Slider_dot" + (this.props.active? " mx_Slider_dotActive": ""); - return <span onClick={this.props.onClick} className="mx_fontSlider_dotValue"> + return <span onClick={this.props.onClick} className="mx_Slider_dotValue"> <div className={className} /> <div> {this.props.label} From abd94a65bd4f5a8e60478fc52d3475318b63b562 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:29:37 +0100 Subject: [PATCH 049/196] Move compoenets/FontSlider to views/Slider --- res/css/_components.scss | 2 +- .../structures/{_FontSlider.scss => _Slider.scss} | 12 ++++++------ res/themes/light/css/_light.scss | 6 +++--- .../FontSlider.tsx => views/elements/Slider.tsx} | 2 ++ .../settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 5 files changed, 14 insertions(+), 12 deletions(-) rename res/css/structures/{_FontSlider.scss => _Slider.scss} (84%) rename src/components/{structures/FontSlider.tsx => views/elements/Slider.tsx} (96%) diff --git a/res/css/_components.scss b/res/css/_components.scss index 9d6629e703..ab602be49e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,7 +7,7 @@ @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; -@import "./structures/_FontSlider.scss"; +@import "./structures/_Slider.scss"; @import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; diff --git a/res/css/structures/_FontSlider.scss b/res/css/structures/_Slider.scss similarity index 84% rename from res/css/structures/_FontSlider.scss rename to res/css/structures/_Slider.scss index 2112ac9a88..51f1688f6b 100644 --- a/res/css/structures/_FontSlider.scss +++ b/res/css/structures/_Slider.scss @@ -39,7 +39,7 @@ limitations under the License. .mx_Slider_bar > hr { width: 100%; - border: 0.2rem solid $fontSlider-background-color; + border: 0.2rem solid $Slider-background-color; } .mx_Slider_selection { @@ -55,7 +55,7 @@ limitations under the License. position: absolute; width: 1.1rem; height: 1.1rem; - background-color: $fontSlider-selection-color; + background-color: $Slider-selection-color; border-radius: 50%; box-shadow: 0 0 6px lightgrey; z-index: 10; @@ -64,7 +64,7 @@ limitations under the License. .mx_Slider_selection > hr { transition: width 0.25s; margin: 0; - border: 0.2rem solid $fontSlider-selection-color; + border: 0.2rem solid $Slider-selection-color; } .mx_Slider_dot { @@ -72,18 +72,18 @@ limitations under the License. height: 1rem; width: 1rem; border-radius: 50%; - background-color: $fontSlider-background-color; + background-color: $Slider-background-color; margin-bottom: 5px; z-index: 0; } .mx_Slider_dotActive { - background-color: $fontSlider-selection-color; + background-color: $Slider-selection-color; } .mx_Slider_dotValue { display: flex; flex-direction: column; align-items: center; - color: $fontSlider-background-color; + color: $Slider-background-color; } diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index b457a8bccb..e06ba33594 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -262,9 +262,9 @@ $togglesw-off-color: #c1c9d6; $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; -// FontSlider -$fontSlider-selection-color: $accent-color; -$fontSlider-background-color: #c1c9d6; +// Slider +$Slider-selection-color: $accent-color; +$Slider-background-color: #c1c9d6; $progressbar-color: #000; diff --git a/src/components/structures/FontSlider.tsx b/src/components/views/elements/Slider.tsx similarity index 96% rename from src/components/structures/FontSlider.tsx rename to src/components/views/elements/Slider.tsx index 7985fa206a..2070f3f167 100644 --- a/src/components/structures/FontSlider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -16,6 +16,7 @@ limitations under the License. import * as React from 'react'; import * as PropTypes from 'prop-types'; +import { replaceableComponent } from '../../../utils/replaceableComponent'; type SliderProps = { // A callback for the selected value @@ -93,6 +94,7 @@ type DotProps = { label: string, } +@replaceableComponent("views.elements.Dot") class Dot extends React.Component<DotProps> { static propTypes = { // Callback for behaviour onclick diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index d09f4b3e6a..9e9f134613 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -20,7 +20,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; -import FontSlider from "../../../../structures/FontSlider"; +import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; @@ -223,7 +223,7 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> - <FontSlider + <Slider values={[12, 14, 16, 18, 20]} value={this.state.fontSize} updateFontSize={this._onFontSizeChanged} From e254675287300f1ccc1fc5e7faadc4264e81bc68 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:34:15 +0100 Subject: [PATCH 050/196] Retain copyright Co-Authored-By: Travis Ralston <travpc@gmail.com> --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 9e9f134613..7843ccbe10 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 82974bd98c01fc673a8fb928f7890c7acd16641b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:37:22 +0100 Subject: [PATCH 051/196] Space out ternaries Co-Authored-By: Travis Ralston <travpc@gmail.com> --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 7843ccbe10..8d1fd348d3 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -99,7 +99,7 @@ export default class StyleUserSettingsTab extends React.Component { }; _onFontSizeChanged = (size) => { - const parsedSize = isNaN(parseInt(size))?SettingsStore.getDefaultValue("font_size"):parseFloat(size); + const parsedSize = isNaN(parseInt(size)) ? SettingsStore.getDefaultValue("font_size") : parseFloat(size); this.setState({fontSize: parsedSize}); SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsedSize); }; From 4525f71b1ce7925379fd9a67fab7b59f52d055b5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:40:18 +0100 Subject: [PATCH 052/196] Missed an import --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 8d1fd348d3..42c8e6d854 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -21,7 +21,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; -import Slider from "../../../elements/Slider"; +import FontSlider from "../../../../structures/FontSlider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; @@ -224,7 +224,7 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> - <Slider + <FontSlider values={[12, 14, 16, 18, 20]} value={this.state.fontSize} updateFontSize={this._onFontSizeChanged} From 02e0ff9e5b526a9d1f6dd9dc4c51c6f17db96ad5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:41:41 +0100 Subject: [PATCH 053/196] Move setting away from 'feature' settings for clarity --- src/settings/Settings.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index dd0103aa78..a044027baf 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -101,12 +101,6 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, - "font_size": { - displayName: _td("Font size"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 16, - controller: new FontSizeController(), - }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), @@ -177,6 +171,12 @@ export const SETTINGS = { displayName: _td("Show padlocks on invite only rooms"), default: true, }, + "font_size": { + displayName: _td("Font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 16, + controller: new FontSizeController(), + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From 4397658bb32654b3a0427d4c1b761c43109e5825 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 11:56:12 +0100 Subject: [PATCH 054/196] Update file name in comments Co-Authored-By: Travis Ralston <travpc@gmail.com> --- src/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme.js b/src/theme.js index 3309acdd01..aee55b5abb 100644 --- a/src/theme.js +++ b/src/theme.js @@ -81,7 +81,7 @@ export class ThemeWatcher { } getEffectiveTheme() { - // Dev note: Much of this logic is replicated in the StyleUserSettingsTab + // Dev note: Much of this logic is replicated in the AppearanceUserSettingsTab // XXX: checking the isLight flag here makes checking it in the ThemeController // itself completely redundant since we just override the result here and we're From 315a272cb4aa1026f42723e7b1877f12e4ecffbc Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 12:03:32 +0100 Subject: [PATCH 055/196] File rename --- ...rSettingsTab.scss => _AppearanceUserSettingsTab.scss} | 4 ++-- .../settings/tabs/user/AppearanceUserSettingsTab.js | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) rename res/css/views/settings/tabs/user/{_StyleUserSettingsTab.scss => _AppearanceUserSettingsTab.scss} (85%) diff --git a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss similarity index 85% rename from res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss rename to res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index f2a98ac426..8c80a35e40 100644 --- a/res/css/views/settings/tabs/user/_StyleUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_StyleUserSettingsTab_themeSection .mx_Field, -.mx_StyleUserSettingsTab_fontScaling .mx_Field { +.mx_AppearanceUserSettingsTab_themeSection .mx_Field, +.mx_AppearanceUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 42c8e6d854..738a5f9178 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -2,6 +2,7 @@ Copyright 2019 New Vector Ltd Copyright 2019, 2020 The Matrix.org Foundation C.I.C. + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -21,7 +22,7 @@ import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore import * as sdk from "../../../../../index"; import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; -import FontSlider from "../../../../structures/FontSlider"; +import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; @@ -204,7 +205,7 @@ export default class StyleUserSettingsTab extends React.Component { .sort((a, b) => a.name.localeCompare(b.name)); const orderedThemes = [...builtInThemes, ...customThemes]; return ( - <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_themeSection"> + <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_themeSection"> <span className="mx_SettingsTab_subheading">{_t("Theme")}</span> {systemThemeSection} <Field id="theme" label={_t("Theme")} element="select" @@ -222,9 +223,9 @@ export default class StyleUserSettingsTab extends React.Component { } _renderFontSection() { - return <div className="mx_SettingsTab_section mx_StyleUserSettingsTab_fontScaling"> + return <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> - <FontSlider + <Slider values={[12, 14, 16, 18, 20]} value={this.state.fontSize} updateFontSize={this._onFontSizeChanged} From 9a585fee0a1611cffb5a4b93d3f0dd7fa81c5bd9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 12:06:10 +0100 Subject: [PATCH 056/196] Move slider themes --- res/css/_components.scss | 3 ++- res/css/{structures => views/elements}/_Slider.scss | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename res/css/{structures => views/elements}/_Slider.scss (100%) diff --git a/res/css/_components.scss b/res/css/_components.scss index ab602be49e..77a9b9f8cf 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -7,7 +7,6 @@ @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; -@import "./structures/_Slider.scss"; @import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; @import "./structures/_HeaderButtons.scss"; @@ -114,6 +113,7 @@ @import "./views/elements/_RichText.scss"; @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_RoomAliasField.scss"; +@import "./views/elements/_Slider.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; @import "./views/elements/_TextWithTooltip.scss"; @@ -202,6 +202,7 @@ @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; @import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss"; +@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss"; @import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss"; @import "./views/settings/tabs/user/_HelpUserSettingsTab.scss"; @import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss"; diff --git a/res/css/structures/_Slider.scss b/res/css/views/elements/_Slider.scss similarity index 100% rename from res/css/structures/_Slider.scss rename to res/css/views/elements/_Slider.scss From dcea1f32b38c1856f05790a2eddcb014a498a486 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 16:18:25 +0100 Subject: [PATCH 057/196] tslint --- src/components/views/elements/Slider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 2070f3f167..f6ab121056 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -58,7 +58,7 @@ export default class Slider extends React.Component<SliderProps> { render(): React.ReactNode { const dots = this.props.values.map(v => - <Dot active={v<=this.props.value} + <Dot active={v <= this.props.value} label={this.props.displayFunc(v)} onClick={() => this.props.onSelectionChange(v)} key={v} @@ -108,7 +108,7 @@ class Dot extends React.Component<DotProps> { } render(): React.ReactNode { - const className = "mx_Slider_dot" + (this.props.active? " mx_Slider_dotActive": ""); + const className = "mx_Slider_dot" + (this.props.active ? " mx_Slider_dotActive" : ""); return <span onClick={this.props.onClick} className="mx_Slider_dotValue"> <div className={className} /> From 715bcb3c96e343b6c2a83d0ba21c16d2257e5be8 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 21 Apr 2020 16:28:41 +0100 Subject: [PATCH 058/196] i18n match file moves --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cff2b8cda4..a3051cbb91 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -396,7 +396,6 @@ "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", "Font scaling": "Font scaling", - "Font size": "Font size", "Message Pinning": "Message Pinning", "Custom user status messages": "Custom user status messages", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", @@ -407,6 +406,7 @@ "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Show padlocks on invite only rooms": "Show padlocks on invite only rooms", + "Font size": "Font size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", From 0d0da6cfdc8d822cdcd8e6c14615aabc41724abf Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 10:19:17 +0100 Subject: [PATCH 059/196] Fix types, abandon propTypes --- src/components/views/elements/Slider.tsx | 45 ++++-------------------- 1 file changed, 7 insertions(+), 38 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index f6ab121056..13f06a4759 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -15,12 +15,10 @@ limitations under the License. */ import * as React from 'react'; -import * as PropTypes from 'prop-types'; -import { replaceableComponent } from '../../../utils/replaceableComponent'; -type SliderProps = { +type IProps = { // A callback for the selected value - onSelectionChange: (value: number) => null; + onSelectionChange: (value: number) => void; // The current value of the slider value: number; @@ -34,24 +32,7 @@ type SliderProps = { } -export default class Slider extends React.Component<SliderProps> { - static propTypes = { - - // A callback for the new value onclick - onSelectionChange: PropTypes.func, - - // The current value of the slider - value: PropTypes.number, - - // The range and values of the slider - // Currently only supports an ascending, constant interval range - values: PropTypes.arrayOf(PropTypes.number), - - // A function for formatting the the values - displayFunc: PropTypes.func, - - }; - +export default class Slider extends React.Component<IProps> { _offset(values: number[], value: number): number { return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } @@ -83,9 +64,9 @@ export default class Slider extends React.Component<SliderProps> { } } -type DotProps = { - // Callback for behaviour onclick - onClick: () => null, +type DotIProps = { + // Callback for behavior onclick + onClick: () => void, // Whether the dot should appear active active: boolean, @@ -94,19 +75,7 @@ type DotProps = { label: string, } -@replaceableComponent("views.elements.Dot") -class Dot extends React.Component<DotProps> { - static propTypes = { - // Callback for behaviour onclick - onClick: PropTypes.func, - - // Whether the dot should appear active - active: PropTypes.bool, - - // The label on the dot - label: PropTypes.string, - } - +class Dot extends React.Component<DotIProps> { render(): React.ReactNode { const className = "mx_Slider_dot" + (this.props.active ? " mx_Slider_dotActive" : ""); From ba362b727c5bdf7b6a76e8fdb3b5cdf1c3afdbc0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 10:19:37 +0100 Subject: [PATCH 060/196] Use onSelectionChange prop --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 738a5f9178..5bb6dcc0e0 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -228,7 +228,7 @@ export default class StyleUserSettingsTab extends React.Component { <Slider values={[12, 14, 16, 18, 20]} value={this.state.fontSize} - updateFontSize={this._onFontSizeChanged} + onSelectionChange={this._onFontSizeChanged} displayFunc={value => value + 'px'} /> <Field From c99e1de803d5813b8d1d4e6c7d4a90e493fa43e6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 10:24:29 +0100 Subject: [PATCH 061/196] Clamp indicated value within value range --- src/components/views/elements/Slider.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 13f06a4759..9f9e1fdef8 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -34,6 +34,17 @@ type IProps = { export default class Slider extends React.Component<IProps> { _offset(values: number[], value: number): number { + const lowest = values[0]; + const highest = values[values.length - 1]; + + if (value < lowest) { + return 0; + } + + if (value > highest) { + return 100; + } + return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; } From 54a65441a54da04f6c4834fe089ac932778d0952 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 10:29:48 +0100 Subject: [PATCH 062/196] Lint ternary statement --- src/fontSize.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontSize.js b/src/fontSize.js index 8dbdb29102..d6b5b020f7 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -40,8 +40,8 @@ export class FontWatcher { }; _setRootFontSize = size => { - let fontSize = this._min_size < size?size:this._min_size; - fontSize = fontSize < this._max_size?fontSize:this._max_size; + let fontSize = this._min_size < size ? size : this._min_size; + fontSize = fontSize < this._max_size ? fontSize : this._max_size; if (fontSize != size) { SettingsStore.setValue("font_size", null, fontSize); } From 8d5965c33c5af0350da208ac8cfe8706dae049ce Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 10:32:00 +0100 Subject: [PATCH 063/196] Fix incorrect call to setValue --- src/fontSize.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fontSize.js b/src/fontSize.js index d6b5b020f7..30c69b7428 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -15,7 +15,7 @@ limitations under the License. */ import dis from './dispatcher'; -import SettingsStore from './settings/SettingsStore'; +import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { constructor(minSize, maxSize) { @@ -43,7 +43,7 @@ export class FontWatcher { let fontSize = this._min_size < size ? size : this._min_size; fontSize = fontSize < this._max_size ? fontSize : this._max_size; if (fontSize != size) { - SettingsStore.setValue("font_size", null, fontSize); + SettingsStore.setValue("font_size", null, SettingLevel.Device, fontSize); } document.querySelector(":root").style.fontSize = fontSize + "px"; } From 26ccd6f07dcb598a04643e82412f6f801e8c65e9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 10:42:31 +0100 Subject: [PATCH 064/196] Cleaner clamping of value range --- src/components/views/elements/Slider.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 9f9e1fdef8..7862373c1c 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -34,18 +34,13 @@ type IProps = { export default class Slider extends React.Component<IProps> { _offset(values: number[], value: number): number { - const lowest = values[0]; - const highest = values[values.length - 1]; + const min = values[0]; + const max = values[values.length - 1]; - if (value < lowest) { - return 0; - } + // Clamp value between min and max + value = Math.min(Math.max(value, min), max); - if (value > highest) { - return 100; - } - - return (value - values[0]) / (values[values.length - 1] - values[0]) * 100; + return (value - min) / (max - min) * 100; } render(): React.ReactNode { From 5f50facfba28291514b258de1d26b4e691d5692a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 11:36:23 +0100 Subject: [PATCH 065/196] Make slider independant of label size --- res/css/views/elements/_Slider.scss | 16 ++++++++++++++-- src/components/views/elements/Slider.tsx | 4 +++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 51f1688f6b..7e9acefa06 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -33,7 +33,7 @@ limitations under the License. position: absolute; height: 1rem; width: 100%; - padding: 0 1.1rem; + padding: 0 0.5rem; // half the width of a dot. align-items: center; } @@ -45,7 +45,7 @@ limitations under the License. .mx_Slider_selection { display: flex; align-items: center; - width: calc(100% - 2.2rem); + width: calc(100% - 1rem); // 2 * half the width of a dot height: 1rem; position: absolute; } @@ -87,3 +87,15 @@ limitations under the License. align-items: center; color: $Slider-background-color; } + +// The following is a hack to center the labels without adding +// any width to the slider's dots. +.mx_Slider_labelContainer { + width: 1rem; +} + +.mx_Slider_label { + position: relative; + width: fit-content; + left: -50%; +} diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 7862373c1c..e341eea317 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -87,9 +87,11 @@ class Dot extends React.Component<DotIProps> { return <span onClick={this.props.onClick} className="mx_Slider_dotValue"> <div className={className} /> - <div> + <div className="mx_Slider_labelContainer"> + <div className="mx_Slider_label"> {this.props.label} </div> + </div> </span>; } } From ee33fc1c20e93ee35120cd70d4c0caf156154cd0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 11:37:02 +0100 Subject: [PATCH 066/196] Remove labels --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 5bb6dcc0e0..046184da69 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -229,7 +229,7 @@ export default class StyleUserSettingsTab extends React.Component { values={[12, 14, 16, 18, 20]} value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} - displayFunc={value => value + 'px'} + displayFunc={value => {}} /> <Field type="text" From 014be5ce5fd1a4805e09350ff5b0e4d98e426bb0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 11:53:29 +0100 Subject: [PATCH 067/196] Add support to disable slider --- src/components/views/elements/Slider.tsx | 28 +++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index e341eea317..ad859bfe82 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -30,6 +30,8 @@ type IProps = { // A function for formatting the the values displayFunc: (value: number) => string; + // Whether the slider is disabled + disabled: boolean; } export default class Slider extends React.Component<IProps> { @@ -47,8 +49,9 @@ export default class Slider extends React.Component<IProps> { const dots = this.props.values.map(v => <Dot active={v <= this.props.value} label={this.props.displayFunc(v)} - onClick={() => this.props.onSelectionChange(v)} + onClick={this.props.disabled ? () => {} : () => this.props.onSelectionChange(v)} key={v} + disabled={this.props.disabled} />); const offset = this._offset(this.props.values, this.props.value); @@ -57,10 +60,13 @@ export default class Slider extends React.Component<IProps> { <div> <div className="mx_Slider_bar"> <hr /> - <div className="mx_Slider_selection"> - <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%)"}} /> - <hr style={{width: offset + "%"}} /> - </div> + { this.props.disabled ? + null : + <div className="mx_Slider_selection"> + <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%)"}} /> + <hr style={{width: offset + "%"}} /> + </div> + } </div> <div className="mx_Slider_dotContainer"> {dots} @@ -79,18 +85,24 @@ type DotIProps = { // The label on the dot label: string, + + // Whether the slider is disabled + disabled: boolean; } class Dot extends React.Component<DotIProps> { render(): React.ReactNode { - const className = "mx_Slider_dot" + (this.props.active ? " mx_Slider_dotActive" : ""); + let className = "mx_Slider_dot" + if (!this.props.disabled && this.props.active) { + className += " mx_Slider_dotActive"; + } return <span onClick={this.props.onClick} className="mx_Slider_dotValue"> <div className={className} /> <div className="mx_Slider_labelContainer"> <div className="mx_Slider_label"> - {this.props.label} - </div> + {this.props.label} + </div> </div> </span>; } From 1486beeaf42903fe75168a16ee3c45e7f516eb39 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 12:02:23 +0100 Subject: [PATCH 068/196] Make slider indpendent of settings styling --- res/css/views/elements/_Slider.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 7e9acefa06..2132381591 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -17,8 +17,6 @@ limitations under the License. .mx_Slider { position: relative; margin: 0px; - - @mixin mx_Settings_fullWidthField; } .mx_Slider_dotContainer { From 98799611cf31b5e2036a61ecac60d9b46d4f29b1 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 15:37:46 +0100 Subject: [PATCH 069/196] Remove padding for alignment reasons --- res/css/views/elements/_Slider.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 2132381591..83f100ff92 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -17,6 +17,7 @@ limitations under the License. .mx_Slider { position: relative; margin: 0px; + flex-grow: 1; } .mx_Slider_dotContainer { @@ -71,7 +72,6 @@ limitations under the License. width: 1rem; border-radius: 50%; background-color: $Slider-background-color; - margin-bottom: 5px; z-index: 0; } From f5d65907512971f4592f9be67d7c6ab2917ff0ef Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 16:11:01 +0100 Subject: [PATCH 070/196] Have max and min font configured in settings --- src/components/structures/MatrixChat.js | 2 +- src/fontSize.js | 11 ++++++----- src/settings/Settings.js | 10 ++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1845e0011d..602d85f048 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -266,7 +266,7 @@ export default createReactClass({ this.dispatcherRef = dis.register(this.onAction); this._themeWatcher = new ThemeWatcher(); - this._fontWatcher = new FontWatcher(10, 20); + this._fontWatcher = new FontWatcher(); this._themeWatcher.start(); this._fontWatcher.start(); diff --git a/src/fontSize.js b/src/fontSize.js index 30c69b7428..2e37921ee6 100644 --- a/src/fontSize.js +++ b/src/fontSize.js @@ -18,9 +18,7 @@ import dis from './dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { - constructor(minSize, maxSize) { - this._min_size = minSize; - this._max_size = maxSize; + constructor() { this._dispatcherRef = null; } @@ -40,8 +38,11 @@ export class FontWatcher { }; _setRootFontSize = size => { - let fontSize = this._min_size < size ? size : this._min_size; - fontSize = fontSize < this._max_size ? fontSize : this._max_size; + const min = SettingsStore.getValue("font_size_min"); + const max = SettingsStore.getValue("font_size_max"); + + const fontSize = Math.max(Math.min(max, size), min); + if (fontSize != size) { SettingsStore.setValue("font_size", null, SettingLevel.Device, fontSize); } diff --git a/src/settings/Settings.js b/src/settings/Settings.js index a044027baf..b144b07e84 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -177,6 +177,16 @@ export const SETTINGS = { default: 16, controller: new FontSizeController(), }, + "font_size_min": { + displayName: _td("Min font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 14, + }, + "font_size_max": { + displayName: _td("Max font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: 24, + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From fe175bb9a89d87307805582040e1f3295a2d0475 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 22 Apr 2020 17:31:49 +0100 Subject: [PATCH 071/196] Styling for the font slider --- .../tabs/user/_AppearanceUserSettingsTab.scss | 20 +++++++++++++++++++ .../tabs/user/AppearanceUserSettingsTab.js | 18 +++++++++++------ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 8c80a35e40..e4285e248c 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -14,7 +14,27 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_AppearanceUserSettingsTab_fontSlider, .mx_AppearanceUserSettingsTab_themeSection .mx_Field, .mx_AppearanceUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } + +.mx_AppearanceUserSettingsTab_fontSlider { + display: flex; + flex-direction: row; + align-items: center; + padding: 10px; + background: #FFFFFF; + border-radius: 10px; +} + +.mx_AppearanceUserSettingsTab_fontSlider_smallText { + font-size: 15px; + padding-right: 10px; +} + +.mx_AppearanceUserSettingsTab_fontSlider_largeText { + font-size: 18px; + padding-left: 10px; +} diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 046184da69..e1bbaab2cc 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -25,6 +25,7 @@ import Field from "../../../elements/Field"; import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; +import _range from "lodash/range"; export default class StyleUserSettingsTab extends React.Component { constructor() { @@ -225,12 +226,17 @@ export default class StyleUserSettingsTab extends React.Component { _renderFontSection() { return <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> - <Slider - values={[12, 14, 16, 18, 20]} - value={this.state.fontSize} - onSelectionChange={this._onFontSizeChanged} - displayFunc={value => {}} - /> + <div className="mx_AppearanceUserSettingsTab_fontSlider"> + <div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div> + <Slider + values={_range(SettingsStore.getValue("font_size_min"), SettingsStore.getValue("font_size_max"), 2)} + value={this.state.fontSize} + onSelectionChange={this._onFontSizeChanged} + displayFunc={value => {}} + disabled={false} + /> + <div className="mx_AppearanceUserSettingsTab_fontSlider_largeText">Aa</div> + </div> <Field type="text" label={_t("Font size")} From 8f0d81e7708a1d210b37f1b70824d7cfd19c3806 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 10:27:41 +0100 Subject: [PATCH 072/196] Linearly interpolate between value intervals. --- src/components/views/elements/Slider.tsx | 36 ++++++++++++++++--- .../tabs/user/AppearanceUserSettingsTab.js | 2 +- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index ad859bfe82..a9fc41c8cc 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -35,14 +35,40 @@ type IProps = { } export default class Slider extends React.Component<IProps> { + // offset is a terrible inverse approximation. + // if the values represents some function f(x) = y where x is the + // index of the array and y = values[x] then offset(f, y) = x + // s.t f(x) = y. + // it assumes a monotonic function and interpolates linearly between + // y values. + // Offset is used for finding the location of a value on a + // non linear slider. _offset(values: number[], value: number): number { - const min = values[0]; - const max = values[values.length - 1]; + // the index of the first number greater than value. + let closest = values.reduce((prev, curr) => { + return (value > curr ? prev + 1 : prev); + }, 0); - // Clamp value between min and max - value = Math.min(Math.max(value, min), max); + // Off the left + if (closest == 0) { + return 0; + } + + // Off the right + if (closest == values.length) { + return 100; + } + + // Now + const closestLessValue = values[closest - 1]; + const closestGreaterValue = values[closest]; + + const intervalWidth = 1 / (values.length - 1); + + const linearInterpolation = (value - closestLessValue) / (closestGreaterValue - closestLessValue) + + return 100 * (closest - 1 + linearInterpolation) * intervalWidth - return (value - min) / (max - min) * 100; } render(): React.ReactNode { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index e1bbaab2cc..949b3bed31 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -229,7 +229,7 @@ export default class StyleUserSettingsTab extends React.Component { <div className="mx_AppearanceUserSettingsTab_fontSlider"> <div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div> <Slider - values={_range(SettingsStore.getValue("font_size_min"), SettingsStore.getValue("font_size_max"), 2)} + values={_range(SettingsStore.getValue("font_size_min"), SettingsStore.getValue("font_size_max") + 2, 2.5)} value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} displayFunc={value => {}} From a16fe09d4275b64939d34f56b1720cb00ef1e93e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 10:58:00 +0100 Subject: [PATCH 073/196] Use em to detach slider from root font-size --- res/css/views/elements/_Slider.scss | 22 +++++++++---------- .../tabs/user/_AppearanceUserSettingsTab.scss | 1 + src/components/views/elements/Slider.tsx | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 83f100ff92..f6982865db 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -30,30 +30,30 @@ limitations under the License. display: flex; box-sizing: border-box; position: absolute; - height: 1rem; + height: 1em; width: 100%; - padding: 0 0.5rem; // half the width of a dot. + padding: 0 0.5em; // half the width of a dot. align-items: center; } .mx_Slider_bar > hr { width: 100%; - border: 0.2rem solid $Slider-background-color; + border: 0.2em solid $Slider-background-color; } .mx_Slider_selection { display: flex; align-items: center; - width: calc(100% - 1rem); // 2 * half the width of a dot - height: 1rem; + width: calc(100% - 1em); // 2 * half the width of a dot + height: 1em; position: absolute; } .mx_Slider_selectionDot { transition: left 0.25s; position: absolute; - width: 1.1rem; - height: 1.1rem; + width: 1.1em; + height: 1.1em; background-color: $Slider-selection-color; border-radius: 50%; box-shadow: 0 0 6px lightgrey; @@ -63,13 +63,13 @@ limitations under the License. .mx_Slider_selection > hr { transition: width 0.25s; margin: 0; - border: 0.2rem solid $Slider-selection-color; + border: 0.2em solid $Slider-selection-color; } .mx_Slider_dot { transition: background-color 0.2s ease-in; - height: 1rem; - width: 1rem; + height: 1em; + width: 1em; border-radius: 50%; background-color: $Slider-background-color; z-index: 0; @@ -89,7 +89,7 @@ limitations under the License. // The following is a hack to center the labels without adding // any width to the slider's dots. .mx_Slider_labelContainer { - width: 1rem; + width: 1em; } .mx_Slider_label { diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index e4285e248c..28a2510508 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -27,6 +27,7 @@ limitations under the License. padding: 10px; background: #FFFFFF; border-radius: 10px; + font-size: 10px; } .mx_AppearanceUserSettingsTab_fontSlider_smallText { diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index a9fc41c8cc..6ec044da41 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -89,7 +89,7 @@ export default class Slider extends React.Component<IProps> { { this.props.disabled ? null : <div className="mx_Slider_selection"> - <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55rem + " + offset + "%)"}} /> + <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55em + " + offset + "%)"}} /> <hr style={{width: offset + "%"}} /> </div> } From 6375e2526324de37ee170637c494bf08a29c4e59 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 11:22:51 +0100 Subject: [PATCH 074/196] Match padding from figma --- .../settings/tabs/user/_AppearanceUserSettingsTab.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 28a2510508..16a14edf85 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -24,7 +24,7 @@ limitations under the License. display: flex; flex-direction: row; align-items: center; - padding: 10px; + padding: 15px; background: #FFFFFF; border-radius: 10px; font-size: 10px; @@ -32,10 +32,12 @@ limitations under the License. .mx_AppearanceUserSettingsTab_fontSlider_smallText { font-size: 15px; - padding-right: 10px; + padding-right: 20px; + padding-left: 5px; } .mx_AppearanceUserSettingsTab_fontSlider_largeText { font-size: 18px; - padding-left: 10px; + padding-left: 20px; + padding-right: 5px; } From 28dca9e52529b3b8f49ee0e92cf448a4875fa403 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 11:33:28 +0100 Subject: [PATCH 075/196] Match figma color scheme --- .../views/settings/tabs/user/_AppearanceUserSettingsTab.scss | 2 +- res/themes/dark/css/_dark.scss | 3 +++ res/themes/light/css/_light.scss | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 16a14edf85..4141fb2fb1 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -25,7 +25,7 @@ limitations under the License. flex-direction: row; align-items: center; padding: 15px; - background: #FFFFFF; + background: $font-slider-bg-color; border-radius: 10px; font-size: 10px; } diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 5d6ba033c8..cb6e7ccdaa 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -177,6 +177,9 @@ $breadcrumb-placeholder-bg-color: #272c35; $user-tile-hover-bg-color: $header-panel-bg-color; +// FontSlider colors +$font-slider-bg-color: $room-highlight-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index e06ba33594..b576b57778 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -306,6 +306,9 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; +// FontSlider colors +$font-slider-bg-color: $input-darker-bg-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { From a83993f1ff18f8c7be1d0949a8c983bbecf2ee9d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 11:49:54 +0100 Subject: [PATCH 076/196] Match margins in settings --- .../views/settings/tabs/user/_AppearanceUserSettingsTab.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss index 4141fb2fb1..e82ae3c575 100644 --- a/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_AppearanceUserSettingsTab.scss @@ -28,6 +28,8 @@ limitations under the License. background: $font-slider-bg-color; border-radius: 10px; font-size: 10px; + margin-top: 24px; + margin-bottom: 24px; } .mx_AppearanceUserSettingsTab_fontSlider_smallText { From c86638c667d023abccb1250825e5c17e1070991c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 12:09:08 +0100 Subject: [PATCH 077/196] add toggle between font slider and custom setting --- .../settings/tabs/user/AppearanceUserSettingsTab.js | 11 +++++++++-- src/i18n/strings/en_EN.json | 3 +++ src/settings/Settings.js | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 949b3bed31..ceb3241b8b 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -36,7 +36,7 @@ export default class StyleUserSettingsTab extends React.Component { ...this._calculateThemeState(), customThemeUrl: "", customThemeMessage: {isError: false, text: ""}, - + useCustomFontSize: SettingsStore.getValue("useCustomFontSize"), }; } @@ -224,6 +224,7 @@ export default class StyleUserSettingsTab extends React.Component { } _renderFontSection() { + const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_fontScaling"> <span className="mx_SettingsTab_subheading">{_t("Font size")}</span> <div className="mx_AppearanceUserSettingsTab_fontSlider"> @@ -233,10 +234,15 @@ export default class StyleUserSettingsTab extends React.Component { value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} displayFunc={value => {}} - disabled={false} + disabled={this.state.useCustomFontSize} /> <div className="mx_AppearanceUserSettingsTab_fontSlider_largeText">Aa</div> </div> + <SettingsFlag + name="useCustomFontSize" + level={SettingLevel.ACCOUNT} + onChange={(checked)=> this.setState({useCustomFontSize: checked})} + /> <Field type="text" label={_t("Font size")} @@ -245,6 +251,7 @@ export default class StyleUserSettingsTab extends React.Component { value={this.state.fontSize} id="font_size_field" onChange={(ev) => this._onFontSizeChanged(ev.target.value)} + disabled={!this.state.useCustomFontSize} /> </div>; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3051cbb91..2c3239900d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -407,6 +407,9 @@ "Show info about bridges in room settings": "Show info about bridges in room settings", "Show padlocks on invite only rooms": "Show padlocks on invite only rooms", "Font size": "Font size", + "Min font size": "Min font size", + "Max font size": "Max font size", + "Custom font size": "Custom font size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index b144b07e84..e0e34179f3 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -187,6 +187,11 @@ export const SETTINGS = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: 24, }, + "useCustomFontSize": { + displayName: _td("Custom font size"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: false, + }, "MessageComposerInput.suggestEmoji": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Enable Emoji suggestions while typing'), From 600a812227acafd8a15732cdabed3b6899415735 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 12:20:10 +0100 Subject: [PATCH 078/196] Add brush icon for appearance setting tab --- res/css/views/dialogs/_UserSettingsDialog.scss | 4 ++++ res/img/feather-customised/brush.svg | 5 +++++ src/components/views/dialogs/UserSettingsDialog.js | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 res/img/feather-customised/brush.svg diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 4d831d7858..7adcc58c4e 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -21,6 +21,10 @@ limitations under the License. mask-image: url('$(res)/img/feather-customised/settings.svg'); } +.mx_UserSettingsDialog_appearanceIcon::before { + mask-image: url('$(res)/img/feather-customised/brush.svg'); +} + .mx_UserSettingsDialog_voiceIcon::before { mask-image: url('$(res)/img/feather-customised/phone.svg'); } diff --git a/res/img/feather-customised/brush.svg b/res/img/feather-customised/brush.svg new file mode 100644 index 0000000000..d7f2738629 --- /dev/null +++ b/res/img/feather-customised/brush.svg @@ -0,0 +1,5 @@ +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M12 16.5C12 18.9853 9.98528 21 7.5 21C6.21514 21 3 21 3 21C3 21 3 17.7004 3 16.5C3 14.0147 5.01472 12 7.5 12C9.98528 12 12 14.0147 12 16.5Z" stroke="#2E2F32" stroke-linejoin="round"/> +<path d="M8.25 12L17.1955 3.69345C18.0632 2.88776 19.4127 2.91274 20.25 3.75V3.75C21.0873 4.58726 21.1122 5.93682 20.3065 6.80449L12 15.75" stroke="#2E2F32"/> +<path d="M11.25 9C11.25 9 12.3929 9.45 13.5 10.5C14.6071 11.55 15 12.75 15 12.75" stroke="#2E2F32"/> +</svg> diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 91ab203753..bf06b8749f 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -69,7 +69,7 @@ export default class UserSettingsDialog extends React.Component { )); tabs.push(new Tab( _td("Appearance"), - "mx_userSettingsDialog_styleIcon", + "mx_UserSettingsDialog_appearanceIcon", <AppearanceUserSettingsTab />, )); tabs.push(new Tab( From e5cb14929602cdc71887b3e95c54f546d9ccdda0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 13:52:08 +0100 Subject: [PATCH 079/196] Handle fontslider input errors correctly --- .../tabs/user/AppearanceUserSettingsTab.js | 33 ++++++++++++++++--- src/i18n/strings/en_EN.json | 3 ++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index ceb3241b8b..4144605999 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -101,11 +101,33 @@ export default class StyleUserSettingsTab extends React.Component { }; _onFontSizeChanged = (size) => { - const parsedSize = isNaN(parseInt(size)) ? SettingsStore.getDefaultValue("font_size") : parseFloat(size); - this.setState({fontSize: parsedSize}); - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, parsedSize); + this.setState({fontSize: size}); + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, size); }; + _onValidateFontSize = ({value}) => { + console.log({value}); + this.setState({fontSize: value}); + + const parsedSize = parseFloat(value); + const min = SettingsStore.getValue("font_size_min"); + const max = SettingsStore.getValue("font_size_max"); + + if (isNaN(parsedSize)) { + return {valid: false, feedback: _t("Size must be a number")}; + } + + console.log({min}); + console.log({max}); + console.log({parsedSize}); + if (!(min <= parsedSize && parsedSize <= max)) { + return {valid: false, feedback: _t('Custom font size can only be between %(min)s pt and %(max)s pt', {min, max})}; + } + + SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, value); + return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})}; + } + _onAddCustomTheme = async () => { let currentThemes = SettingsStore.getValue("custom_themes"); if (!currentThemes) currentThemes = []; @@ -247,10 +269,11 @@ export default class StyleUserSettingsTab extends React.Component { type="text" label={_t("Font size")} autoComplete="off" - placeholder={SettingsStore.getValue("font_size", null).toString()} + placeholder={this.state.fontSize} value={this.state.fontSize} id="font_size_field" - onChange={(ev) => this._onFontSizeChanged(ev.target.value)} + onValidate={this._onValidateFontSize} + onChange={({value}) => this.setState({fontSize: value})} disabled={!this.state.useCustomFontSize} /> </div>; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2c3239900d..fa3d5e3f41 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -747,6 +747,9 @@ "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.", "Manage integrations": "Manage integrations", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", + "Size must be a number": "Size must be a number", + "Custom font size can only be between %(min)s pt and %(max)s pt": "Custom font size can only be between %(min)s pt and %(max)s pt", + "Use between %(min)s pt and %(max)s pt": "Use between %(min)s pt and %(max)s pt", "Invalid theme schema.": "Invalid theme schema.", "Error downloading theme information.": "Error downloading theme information.", "Theme added!": "Theme added!", From a087f5ea400fd7a8fc7e6f207fdb15def6e4e2f3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 13:55:10 +0100 Subject: [PATCH 080/196] Lint --- .../tabs/user/AppearanceUserSettingsTab.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 4144605999..5c285d12e6 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -117,11 +117,11 @@ export default class StyleUserSettingsTab extends React.Component { return {valid: false, feedback: _t("Size must be a number")}; } - console.log({min}); - console.log({max}); - console.log({parsedSize}); if (!(min <= parsedSize && parsedSize <= max)) { - return {valid: false, feedback: _t('Custom font size can only be between %(min)s pt and %(max)s pt', {min, max})}; + return { + valid: false, + feedback: _t('Custom font size can only be between %(min)s pt and %(max)s pt', {min, max}), + }; } SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, value); @@ -252,7 +252,11 @@ export default class StyleUserSettingsTab extends React.Component { <div className="mx_AppearanceUserSettingsTab_fontSlider"> <div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div> <Slider - values={_range(SettingsStore.getValue("font_size_min"), SettingsStore.getValue("font_size_max") + 2, 2.5)} + values={_range( + SettingsStore.getValue("font_size_min"), + SettingsStore.getValue("font_size_max")+ 2, + 2.5, + )} value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} displayFunc={value => {}} From 06f4eca05d51aaff8986406296ccb0844a66dfe4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 14:15:33 +0100 Subject: [PATCH 081/196] Background opacity --- res/themes/light/css/_light.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index b576b57778..ed7eae48f7 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -307,7 +307,7 @@ $breadcrumb-placeholder-bg-color: #e8eef5; $user-tile-hover-bg-color: $header-panel-bg-color; // FontSlider colors -$font-slider-bg-color: $input-darker-bg-color; +$font-slider-bg-color: rgba($input-darker-bg-color, 0.2); // ***** Mixins! ***** From 4b4599c1d81b4200f41158cb8febe2fcb9121c3a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 23 Apr 2020 14:39:11 +0100 Subject: [PATCH 082/196] tslint --- src/components/views/elements/Slider.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 6ec044da41..559bdd9ce2 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -36,7 +36,7 @@ type IProps = { export default class Slider extends React.Component<IProps> { // offset is a terrible inverse approximation. - // if the values represents some function f(x) = y where x is the + // if the values represents some function f(x) = y where x is the // index of the array and y = values[x] then offset(f, y) = x // s.t f(x) = y. // it assumes a monotonic function and interpolates linearly between @@ -50,16 +50,16 @@ export default class Slider extends React.Component<IProps> { }, 0); // Off the left - if (closest == 0) { + if (closest === 0) { return 0; } // Off the right - if (closest == values.length) { + if (closest === values.length) { return 100; } - // Now + // Now const closestLessValue = values[closest - 1]; const closestGreaterValue = values[closest]; From 3962c98c9beaeee3f1685e63fe3604ed8a1a68eb Mon Sep 17 00:00:00 2001 From: Pauli Virtanen <pav@iki.fi> Date: Thu, 23 Apr 2020 22:53:02 +0300 Subject: [PATCH 083/196] Ensure PersistedElements are refreshed when AuxPanel scrolls If the screen is not tall enough, AuxPanel starts scrolling its content. If it contains PersistedElements, they need to be notified about scrolling as they only listen on resize events to move their element. Signed-off-by: Pauli Virtanen <pav@iki.fi> --- src/components/views/rooms/AuxPanel.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index e102b0dba4..00bdb1c45a 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -141,6 +141,15 @@ export default createReactClass({ return counters; }, + _onScroll: function(rect) { + if (this.props.onResize) { + this.props.onResize(); + } + + /* Force refresh of PersistedElements which may be partially hidden */ + window.dispatchEvent(new Event('resize')); + }, + render: function() { const CallView = sdk.getComponent("voip.CallView"); const TintableSvg = sdk.getComponent("elements.TintableSvg"); @@ -265,7 +274,7 @@ export default createReactClass({ } return ( - <AutoHideScrollbar className={classes} style={style} > + <AutoHideScrollbar className={classes} style={style} onScroll={this._onScroll}> { stateViews } { appsDrawer } { fileDropTarget } From d690d4bed2cb1562fface956ed0c4e34e4e35c54 Mon Sep 17 00:00:00 2001 From: Pauli Virtanen <pav@iki.fi> Date: Tue, 21 Apr 2020 01:19:39 +0300 Subject: [PATCH 084/196] Prevent PersistedElements overflowing scrolled areas As the DOM element is not in reality contained inside "the parent", it may overflow the area if the parent gets partially hidden by scrolling etc. To make the effect visually less annoying, emulate this by clipping to the element wrapper. This is not a full general-purpose fix, but improves the current situation. Signed-off-by: Pauli Virtanen <pav@iki.fi> --- res/css/views/rooms/_AppsDrawer.scss | 4 ++ .../views/elements/PersistedElement.js | 58 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 1b1bab67bc..e4743f189e 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -96,6 +96,10 @@ $AppsDrawerBodyHeight: 273px; height: $AppsDrawerBodyHeight; } +.mx_AppTile_persistedWrapper > div { + height: 100%; +} + .mx_AppTile_mini .mx_AppTile_persistedWrapper { height: 114px; } diff --git a/src/components/views/elements/PersistedElement.js b/src/components/views/elements/PersistedElement.js index 53f2501f19..18fa2aafef 100644 --- a/src/components/views/elements/PersistedElement.js +++ b/src/components/views/elements/PersistedElement.js @@ -156,16 +156,70 @@ export default class PersistedElement extends React.Component { child.style.display = visible ? 'block' : 'none'; } + /* + * Clip element bounding rectangle to that of the parent elements. + * This is not a full visibility check, but prevents the persisted + * element from overflowing parent containers when inside a scrolled + * area. + */ + _getClippedBoundingClientRect(element) { + let parentElement = element.parentElement; + let rect = element.getBoundingClientRect(); + + rect = new DOMRect(rect.left, rect.top, rect.width, rect.height); + + while (parentElement) { + const parentRect = parentElement.getBoundingClientRect(); + + if (parentRect.left > rect.left) { + rect.width = rect.width - (parentRect.left - rect.left); + rect.x = parentRect.x; + } + + if (parentRect.top > rect.top) { + rect.height = rect.height - (parentRect.top - rect.top); + rect.y = parentRect.y; + } + + if (parentRect.right < rect.right) { + rect.width = rect.width - (rect.right - parentRect.right); + } + + if (parentRect.bottom < rect.bottom) { + rect.height = rect.height - (rect.bottom - parentRect.bottom); + } + + parentElement = parentElement.parentElement; + } + + if (rect.width < 0) rect.width = 0; + if (rect.height < 0) rect.height = 0; + + return rect; + } + updateChildPosition(child, parent) { if (!child || !parent) return; const parentRect = parent.getBoundingClientRect(); + const clipRect = this._getClippedBoundingClientRect(parent); + + Object.assign(child.parentElement.style, { + position: 'absolute', + top: clipRect.top + 'px', + left: clipRect.left + 'px', + width: clipRect.width + 'px', + height: clipRect.height + 'px', + overflow: "hidden", + }); + Object.assign(child.style, { position: 'absolute', - top: parentRect.top + 'px', - left: parentRect.left + 'px', + top: (parentRect.top - clipRect.top) + 'px', + left: (parentRect.left - clipRect.left) + 'px', width: parentRect.width + 'px', height: parentRect.height + 'px', + overflow: "hidden", }); } From bfba5e6cfe8e2be24a135af072a6c0b2a41dbfbb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 27 Apr 2020 16:57:38 +0100 Subject: [PATCH 085/196] Fix member info avatar size --- src/components/views/avatars/BaseAvatar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index a17124b9ad..cbe083f3a2 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -208,8 +208,8 @@ export default createReactClass({ onClick={onClick} onError={this.onError} style={{ - width: toRem(width), - height: toRem(height) + width: {width}, + height: {height}, }} title={title} alt="" inputRef={inputRef} From a8407c9508a38b93465ab030e4d99ab8c86212ce Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:00:15 +0100 Subject: [PATCH 086/196] Use purecomponent Co-Authored-By: Travis Ralston <travpc@gmail.com> --- src/components/views/elements/Slider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 559bdd9ce2..3dfd0c686e 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -116,7 +116,7 @@ type DotIProps = { disabled: boolean; } -class Dot extends React.Component<DotIProps> { +class Dot extends React.PureComponent<DotIProps> { render(): React.ReactNode { let className = "mx_Slider_dot" if (!this.props.disabled && this.props.active) { From c268b98ded295f9679ddf7eddef436a67ab86bb3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:17:50 +0100 Subject: [PATCH 087/196] Use faster lookup method Co-Authored-By: Travis Ralston <travpc@gmail.com> --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 5c285d12e6..ed7d9ef495 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -168,7 +168,7 @@ export default class StyleUserSettingsTab extends React.Component { <div className="mx_SettingsTab"> <div className="mx_SettingsTab_heading">{_t("Appearance")}</div> {this._renderThemeSection()} - {SettingsStore.getValue("feature_font_scaling") ? this._renderFontSection() : null} + {SettingsStore.isFeatureEnabled("feature_font_scaling") ? this._renderFontSection() : null} </div> ); } From f91613f112d2b33839f32eb5500fd3a95f796b95 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 13:53:16 +0100 Subject: [PATCH 088/196] Remove redundent selectors. Check _AppearanceUserSettingsTab --- res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 45aecd032f..5cc220bd33 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -15,8 +15,6 @@ limitations under the License. */ .mx_GeneralUserSettingsTab_changePassword .mx_Field, -.mx_StyleUserSettingsTab_themeSection .mx_Field, -.mx_StyleUserSettingsTab_fontScaling .mx_Field { @mixin mx_Settings_fullWidthField; } From 137b94703aad9344b5f4ab38d4b6e7e441396ab9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 13:59:00 +0100 Subject: [PATCH 089/196] Lint types --- src/components/views/elements/Slider.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 3dfd0c686e..722401801c 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -16,22 +16,22 @@ limitations under the License. import * as React from 'react'; -type IProps = { - // A callback for the selected value - onSelectionChange: (value: number) => void; +interface IProps { + // A callback for the selected value + onSelectionChange: (value: number) => void; - // The current value of the slider - value: number; + // The current value of the slider + value: number; - // The range and values of the slider - // Currently only supports an ascending, constant interval range - values: number[]; + // The range and values of the slider + // Currently only supports an ascending, constant interval range + values: number[]; - // A function for formatting the the values - displayFunc: (value: number) => string; + // A function for formatting the the values + displayFunc: (value: number) => string; - // Whether the slider is disabled - disabled: boolean; + // Whether the slider is disabled + disabled: boolean; } export default class Slider extends React.Component<IProps> { From 175b5e70b60f23dc446aba22da4f00d56aa2d624 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:09:54 +0100 Subject: [PATCH 090/196] Lint Slider --- src/components/views/elements/Slider.tsx | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 722401801c..6712ddd7fd 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -43,7 +43,7 @@ export default class Slider extends React.Component<IProps> { // y values. // Offset is used for finding the location of a value on a // non linear slider. - _offset(values: number[], value: number): number { + private offset(values: number[], value: number): number { // the index of the first number greater than value. let closest = values.reduce((prev, curr) => { return (value > curr ? prev + 1 : prev); @@ -80,19 +80,21 @@ export default class Slider extends React.Component<IProps> { disabled={this.props.disabled} />); - const offset = this._offset(this.props.values, this.props.value); + let selection = null; + + if (this.props.disabled) { + const offset = this.offset(this.props.values, this.props.value); + selection = <div className="mx_Slider_selection"> + <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55em + " + offset + "%)"}} /> + <hr style={{width: offset + "%"}} /> + </div> + } return <div className="mx_Slider"> <div> <div className="mx_Slider_bar"> <hr /> - { this.props.disabled ? - null : - <div className="mx_Slider_selection"> - <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55em + " + offset + "%)"}} /> - <hr style={{width: offset + "%"}} /> - </div> - } + { selection } </div> <div className="mx_Slider_dotContainer"> {dots} @@ -102,7 +104,7 @@ export default class Slider extends React.Component<IProps> { } } -type DotIProps = { +interface IDotProps { // Callback for behavior onclick onClick: () => void, @@ -116,7 +118,7 @@ type DotIProps = { disabled: boolean; } -class Dot extends React.PureComponent<DotIProps> { +class Dot extends React.PureComponent<IDotProps> { render(): React.ReactNode { let className = "mx_Slider_dot" if (!this.props.disabled && this.props.active) { From 57d880ca5e2bde20a030f13d3f2257fe23e654b4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:24:44 +0100 Subject: [PATCH 091/196] Use correct name and indentation --- .../settings/tabs/user/AppearanceUserSettingsTab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index ed7d9ef495..6c94a82c95 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -27,7 +27,7 @@ import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; import _range from "lodash/range"; -export default class StyleUserSettingsTab extends React.Component { +export default class AppearanceUserSettingsTab extends React.Component { constructor() { super(); @@ -231,9 +231,10 @@ export default class StyleUserSettingsTab extends React.Component { <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_themeSection"> <span className="mx_SettingsTab_subheading">{_t("Theme")}</span> {systemThemeSection} - <Field id="theme" label={_t("Theme")} element="select" - value={this.state.theme} onChange={this._onThemeChange} - disabled={this.state.useSystemTheme} + <Field + id="theme" label={_t("Theme")} element="select" + value={this.state.theme} onChange={this._onThemeChange} + disabled={this.state.useSystemTheme} > {orderedThemes.map(theme => { return <option key={theme.id} value={theme.id}>{theme.name}</option>; From 93f24f12dcf839f35231e8ca9083f670c3f626d7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:26:08 +0100 Subject: [PATCH 092/196] Match filename to class --- src/{fontSize.js => FontWatcher.js} | 0 src/components/structures/MatrixChat.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{fontSize.js => FontWatcher.js} (100%) diff --git a/src/fontSize.js b/src/FontWatcher.js similarity index 100% rename from src/fontSize.js rename to src/FontWatcher.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 602d85f048..fec37472be 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -66,7 +66,7 @@ import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { defer } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; -import { FontWatcher } from '../../fontSize'; +import { FontWatcher } from '../../FontWatcher'; /** constants for MatrixChat.state.view */ export const VIEWS = { From 9ca843fdcbc9f6d5d12dddac23a11af686cc702e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:27:18 +0100 Subject: [PATCH 093/196] Correct return type in docs Co-Authored-By: Travis Ralston <travpc@gmail.com> --- src/settings/SettingsStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 70ea5ac57c..b6856a5a6a 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -373,7 +373,7 @@ export default class SettingsStore { /** * Gets the default value of a setting. * @param {string} settingName The name of the setting to read the value of. - * @return {*} The value, or null if not found + * @return {*} The default value */ static getDefaultValue(settingName, roomId = null, excludeDefault = false) { // Verify that the setting is actually a setting From fe326b9f08534d70c36b0484e727304e8396ba8c Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:30:56 +0100 Subject: [PATCH 094/196] Enfore function name capitalisation --- src/components/structures/RoomSubList.js | 2 +- src/components/views/avatars/BaseAvatar.js | 2 +- src/components/views/rooms/EventTile.js | 4 ++-- src/components/views/rooms/ReadReceiptMarker.js | 2 +- src/utils/rem.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 1e3e15b4ec..b1e0bb9f9b 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,7 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; -import toRem from "../../utils/rem"; +import {toRem} from "../../utils/rem"; // turn this on for drop & drag console debugging galore const debug = false; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index cbe083f3a2..e94a83f70b 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,7 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import toRem from "../../../utils/rem"; +import {toRem} from "../../../utils/rem"; export default createReactClass({ displayName: 'BaseAvatar', diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index af14f6922c..0881fb3b67 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -34,7 +34,7 @@ import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; import * as ObjectUtils from "../../../ObjectUtils"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {E2E_STATE} from "./E2EIcon"; -import torem from "../../../utils/rem"; +import {toRem} from "../../../utils/rem"; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', @@ -474,7 +474,7 @@ export default createReactClass({ if (remainder > 0) { remText = <span className="mx_EventTile_readAvatarRemainder" onClick={this.toggleAllReadAvatars} - style={{ right: "calc(" + torem(-left) + " + " + receiptOffset + "px)" }}>{ remainder }+ + style={{ right: "calc(" + toRem(-left) + " + " + receiptOffset + "px)" }}>{ remainder }+ </span>; } } diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index 85d443d55a..20d39a7f84 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -23,7 +23,7 @@ import { _t } from '../../../languageHandler'; import {formatDate} from '../../../DateUtils'; import Velociraptor from "../../../Velociraptor"; import * as sdk from "../../../index"; -import toRem from "../../../utils/rem"; +import {toRem} from "../../../utils/rem"; let bounce = false; try { diff --git a/src/utils/rem.js b/src/utils/rem.js index 1f18c9de05..6278a91aa2 100644 --- a/src/utils/rem.js +++ b/src/utils/rem.js @@ -15,6 +15,6 @@ limitations under the License. */ // converts a pixel value to rem. -export default function(pixelVal) { +export function toRem(pixelVal) { return pixelVal / 15 + "rem"; } From 1289367a6b63f8e04e53da6ae5a2ae7e5a8e5455 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 14:31:24 +0100 Subject: [PATCH 095/196] Fix indentation --- src/utils/rem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/rem.js b/src/utils/rem.js index 6278a91aa2..3729b4d596 100644 --- a/src/utils/rem.js +++ b/src/utils/rem.js @@ -16,5 +16,5 @@ limitations under the License. // converts a pixel value to rem. export function toRem(pixelVal) { - return pixelVal / 15 + "rem"; + return pixelVal / 15 + "rem"; } From eb72245493c1dbe163f85afbf97a26b68078e525 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 15:19:12 +0100 Subject: [PATCH 096/196] fix syntax error --- res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss index 5cc220bd33..0af7e30d97 100644 --- a/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_GeneralUserSettingsTab_changePassword .mx_Field, +.mx_GeneralUserSettingsTab_changePassword .mx_Field { @mixin mx_Settings_fullWidthField; } From af8430b98aa5e47116e76ce3547da955ad18b1dd Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 15:48:54 +0100 Subject: [PATCH 097/196] Inverted boolean --- src/components/views/elements/Slider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index 6712ddd7fd..adb2a6063b 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -82,7 +82,7 @@ export default class Slider extends React.Component<IProps> { let selection = null; - if (this.props.disabled) { + if (!this.props.disabled) { const offset = this.offset(this.props.values, this.props.value); selection = <div className="mx_Slider_selection"> <div className="mx_Slider_selectionDot" style={{left: "calc(-0.55em + " + offset + "%)"}} /> From 4e6748416c3d68757588e563d24f516dc17880c6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 15:53:12 +0100 Subject: [PATCH 098/196] Fix i18n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bff1b8f415..58226595f7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -405,7 +405,6 @@ "Support adding custom themes": "Support adding custom themes", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", - "Show padlocks on invite only rooms": "Show padlocks on invite only rooms", "Font size": "Font size", "Min font size": "Min font size", "Max font size": "Max font size", From 132a753deb787bb626b2431b7f0434debc3c1b74 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 28 Apr 2020 15:55:26 +0100 Subject: [PATCH 099/196] Lint getDefaultValue --- src/settings/SettingsStore.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index b6856a5a6a..688925de40 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -373,9 +373,10 @@ export default class SettingsStore { /** * Gets the default value of a setting. * @param {string} settingName The name of the setting to read the value of. + * @param {String} roomId The room ID to read the setting value in, may be null. * @return {*} The default value */ - static getDefaultValue(settingName, roomId = null, excludeDefault = false) { + static getDefaultValue(settingName) { // Verify that the setting is actually a setting if (!SETTINGS[settingName]) { throw new Error("Setting '" + settingName + "' does not appear to be a setting."); From 2acb1663eb67473738511c3b6aa22899c9344cb3 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 01:01:56 +0100 Subject: [PATCH 100/196] Appease the prop types --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 6c94a82c95..d089b4f6e0 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -274,8 +274,8 @@ export default class AppearanceUserSettingsTab extends React.Component { type="text" label={_t("Font size")} autoComplete="off" - placeholder={this.state.fontSize} - value={this.state.fontSize} + placeholder={toString(this.state.fontSize)} + value={toString(this.state.fontSize)} id="font_size_field" onValidate={this._onValidateFontSize} onChange={({value}) => this.setState({fontSize: value})} From f7b3662e0b306d66feefe7ac03211165518565f7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 10:32:05 +0100 Subject: [PATCH 101/196] Fully appease prop types --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index d089b4f6e0..6fd44b691d 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -107,7 +107,6 @@ export default class AppearanceUserSettingsTab extends React.Component { _onValidateFontSize = ({value}) => { console.log({value}); - this.setState({fontSize: value}); const parsedSize = parseFloat(value); const min = SettingsStore.getValue("font_size_min"); @@ -274,11 +273,11 @@ export default class AppearanceUserSettingsTab extends React.Component { type="text" label={_t("Font size")} autoComplete="off" - placeholder={toString(this.state.fontSize)} - value={toString(this.state.fontSize)} + placeholder={this.state.fontSize.toString()} + value={this.state.fontSize.toString()} id="font_size_field" onValidate={this._onValidateFontSize} - onChange={({value}) => this.setState({fontSize: value})} + onChange={(value) => this.setState({fontSize: value.target.value})} disabled={!this.state.useCustomFontSize} /> </div>; From bab7d5f461a6c51d142fe9ff7d5be6cd4cfd9bbb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 6 May 2020 17:25:54 +0100 Subject: [PATCH 102/196] Some lints --- res/css/views/elements/_Slider.scss | 12 ++++++------ res/themes/light/css/_light.scss | 4 ++-- src/FontWatcher.js | 12 ++++++------ src/components/views/elements/Slider.tsx | 2 +- .../tabs/user/AppearanceUserSettingsTab.js | 14 +++++++------- src/settings/Settings.js | 6 +++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index f6982865db..09afb58b12 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -38,7 +38,7 @@ limitations under the License. .mx_Slider_bar > hr { width: 100%; - border: 0.2em solid $Slider-background-color; + border: 0.2em solid $slider-background-color; } .mx_Slider_selection { @@ -54,7 +54,7 @@ limitations under the License. position: absolute; width: 1.1em; height: 1.1em; - background-color: $Slider-selection-color; + background-color: $slider-selection-color; border-radius: 50%; box-shadow: 0 0 6px lightgrey; z-index: 10; @@ -63,7 +63,7 @@ limitations under the License. .mx_Slider_selection > hr { transition: width 0.25s; margin: 0; - border: 0.2em solid $Slider-selection-color; + border: 0.2em solid $slider-selection-color; } .mx_Slider_dot { @@ -71,19 +71,19 @@ limitations under the License. height: 1em; width: 1em; border-radius: 50%; - background-color: $Slider-background-color; + background-color: $slider-background-color; z-index: 0; } .mx_Slider_dotActive { - background-color: $Slider-selection-color; + background-color: $slider-selection-color; } .mx_Slider_dotValue { display: flex; flex-direction: column; align-items: center; - color: $Slider-background-color; + color: $slider-background-color; } // The following is a hack to center the labels without adding diff --git a/res/themes/light/css/_light.scss b/res/themes/light/css/_light.scss index ed7eae48f7..78fe2a74c5 100644 --- a/res/themes/light/css/_light.scss +++ b/res/themes/light/css/_light.scss @@ -263,8 +263,8 @@ $togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; // Slider -$Slider-selection-color: $accent-color; -$Slider-background-color: #c1c9d6; +$slider-selection-color: $accent-color; +$slider-background-color: #c1c9d6; $progressbar-color: #000; diff --git a/src/FontWatcher.js b/src/FontWatcher.js index 2e37921ee6..561edc4662 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -23,7 +23,7 @@ export class FontWatcher { } start() { - this._setRootFontSize(SettingsStore.getValue("font_size")); + this._setRootFontSize(SettingsStore.getValue("fontSize")); this._dispatcherRef = dis.register(this._onAction); } @@ -37,15 +37,15 @@ export class FontWatcher { } }; - _setRootFontSize = size => { - const min = SettingsStore.getValue("font_size_min"); - const max = SettingsStore.getValue("font_size_max"); + _setRootFontSize = (size) => { + const min = SettingsStore.getValue("fontSizeMin"); + const max = SettingsStore.getValue("fontSizeMax"); const fontSize = Math.max(Math.min(max, size), min); if (fontSize != size) { - SettingsStore.setValue("font_size", null, SettingLevel.Device, fontSize); + SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); } document.querySelector(":root").style.fontSize = fontSize + "px"; - } + }; } diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index adb2a6063b..e181f0d9e3 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -47,7 +47,7 @@ export default class Slider extends React.Component<IProps> { // the index of the first number greater than value. let closest = values.reduce((prev, curr) => { return (value > curr ? prev + 1 : prev); - }, 0); + }, 0); // Off the left if (closest === 0) { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 6fd44b691d..ac98664be0 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -32,7 +32,7 @@ export default class AppearanceUserSettingsTab extends React.Component { super(); this.state = { - fontSize: SettingsStore.getValue("font_size", null), + fontSize: SettingsStore.getValue("fontSize", null), ...this._calculateThemeState(), customThemeUrl: "", customThemeMessage: {isError: false, text: ""}, @@ -102,15 +102,15 @@ export default class AppearanceUserSettingsTab extends React.Component { _onFontSizeChanged = (size) => { this.setState({fontSize: size}); - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, size); + SettingsStore.setValue("fontSize", null, SettingLevel.DEVICE, size); }; _onValidateFontSize = ({value}) => { console.log({value}); const parsedSize = parseFloat(value); - const min = SettingsStore.getValue("font_size_min"); - const max = SettingsStore.getValue("font_size_max"); + const min = SettingsStore.getValue("fontSizeMin"); + const max = SettingsStore.getValue("fontSizeMax"); if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; @@ -123,7 +123,7 @@ export default class AppearanceUserSettingsTab extends React.Component { }; } - SettingsStore.setValue("font_size", null, SettingLevel.DEVICE, value); + SettingsStore.setValue("fontSize", null, SettingLevel.DEVICE, value); return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})}; } @@ -253,8 +253,8 @@ export default class AppearanceUserSettingsTab extends React.Component { <div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div> <Slider values={_range( - SettingsStore.getValue("font_size_min"), - SettingsStore.getValue("font_size_max")+ 2, + SettingsStore.getValue("fontSizeMin"), + SettingsStore.getValue("fontSizeMax")+ 2, 2.5, )} value={this.state.fontSize} diff --git a/src/settings/Settings.js b/src/settings/Settings.js index f14dc252ea..34610c0caf 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -165,18 +165,18 @@ export const SETTINGS = { displayName: _td("Show info about bridges in room settings"), default: false, }, - "font_size": { + "fontSize": { displayName: _td("Font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: 16, controller: new FontSizeController(), }, - "font_size_min": { + "fontSizeMin": { displayName: _td("Min font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: 14, }, - "font_size_max": { + "fontSizeMax": { displayName: _td("Max font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: 24, From a38d5eb2244711d89e9c51df2e3f5abf20ed6409 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 14:57:45 +0100 Subject: [PATCH 103/196] add useIRCLayout setting --- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a24b2bde73..3dcba0f546 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -408,6 +408,7 @@ "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", + "Use IRC layout": "Use IRC layout", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 5c6d843349..c51bf44ee5 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -94,6 +94,12 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, + "feature_alternate_message_layouts": { + isFeature: true, + displayName: _td("Alternate message layouts"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), @@ -164,6 +170,11 @@ export const SETTINGS = { default: true, invertedSettingName: 'MessageComposerInput.dontSuggestEmoji', }, + "useIRCLayout": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Use IRC layout'), + default: false, + }, "useCompactLayout": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Use compact timeline layout'), From e0c89f6180331350485ca7331ff9e190a83a3d5f Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 15:06:38 +0100 Subject: [PATCH 104/196] Add switch between layout classes --- src/components/structures/MessagePanel.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6fbfdb504b..0123d43920 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -117,6 +117,7 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), + useIRCLayout: SettingsStore.getValue("useIRCLayout"), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -169,6 +170,8 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); + + this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange); } componentDidMount() { @@ -178,6 +181,7 @@ export default class MessagePanel extends React.Component { componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); + SettingsStore.unwatchSetting(this._layoutWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -196,6 +200,12 @@ export default class MessagePanel extends React.Component { }); }; + onLayoutChange = () => { + this.setState({ + useIRCLayout: SettingsStore.getValue("useIRCLayout"), + }); + } + /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -779,6 +789,8 @@ export default class MessagePanel extends React.Component { this.props.className, { "mx_MessagePanel_alwaysShowTimestamps": this.props.alwaysShowTimestamps, + "mx_IRCLayout": this.state.useIRCLayout, + "mx_GroupLayout": !this.state.useIRCLayout, }, ); From c1e740a59603ae178364cac18c8ecdd5443c9df6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 15:07:17 +0100 Subject: [PATCH 105/196] Break out group layout settings --- res/css/views/rooms/_EventTile.scss | 12 ------- res/css/views/rooms/_GroupLayout.scss | 52 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 res/css/views/rooms/_GroupLayout.scss diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 752cf982f6..b9a41c4310 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -68,11 +68,9 @@ limitations under the License. display: inline-block; /* anti-zalgo, with overflow hidden */ overflow: hidden; cursor: pointer; - padding-left: 65px; /* left gutter */ padding-bottom: 0px; padding-top: 0px; margin: 0px; - line-height: $font-17px; /* the next three lines, along with overflow hidden, truncate long display names */ white-space: nowrap; text-overflow: ellipsis; @@ -101,12 +99,9 @@ limitations under the License. .mx_EventTile .mx_MessageTimestamp { display: block; - visibility: hidden; white-space: nowrap; left: 0px; - width: 46px; /* 8 + 30 (avatar) + 8 */ text-align: center; - position: absolute; user-select: none; } @@ -117,10 +112,7 @@ limitations under the License. .mx_EventTile_line, .mx_EventTile_reply { position: relative; padding-left: 65px; /* left gutter */ - padding-top: 3px; - padding-bottom: 3px; border-radius: 4px; - line-height: $font-22px; } .mx_RoomView_timeline_rr_enabled, @@ -151,10 +143,6 @@ limitations under the License. margin-right: 10px; } -.mx_EventTile_info .mx_EventTile_line { - padding-left: 83px; -} - /* HACK to override line-height which is already marked important elsewhere */ .mx_EventTile_bigEmoji.mx_EventTile_bigEmoji { font-size: 48px !important; diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss new file mode 100644 index 0000000000..6528d6c6cd --- /dev/null +++ b/res/css/views/rooms/_GroupLayout.scss @@ -0,0 +1,52 @@ +/* +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. +*/ + +$left-gutter: 65px; + +.mx_GroupLayout { + + .mx_EventTile { + > .mx_SenderProfile { + line-height: $font-17px; + padding-left: $left-gutter; + } + + > .mx_EventTile_line { + padding-left: $left-gutter; + } + + > .mx_EventTile_avatar { + position: absolute; + } + + .mx_MessageTimestamp { + visibility: hidden; + position: absolute; + width: 46px; /* 8 + 30 (avatar) + 8 */ + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 3px; + padding-bottom: 3px; + line-height: $font-22px; + } + } + + .mx_EventTile_info .mx_EventTile_line { + padding-left: 83px; + } +} From 10c8d253c86334e11c3a8582db6d63e073bcb79e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 15:07:41 +0100 Subject: [PATCH 106/196] Create irc layout --- res/css/views/rooms/_IRCLayout.scss | 124 ++++++++++++++++++++++ src/components/structures/MessagePanel.js | 1 + src/components/views/rooms/EventTile.js | 30 ++++-- 3 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 res/css/views/rooms/_IRCLayout.scss diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss new file mode 100644 index 0000000000..6152749573 --- /dev/null +++ b/res/css/views/rooms/_IRCLayout.scss @@ -0,0 +1,124 @@ +/* +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. +*/ + +$name-width: 50px; +$icon-width: 14px; +$timestamp-width: 45px; +$right-padding: 5px; + +.mx_IRCLayout { + + line-height: $font-22px !important; + + .mx_EventTile { + display: flex; + flex-direction: row; + align-items: flex-start; + + > * { + margin-right: $right-padding; + } + + > .mx_EventTile_msgOption { + order: 4; + flex-shrink: 0; + } + + > .mx_SenderProfile { + order: 2; + flex-shrink: 0; + width: $name-width; + text-overflow: ellipsis; + text-align: right; + display: flex; + align-items: center; + } + + > .mx_EventTile_line { + order: 3; + flex-grow: 1; + } + + > .mx_EventTile_avatar { + order: 1; + position: relative; + top: 0; + left: 0; + flex-shrink: 0; + height: 22px; + display: flex; + align-items: center; + + > .mx_BaseAvatar { + height: 1rem; + width: 1rem; + } + } + + .mx_MessageTimestamp { + font-size: $font-10px; + width: $timestamp-width; + text-align: right; + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding: 0; + } + + .mx_EventTile_e2eIcon { + position: relative; + right: unset; + left: unset; + top: -2px; + padding: 0; + } + + .mx_EventTile_line > * { + display: inline-block; + } + } + + .mx_EventListSummary { + > .mx_EventTile_line { + padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding + } + } + + .mx_EventTile.mx_EventTile_info { + .mx_EventTile_avatar { + left: calc($name-width + 10px + $icon-width); + top: 0; + } + + .mx_EventTile_line { + left: calc($name-width + 10px + $icon-width); + } + + .mx_TextualEvent { + line-height: $font-22px; + } + } + + .mx_EventTile_continuation:not(.mx_EventTile_info) { + .mx_EventTile_avatar { + visibility: hidden; + } + + .mx_SenderProfile { + visibility: hidden; + } + } +} diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 0123d43920..80e5d17bf3 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -607,6 +607,7 @@ export default class MessagePanel extends React.Component { isSelectedEvent={highlight} getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} + useIRCLayout={this.state.useIRCLayout} /> </TileErrorBoundary> </li>, diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index a64fd82eb5..2da9677a17 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -206,6 +206,9 @@ export default createReactClass({ // whether to show reactions for this event showReactions: PropTypes.bool, + + // whether to use the irc layout + useIRCLayout: PropTypes.bool, }, getDefaultProps: function() { @@ -653,6 +656,8 @@ export default createReactClass({ const classes = classNames({ mx_EventTile_bubbleContainer: isBubbleMessage, mx_EventTile: true, + mx_EventTile_irc: this.props.useIRCLayout, + mx_EventTile_group: !this.props.useIRCLayout, mx_EventTile_isEditing: isEditing, mx_EventTile_info: isInfoMessage, mx_EventTile_12hr: this.props.isTwelveHour, @@ -696,6 +701,9 @@ export default createReactClass({ // joins/parts/etc avatarSize = 14; needsSenderProfile = false; + } else if (this.props.useIRCLayout) { + avatarSize = 14; + needsSenderProfile = true; } else if (this.props.continuation && this.props.tileShape !== "file_grid") { // no avatar or sender profile for continuation messages avatarSize = 0; @@ -879,21 +887,29 @@ export default createReactClass({ this.props.permalinkCreator, this._replyThread, ); + + const linkedTimestamp = <a + href={permalink} + onClick={this.onPermalinkClicked} + aria-label={formatTime(new Date(this.props.mxEvent.getTs()), this.props.isTwelveHour)} + > + { timestamp } + </a>; + + const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; + const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; + + // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return ( <div className={classes} tabIndex={-1}> + { ircTimestamp } <div className="mx_EventTile_msgOption"> { readAvatars } </div> { sender } <div className="mx_EventTile_line"> - <a - href={permalink} - onClick={this.onPermalinkClicked} - aria-label={formatTime(new Date(this.props.mxEvent.getTs()), this.props.isTwelveHour)} - > - { timestamp } - </a> + { groupTimestamp } { !isBubbleMessage && this._renderE2EPadlock() } { thread } <EventTileType ref={this._tile} From 37e8dc04776a5c38f832d5887b8d974aa9de67e2 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 15:07:47 +0100 Subject: [PATCH 107/196] Include new css files --- res/css/_components.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/_components.scss b/res/css/_components.scss index 0ba2b609e8..a66d15af18 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -159,6 +159,8 @@ @import "./views/rooms/_EditMessageComposer.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; +@import "./views/rooms/_EventTile_group.scss"; +@import "./views/rooms/_EventTile_irc.scss"; @import "./views/rooms/_InviteOnlyIcon.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; From 6c3e3161de314b109810db0987f04877e5f42a10 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 29 Apr 2020 15:29:25 +0100 Subject: [PATCH 108/196] Reduce padding --- res/css/views/rooms/_IRCLayout.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 6152749573..94ff681029 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -27,6 +27,7 @@ $right-padding: 5px; display: flex; flex-direction: row; align-items: flex-start; + padding-top: 0; > * { margin-right: $right-padding; From 54d211a847c8f198c2547c0cc9fc6c9715d142e5 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 4 May 2020 21:40:52 +0100 Subject: [PATCH 109/196] Index file name changes --- res/css/_components.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/_components.scss b/res/css/_components.scss index a66d15af18..5466b785c0 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -159,8 +159,8 @@ @import "./views/rooms/_EditMessageComposer.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; -@import "./views/rooms/_EventTile_group.scss"; -@import "./views/rooms/_EventTile_irc.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"; From 67249e1e9c17c9886812e1e2e07a71e298d2bb9e Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 4 May 2020 21:53:48 +0100 Subject: [PATCH 110/196] Fix hover --- res/css/views/rooms/_IRCLayout.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 94ff681029..10d8c701c3 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -122,4 +122,12 @@ $right-padding: 5px; visibility: hidden; } } + + // Suppress highlight thing from the normal Layout. + .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, + .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line, + .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line { + padding-left: 0; + border-left: 0; + } } From 027826c2e1ca45cda9588a8d5cee257581048987 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 5 May 2020 10:54:44 +0100 Subject: [PATCH 111/196] Replies have the same layout as messages --- res/css/views/rooms/_IRCLayout.scss | 13 +++++++-- src/components/views/elements/ReplyThread.js | 9 ++++-- src/components/views/rooms/EventTile.js | 29 ++++++++++---------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 10d8c701c3..c7cf2c31c2 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -63,9 +63,12 @@ $right-padding: 5px; display: flex; align-items: center; - > .mx_BaseAvatar { - height: 1rem; - width: 1rem; + // Need to use important to override the js provided height and width values. + > .mx_BaseAvatar, .mx_BaseAvatar > * { + height: $font-14px !important; + width: $font-14px !important; + font-size: $font-10px !important; + line-height: $font-14px !important; } } @@ -90,6 +93,10 @@ $right-padding: 5px; .mx_EventTile_line > * { display: inline-block; } + + .mx_EventTile_reply { + order: 3; + } } .mx_EventListSummary { diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index eae2d13f8a..a8f9599f35 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -37,6 +37,8 @@ export default class ReplyThread extends React.Component { // called when the ReplyThread contents has changed, including EventTiles thereof onHeightChanged: PropTypes.func.isRequired, permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, + // Specifies which layout to use. + useIRCLayout: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -176,12 +178,12 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { if (!ReplyThread.getParentEventId(parentEv)) { return <div />; } return <ReplyThread parentEv={parentEv} onHeightChanged={onHeightChanged} - ref={ref} permalinkCreator={permalinkCreator} />; + ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} />; } componentDidMount() { @@ -331,7 +333,8 @@ export default class ReplyThread extends React.Component { onHeightChanged={this.props.onHeightChanged} permalinkCreator={this.props.permalinkCreator} isRedacted={ev.isRedacted()} - isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} /> + isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} + useIRCLayout={this.props.useIRCLayout} /> </blockquote>; }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 2da9677a17..837c8929b9 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -795,6 +795,17 @@ export default createReactClass({ />; } + const linkedTimestamp = <a + href={permalink} + onClick={this.onPermalinkClicked} + aria-label={formatTime(new Date(this.props.mxEvent.getTs()), this.props.isTwelveHour)} + > + { timestamp } + </a>; + + const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; + const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; + switch (this.props.tileShape) { case 'notif': { const room = this.context.getRoom(this.props.mxEvent.getRoomId()); @@ -862,12 +873,11 @@ export default createReactClass({ } return ( <div className={classes}> + { ircTimestamp } { avatar } { sender } <div className="mx_EventTile_reply"> - <a href={permalink} onClick={this.onPermalinkClicked}> - { timestamp } - </a> + { groupTimestamp } { !isBubbleMessage && this._renderE2EPadlock() } { thread } <EventTileType ref={this._tile} @@ -886,20 +896,9 @@ export default createReactClass({ this.props.onHeightChanged, this.props.permalinkCreator, this._replyThread, + this.props.useIRCLayout, ); - const linkedTimestamp = <a - href={permalink} - onClick={this.onPermalinkClicked} - aria-label={formatTime(new Date(this.props.mxEvent.getTs()), this.props.isTwelveHour)} - > - { timestamp } - </a>; - - const groupTimestamp = !this.props.useIRCLayout ? linkedTimestamp : null; - const ircTimestamp = this.props.useIRCLayout ? linkedTimestamp : null; - - // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers return ( <div className={classes} tabIndex={-1}> From 0af265bf93e703c42492c51b41a23f485f4099fe Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 5 May 2020 16:24:50 +0100 Subject: [PATCH 112/196] Fix replies --- res/css/views/rooms/_IRCLayout.scss | 9 ++++++++- src/components/views/elements/ReplyThread.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index c7cf2c31c2..b45d34013c 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -90,10 +90,17 @@ $right-padding: 5px; padding: 0; } - .mx_EventTile_line > * { + .mx_EventTile_line .mx_EventTile_content, + .mx_EventTile_line .mx_EventTile_e2eIcon, + .mx_eventTile_line > div { display: inline-block; } + .mx_EvenTile_line .mx_MessageActionBar, + .mx_EvenTile_line .mx_ReplyThread_wrapper { + display: block; + } + .mx_EventTile_reply { order: 3; } diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index a8f9599f35..52a94110ba 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -338,7 +338,7 @@ export default class ReplyThread extends React.Component { </blockquote>; }); - return <div> + return <div className="mx_ReplyThread_wrapper"> <div>{ header }</div> <div>{ evTiles }</div> </div>; From 07c2d0cb0256ff7ab359416222d6411e0100594a Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 6 May 2020 09:24:33 +0100 Subject: [PATCH 113/196] Composer reply previews have group layout --- src/components/views/rooms/MessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 4749742a7d..663db5e942 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -370,7 +370,7 @@ export default class MessageComposer extends React.Component { } return ( - <div className="mx_MessageComposer"> + <div className="mx_MessageComposer mx_GroupLayout"> <div className="mx_MessageComposer_wrapper"> <div className="mx_MessageComposer_row"> { controls } From 5568e6488d18c2ef8da79514f852ac562cdcf049 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 6 May 2020 09:51:01 +0100 Subject: [PATCH 114/196] Fix encryption badge layout --- res/css/views/rooms/_IRCLayout.scss | 2 +- src/components/views/elements/ReplyThread.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index b45d34013c..968d8ebdea 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -92,7 +92,7 @@ $right-padding: 5px; .mx_EventTile_line .mx_EventTile_content, .mx_EventTile_line .mx_EventTile_e2eIcon, - .mx_eventTile_line > div { + .mx_EventTile_line .mx_ReplyThread_wrapper_empty { display: inline-block; } diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 52a94110ba..de242f5632 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -180,7 +180,7 @@ export default class ReplyThread extends React.Component { static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { if (!ReplyThread.getParentEventId(parentEv)) { - return <div />; + return <div className="mx_ReplyThread_wrapper_empty" />; } return <ReplyThread parentEv={parentEv} onHeightChanged={onHeightChanged} ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} />; From 771ae5e18f23b7b7954950b43fa7e5e52b70b0e8 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 7 May 2020 13:55:23 +0100 Subject: [PATCH 115/196] Fix encryption badge layouts and replies. Begin removing dependence on slider. Move settings to labs. Username disambiguation. --- res/css/views/rooms/_IRCLayout.scss | 34 +++++++++++++++---- src/components/structures/MessagePanel.js | 16 +++++++-- src/components/views/elements/ReplyThread.js | 10 ++++-- .../views/messages/SenderProfile.js | 3 ++ src/components/views/rooms/EventTile.js | 6 +++- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.js | 17 +++++++--- 7 files changed, 69 insertions(+), 18 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 968d8ebdea..e4536aec20 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -14,14 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -$name-width: 50px; +$name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; .mx_IRCLayout { - line-height: $font-22px !important; + line-height: $font-20px !important; .mx_EventTile { display: flex; @@ -46,11 +46,13 @@ $right-padding: 5px; text-align: right; display: flex; align-items: center; + overflow: visible; } > .mx_EventTile_line { order: 3; flex-grow: 1; + margin-bottom: -6px; } > .mx_EventTile_avatar { @@ -90,10 +92,13 @@ $right-padding: 5px; padding: 0; } - .mx_EventTile_line .mx_EventTile_content, - .mx_EventTile_line .mx_EventTile_e2eIcon, - .mx_EventTile_line .mx_ReplyThread_wrapper_empty { - display: inline-block; + .mx_EventTile_line { + .mx_EventTile_e2eIcon, + .mx_TextualEvent, + .mx_MTextBody, + .mx_ReplyThread_wrapper_empty { + display: inline-block; + } } .mx_EvenTile_line .mx_MessageActionBar, @@ -104,6 +109,10 @@ $right-padding: 5px; .mx_EventTile_reply { order: 3; } + + .mx_EditMessageComposer_buttons { + position: relative; + } } .mx_EventListSummary { @@ -144,4 +153,17 @@ $right-padding: 5px; padding-left: 0; border-left: 0; } + + .mx_SenderProfile_hover { + background-color: $primary-bg-color; + overflow: hidden; + width: $name-width; + transition: width 2s; + } + + .mx_SenderProfile_hover:hover { + overflow: visible; + width: auto; + z-index: 10; + } } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 80e5d17bf3..66ed5ee81f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -117,7 +117,8 @@ export default class MessagePanel extends React.Component { // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: SettingsStore.getValue("useIRCLayout"), + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -171,7 +172,8 @@ export default class MessagePanel extends React.Component { this._showTypingNotificationsWatcherRef = SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); - this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange); + this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); + this._displayAvatarsWatcherRef = SettingsStore.watchSetting("feature_no_timeline_avatars", null, this.onDisplayAvatarsChange); } componentDidMount() { @@ -182,6 +184,7 @@ export default class MessagePanel extends React.Component { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); SettingsStore.unwatchSetting(this._layoutWatcherRef); + SettingsStore.unwatchSetting(this._displayAvatarsWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -202,7 +205,13 @@ export default class MessagePanel extends React.Component { onLayoutChange = () => { this.setState({ - useIRCLayout: SettingsStore.getValue("useIRCLayout"), + useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + }); + } + + onDisplayAvatarsChange = () => { + this.setState({ + displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), }); } @@ -608,6 +617,7 @@ export default class MessagePanel extends React.Component { getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} useIRCLayout={this.state.useIRCLayout} + displayAvatars={this.state.displayAvatars} /> </TileErrorBoundary> </li>, diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index de242f5632..d1d46e709a 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -39,6 +39,8 @@ export default class ReplyThread extends React.Component { permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, // Specifies which layout to use. useIRCLayout: PropTypes.bool, + // Specifies whether to display avatars. + displayAvatars: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -178,12 +180,12 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout, displayAvatars) { if (!ReplyThread.getParentEventId(parentEv)) { return <div className="mx_ReplyThread_wrapper_empty" />; } return <ReplyThread parentEv={parentEv} onHeightChanged={onHeightChanged} - ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} />; + ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} displayAvatars={displayAvatars} />; } componentDidMount() { @@ -334,7 +336,9 @@ export default class ReplyThread extends React.Component { permalinkCreator={this.props.permalinkCreator} isRedacted={ev.isRedacted()} isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} - useIRCLayout={this.props.useIRCLayout} /> + useIRCLayout={this.props.useIRCLayout} + displayAvatars={this.props.displayAvatars} + /> </blockquote>; }); diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index bed93b68c3..d95c9d685a 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -131,7 +131,10 @@ export default createReactClass({ return ( <div className="mx_SenderProfile" dir="auto" onClick={this.props.onClick}> + <div className="mx_SenderProfile_hover"> + { content } { content } + </div> </div> ); }, diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 837c8929b9..b2daa5667f 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -209,6 +209,9 @@ export default createReactClass({ // whether to use the irc layout useIRCLayout: PropTypes.bool, + + // whether to display avatars + displayAvatars: PropTypes.bool, }, getDefaultProps: function() { @@ -713,7 +716,7 @@ export default createReactClass({ needsSenderProfile = true; } - if (this.props.mxEvent.sender && avatarSize) { + if (this.props.mxEvent.sender && avatarSize && this.props.displayAvatars) { avatar = ( <div className="mx_EventTile_avatar"> <MemberAvatar member={this.props.mxEvent.sender} @@ -897,6 +900,7 @@ export default createReactClass({ this.props.permalinkCreator, this._replyThread, this.props.useIRCLayout, + this.props.displayAvatars, ); // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3dcba0f546..a3cd88f9ae 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -409,6 +409,7 @@ "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use IRC layout": "Use IRC layout", + "Display user avatars on messages": "Display user avatars on messages", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index c51bf44ee5..7942aa67fc 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -143,6 +143,18 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_irc_ui": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Use IRC layout'), + default: false, + isFeature: true, + }, + "feature_no_timeline_avatars": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Display user avatars on messages'), + default: true, + isFeature: true, + }, "mjolnirRooms": { supportedLevels: ['account'], default: [], @@ -170,11 +182,6 @@ export const SETTINGS = { default: true, invertedSettingName: 'MessageComposerInput.dontSuggestEmoji', }, - "useIRCLayout": { - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td('Use IRC layout'), - default: false, - }, "useCompactLayout": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Use compact timeline layout'), From 5a0fdb36c0ec70ddb87f1f918b953c4389392509 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 7 May 2020 14:06:40 +0100 Subject: [PATCH 116/196] Remove unused setting --- src/settings/Settings.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 7942aa67fc..032b0ee906 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -94,12 +94,6 @@ export const SETTINGS = { // // not use this for new settings. // invertedSettingName: "my-negative-setting", // }, - "feature_alternate_message_layouts": { - isFeature: true, - displayName: _td("Alternate message layouts"), - supportedLevels: LEVELS_FEATURE, - default: false, - }, "feature_pinning": { isFeature: true, displayName: _td("Message Pinning"), From bc5fc57dd667d89fd774949cb2b940d198518a90 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 7 May 2020 14:22:15 +0100 Subject: [PATCH 117/196] Lint This is why we shouldn't rely on regex --- src/components/structures/MessagePanel.js | 6 +++++- src/components/views/elements/ReplyThread.js | 9 +++++++-- src/i18n/strings/en_EN.json | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 66ed5ee81f..f46df0175a 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -173,7 +173,11 @@ export default class MessagePanel extends React.Component { SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); - this._displayAvatarsWatcherRef = SettingsStore.watchSetting("feature_no_timeline_avatars", null, this.onDisplayAvatarsChange); + this._displayAvatarsWatcherRef = SettingsStore.watchSetting( + "feature_no_timeline_avatars", + null, + this.onDisplayAvatarsChange, + ); } componentDidMount() { diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index d1d46e709a..6bfda5dd94 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -184,8 +184,13 @@ export default class ReplyThread extends React.Component { if (!ReplyThread.getParentEventId(parentEv)) { return <div className="mx_ReplyThread_wrapper_empty" />; } - return <ReplyThread parentEv={parentEv} onHeightChanged={onHeightChanged} - ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} displayAvatars={displayAvatars} />; + return <ReplyThread + parentEv={parentEv} + onHeightChanged={onHeightChanged} + ref={ref} + permalinkCreator={permalinkCreator} + useIRCLayout={useIRCLayout} + displayAvatars={displayAvatars} />; } componentDidMount() { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3cd88f9ae..ca62eb44fa 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -405,11 +405,11 @@ "Multiple integration managers": "Multiple integration managers", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Support adding custom themes": "Support adding custom themes", + "Use IRC layout": "Use IRC layout", + "Display user avatars on messages": "Display user avatars on messages", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", - "Use IRC layout": "Use IRC layout", - "Display user avatars on messages": "Display user avatars on messages", "Use compact timeline layout": "Use compact timeline layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", From ac95172ed4e6981bc5615fb982176d4a8c17a866 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Thu, 7 May 2020 14:56:45 +0100 Subject: [PATCH 118/196] tighter layout --- res/css/views/rooms/_IRCLayout.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index e4536aec20..fcdeef6590 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -21,7 +21,7 @@ $right-padding: 5px; .mx_IRCLayout { - line-height: $font-20px !important; + line-height: $font-18px !important; .mx_EventTile { display: flex; @@ -52,7 +52,6 @@ $right-padding: 5px; > .mx_EventTile_line { order: 3; flex-grow: 1; - margin-bottom: -6px; } > .mx_EventTile_avatar { From 9b7c63a7116c369d3e3e59c3a643067b6666ebca Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Fri, 8 May 2020 20:53:32 +0100 Subject: [PATCH 119/196] Duplicated names --- src/components/views/messages/SenderProfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index d95c9d685a..d512b186e9 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -133,7 +133,6 @@ export default createReactClass({ <div className="mx_SenderProfile" dir="auto" onClick={this.props.onClick}> <div className="mx_SenderProfile_hover"> { content } - { content } </div> </div> ); From 82396661cf86d7c41285cded2d22523e84f3bc23 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Fri, 8 May 2020 22:21:26 +0100 Subject: [PATCH 120/196] Implement nitpicks - usernames are elipsed - icon alignment fixed - replies are more dense - reply messages respond to name widths - fixed between message padding problem (flex ftw) --- res/css/views/rooms/_IRCLayout.scss | 62 +++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index fcdeef6590..f5d8664884 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -18,12 +18,19 @@ $name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; +$irc-line-height: $font-18px; .mx_IRCLayout { - line-height: $font-18px !important; + line-height: $irc-line-height !important; .mx_EventTile { + + // timestamps are links which shouldn't be underlined + > a { + text-decoration: none; + } + display: flex; flex-direction: row; align-items: flex-start; @@ -49,7 +56,10 @@ $right-padding: 5px; overflow: visible; } - > .mx_EventTile_line { + .mx_EventTile_line, .mx_EventTile_reply { + padding: 0; + display: flex; + flex-direction: column; order: 3; flex-grow: 1; } @@ -60,7 +70,7 @@ $right-padding: 5px; top: 0; left: 0; flex-shrink: 0; - height: 22px; + height: $irc-line-height; display: flex; align-items: center; @@ -79,10 +89,6 @@ $right-padding: 5px; text-align: right; } - .mx_EventTile_line, .mx_EventTile_reply { - padding: 0; - } - .mx_EventTile_e2eIcon { position: relative; right: unset; @@ -98,6 +104,8 @@ $right-padding: 5px; .mx_ReplyThread_wrapper_empty { display: inline-block; } + + } .mx_EvenTile_line .mx_MessageActionBar, @@ -114,10 +122,25 @@ $right-padding: 5px; } } + .mx_EventTile_emote { + > .mx_EventTile_avatar { + margin-left: calc($name-width + $icon-width + $right-padding); + } + } + + blockquote { + margin: 0; + } + .mx_EventListSummary { > .mx_EventTile_line { padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding } + + .mx_EventListSummary_avatars { + padding: 0; + margin: 0; + } } .mx_EventTile.mx_EventTile_info { @@ -131,16 +154,16 @@ $right-padding: 5px; } .mx_TextualEvent { - line-height: $font-22px; + line-height: $irc-line-height; } } .mx_EventTile_continuation:not(.mx_EventTile_info) { - .mx_EventTile_avatar { + > .mx_EventTile_avatar { visibility: hidden; } - .mx_SenderProfile { + > .mx_SenderProfile { visibility: hidden; } } @@ -156,8 +179,15 @@ $right-padding: 5px; .mx_SenderProfile_hover { background-color: $primary-bg-color; overflow: hidden; - width: $name-width; - transition: width 2s; + + > span { + display: flex; + + > .mx_SenderProfile_name { + overflow: hidden; + text-overflow: ellipsis; + } + } } .mx_SenderProfile_hover:hover { @@ -165,4 +195,12 @@ $right-padding: 5px; width: auto; z-index: 10; } + + .mx_ReplyThread { + margin: 0; + .mx_SenderProfile { + width: unset; + max-width: $name-width; + } + } } From fef4d882c44dcf93b677bac50cc09097df7f7839 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Fri, 8 May 2020 22:35:40 +0100 Subject: [PATCH 121/196] lint --- res/css/views/rooms/_IRCLayout.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index f5d8664884..301f712ffb 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -104,8 +104,6 @@ $irc-line-height: $font-18px; .mx_ReplyThread_wrapper_empty { display: inline-block; } - - } .mx_EvenTile_line .mx_MessageActionBar, From 5029c3f1434b33fe8b4aae825f5f3e03d4425dc0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 02:16:43 +0100 Subject: [PATCH 122/196] Implement IRC draggable display name width --- res/css/views/rooms/_IRCLayout.scss | 23 +++-- src/components/structures/MessagePanel.js | 7 ++ src/components/structures/ScrollPanel.js | 6 ++ src/components/views/elements/Draggable.tsx | 84 ++++++++++++++++++ .../views/elements/ErrorBoundary.js | 2 +- .../elements/IRCTimelineProfileResizer.tsx | 86 +++++++++++++++++++ src/settings/Settings.js | 7 ++ 7 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 src/components/views/elements/Draggable.tsx create mode 100644 src/components/views/elements/IRCTimelineProfileResizer.tsx diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 301f712ffb..159cfc0aad 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -$name-width: 70px; $icon-width: 14px; $timestamp-width: 45px; $right-padding: 5px; $irc-line-height: $font-18px; .mx_IRCLayout { + --name-width: 70px; line-height: $irc-line-height !important; @@ -48,7 +48,7 @@ $irc-line-height: $font-18px; > .mx_SenderProfile { order: 2; flex-shrink: 0; - width: $name-width; + width: var(--name-width); text-overflow: ellipsis; text-align: right; display: flex; @@ -122,7 +122,7 @@ $irc-line-height: $font-18px; .mx_EventTile_emote { > .mx_EventTile_avatar { - margin-left: calc($name-width + $icon-width + $right-padding); + margin-left: calc(var(--name-width) + $icon-width + $right-padding); } } @@ -132,7 +132,7 @@ $irc-line-height: $font-18px; .mx_EventListSummary { > .mx_EventTile_line { - padding-left: calc($name-width + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding + padding-left: calc(var(--name-width) + $icon-width + $timestamp-width + 3 * $right-padding); // 15 px of padding } .mx_EventListSummary_avatars { @@ -143,12 +143,12 @@ $irc-line-height: $font-18px; .mx_EventTile.mx_EventTile_info { .mx_EventTile_avatar { - left: calc($name-width + 10px + $icon-width); + left: calc(var(--name-width) + 10px + $icon-width); top: 0; } .mx_EventTile_line { - left: calc($name-width + 10px + $icon-width); + left: calc(var(--name-width) + 10px + $icon-width); } .mx_TextualEvent { @@ -198,7 +198,16 @@ $irc-line-height: $font-18px; margin: 0; .mx_SenderProfile { width: unset; - max-width: $name-width; + max-width: var(--name-width); } } + + .mx_ProfileResizer { + position: absolute; + height: 100%; + width: 15px; + left: calc(80px + var(--name-width)); + cursor: col-resize; + z-index: 100; + } } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index f46df0175a..1c10efb346 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -29,6 +29,7 @@ import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; import {textForEvent} from "../../TextForEvent"; +import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = ['m.sticker', 'm.room.message']; @@ -819,6 +820,11 @@ export default class MessagePanel extends React.Component { ); } + let ircResizer = null; + if (this.state.useIRCLayout) { + ircResizer = <IRCTimelineProfileResizer minWidth={20} maxWidth={600} roomId={this.props.room.roomId} />; + } + return ( <ErrorBoundary> <ScrollPanel @@ -831,6 +837,7 @@ export default class MessagePanel extends React.Component { style={style} stickyBottom={this.props.stickyBottom} resizeNotifier={this.props.resizeNotifier} + fixedChildren={ircResizer} > { topSpinner } { this._getEventTiles() } diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 4f44c1a169..cb0114b243 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -144,6 +144,11 @@ export default createReactClass({ /* resizeNotifier: ResizeNotifier to know when middle column has changed size */ resizeNotifier: PropTypes.object, + + /* fixedChildren: allows for children to be passed which are rendered outside + * of the wrapper + */ + fixedChildren: PropTypes.node, }, getDefaultProps: function() { @@ -881,6 +886,7 @@ export default createReactClass({ return (<AutoHideScrollbar wrappedRef={this._collectScroll} onScroll={this.onScroll} className={`mx_ScrollPanel ${this.props.className}`} style={this.props.style}> + { this.props.fixedChildren } <div className="mx_RoomView_messageListWrapper"> <ol ref={this._itemlist} className="mx_RoomView_MessageList" aria-live="polite" role="list"> { this.props.children } diff --git a/src/components/views/elements/Draggable.tsx b/src/components/views/elements/Draggable.tsx new file mode 100644 index 0000000000..98f86fd524 --- /dev/null +++ b/src/components/views/elements/Draggable.tsx @@ -0,0 +1,84 @@ +/* +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'; + +interface IProps { + className: string, + dragFunc: (currentLocation: ILocationState, event: MouseEvent) => ILocationState, + onMouseUp: (event: MouseEvent) => void, +} + +interface IState { + onMouseMove: (event: MouseEvent) => void, + onMouseUp: (event: MouseEvent) => void, + location: ILocationState, +} + +export interface ILocationState { + currentX: number, + currentY: number, +} + +export default class Draggable extends React.Component<IProps, IState> { + + constructor(props: IProps) { + super(props); + + this.state = { + onMouseMove: this.onMouseMove.bind(this), + onMouseUp: this.onMouseUp.bind(this), + location: { + currentX: 0, + currentY: 0, + }, + }; + } + + private onMouseDown = (event: MouseEvent): void => { + this.setState({ + location: { + currentX: event.clientX, + currentY: event.clientY, + }, + }); + + document.addEventListener("mousemove", this.state.onMouseMove); + document.addEventListener("mouseup", this.state.onMouseUp); + console.log("Mouse down") + } + + private onMouseUp = (event: MouseEvent): void => { + document.removeEventListener("mousemove", this.state.onMouseMove); + document.removeEventListener("mouseup", this.state.onMouseUp); + this.props.onMouseUp(event); + console.log("Mouse up") + } + + private onMouseMove(event: MouseEvent): void { + console.log("Mouse Move") + const newLocation = this.props.dragFunc(this.state.location, event); + + this.setState({ + location: newLocation, + }); + } + + render() { + return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} /> + } + +} \ No newline at end of file diff --git a/src/components/views/elements/ErrorBoundary.js b/src/components/views/elements/ErrorBoundary.js index a043b350ab..1abd11f838 100644 --- a/src/components/views/elements/ErrorBoundary.js +++ b/src/components/views/elements/ErrorBoundary.js @@ -73,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent { if (this.state.error) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; - return <div className="mx_ErrorBoundary"> + return <div className="mx_ErrorBoundary mx_IRCLayout"> <div className="mx_ErrorBoundary_body"> <h1>{_t("Something went wrong!")}</h1> <p>{_t( diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx new file mode 100644 index 0000000000..80a86b2005 --- /dev/null +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -0,0 +1,86 @@ +/* +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 SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; +import Draggable, {ILocationState} from './Draggable'; + +interface IProps { + // Current room + roomId: string, + minWidth: number, + maxWidth: number, +}; + +interface IState { + width: number, + IRCLayoutRoot: HTMLElement, +}; + +export default class IRCTimelineProfileResizer extends React.Component<IProps, IState> { + constructor(props: IProps) { + super(props); + + this.state = { + width: SettingsStore.getValue("ircDisplayNameWidth", this.props.roomId), + IRCLayoutRoot: null, + } + }; + + componentDidMount() { + this.setState({ + IRCLayoutRoot: document.querySelector(".mx_IRCLayout") as HTMLElement, + }, () => this.updateCSSWidth(this.state.width)) + } + + private dragFunc = (location: ILocationState, event: React.MouseEvent<Element, MouseEvent>): ILocationState => { + const offset = event.clientX - location.currentX; + const newWidth = this.state.width + offset; + + console.log({offset}) + // If we're trying to go smaller than min width, don't. + if (this.state.width <= this.props.minWidth && offset <= 0) { + return location; + } + + if (this.state.width >= this.props.maxWidth && offset >= 0) { + return location; + } + + this.setState({ + width: newWidth, + }); + + this.updateCSSWidth.bind(this)(newWidth); + + return { + currentX: event.clientX, + currentY: location.currentY, + } + } + + private updateCSSWidth(newWidth: number) { + this.state.IRCLayoutRoot.style.setProperty("--name-width", newWidth + "px"); + } + + private onMoueUp(event: MouseEvent) { + SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width); + } + + render() { + return <Draggable className="mx_ProfileResizer" dragFunc={this.dragFunc.bind(this)} onMouseUp={this.onMoueUp.bind(this)}/> + } +}; \ No newline at end of file diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 032b0ee906..6d741ba3a5 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -531,4 +531,11 @@ export const SETTINGS = { MatrixClient.prototype.setCryptoTrustCrossSignedDevices, true, ), }, + "ircDisplayNameWidth": { + // We specifically want to have room-device > device so that users may set a device default + // with a per-room override. + supportedLevels: ['room-device', 'device'], + displayName: _td("IRC display name width"), + default: 80, + }, }; From d8b6b7b976607d697125b4bdfa5c30b2912edf1c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 13 May 2020 06:24:04 +0100 Subject: [PATCH 123/196] Pass screenAfterLogin through SSO in the callback url Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.js | 14 ++++++-------- src/components/structures/MatrixChat.tsx | 9 ++++++++- src/components/structures/auth/Login.js | 7 +++++-- src/components/structures/auth/SoftLogout.js | 4 +++- src/components/views/auth/Welcome.js | 3 ++- src/components/views/elements/SSOButton.js | 5 +++-- 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 7214031586..8a950dc2e3 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -167,13 +167,9 @@ export default class BasePlatform { setLanguage(preferredLangs: string[]) {} - getSSOCallbackUrl(hsUrl: string, isUrl: string): URL { + getSSOCallbackUrl(hsUrl: string, isUrl: string, fragmentAfterLogin: string): URL { const url = new URL(window.location.href); - // XXX: at this point, the fragment will always be #/login, which is no - // use to anyone. Ideally, we would get the intended fragment from - // MatrixChat.screenAfterLogin so that you could follow #/room links etc - // through an SSO login. - url.hash = ""; + url.hash = fragmentAfterLogin || ""; url.searchParams.set("homeserver", hsUrl); url.searchParams.set("identityServer", isUrl); return url; @@ -183,9 +179,11 @@ export default class BasePlatform { * Begin Single Sign On flows. * @param {MatrixClient} mxClient the matrix client using which we should start the flow * @param {"sso"|"cas"} loginType the type of SSO it is, CAS/SSO. + * @param {string} fragmentAfterLogin the hash to pass to the app during sso callback. */ - startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") { - const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl()); + startSingleSignOn(mxClient: MatrixClient, loginType: "sso" | "cas", fragmentAfterLogin: string) { + const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl(), + fragmentAfterLogin); window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 3929711406..8ff8abb190 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1973,6 +1973,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { render() { // console.log(`Rendering MatrixChat with view ${this.state.view}`); + let fragmentAfterLogin = ""; + if (this.props.initialScreenAfterLogin) { + fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`; + } + let view; if (this.state.view === Views.LOADING) { @@ -2052,7 +2057,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { } } else if (this.state.view === Views.WELCOME) { const Welcome = sdk.getComponent('auth.Welcome'); - view = <Welcome {...this.getServerProperties()} />; + view = <Welcome {...this.getServerProperties()} fragmentAfterLogin={fragmentAfterLogin} />; } else if (this.state.view === Views.REGISTER) { const Registration = sdk.getComponent('structures.auth.Registration'); view = ( @@ -2091,6 +2096,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { defaultDeviceDisplayName={this.props.defaultDeviceDisplayName} onForgotPasswordClick={this.onForgotPasswordClick} onServerConfigChange={this.onServerConfigChange} + fragmentAfterLogin={fragmentAfterLogin} {...this.getServerProperties()} /> ); @@ -2100,6 +2106,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { <SoftLogout realQueryParams={this.props.realQueryParams} onTokenLoginCompleted={this.props.onTokenLoginCompleted} + fragmentAfterLogin={fragmentAfterLogin} /> ); } else { diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 5d3cb69417..e65a223bd6 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -355,7 +355,8 @@ export default createReactClass({ ev.preventDefault(); ev.stopPropagation(); const ssoKind = step === 'm.login.sso' ? 'sso' : 'cas'; - PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind); + PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind, + this.props.fragmentAfterLogin); } else { // Don't intercept - just go through to the register page this.onRegisterClick(ev); @@ -628,7 +629,9 @@ export default createReactClass({ <SSOButton className="mx_Login_sso_link mx_Login_submit" matrixClient={this._loginLogic.createTemporaryClient()} - loginType={loginType} /> + loginType={loginType} + fragmentAfterLogin={this.props.fragmentAfterLogin} + /> </div> ); }, diff --git a/src/components/structures/auth/SoftLogout.js b/src/components/structures/auth/SoftLogout.js index 08ab7e8a61..ede1041f8a 100644 --- a/src/components/structures/auth/SoftLogout.js +++ b/src/components/structures/auth/SoftLogout.js @@ -244,7 +244,9 @@ export default class SoftLogout extends React.Component { <p>{introText}</p> <SSOButton matrixClient={MatrixClientPeg.get()} - loginType={this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso"} /> + loginType={this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso"} + fragmentAfterLogin={this.props.fragmentAfterLogin} + /> </div> ); } diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index 7cbcf65d3c..91ba368f70 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -45,7 +45,8 @@ export default class Welcome extends React.PureComponent { idBaseUrl: isUrl, }); const plaf = PlatformPeg.get(); - const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl()); + const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl(), + this.props.fragmentAfterLogin); return ( <AuthPage> diff --git a/src/components/views/elements/SSOButton.js b/src/components/views/elements/SSOButton.js index 3e0757924b..1126ae3cd7 100644 --- a/src/components/views/elements/SSOButton.js +++ b/src/components/views/elements/SSOButton.js @@ -21,9 +21,9 @@ import PlatformPeg from "../../../PlatformPeg"; import AccessibleButton from "./AccessibleButton"; import {_t} from "../../../languageHandler"; -const SSOButton = ({matrixClient, loginType, ...props}) => { +const SSOButton = ({matrixClient, loginType, fragmentAfterLogin, ...props}) => { const onClick = () => { - PlatformPeg.get().startSingleSignOn(matrixClient, loginType); + PlatformPeg.get().startSingleSignOn(matrixClient, loginType, fragmentAfterLogin); }; return ( @@ -36,6 +36,7 @@ const SSOButton = ({matrixClient, loginType, ...props}) => { SSOButton.propTypes = { matrixClient: PropTypes.object.isRequired, // does not use context as may use a temporary client loginType: PropTypes.oneOf(["sso", "cas"]), // defaults to "sso" in base-apis + fragmentAfterLogin: PropTypes.string, }; export default SSOButton; From 52e3c97f8c9a2032fe92b2b4bb5fc68c0f6957b5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@googlemail.com> Date: Wed, 13 May 2020 06:36:14 +0100 Subject: [PATCH 124/196] Revert "ImageView make clicking off it easier" --- res/css/views/elements/_ImageView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 983ef074f2..0a4ed2a194 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -37,7 +37,7 @@ limitations under the License. order: 2; /* min-width hack needed for FF */ min-width: 0px; - max-height: 90%; + height: 90%; flex: 15 15 0; display: flex; align-items: center; From d11923e2e3d3f4189d8848349f0ec1073ea8f91e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 13 May 2020 10:38:32 +0100 Subject: [PATCH 125/196] Add new keyboard shortcuts for jump to unread and upload file Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/accessibility/KeyboardShortcuts.tsx | 41 +++++++++++++++---- src/components/structures/RoomView.js | 14 ++++++- src/components/views/rooms/MessageComposer.js | 13 +++++- 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/accessibility/KeyboardShortcuts.tsx b/src/accessibility/KeyboardShortcuts.tsx index bcbf3d6810..f527ab4a14 100644 --- a/src/accessibility/KeyboardShortcuts.tsx +++ b/src/accessibility/KeyboardShortcuts.tsx @@ -34,6 +34,7 @@ export enum Categories { CALLS = "Calls", COMPOSER = "Composer", ROOM_LIST = "Room List", + ROOM = "Room", AUTOCOMPLETE = "Autocomplete", } @@ -142,6 +143,34 @@ const shortcuts: Record<Categories, IShortcut[]> = { }, ], + [Categories.ROOM]: [ + { + keybinds: [{ + key: Key.PAGE_UP, + }, { + key: Key.PAGE_DOWN, + }], + description: _td("Scroll up/down in the timeline"), + }, { + keybinds: [{ + key: Key.ESCAPE, + }], + description: _td("Dismiss read marker and jump to bottom"), + }, { + keybinds: [{ + modifiers: [Modifiers.SHIFT], + key: Key.PAGE_UP, + }], + description: _td("Jump to oldest unread message"), + }, { + keybinds: [{ + modifiers: [CMD_OR_CTRL, Modifiers.SHIFT], + key: Key.U, + }], + description: _td("Upload a file"), + } + ], + [Categories.ROOM_LIST]: [ { keybinds: [{ @@ -181,13 +210,6 @@ const shortcuts: Record<Categories, IShortcut[]> = { [Categories.NAVIGATION]: [ { - keybinds: [{ - key: Key.PAGE_UP, - }, { - key: Key.PAGE_DOWN, - }], - description: _td("Scroll up/down in the timeline"), - }, { keybinds: [{ modifiers: [Modifiers.ALT, Modifiers.SHIFT], key: Key.ARROW_UP, @@ -257,10 +279,11 @@ const shortcuts: Record<Categories, IShortcut[]> = { const categoryOrder = [ Categories.COMPOSER, - Categories.CALLS, - Categories.ROOM_LIST, Categories.AUTOCOMPLETE, + Categories.ROOM, + Categories.ROOM_LIST, Categories.NAVIGATION, + Categories.CALLS, ]; interface IModal { diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f53929df4a..e413c33efa 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -41,7 +41,7 @@ import * as ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; import eventSearch from '../../Searching'; -import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard'; +import {isOnlyCtrlOrCmdIgnoreShiftKeyEvent, isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard'; import MainSplit from './MainSplit'; import RightPanel from './RightPanel'; @@ -588,6 +588,18 @@ export default createReactClass({ handled = true; } break; + case Key.PAGE_UP: + if (!ev.altKey && !ev.ctrlKey && ev.shiftKey && !ev.metaKey) { + this.jumpToReadMarker(); + handled = true; + } + break; + case Key.U.toUpperCase(): + if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) { + dis.dispatch({ action: "upload_file" }) + handled = true; + } + break; } if (handled) { diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 4749742a7d..1142e91057 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -114,8 +114,19 @@ class UploadButton extends React.Component { this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this); this._uploadInput = createRef(); + this._dispatcherRef = dis.register(this.onAction); } + componentWillUnmount() { + dis.unregister(this._dispatcherRef); + } + + onAction = payload => { + if (payload.action === "upload_file") { + this.onUploadClick(); + } + }; + onUploadClick(ev) { if (MatrixClientPeg.get().isGuest()) { dis.dispatch({action: 'require_registration'}); @@ -128,7 +139,7 @@ class UploadButton extends React.Component { if (ev.target.files.length === 0) return; // take a copy so we can safely reset the value of the form control - // (Note it is a FileList: we can't use slice or sesnible iteration). + // (Note it is a FileList: we can't use slice or sensible iteration). const tfiles = []; for (let i = 0; i < ev.target.files.length; ++i) { tfiles.push(ev.target.files[i]); From 0e05e6db86467c730b853c8f8d7009f2a28d8e85 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 13 May 2020 10:40:23 +0100 Subject: [PATCH 126/196] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b2fe409aa5..d469103897 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2284,13 +2284,16 @@ "Cancel replying to a message": "Cancel replying to a message", "Toggle microphone mute": "Toggle microphone mute", "Toggle video on/off": "Toggle video on/off", + "Scroll up/down in the timeline": "Scroll up/down in the timeline", + "Dismiss read marker and jump to bottom": "Dismiss read marker and jump to bottom", + "Jump to oldest unread message": "Jump to oldest unread message", + "Upload a file": "Upload a file", "Jump to room search": "Jump to room search", "Navigate up/down in the room list": "Navigate up/down in the room list", "Select room from the room list": "Select room from the room list", "Collapse room list section": "Collapse room list section", "Expand room list section": "Expand room list section", "Clear room list filter field": "Clear room list filter field", - "Scroll up/down in the timeline": "Scroll up/down in the timeline", "Previous/next unread room or DM": "Previous/next unread room or DM", "Previous/next room or DM": "Previous/next room or DM", "Toggle the top left menu": "Toggle the top left menu", From 51f59c6c327873bd796eab6bdd91f20c854a66a1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 13 May 2020 11:40:56 +0100 Subject: [PATCH 127/196] UserView, show Welcome page in the mid panel instead of empty space Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/UserView.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/UserView.js b/src/components/structures/UserView.js index 493cc136d1..694592af88 100644 --- a/src/components/structures/UserView.js +++ b/src/components/structures/UserView.js @@ -22,6 +22,7 @@ import {MatrixClientPeg} from "../../MatrixClientPeg"; import * as sdk from "../../index"; import Modal from '../../Modal'; import { _t } from '../../languageHandler'; +import HomePage from "./HomePage"; export default class UserView extends React.Component { static get propTypes() { @@ -79,7 +80,7 @@ export default class UserView extends React.Component { const RightPanel = sdk.getComponent('structures.RightPanel'); const MainSplit = sdk.getComponent('structures.MainSplit'); const panel = <RightPanel user={this.state.member} />; - return (<MainSplit panel={panel}><div style={{flex: "1"}} /></MainSplit>); + return (<MainSplit panel={panel}><HomePage /></MainSplit>); } else { return (<div />); } From 3f04f5163a963b9193199abb340719061ae0e921 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 14:04:46 +0100 Subject: [PATCH 128/196] Implement more nitpicks - fix avatar inital aligment - right align names - set flair height to avatar's - fix conditions for resizing to be more stable --- res/css/views/rooms/_IRCLayout.scss | 13 ++++++++++++- .../views/elements/IRCTimelineProfileResizer.tsx | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 159cfc0aad..8d48c72f8a 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -54,6 +54,7 @@ $irc-line-height: $font-18px; display: flex; align-items: center; overflow: visible; + justify-content: flex-end; } .mx_EventTile_line, .mx_EventTile_reply { @@ -79,7 +80,7 @@ $irc-line-height: $font-18px; height: $font-14px !important; width: $font-14px !important; font-size: $font-10px !important; - line-height: $font-14px !important; + line-height: $font-15px !important; } } @@ -188,6 +189,10 @@ $irc-line-height: $font-18px; } } + .mx_SenderProfile:hover { + justify-content: flex-start; + } + .mx_SenderProfile_hover:hover { overflow: visible; width: auto; @@ -210,4 +215,10 @@ $irc-line-height: $font-18px; cursor: col-resize; z-index: 100; } + + // Need to use important to override the js provided height and width values. + .mx_Flair > img { + height: $font-14px !important; + width: $font-14px !important; + } } diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 80a86b2005..44ceeb9b7b 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -52,11 +52,11 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I console.log({offset}) // If we're trying to go smaller than min width, don't. - if (this.state.width <= this.props.minWidth && offset <= 0) { + if (newWidth < this.props.minWidth) { return location; } - if (this.state.width >= this.props.maxWidth && offset >= 0) { + if (newWidth > this.props.maxWidth) { return location; } From 312b616d7701e9b9f8460d210bc22a07c1253be9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 14:11:16 +0100 Subject: [PATCH 129/196] fix i18n --- src/i18n/strings/en_EN.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ca62eb44fa..37b9c1dfc8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -453,6 +453,7 @@ "Keep recovery passphrase in memory for this session": "Keep recovery passphrase in memory for this session", "How fast should messages be downloaded.": "How fast should messages be downloaded.", "Manually verify all remote sessions": "Manually verify all remote sessions", + "IRC display name width": "IRC display name width", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", From 328bb7bcaf05621443c7a75ab791a9c8eb0ac884 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 15:24:08 +0100 Subject: [PATCH 130/196] Remove all animations --- res/css/_common.scss | 1 - res/css/views/elements/_Slider.scss | 3 --- 2 files changed, 4 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index 687a238c8e..03442ca510 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -19,7 +19,6 @@ limitations under the License. @import "./_font-sizes.scss"; :root { - transition: font-size 0.25s; font-size: 15px; } diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 09afb58b12..06c3c4c98b 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -50,7 +50,6 @@ limitations under the License. } .mx_Slider_selectionDot { - transition: left 0.25s; position: absolute; width: 1.1em; height: 1.1em; @@ -61,13 +60,11 @@ limitations under the License. } .mx_Slider_selection > hr { - transition: width 0.25s; margin: 0; border: 0.2em solid $slider-selection-color; } .mx_Slider_dot { - transition: background-color 0.2s ease-in; height: 1em; width: 1em; border-radius: 50%; From fea219915f4a666d73259406323b185a9d5067f6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 15:26:11 +0100 Subject: [PATCH 131/196] fix code regeression --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 0cee29233f..21e406aa23 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -60,9 +60,6 @@ export default class GeneralUserSettingsTab extends React.Component { emails: [], msisdns: [], loading3pids: true, // whether or not the emails and msisdns have been loaded - ...this._calculateThemeState(), - customThemeUrl: "", - customThemeMessage: {isError: false, text: ""}, }; this.dispatcherRef = dis.register(this._onAction); From 20ec900405721dce6faf130a20223ff4faa01ff6 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 15:36:53 +0100 Subject: [PATCH 132/196] Set font range --- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 6 +----- src/settings/Settings.js | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index ac98664be0..1ccc744dc7 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -252,11 +252,7 @@ export default class AppearanceUserSettingsTab extends React.Component { <div className="mx_AppearanceUserSettingsTab_fontSlider"> <div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div> <Slider - values={_range( - SettingsStore.getValue("fontSizeMin"), - SettingsStore.getValue("fontSizeMax")+ 2, - 2.5, - )} + values={[13, 14, 15, 18, 20]} value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} displayFunc={value => {}} diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 34610c0caf..afe8d2cecc 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -174,12 +174,12 @@ export const SETTINGS = { "fontSizeMin": { displayName: _td("Min font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 14, + default: 13, }, "fontSizeMax": { displayName: _td("Max font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 24, + default: 20, }, "useCustomFontSize": { displayName: _td("Custom font size"), From 5c2abcf1a4b7bd9cbc35dd6c297073db56eb8a52 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 17:05:37 +0100 Subject: [PATCH 133/196] Show username on continuations --- res/css/views/rooms/_IRCLayout.scss | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index 8d48c72f8a..f2a616f9c9 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -157,16 +157,6 @@ $irc-line-height: $font-18px; } } - .mx_EventTile_continuation:not(.mx_EventTile_info) { - > .mx_EventTile_avatar { - visibility: hidden; - } - - > .mx_SenderProfile { - visibility: hidden; - } - } - // Suppress highlight thing from the normal Layout. .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line, From fc6e5227aced6f801bc1f1ffa3c7cfc86d84ef6d Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 22:08:29 +0100 Subject: [PATCH 134/196] FIx roomsublist heights. - also fiddles the font size numbers --- res/css/structures/_TagPanel.scss | 2 +- res/css/views/rooms/_RoomTile.scss | 3 ++- src/components/structures/RoomSubList.js | 4 ++-- src/components/views/avatars/BaseAvatar.js | 20 +++++++++---------- src/components/views/rooms/EventTile.js | 2 +- .../views/rooms/ReadReceiptMarker.js | 2 +- .../tabs/user/AppearanceUserSettingsTab.js | 2 +- src/utils/{rem.js => units.ts} | 9 ++++++++- 8 files changed, 26 insertions(+), 18 deletions(-) rename src/utils/{rem.js => units.ts} (77%) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 536c88be63..1f8443e395 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -69,7 +69,7 @@ limitations under the License. height: 100%; } .mx_TagPanel .mx_TagPanel_tagTileContainer > div { - height: $font-40px; + height: 40px; padding: 10px 0 9px 0; } diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 5bc7d5624d..759dce5afa 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -20,7 +20,7 @@ limitations under the License. flex-direction: row; align-items: center; cursor: pointer; - height: $font-34px; + height: 32px; margin: 0; padding: 0 8px 0 10px; position: relative; @@ -81,6 +81,7 @@ limitations under the License. .mx_RoomTile_avatar_container { position: relative; + display: flex; } .mx_RoomTile_avatar { diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index c8f9ba1713..245b5f9bea 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -32,7 +32,7 @@ import RoomTile from "../views/rooms/RoomTile"; import LazyRenderList from "../views/elements/LazyRenderList"; import {_t} from "../../languageHandler"; import {RovingTabIndexWrapper} from "../../accessibility/RovingTabIndex"; -import {toRem} from "../../utils/rem"; +import {toPx} from "../../utils/units"; // turn this on for drop & drag console debugging galore const debug = false; @@ -420,7 +420,7 @@ export default class RoomSubList extends React.PureComponent { setHeight = (height) => { if (this._subList.current) { - this._subList.current.style.height = toRem(height); + this._subList.current.style.height = toPx(height); } this._updateLazyRenderHeight(height); }; diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index a9bee9cda0..704e6438c8 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -24,7 +24,7 @@ import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import {toRem} from "../../../utils/rem"; +import {toPx} from "../../../utils/units"; export default createReactClass({ displayName: 'BaseAvatar', @@ -166,9 +166,9 @@ export default createReactClass({ const textNode = ( <span className="mx_BaseAvatar_initial" aria-hidden="true" style={{ - fontSize: toRem(width * 0.65), - width: toRem(width), - lineHeight: toRem(height), + fontSize: toPx(width * 0.65), + width: toPx(width), + lineHeight: toPx(height), }} > { initialLetter } @@ -179,8 +179,8 @@ export default createReactClass({ alt="" title={title} onError={this.onError} aria-hidden="true" style={{ - width: toRem(width), - height: toRem(height) + width: toPx(width), + height: toPx(height) }} /> ); if (onClick != null) { @@ -210,8 +210,8 @@ export default createReactClass({ onClick={onClick} onError={this.onError} style={{ - width: toRem(width), - height: toRem(height), + width: toPx(width), + height: toPx(height), }} title={title} alt="" inputRef={inputRef} @@ -224,8 +224,8 @@ export default createReactClass({ src={imageUrl} onError={this.onError} style={{ - width: toRem(width), - height: toRem(height), + width: toPx(width), + height: toPx(height), }} title={title} alt="" ref={inputRef} diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 0881fb3b67..993639321a 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -34,7 +34,7 @@ import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; import * as ObjectUtils from "../../../ObjectUtils"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {E2E_STATE} from "./E2EIcon"; -import {toRem} from "../../../utils/rem"; +import {toRem} from "../../../utils/units"; const eventTileTypes = { 'm.room.message': 'messages.MessageEvent', diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index 20d39a7f84..2fe577377d 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -23,7 +23,7 @@ import { _t } from '../../../languageHandler'; import {formatDate} from '../../../DateUtils'; import Velociraptor from "../../../Velociraptor"; import * as sdk from "../../../index"; -import {toRem} from "../../../utils/rem"; +import {toRem} from "../../../utils/units"; let bounce = false; try { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 1ccc744dc7..63857ed9c2 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -252,7 +252,7 @@ export default class AppearanceUserSettingsTab extends React.Component { <div className="mx_AppearanceUserSettingsTab_fontSlider"> <div className="mx_AppearanceUserSettingsTab_fontSlider_smallText">Aa</div> <Slider - values={[13, 14, 15, 18, 20]} + values={[13, 15, 16, 18, 20]} value={this.state.fontSize} onSelectionChange={this._onFontSizeChanged} displayFunc={value => {}} diff --git a/src/utils/rem.js b/src/utils/units.ts similarity index 77% rename from src/utils/rem.js rename to src/utils/units.ts index 8784502961..54dd6b0523 100644 --- a/src/utils/rem.js +++ b/src/utils/units.ts @@ -14,7 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* Simple utils for formatting style values + */ + // converts a pixel value to rem. -export function toRem(pixelValue) { +export function toRem(pixelValue: number): string { return pixelValue / 15 + "rem"; } + +export function toPx(pixelValue: number): string { + return pixelValue + "px"; +} From 03e090f4e6f65f71698d7ef118b491bd7ed580d0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 13 May 2020 22:55:14 +0100 Subject: [PATCH 135/196] fix chevron --- res/css/structures/_TopLeftMenuButton.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/structures/_TopLeftMenuButton.scss b/res/css/structures/_TopLeftMenuButton.scss index 53d44e7c24..8d2e36bcd6 100644 --- a/res/css/structures/_TopLeftMenuButton.scss +++ b/res/css/structures/_TopLeftMenuButton.scss @@ -43,7 +43,7 @@ limitations under the License. margin: 0 7px; mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); mask-repeat: no-repeat; - width: 10px; + width: $font-22px; height: 6px; background-color: $roomsublist-label-fg-color; } From fa83df4bde7b317737a27a8cb24d2b44aa633750 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Wed, 13 May 2020 21:07:29 -0600 Subject: [PATCH 136/196] Convert dispatcher to TypeScript and replace async usage with new class Due to TypeScript and flux's types being annoying and highly typesafe, we need an AsyncActionPayload which intentionally doesn't use the 'action' property. This looks a bit awkward, though for the rare cases we do actually fire async actions it should be fine enough. The call signature changes slightly for async events, therefore this commit also updates its usages for async events. The `fire()` function is to be used in a future commit. Remove biased comment --- package.json | 1 + src/actions/RoomListActions.js | 145 ----------------- src/actions/RoomListActions.ts | 151 ++++++++++++++++++ src/actions/TagOrderActions.js | 109 ------------- src/actions/TagOrderActions.ts | 112 +++++++++++++ .../{actionCreators.js => actionCreators.ts} | 14 +- src/dispatcher.js | 58 ------- src/dispatcher.ts | 121 ++++++++++++++ tsconfig.json | 3 +- yarn.lock | 21 +++ 10 files changed, 417 insertions(+), 318 deletions(-) delete mode 100644 src/actions/RoomListActions.js create mode 100644 src/actions/RoomListActions.ts delete mode 100644 src/actions/TagOrderActions.js create mode 100644 src/actions/TagOrderActions.ts rename src/actions/{actionCreators.js => actionCreators.ts} (80%) delete mode 100644 src/dispatcher.js create mode 100644 src/dispatcher.ts diff --git a/package.json b/package.json index 92d228a812..2258d5e3b6 100644 --- a/package.json +++ b/package.json @@ -118,6 +118,7 @@ "@babel/register": "^7.7.4", "@peculiar/webcrypto": "^1.0.22", "@types/classnames": "^2.2.10", + "@types/flux": "^3.1.9", "@types/modernizr": "^3.5.3", "@types/react": "16.9", "babel-eslint": "^10.0.3", diff --git a/src/actions/RoomListActions.js b/src/actions/RoomListActions.js deleted file mode 100644 index 10a3848dda..0000000000 --- a/src/actions/RoomListActions.js +++ /dev/null @@ -1,145 +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. -*/ - -import { asyncAction } from './actionCreators'; -import RoomListStore, {TAG_DM} from '../stores/RoomListStore'; -import Modal from '../Modal'; -import * as Rooms from '../Rooms'; -import { _t } from '../languageHandler'; -import * as sdk from '../index'; - -const RoomListActions = {}; - -/** - * Creates an action thunk that will do an asynchronous request to - * tag room. - * - * @param {MatrixClient} matrixClient the matrix client to set the - * account data on. - * @param {Room} room the room to tag. - * @param {string} oldTag the tag to remove (unless oldTag ==== newTag) - * @param {string} newTag the tag with which to tag the room. - * @param {?number} oldIndex the previous position of the room in the - * list of rooms. - * @param {?number} newIndex the new position of the room in the list - * of rooms. - * @returns {function} an action thunk. - * @see asyncAction - */ -RoomListActions.tagRoom = function(matrixClient, room, oldTag, newTag, oldIndex, newIndex) { - let metaData = null; - - // Is the tag ordered manually? - if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { - const lists = RoomListStore.getRoomLists(); - const newList = [...lists[newTag]]; - - newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); - - // If the room was moved "down" (increasing index) in the same list we - // need to use the orders of the tiles with indices shifted by +1 - const offset = ( - newTag === oldTag && oldIndex < newIndex - ) ? 1 : 0; - - const indexBefore = offset + newIndex - 1; - const indexAfter = offset + newIndex; - - const prevOrder = indexBefore <= 0 ? - 0 : newList[indexBefore].tags[newTag].order; - const nextOrder = indexAfter >= newList.length ? - 1 : newList[indexAfter].tags[newTag].order; - - metaData = { - order: (prevOrder + nextOrder) / 2.0, - }; - } - - return asyncAction('RoomListActions.tagRoom', () => { - const promises = []; - const roomId = room.roomId; - - // Evil hack to get DMs behaving - if ((oldTag === undefined && newTag === TAG_DM) || - (oldTag === TAG_DM && newTag === undefined) - ) { - return Rooms.guessAndSetDMRoom( - room, newTag === TAG_DM, - ).catch((err) => { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to set direct chat tag " + err); - Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, { - title: _t('Failed to set direct chat tag'), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - }); - } - - const hasChangedSubLists = oldTag !== newTag; - - // More evilness: We will still be dealing with moving to favourites/low prio, - // but we avoid ever doing a request with TAG_DM. - // - // if we moved lists, remove the old tag - if (oldTag && oldTag !== TAG_DM && - hasChangedSubLists - ) { - const promiseToDelete = matrixClient.deleteRoomTag( - roomId, oldTag, - ).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, { - title: _t('Failed to remove tag %(tagName)s from room', {tagName: oldTag}), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - }); - - promises.push(promiseToDelete); - } - - // if we moved lists or the ordering changed, add the new tag - if (newTag && newTag !== TAG_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 ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to add tag " + newTag + " to room: " + err); - Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { - title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}), - description: ((err && err.message) ? err.message : _t('Operation failed')), - }); - - throw err; - }); - - promises.push(promiseToAdd); - } - - return Promise.all(promises); - }, () => { - // For an optimistic update - return { - room, oldTag, newTag, metaData, - }; - }); -}; - -export default RoomListActions; diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts new file mode 100644 index 0000000000..8ad3ad0781 --- /dev/null +++ b/src/actions/RoomListActions.ts @@ -0,0 +1,151 @@ +/* +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. +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 { asyncAction } from './actionCreators'; +import RoomListStore, { TAG_DM } from '../stores/RoomListStore'; +import Modal from '../Modal'; +import * as Rooms from '../Rooms'; +import { _t } from '../languageHandler'; +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"; + +export default class RoomListActions { + /** + * Creates an action thunk that will do an asynchronous request to + * tag room. + * + * @param {MatrixClient} matrixClient the matrix client to set the + * account data on. + * @param {Room} room the room to tag. + * @param {string} oldTag the tag to remove (unless oldTag ==== newTag) + * @param {string} newTag the tag with which to tag the room. + * @param {?number} oldIndex the previous position of the room in the + * list of rooms. + * @param {?number} newIndex the new position of the room in the list + * of rooms. + * @returns {AsyncActionPayload} an async action payload + * @see asyncAction + */ + public static tagRoom( + matrixClient: MatrixClient, room: Room, + oldTag: string, newTag: string, + oldIndex: number | null, newIndex: number | null, + ): AsyncActionPayload { + let metaData = null; + + // Is the tag ordered manually? + if (newTag && !newTag.match(/^(m\.lowpriority|im\.vector\.fake\.(invite|recent|direct|archived))$/)) { + const lists = RoomListStore.getRoomLists(); + const newList = [...lists[newTag]]; + + newList.sort((a, b) => a.tags[newTag].order - b.tags[newTag].order); + + // If the room was moved "down" (increasing index) in the same list we + // need to use the orders of the tiles with indices shifted by +1 + const offset = ( + newTag === oldTag && oldIndex < newIndex + ) ? 1 : 0; + + const indexBefore = offset + newIndex - 1; + const indexAfter = offset + newIndex; + + const prevOrder = indexBefore <= 0 ? + 0 : newList[indexBefore].tags[newTag].order; + const nextOrder = indexAfter >= newList.length ? + 1 : newList[indexAfter].tags[newTag].order; + + metaData = { + order: (prevOrder + nextOrder) / 2.0, + }; + } + + return asyncAction('RoomListActions.tagRoom', () => { + const promises = []; + const roomId = room.roomId; + + // Evil hack to get DMs behaving + if ((oldTag === undefined && newTag === TAG_DM) || + (oldTag === TAG_DM && newTag === undefined) + ) { + return Rooms.guessAndSetDMRoom( + room, newTag === TAG_DM, + ).catch((err) => { + const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Failed to set direct chat tag " + err); + Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, { + title: _t('Failed to set direct chat tag'), + description: ((err && err.message) ? err.message : _t('Operation failed')), + }); + }); + } + + const hasChangedSubLists = oldTag !== newTag; + + // More evilness: We will still be dealing with moving to favourites/low prio, + // but we avoid ever doing a request with TAG_DM. + // + // if we moved lists, remove the old tag + if (oldTag && oldTag !== TAG_DM && + hasChangedSubLists + ) { + const promiseToDelete = matrixClient.deleteRoomTag( + roomId, oldTag, + ).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, { + title: _t('Failed to remove tag %(tagName)s from room', {tagName: oldTag}), + description: ((err && err.message) ? err.message : _t('Operation failed')), + }); + }); + + promises.push(promiseToDelete); + } + + // if we moved lists or the ordering changed, add the new tag + if (newTag && newTag !== TAG_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 ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + console.error("Failed to add tag " + newTag + " to room: " + err); + Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { + title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}), + description: ((err && err.message) ? err.message : _t('Operation failed')), + }); + + throw err; + }); + + promises.push(promiseToAdd); + } + + return Promise.all(promises); + }, () => { + // For an optimistic update + return { + room, oldTag, newTag, metaData, + }; + }); + } +} diff --git a/src/actions/TagOrderActions.js b/src/actions/TagOrderActions.js deleted file mode 100644 index a257ff16d8..0000000000 --- a/src/actions/TagOrderActions.js +++ /dev/null @@ -1,109 +0,0 @@ -/* -Copyright 2017 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. -*/ - -import Analytics from '../Analytics'; -import { asyncAction } from './actionCreators'; -import TagOrderStore from '../stores/TagOrderStore'; - -const TagOrderActions = {}; - -/** - * Creates an action thunk that will do an asynchronous request to - * move a tag in TagOrderStore to destinationIx. - * - * @param {MatrixClient} matrixClient the matrix client to set the - * account data on. - * @param {string} tag the tag to move. - * @param {number} destinationIx the new position of the tag. - * @returns {function} an action thunk that will dispatch actions - * indicating the status of the request. - * @see asyncAction - */ -TagOrderActions.moveTag = function(matrixClient, tag, destinationIx) { - // Only commit tags if the state is ready, i.e. not null - let tags = TagOrderStore.getOrderedTags(); - let removedTags = TagOrderStore.getRemovedTagsAccountData() || []; - if (!tags) { - return; - } - - tags = tags.filter((t) => t !== tag); - tags = [...tags.slice(0, destinationIx), tag, ...tags.slice(destinationIx)]; - - removedTags = removedTags.filter((t) => t !== tag); - - const storeId = TagOrderStore.getStoreId(); - - return asyncAction('TagOrderActions.moveTag', () => { - Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); - return matrixClient.setAccountData( - 'im.vector.web.tag_ordering', - {tags, removedTags, _storeId: storeId}, - ); - }, () => { - // For an optimistic update - return {tags, removedTags}; - }); -}; - -/** - * Creates an action thunk that will do an asynchronous request to - * label a tag as removed in im.vector.web.tag_ordering account data. - * - * The reason this is implemented with new state `removedTags` is that - * we incrementally and initially populate `tags` with groups that - * have been joined. If we remove a group from `tags`, it will just - * get added (as it looks like a group we've recently joined). - * - * NB: If we ever support adding of tags (which is planned), we should - * take special care to remove the tag from `removedTags` when we add - * it. - * - * @param {MatrixClient} matrixClient the matrix client to set the - * account data on. - * @param {string} tag the tag to remove. - * @returns {function} an action thunk that will dispatch actions - * indicating the status of the request. - * @see asyncAction - */ -TagOrderActions.removeTag = function(matrixClient, tag) { - // Don't change tags, just removedTags - const tags = TagOrderStore.getOrderedTags(); - const removedTags = TagOrderStore.getRemovedTagsAccountData() || []; - - if (removedTags.includes(tag)) { - // Return a thunk that doesn't do anything, we don't even need - // an asynchronous action here, the tag is already removed. - return () => {}; - } - - removedTags.push(tag); - - const storeId = TagOrderStore.getStoreId(); - - return asyncAction('TagOrderActions.removeTag', () => { - Analytics.trackEvent('TagOrderActions', 'removeTag'); - return matrixClient.setAccountData( - 'im.vector.web.tag_ordering', - {tags, removedTags, _storeId: storeId}, - ); - }, () => { - // For an optimistic update - return {removedTags}; - }); -}; - -export default TagOrderActions; diff --git a/src/actions/TagOrderActions.ts b/src/actions/TagOrderActions.ts new file mode 100644 index 0000000000..57dd0b8793 --- /dev/null +++ b/src/actions/TagOrderActions.ts @@ -0,0 +1,112 @@ +/* +Copyright 2017 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import Analytics from '../Analytics'; +import { asyncAction } from './actionCreators'; +import TagOrderStore from '../stores/TagOrderStore'; +import { AsyncActionPayload } from "../dispatcher"; +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. + * + * @param {MatrixClient} matrixClient the matrix client to set the + * account data on. + * @param {string} tag the tag to move. + * @param {number} destinationIx the new position of the tag. + * @returns {AsyncActionPayload} an async action payload that will + * dispatch actions indicating the status of the request. + * @see asyncAction + */ + public static moveTag(matrixClient: MatrixClient, tag: string, destinationIx: number): AsyncActionPayload { + // Only commit tags if the state is ready, i.e. not null + let tags = TagOrderStore.getOrderedTags(); + let removedTags = TagOrderStore.getRemovedTagsAccountData() || []; + if (!tags) { + return; + } + + tags = tags.filter((t) => t !== tag); + tags = [...tags.slice(0, destinationIx), tag, ...tags.slice(destinationIx)]; + + removedTags = removedTags.filter((t) => t !== tag); + + const storeId = TagOrderStore.getStoreId(); + + return asyncAction('TagOrderActions.moveTag', () => { + Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); + return matrixClient.setAccountData( + 'im.vector.web.tag_ordering', + {tags, removedTags, _storeId: storeId}, + ); + }, () => { + // For an optimistic update + return {tags, removedTags}; + }); + }; + + /** + * Creates an action thunk that will do an asynchronous request to + * label a tag as removed in im.vector.web.tag_ordering account data. + * + * The reason this is implemented with new state `removedTags` is that + * we incrementally and initially populate `tags` with groups that + * have been joined. If we remove a group from `tags`, it will just + * get added (as it looks like a group we've recently joined). + * + * NB: If we ever support adding of tags (which is planned), we should + * take special care to remove the tag from `removedTags` when we add + * it. + * + * @param {MatrixClient} matrixClient the matrix client to set the + * account data on. + * @param {string} tag the tag to remove. + * @returns {function} an async action payload that will dispatch + * actions indicating the status of the request. + * @see asyncAction + */ + public static removeTag(matrixClient: MatrixClient, tag: string): AsyncActionPayload { + // Don't change tags, just removedTags + const tags = TagOrderStore.getOrderedTags(); + const removedTags = TagOrderStore.getRemovedTagsAccountData() || []; + + if (removedTags.includes(tag)) { + // Return a thunk that doesn't do anything, we don't even need + // an asynchronous action here, the tag is already removed. + return () => { + }; + } + + removedTags.push(tag); + + const storeId = TagOrderStore.getStoreId(); + + return asyncAction('TagOrderActions.removeTag', () => { + Analytics.trackEvent('TagOrderActions', 'removeTag'); + return matrixClient.setAccountData( + 'im.vector.web.tag_ordering', + {tags, removedTags, _storeId: storeId}, + ); + }, () => { + // For an optimistic update + return {removedTags}; + }); + } +} diff --git a/src/actions/actionCreators.js b/src/actions/actionCreators.ts similarity index 80% rename from src/actions/actionCreators.js rename to src/actions/actionCreators.ts index 967ce609e7..3a0132a969 100644 --- a/src/actions/actionCreators.js +++ b/src/actions/actionCreators.ts @@ -1,5 +1,6 @@ /* Copyright 2017 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { AsyncActionPayload } from "../dispatcher"; + /** * Create an action thunk that will dispatch actions indicating the current * status of the Promise returned by fn. @@ -25,9 +28,9 @@ limitations under the License. * @param {function?} pendingFn a function that returns an object to assign * to the `request` key of the ${id}.pending * payload. - * @returns {function} an action thunk - a function that uses its single - * argument as a dispatch function to dispatch the - * following actions: + * @returns {AsyncActionPayload} an async action payload. Includes a function + * that uses its single argument as a dispatch function + * to dispatch the following actions: * `${id}.pending` and either * `${id}.success` or * `${id}.failure`. @@ -41,8 +44,8 @@ limitations under the License. * result is the result of the promise returned by * `fn`. */ -export function asyncAction(id, fn, pendingFn) { - return (dispatch) => { +export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () => any): AsyncActionPayload { + const helper = (dispatch) => { dispatch({ action: id + '.pending', request: @@ -54,4 +57,5 @@ export function asyncAction(id, fn, pendingFn) { dispatch({action: id + '.failure', err}); }); }; + return new AsyncActionPayload(helper); } diff --git a/src/dispatcher.js b/src/dispatcher.js deleted file mode 100644 index 5dfaa11345..0000000000 --- a/src/dispatcher.js +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 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. -*/ - -'use strict'; - -import flux from "flux"; - -class MatrixDispatcher extends flux.Dispatcher { - /** - * @param {Object|function} payload Required. The payload to dispatch. - * If an Object, must contain at least an 'action' key. - * If a function, must have the signature (dispatch) => {...}. - * @param {boolean=} sync Optional. Pass true to dispatch - * synchronously. This is useful for anything triggering - * an operation that the browser requires user interaction - * for. - */ - dispatch(payload, sync) { - // Allow for asynchronous dispatching by accepting payloads that have the - // type `function (dispatch) {...}` - if (typeof payload === 'function') { - payload((action) => { - this.dispatch(action, sync); - }); - return; - } - - if (sync) { - super.dispatch(payload); - } else { - // Unless the caller explicitly asked for us to dispatch synchronously, - // we always set a timeout to do this: The flux dispatcher complains - // if you dispatch from within a dispatch, so rather than action - // handlers having to worry about not calling anything that might - // then dispatch, we just do dispatches asynchronously. - setTimeout(super.dispatch.bind(this, payload), 0); - } - } -} - -if (global.mxDispatcher === undefined) { - global.mxDispatcher = new MatrixDispatcher(); -} -export default global.mxDispatcher; diff --git a/src/dispatcher.ts b/src/dispatcher.ts new file mode 100644 index 0000000000..fce99df055 --- /dev/null +++ b/src/dispatcher.ts @@ -0,0 +1,121 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2017 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { Dispatcher } from "flux"; + +export enum Action { + // TODO: Populate with actual actions +} + +// Dispatcher actions also extend into any arbitrary string, so support that. +export type DispatcherAction = Action | string; + +/** + * The base dispatch type exposed by our dispatcher. + */ +export interface ActionPayload { + [property: string]: any; // effectively makes this 'extends Object' + action: DispatcherAction; +} + +/** + * The function the dispatcher calls when ready for an AsyncActionPayload. The + * single argument is used to start a dispatch. First the dispatcher calls the + * outer function, then when the called function is ready it calls the cb + * function to issue the dispatch. It may call the callback repeatedly if needed. + */ +export type AsyncActionFn = (cb: (action: ActionPayload) => void) => void; + +/** + * An async version of ActionPayload + */ +export class AsyncActionPayload implements ActionPayload { + /** + * The function the dispatcher should call. + */ + public readonly fn: AsyncActionFn; + + /** + * @deprecated Not used on AsyncActionPayload. + */ + public get action(): DispatcherAction { + return "NOT_USED"; + } + + /** + * Create a new AsyncActionPayload with the given ready function. + * @param {AsyncActionFn} readyFn The function to be called when the + * dispatcher is ready. + */ + public constructor(readyFn: AsyncActionFn) { + this.fn = readyFn; + } +} + +/** + * A dispatcher for ActionPayloads (the default within the SDK). + */ +export class MatrixDispatcher extends Dispatcher<ActionPayload> { + /** + * Dispatches an event on the dispatcher's event bus. + * @param {ActionPayload} payload Required. The payload to dispatch. + * @param {boolean=false} sync Optional. Pass true to dispatch + * synchronously. This is useful for anything triggering + * an operation that the browser requires user interaction + * for. Default false (async). + */ + dispatch(payload: ActionPayload, sync = false) { + if (payload instanceof AsyncActionPayload) { + payload.fn((action: ActionPayload) => { + this.dispatch(action, sync); + }); + return; + } + + if (sync) { + super.dispatch(payload); + } else { + // Unless the caller explicitly asked for us to dispatch synchronously, + // we always set a timeout to do this: The flux dispatcher complains + // if you dispatch from within a dispatch, so rather than action + // handlers having to worry about not calling anything that might + // then dispatch, we just do dispatches asynchronously. + setTimeout(super.dispatch.bind(this, payload), 0); + } + } + + /** + * Shorthand for dispatch({action: Action.WHATEVER}, sync). No additional + * properties can be included with this version. + * @param {Action} action The action to dispatch. + * @param {boolean=false} sync Whether the dispatch should be sync or not. + * @see dispatch(action: ActionPayload, sync: boolean) + */ + fire(action: Action, sync = false) { + this.dispatch({action}, sync); + } +} + +export const defaultDispatcher = new MatrixDispatcher(); + +const anyGlobal = <any>global; +if (!anyGlobal.mxDispatcher) { + anyGlobal.mxDispatcher = defaultDispatcher; +} + +export default defaultDispatcher; diff --git a/tsconfig.json b/tsconfig.json index b87f640734..8a01ca335e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,8 @@ "jsx": "react", "types": [ "node", - "react" + "react", + "flux" ] }, "include": [ diff --git a/yarn.lock b/yarn.lock index 520e976b17..99a9d07c7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1218,6 +1218,19 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== +"@types/fbemitter@*": + version "2.0.32" + resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c" + integrity sha1-jtIE2g9U6cjq7DGx7skeJRMtCCw= + +"@types/flux@^3.1.9": + version "3.1.9" + resolved "https://registry.yarnpkg.com/@types/flux/-/flux-3.1.9.tgz#ddfc9641ee2e2e6cb6cd730c6a48ef82e2076711" + integrity sha512-bSbDf4tTuN9wn3LTGPnH9wnSSLtR5rV7UPWFpM00NJ1pSTBwCzeZG07XsZ9lBkxwngrqjDtM97PLt5IuIdCQUA== + dependencies: + "@types/fbemitter" "*" + "@types/react" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -1272,6 +1285,14 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/react@*": + version "16.9.35" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368" + integrity sha512-q0n0SsWcGc8nDqH2GJfWQWUOmZSJhXV64CjVN5SvcNti3TdEaA3AH0D8DwNmMdzjMAC/78tB8nAZIlV8yTz+zQ== + dependencies: + "@types/prop-types" "*" + csstype "^2.2.0" + "@types/react@16.9": version "16.9.32" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383" From 90a898d03ff5e6da0a436136de167ee1547297c5 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Wed, 13 May 2020 20:41:41 -0600 Subject: [PATCH 137/196] Move dispatcher into a subdirectory We're expecting to have a whole bunch of types for the dispatched payloads, so pull the thing into a directory we can throw them in. --- src/BasePlatform.js | 2 +- src/CallHandler.js | 2 +- src/ContentMessages.js | 2 +- src/FromWidgetPostMessageApi.js | 2 +- src/Lifecycle.js | 2 +- src/Modal.js | 2 +- src/Notifier.js | 2 +- src/Presence.js | 2 +- src/Registration.js | 2 +- src/Resend.js | 2 +- src/ScalarMessaging.js | 2 +- src/SlashCommands.tsx | 2 +- src/UserActivity.js | 2 +- src/actions/MatrixActionCreators.js | 2 +- src/actions/RoomListActions.ts | 2 +- src/actions/TagOrderActions.ts | 2 +- src/actions/actionCreators.ts | 2 +- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 2 +- .../views/dialogs/keybackup/NewRecoveryMethodDialog.js | 2 +- .../views/dialogs/keybackup/RecoveryMethodRemovedDialog.js | 2 +- src/components/structures/CustomRoomTagPanel.js | 2 +- src/components/structures/EmbeddedPage.js | 2 +- src/components/structures/GroupView.js | 2 +- src/components/structures/HomePage.tsx | 2 +- src/components/structures/LeftPanel.js | 2 +- src/components/structures/LoggedInView.tsx | 2 +- src/components/structures/MatrixChat.tsx | 2 +- src/components/structures/MyGroups.js | 2 +- src/components/structures/RightPanel.js | 2 +- src/components/structures/RoomDirectory.js | 2 +- src/components/structures/RoomStatusBar.js | 2 +- src/components/structures/RoomSubList.js | 2 +- src/components/structures/RoomView.js | 2 +- src/components/structures/SearchBox.js | 2 +- src/components/structures/TagPanel.js | 2 +- src/components/structures/TagPanelButtons.js | 2 +- src/components/structures/TimelinePanel.js | 2 +- src/components/structures/TopLeftMenuButton.js | 2 +- src/components/structures/UploadBar.js | 2 +- src/components/structures/auth/Registration.js | 2 +- src/components/structures/auth/SoftLogout.js | 2 +- src/components/views/avatars/MemberAvatar.js | 2 +- src/components/views/context_menus/MessageContextMenu.js | 2 +- src/components/views/context_menus/RoomTileContextMenu.js | 2 +- src/components/views/context_menus/TagTileContextMenu.js | 2 +- src/components/views/context_menus/TopLeftMenu.js | 2 +- src/components/views/dialogs/AddressPickerDialog.js | 2 +- src/components/views/dialogs/CreateGroupDialog.js | 2 +- src/components/views/dialogs/CryptoStoreTooNewDialog.js | 2 +- src/components/views/dialogs/DeviceVerifyDialog.js | 2 +- src/components/views/dialogs/IntegrationsDisabledDialog.js | 2 +- src/components/views/dialogs/InviteDialog.js | 2 +- src/components/views/dialogs/LogoutDialog.js | 2 +- src/components/views/dialogs/RoomSettingsDialog.js | 2 +- src/components/views/elements/ActionButton.js | 2 +- src/components/views/elements/AppTile.js | 2 +- src/components/views/elements/Flair.js | 2 +- src/components/views/elements/PersistedElement.js | 2 +- src/components/views/elements/Pill.js | 2 +- src/components/views/elements/ReplyThread.js | 2 +- src/components/views/elements/TagTile.js | 2 +- src/components/views/elements/Tooltip.js | 2 +- src/components/views/globals/CookieBar.js | 2 +- src/components/views/groups/GroupInviteTile.js | 2 +- src/components/views/groups/GroupMemberInfo.js | 2 +- src/components/views/groups/GroupMemberList.js | 2 +- src/components/views/groups/GroupMemberTile.js | 2 +- src/components/views/groups/GroupRoomInfo.js | 2 +- src/components/views/groups/GroupRoomTile.js | 2 +- src/components/views/groups/GroupTile.js | 2 +- src/components/views/messages/MKeyVerificationRequest.js | 2 +- src/components/views/messages/MessageActionBar.js | 2 +- src/components/views/messages/RoomCreate.js | 2 +- src/components/views/messages/TextualBody.js | 2 +- src/components/views/right_panel/HeaderButtons.js | 2 +- src/components/views/right_panel/UserInfo.js | 2 +- src/components/views/room_settings/ColorSettings.js | 2 +- src/components/views/room_settings/UrlPreviewSettings.js | 2 +- src/components/views/rooms/AppsDrawer.js | 2 +- src/components/views/rooms/AuxPanel.js | 2 +- src/components/views/rooms/EditMessageComposer.js | 2 +- src/components/views/rooms/EventTile.js | 2 +- src/components/views/rooms/ForwardMessage.js | 2 +- src/components/views/rooms/MemberInfo.js | 2 +- src/components/views/rooms/MemberList.js | 2 +- src/components/views/rooms/MemberTile.js | 2 +- src/components/views/rooms/MessageComposer.js | 2 +- src/components/views/rooms/PinnedEventTile.js | 2 +- src/components/views/rooms/ReplyPreview.js | 2 +- src/components/views/rooms/RoomBreadcrumbs.js | 2 +- src/components/views/rooms/RoomDetailList.js | 2 +- src/components/views/rooms/RoomList.js | 2 +- src/components/views/rooms/RoomPreviewBar.js | 2 +- src/components/views/rooms/RoomTile.js | 2 +- src/components/views/rooms/SendMessageComposer.js | 2 +- src/components/views/rooms/Stickerpicker.js | 2 +- src/components/views/rooms/ThirdPartyMemberInfo.js | 2 +- src/components/views/settings/ChangePassword.js | 2 +- src/components/views/settings/EnableNotificationsButton.js | 2 +- src/components/views/settings/IntegrationManager.js | 2 +- src/components/views/settings/SetIdServer.js | 2 +- .../views/settings/tabs/room/AdvancedRoomSettingsTab.js | 2 +- .../views/settings/tabs/room/GeneralRoomSettingsTab.js | 2 +- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 2 +- .../views/settings/tabs/user/SecurityUserSettingsTab.js | 2 +- src/components/views/toasts/BulkUnverifiedSessionsToast.js | 2 +- src/components/views/toasts/VerificationRequestToast.js | 2 +- src/components/views/voip/CallPreview.js | 2 +- src/components/views/voip/CallView.js | 2 +- src/components/views/voip/IncomingCallBox.js | 2 +- src/components/views/voip/VideoView.js | 2 +- src/createRoom.js | 2 +- src/cryptodevices.js | 2 +- src/{ => dispatcher}/dispatcher.ts | 0 src/mjolnir/Mjolnir.js | 2 +- src/settings/SettingsStore.js | 2 +- src/settings/controllers/CustomStatusController.js | 2 +- src/stores/CustomRoomTagStore.js | 2 +- src/stores/GroupStore.js | 2 +- src/stores/LifecycleStore.js | 2 +- src/stores/RightPanelStore.js | 2 +- src/stores/RoomListStore.js | 2 +- src/stores/RoomViewStore.js | 2 +- src/stores/SessionStore.js | 2 +- src/stores/TagOrderStore.js | 2 +- src/theme.js | 2 +- src/utils/WidgetUtils.js | 2 +- src/verification.js | 2 +- test/components/views/rooms/RoomList-test.js | 2 +- test/test-utils.js | 2 +- 130 files changed, 129 insertions(+), 129 deletions(-) rename src/{ => dispatcher}/dispatcher.ts (100%) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 7214031586..46bd9150df 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -20,7 +20,7 @@ limitations under the License. */ import {MatrixClient} from "matrix-js-sdk"; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; /** diff --git a/src/CallHandler.js b/src/CallHandler.js index 2bfe10850a..c95ed16eb3 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -59,7 +59,7 @@ import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; import Matrix from 'matrix-js-sdk'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import { showUnknownDeviceDialogForCalls } from './cryptodevices'; import WidgetUtils from './utils/WidgetUtils'; import WidgetEchoStore from './stores/WidgetEchoStore'; diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 34379c029b..4f5a1a1220 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -18,7 +18,7 @@ limitations under the License. 'use strict'; import extend from './extend'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import {MatrixClientPeg} from './MatrixClientPeg'; import * as sdk from './index'; import { _t } from './languageHandler'; diff --git a/src/FromWidgetPostMessageApi.js b/src/FromWidgetPostMessageApi.js index c9793d40f7..102afa6bf1 100644 --- a/src/FromWidgetPostMessageApi.js +++ b/src/FromWidgetPostMessageApi.js @@ -17,7 +17,7 @@ limitations under the License. */ import URL from 'url'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import WidgetMessagingEndpoint from './WidgetMessagingEndpoint'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; import {MatrixClientPeg} from "./MatrixClientPeg"; diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 1baa6c8e0c..22c5d48317 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -26,7 +26,7 @@ import Analytics from './Analytics'; import Notifier from './Notifier'; import UserActivity from './UserActivity'; import Presence from './Presence'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import DMRoomMap from './utils/DMRoomMap'; import Modal from './Modal'; import * as sdk from './index'; diff --git a/src/Modal.js b/src/Modal.js index de441740f1..9b9f190d58 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import ReactDOM from 'react-dom'; import Analytics from './Analytics'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import {defer} from './utils/promise'; import AsyncWrapper from './AsyncWrapper'; diff --git a/src/Notifier.js b/src/Notifier.js index ec92840998..2ffa92452b 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -21,7 +21,7 @@ import PlatformPeg from './PlatformPeg'; import * as TextForEvent from './TextForEvent'; import Analytics from './Analytics'; import * as Avatar from './Avatar'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; diff --git a/src/Presence.js b/src/Presence.js index 2fc13a090b..42bca35f96 100644 --- a/src/Presence.js +++ b/src/Presence.js @@ -17,7 +17,7 @@ limitations under the License. */ import {MatrixClientPeg} from "./MatrixClientPeg"; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import Timer from './utils/Timer'; // Time in ms after that a user is considered as unavailable/away diff --git a/src/Registration.js b/src/Registration.js index ca162bac03..32c3d9cc35 100644 --- a/src/Registration.js +++ b/src/Registration.js @@ -20,7 +20,7 @@ limitations under the License. * registration code. */ -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import Modal from './Modal'; import { _t } from './languageHandler'; diff --git a/src/Resend.js b/src/Resend.js index 6d6c18cf27..f5f24bffa5 100644 --- a/src/Resend.js +++ b/src/Resend.js @@ -16,7 +16,7 @@ limitations under the License. */ import {MatrixClientPeg} from './MatrixClientPeg'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import { EventStatus } from 'matrix-js-sdk'; export default class Resend { diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 9731e42825..315c2d86f4 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -238,7 +238,7 @@ Example: import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixEvent } from 'matrix-js-sdk'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import WidgetUtils from './utils/WidgetUtils'; import RoomViewStore from './stores/RoomViewStore'; import { _t } from './languageHandler'; diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index fbb9e2eb0e..fd157b2b4c 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -21,7 +21,7 @@ limitations under the License. import * as React from 'react'; import {MatrixClientPeg} from './MatrixClientPeg'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import * as sdk from './index'; import {_t, _td} from './languageHandler'; import Modal from './Modal'; diff --git a/src/UserActivity.js b/src/UserActivity.js index 0d1b4d0cc0..0174aebaf5 100644 --- a/src/UserActivity.js +++ b/src/UserActivity.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import Timer from './utils/Timer'; // important these are larger than the timeouts of timers diff --git a/src/actions/MatrixActionCreators.js b/src/actions/MatrixActionCreators.js index c89ec44435..93a4fcf07c 100644 --- a/src/actions/MatrixActionCreators.js +++ b/src/actions/MatrixActionCreators.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; // TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events // become dispatches in the same place. diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts index 8ad3ad0781..0cdd3a86d8 100644 --- a/src/actions/RoomListActions.ts +++ b/src/actions/RoomListActions.ts @@ -23,7 +23,7 @@ import { _t } from '../languageHandler'; 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"; +import { AsyncActionPayload } from "../dispatcher/dispatcher"; export default class RoomListActions { /** diff --git a/src/actions/TagOrderActions.ts b/src/actions/TagOrderActions.ts index 57dd0b8793..fa86e3a6b1 100644 --- a/src/actions/TagOrderActions.ts +++ b/src/actions/TagOrderActions.ts @@ -18,7 +18,7 @@ limitations under the License. import Analytics from '../Analytics'; import { asyncAction } from './actionCreators'; import TagOrderStore from '../stores/TagOrderStore'; -import { AsyncActionPayload } from "../dispatcher"; +import { AsyncActionPayload } from "../dispatcher/dispatcher"; import { MatrixClient } from "matrix-js-sdk/src/client"; export default class TagOrderActions { diff --git a/src/actions/actionCreators.ts b/src/actions/actionCreators.ts index 3a0132a969..78525a9509 100644 --- a/src/actions/actionCreators.ts +++ b/src/actions/actionCreators.ts @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { AsyncActionPayload } from "../dispatcher"; +import { AsyncActionPayload } from "../dispatcher/dispatcher"; /** * Create an action thunk that will dispatch actions indicating the current diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 120b086ef6..8af0bf278e 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import * as sdk from '../../../../index'; import PropTypes from 'prop-types'; -import dis from "../../../../dispatcher"; +import dis from "../../../../dispatcher/dispatcher"; import { _t } from '../../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js index 9e2264a960..f00f2d9c3c 100644 --- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js +++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js @@ -19,7 +19,7 @@ import React from "react"; import PropTypes from "prop-types"; import * as sdk from "../../../../index"; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; -import dis from "../../../../dispatcher"; +import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; diff --git a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js index c5222dafd5..722334cd70 100644 --- a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js +++ b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js @@ -18,7 +18,7 @@ limitations under the License. import React from "react"; import PropTypes from "prop-types"; import * as sdk from "../../../../index"; -import dis from "../../../../dispatcher"; +import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; diff --git a/src/components/structures/CustomRoomTagPanel.js b/src/components/structures/CustomRoomTagPanel.js index 6e392ea505..2753d5c4da 100644 --- a/src/components/structures/CustomRoomTagPanel.js +++ b/src/components/structures/CustomRoomTagPanel.js @@ -18,7 +18,7 @@ import React from 'react'; import CustomRoomTagStore from '../../stores/CustomRoomTagStore'; import AutoHideScrollbar from './AutoHideScrollbar'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import classNames from 'classnames'; import * as FormattingUtils from '../../utils/FormattingUtils'; diff --git a/src/components/structures/EmbeddedPage.js b/src/components/structures/EmbeddedPage.js index 0aababf030..49ba3d1227 100644 --- a/src/components/structures/EmbeddedPage.js +++ b/src/components/structures/EmbeddedPage.js @@ -23,7 +23,7 @@ import PropTypes from 'prop-types'; import request from 'browser-request'; import { _t } from '../../languageHandler'; import sanitizeHtml from 'sanitize-html'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import classnames from 'classnames'; import MatrixClientContext from "../../contexts/MatrixClientContext"; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 3b32e5c907..1311d6e4f6 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -21,7 +21,7 @@ import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import { getHostingLink } from '../../utils/HostingLink'; import { sanitizedHtmlNode } from '../../HtmlUtils'; import { _t, _td } from '../../languageHandler'; diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index ddf9cd6d00..ff8d35a114 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -21,7 +21,7 @@ import { getHomePageUrl } from "../../utils/pages"; import { _t } from "../../languageHandler"; import SdkConfig from "../../SdkConfig"; import * as sdk from "../../index"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; const onClickSendDm = () => dis.dispatch({action: 'view_create_chat'}); const onClickExplore = () => dis.dispatch({action: 'view_room_directory'}); diff --git a/src/components/structures/LeftPanel.js b/src/components/structures/LeftPanel.js index a9cd12199b..adba858fa3 100644 --- a/src/components/structures/LeftPanel.js +++ b/src/components/structures/LeftPanel.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Key } from '../../Keyboard'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import * as VectorConferenceHandler from '../../VectorConferenceHandler'; import SettingsStore from '../../settings/SettingsStore'; import {_t} from "../../languageHandler"; diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 9de2aac8e9..e2aa523b8c 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -27,7 +27,7 @@ import PageTypes from '../../PageTypes'; import CallMediaHandler from '../../CallMediaHandler'; import { fixupColorFonts } from '../../utils/FontManager'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import sessionStore from '../../stores/SessionStore'; import {MatrixClientPeg, MatrixClientCreds} from '../../MatrixClientPeg'; import SettingsStore from "../../settings/SettingsStore"; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index e553057acb..973d301420 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -34,7 +34,7 @@ import {MatrixClientPeg} from "../../MatrixClientPeg"; import PlatformPeg from "../../PlatformPeg"; import SdkConfig from "../../SdkConfig"; import * as RoomListSorter from "../../RoomListSorter"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; import Notifier from '../../Notifier'; import Modal from "../../Modal"; diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index f179cab6ad..e2a3d6e71f 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -19,7 +19,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import * as sdk from '../../index'; import { _t } from '../../languageHandler'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import AccessibleButton from '../views/elements/AccessibleButton'; import MatrixClientContext from "../../contexts/MatrixClientContext"; import AutoHideScrollbar from "./AutoHideScrollbar"; diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 34652098b3..c6c330a202 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -22,7 +22,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import RateLimitedFunc from '../../ratelimitedfunc'; import { showGroupInviteDialog, showGroupAddRoomDialog } from '../../GroupAddressPicker'; import GroupStore from '../../stores/GroupStore'; diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 86353cd532..eb784a1083 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -20,7 +20,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import {MatrixClientPeg} from "../../MatrixClientPeg"; import * as sdk from "../../index"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; import Modal from "../../Modal"; import { linkifyAndSanitizeHtml } from '../../HtmlUtils'; import PropTypes from 'prop-types'; diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 639f38a119..ae628fd06a 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -25,7 +25,7 @@ import * as sdk from '../../index'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import Resend from '../../Resend'; import * as cryptodevices from '../../cryptodevices'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils'; const STATUS_BAR_HIDDEN = 0; diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 4e8e51c3cc..d1abacd2d8 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -20,7 +20,7 @@ limitations under the License. import React, {createRef} from 'react'; import classNames from 'classnames'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import * as Unread from '../../Unread'; import * as RoomNotifs from '../../RoomNotifs'; import * as FormattingUtils from '../../utils/FormattingUtils'; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f53929df4a..fc98758bda 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -34,7 +34,7 @@ import ContentMessages from '../../ContentMessages'; import Modal from '../../Modal'; import * as sdk from '../../index'; import CallHandler from '../../CallHandler'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import Tinter from '../../Tinter'; import rate_limited_func from '../../ratelimitedfunc'; import * as ObjectUtils from '../../ObjectUtils'; diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js index 0f3f8a6be9..7e9d290bce 100644 --- a/src/components/structures/SearchBox.js +++ b/src/components/structures/SearchBox.js @@ -19,7 +19,7 @@ import React, {createRef} from 'react'; import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import { Key } from '../../Keyboard'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import { throttle } from 'lodash'; import AccessibleButton from '../../components/views/elements/AccessibleButton'; import classNames from 'classnames'; diff --git a/src/components/structures/TagPanel.js b/src/components/structures/TagPanel.js index 6642cce098..713ed004b0 100644 --- a/src/components/structures/TagPanel.js +++ b/src/components/structures/TagPanel.js @@ -22,7 +22,7 @@ import TagOrderStore from '../../stores/TagOrderStore'; import GroupActions from '../../actions/GroupActions'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import { _t } from '../../languageHandler'; import { Droppable } from 'react-beautiful-dnd'; diff --git a/src/components/structures/TagPanelButtons.js b/src/components/structures/TagPanelButtons.js index 93a596baa3..4b00da3cbf 100644 --- a/src/components/structures/TagPanelButtons.js +++ b/src/components/structures/TagPanelButtons.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import createReactClass from 'create-react-class'; import * as sdk from '../../index'; -import dis from '../../dispatcher'; +import dis from '../../dispatcher/dispatcher'; import Modal from '../../Modal'; import { _t } from '../../languageHandler'; diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 6a08cd78eb..da1369c45f 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -29,7 +29,7 @@ import {MatrixClientPeg} from "../../MatrixClientPeg"; import * as ObjectUtils from "../../ObjectUtils"; import UserActivity from "../../UserActivity"; import Modal from "../../Modal"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; import * as sdk from "../../index"; import { Key } from '../../Keyboard'; import Timer from '../../utils/Timer'; diff --git a/src/components/structures/TopLeftMenuButton.js b/src/components/structures/TopLeftMenuButton.js index ebd7aaae89..234dc661f9 100644 --- a/src/components/structures/TopLeftMenuButton.js +++ b/src/components/structures/TopLeftMenuButton.js @@ -22,7 +22,7 @@ import BaseAvatar from '../views/avatars/BaseAvatar'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import * as Avatar from '../../Avatar'; import { _t } from '../../languageHandler'; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; import {ContextMenu, ContextMenuButton} from "./ContextMenu"; const AVATAR_SIZE = 28; diff --git a/src/components/structures/UploadBar.js b/src/components/structures/UploadBar.js index 1aec63f04e..421d1d79a7 100644 --- a/src/components/structures/UploadBar.js +++ b/src/components/structures/UploadBar.js @@ -19,7 +19,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import ContentMessages from '../../ContentMessages'; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; import filesize from "filesize"; import { _t } from '../../languageHandler'; diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 7818496e71..a30a13aceb 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -32,7 +32,7 @@ import * as Lifecycle from '../../../Lifecycle'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import AuthPage from "../../views/auth/AuthPage"; import Login from "../../../Login"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; // Phases // Show controls to configure server details diff --git a/src/components/structures/auth/SoftLogout.js b/src/components/structures/auth/SoftLogout.js index 08ab7e8a61..7c25767291 100644 --- a/src/components/structures/auth/SoftLogout.js +++ b/src/components/structures/auth/SoftLogout.js @@ -18,7 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {_t} from '../../../languageHandler'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as Lifecycle from '../../../Lifecycle'; import Modal from '../../../Modal'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; diff --git a/src/components/views/avatars/MemberAvatar.js b/src/components/views/avatars/MemberAvatar.js index 826aa5fddf..89314cfef7 100644 --- a/src/components/views/avatars/MemberAvatar.js +++ b/src/components/views/avatars/MemberAvatar.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as Avatar from '../../../Avatar'; import * as sdk from "../../../index"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; export default createReactClass({ displayName: 'MemberAvatar', diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index 70ab80e6cc..2c835e6967 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -23,7 +23,7 @@ import createReactClass from 'create-react-class'; import {EventStatus} from 'matrix-js-sdk'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index d281656bbe..b08cf3be60 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -24,7 +24,7 @@ import classNames from 'classnames'; import * as sdk from '../../../index'; import { _t, _td } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import DMRoomMap from '../../../utils/DMRoomMap'; import * as Rooms from '../../../Rooms'; import * as RoomNotifs from '../../../RoomNotifs'; diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js index 7313a278cc..ff1a7f1b14 100644 --- a/src/components/views/context_menus/TagTileContextMenu.js +++ b/src/components/views/context_menus/TagTileContextMenu.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import TagOrderActions from '../../../actions/TagOrderActions'; import * as sdk from '../../../index'; import {MenuItem} from "../../structures/ContextMenu"; diff --git a/src/components/views/context_menus/TopLeftMenu.js b/src/components/views/context_menus/TopLeftMenu.js index 4448ecd041..3ec857be2f 100644 --- a/src/components/views/context_menus/TopLeftMenu.js +++ b/src/components/views/context_menus/TopLeftMenu.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import LogoutDialog from "../dialogs/LogoutDialog"; import Modal from "../../../Modal"; diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js index 451ec9cfde..41a819c005 100644 --- a/src/components/views/dialogs/AddressPickerDialog.js +++ b/src/components/views/dialogs/AddressPickerDialog.js @@ -24,7 +24,7 @@ import createReactClass from 'create-react-class'; import { _t, _td } from '../../../languageHandler'; import * as sdk from '../../../index'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { addressTypes, getAddressType } from '../../../UserAddress.js'; import GroupStore from '../../../stores/GroupStore'; import * as Email from '../../../email'; diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js index d465ef26a2..10285ccee0 100644 --- a/src/components/views/dialogs/CreateGroupDialog.js +++ b/src/components/views/dialogs/CreateGroupDialog.js @@ -18,7 +18,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import PropTypes from 'prop-types'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; diff --git a/src/components/views/dialogs/CryptoStoreTooNewDialog.js b/src/components/views/dialogs/CryptoStoreTooNewDialog.js index 11e202b0cc..081e84696c 100644 --- a/src/components/views/dialogs/CryptoStoreTooNewDialog.js +++ b/src/components/views/dialogs/CryptoStoreTooNewDialog.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index a3f9430476..51f905d542 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -25,7 +25,7 @@ import * as FormattingUtils from '../../../utils/FormattingUtils'; import { _t } from '../../../languageHandler'; import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import {ensureDMExists} from "../../../createRoom"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import SettingsStore from '../../../settings/SettingsStore'; import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode"; import VerificationQREmojiOptions from "../verification/VerificationQREmojiOptions"; diff --git a/src/components/views/dialogs/IntegrationsDisabledDialog.js b/src/components/views/dialogs/IntegrationsDisabledDialog.js index 1ca638f0ab..2677963281 100644 --- a/src/components/views/dialogs/IntegrationsDisabledDialog.js +++ b/src/components/views/dialogs/IntegrationsDisabledDialog.js @@ -18,7 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {_t} from "../../../languageHandler"; import * as sdk from "../../../index"; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; export default class IntegrationsDisabledDialog extends React.Component { static propTypes = { diff --git a/src/components/views/dialogs/InviteDialog.js b/src/components/views/dialogs/InviteDialog.js index 7cbbf8ba64..e62c417a70 100644 --- a/src/components/views/dialogs/InviteDialog.js +++ b/src/components/views/dialogs/InviteDialog.js @@ -27,7 +27,7 @@ import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; import * as Email from "../../../email"; import {getDefaultIdentityServerUrl, useDefaultIdentityServer} from "../../../utils/IdentityServerUtils"; import {abbreviateUrl} from "../../../utils/UrlUtils"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import IdentityAuthClient from "../../../IdentityAuthClient"; import Modal from "../../../Modal"; import {humanizeTime} from "../../../utils/humanize"; diff --git a/src/components/views/dialogs/LogoutDialog.js b/src/components/views/dialogs/LogoutDialog.js index 23a6692b39..930acaa0b8 100644 --- a/src/components/views/dialogs/LogoutDialog.js +++ b/src/components/views/dialogs/LogoutDialog.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import Modal from '../../../Modal'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.js index c5f807b23c..c2b98cd9f3 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.js @@ -27,7 +27,7 @@ import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsT import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; import * as sdk from "../../../index"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import SettingsStore from "../../../settings/SettingsStore"; export default class RoomSettingsDialog extends React.Component { diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js index d2277bd69a..7536d66653 100644 --- a/src/components/views/elements/ActionButton.js +++ b/src/components/views/elements/ActionButton.js @@ -18,7 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import AccessibleButton from './AccessibleButton'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as sdk from '../../../index'; import Analytics from '../../../Analytics'; diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index 80db1718f6..527436b0e4 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -31,7 +31,7 @@ import AppPermission from './AppPermission'; import AppWarning from './AppWarning'; import MessageSpinner from './MessageSpinner'; import WidgetUtils from '../../../utils/WidgetUtils'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; import classNames from 'classnames'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; diff --git a/src/components/views/elements/Flair.js b/src/components/views/elements/Flair.js index bc657e9e91..0f06904b68 100644 --- a/src/components/views/elements/Flair.js +++ b/src/components/views/elements/Flair.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import FlairStore from '../../../stores/FlairStore'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; diff --git a/src/components/views/elements/PersistedElement.js b/src/components/views/elements/PersistedElement.js index 53f2501f19..7f9bfdebf4 100644 --- a/src/components/views/elements/PersistedElement.js +++ b/src/components/views/elements/PersistedElement.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import ResizeObserver from 'resize-observer-polyfill'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; // Shamelessly ripped off Modal.js. There's probably a better way // of doing reusable widgets like dialog boxes & menus where we go and diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 1df12738c5..3e4ddb99e8 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; import { Room, RoomMember } from 'matrix-js-sdk'; import PropTypes from 'prop-types'; diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index eae2d13f8a..8d3a7307b9 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -19,7 +19,7 @@ import React from 'react'; import * as sdk from '../../../index'; import {_t} from '../../../languageHandler'; import PropTypes from 'prop-types'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import {wantsDateSeparator} from '../../../DateUtils'; import {MatrixEvent} from 'matrix-js-sdk'; import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; diff --git a/src/components/views/elements/TagTile.js b/src/components/views/elements/TagTile.js index d8983ac2ea..1af681dadc 100644 --- a/src/components/views/elements/TagTile.js +++ b/src/components/views/elements/TagTile.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import classNames from 'classnames'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { isOnlyCtrlOrCmdIgnoreShiftKeyEvent } from '../../../Keyboard'; import * as FormattingUtils from '../../../utils/FormattingUtils'; diff --git a/src/components/views/elements/Tooltip.js b/src/components/views/elements/Tooltip.js index fd845d9db3..4807ade3db 100644 --- a/src/components/views/elements/Tooltip.js +++ b/src/components/views/elements/Tooltip.js @@ -22,7 +22,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; const MIN_TOOLTIP_HEIGHT = 25; diff --git a/src/components/views/globals/CookieBar.js b/src/components/views/globals/CookieBar.js index 8774e4f1fa..bf264686d0 100644 --- a/src/components/views/globals/CookieBar.js +++ b/src/components/views/globals/CookieBar.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; import Analytics from '../../../Analytics'; diff --git a/src/components/views/groups/GroupInviteTile.js b/src/components/views/groups/GroupInviteTile.js index 91c930525d..bc5334f2de 100644 --- a/src/components/views/groups/GroupInviteTile.js +++ b/src/components/views/groups/GroupInviteTile.js @@ -20,7 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import {_t} from '../../../languageHandler'; import classNames from 'classnames'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js index 9f5660386b..0d08771676 100644 --- a/src/components/views/groups/GroupMemberInfo.js +++ b/src/components/views/groups/GroupMemberInfo.js @@ -19,7 +19,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import Modal from '../../../Modal'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index ca374d1309..b42e325a89 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -19,7 +19,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import GroupStore from '../../../stores/GroupStore'; import PropTypes from 'prop-types'; import { showGroupInviteDialog } from '../../../GroupAddressPicker'; diff --git a/src/components/views/groups/GroupMemberTile.js b/src/components/views/groups/GroupMemberTile.js index f99d5c768c..05e3f6ac2a 100644 --- a/src/components/views/groups/GroupMemberTile.js +++ b/src/components/views/groups/GroupMemberTile.js @@ -20,7 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { GroupMemberType } from '../../../groups'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; diff --git a/src/components/views/groups/GroupRoomInfo.js b/src/components/views/groups/GroupRoomInfo.js index 3a20de7eca..8c9b39675e 100644 --- a/src/components/views/groups/GroupRoomInfo.js +++ b/src/components/views/groups/GroupRoomInfo.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import Modal from '../../../Modal'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/groups/GroupRoomTile.js b/src/components/views/groups/GroupRoomTile.js index 94d143b263..fd6969a49a 100644 --- a/src/components/views/groups/GroupRoomTile.js +++ b/src/components/views/groups/GroupRoomTile.js @@ -18,7 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { GroupRoomType } from '../../../groups'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; diff --git a/src/components/views/groups/GroupTile.js b/src/components/views/groups/GroupTile.js index b845a83c2a..44ce69ee39 100644 --- a/src/components/views/groups/GroupTile.js +++ b/src/components/views/groups/GroupTile.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import { Draggable, Droppable } from 'react-beautiful-dnd'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import FlairStore from '../../../stores/FlairStore'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index f49ae1b6b1..a5b1ae26bb 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -21,7 +21,7 @@ import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import {getNameForEventRoom, userLabelForEventRoom} from '../../../utils/KeyVerificationStateObserver'; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; export default class MKeyVerificationRequest extends React.Component { diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 0cde90e417..48b9c58cb8 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import Modal from '../../../Modal'; import {aboveLeftOf, ContextMenu, ContextMenuButton, useContextMenu} from '../../structures/ContextMenu'; import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; diff --git a/src/components/views/messages/RoomCreate.js b/src/components/views/messages/RoomCreate.js index b5749ced97..95bc460636 100644 --- a/src/components/views/messages/RoomCreate.js +++ b/src/components/views/messages/RoomCreate.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 882e331675..c762b95f83 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -25,7 +25,7 @@ import * as HtmlUtils from '../../../HtmlUtils'; import {formatDate} from '../../../DateUtils'; import * as sdk from '../../../index'; import Modal from '../../../Modal'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import * as ContextMenu from '../../structures/ContextMenu'; import SettingsStore from "../../../settings/SettingsStore"; diff --git a/src/components/views/right_panel/HeaderButtons.js b/src/components/views/right_panel/HeaderButtons.js index 03b03218ee..1c66fe5828 100644 --- a/src/components/views/right_panel/HeaderButtons.js +++ b/src/components/views/right_panel/HeaderButtons.js @@ -19,7 +19,7 @@ limitations under the License. */ import React from 'react'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import RightPanelStore from "../../../stores/RightPanelStore"; export const HEADER_KIND_ROOM = "room"; diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 478dc90418..daf1a9490d 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -21,7 +21,7 @@ import React, {useCallback, useMemo, useState, useEffect, useContext} from 'reac import PropTypes from 'prop-types'; import classNames from 'classnames'; import {Group, RoomMember, User} from 'matrix-js-sdk'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import Modal from '../../../Modal'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index 1e06da0cd8..1d26e956ab 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import Tinter from '../../../Tinter'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; const ROOM_COLORS = [ diff --git a/src/components/views/room_settings/UrlPreviewSettings.js b/src/components/views/room_settings/UrlPreviewSettings.js index 5de355ebd7..16dffd857b 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.js +++ b/src/components/views/room_settings/UrlPreviewSettings.js @@ -23,7 +23,7 @@ import createReactClass from 'create-react-class'; import * as sdk from "../../../index"; import { _t, _td } from '../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js index b64eb33435..06dfffad30 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.js @@ -21,7 +21,7 @@ import createReactClass from 'create-react-class'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import AppTile from '../elements/AppTile'; import Modal from '../../../Modal'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as sdk from '../../../index'; import * as ScalarMessaging from '../../../ScalarMessaging'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index e102b0dba4..d6a3b156d5 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import * as sdk from '../../../index'; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import * as ObjectUtils from '../../../ObjectUtils'; import AppsDrawer from './AppsDrawer'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index 8353940c90..88ed76f118 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -18,7 +18,7 @@ import React from 'react'; import * as sdk from '../../../index'; import {_t} from '../../../languageHandler'; import PropTypes from 'prop-types'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import EditorModel from '../../../editor/model'; import {getCaretOffsetAndText} from '../../../editor/dom'; import {htmlSerializeIfNeeded, textSerialize, containsEmote, stripEmoteCommand} from '../../../editor/serialize'; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 33ee8a0f63..91fcacf0e5 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -25,7 +25,7 @@ import classNames from "classnames"; import { _t, _td } from '../../../languageHandler'; import * as TextForEvent from "../../../TextForEvent"; import * as sdk from "../../../index"; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; import {EventStatus} from 'matrix-js-sdk'; import {formatTime} from "../../../DateUtils"; diff --git a/src/components/views/rooms/ForwardMessage.js b/src/components/views/rooms/ForwardMessage.js index a3c00598a7..03ca32c5e6 100644 --- a/src/components/views/rooms/ForwardMessage.js +++ b/src/components/views/rooms/ForwardMessage.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import { _t } from '../../../languageHandler'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import {Key} from '../../../Keyboard'; diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 6b03000961..5d8e03a050 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -31,7 +31,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import classNames from 'classnames'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import Modal from '../../../Modal'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 3779d461bb..e2d7e3f8e0 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -20,7 +20,7 @@ import React from 'react'; import createReactClass from 'create-react-class'; import { _t } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import {isValid3pidInvite} from "../../../RoomInvite"; import rate_limited_func from "../../../ratelimitedfunc"; diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 1c609afcaa..6842e27ed3 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -20,7 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from "../../../index"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from "../../../MatrixClientPeg"; diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 4749742a7d..c05fe3d3a2 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -20,7 +20,7 @@ import { _t } from '../../../languageHandler'; import CallHandler from '../../../CallHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import RoomViewStore from '../../../stores/RoomViewStore'; import Stickerpicker from './Stickerpicker'; import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; diff --git a/src/components/views/rooms/PinnedEventTile.js b/src/components/views/rooms/PinnedEventTile.js index 28fc8fc338..924385d226 100644 --- a/src/components/views/rooms/PinnedEventTile.js +++ b/src/components/views/rooms/PinnedEventTile.js @@ -18,7 +18,7 @@ import React from "react"; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import AccessibleButton from "../elements/AccessibleButton"; import MessageEvent from "../messages/MessageEvent"; import MemberAvatar from "../avatars/MemberAvatar"; diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index b28494c65a..e7cd2b4c0d 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -15,7 +15,7 @@ limitations under the License. */ import React from 'react'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import RoomViewStore from '../../../stores/RoomViewStore'; diff --git a/src/components/views/rooms/RoomBreadcrumbs.js b/src/components/views/rooms/RoomBreadcrumbs.js index 86c0d7ca96..fe443d720f 100644 --- a/src/components/views/rooms/RoomBreadcrumbs.js +++ b/src/components/views/rooms/RoomBreadcrumbs.js @@ -15,7 +15,7 @@ limitations under the License. */ import React, {createRef} from "react"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; diff --git a/src/components/views/rooms/RoomDetailList.js b/src/components/views/rooms/RoomDetailList.js index db7b86da4f..5b45cfc29a 100644 --- a/src/components/views/rooms/RoomDetailList.js +++ b/src/components/views/rooms/RoomDetailList.js @@ -15,7 +15,7 @@ limitations under the License. */ import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import React from 'react'; import { _t } from '../../../languageHandler'; import PropTypes from 'prop-types'; diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 289a89a206..1c59a5d8d0 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -35,7 +35,7 @@ import GroupStore from '../../../stores/GroupStore'; import RoomSubList from '../../structures/RoomSubList'; import ResizeHandle from '../elements/ResizeHandle'; import CallHandler from "../../../CallHandler"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import * as sdk from "../../../index"; import * as Receipt from "../../../utils/Receipt"; import {Resizer} from '../../../resizer'; diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index fe7c57d811..30e6ae9c58 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -21,7 +21,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import * as sdk from '../../../index'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; import { _t } from '../../../languageHandler'; import IdentityAuthClient from '../../../IdentityAuthClient'; diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 6a23fe309b..44e5ae7643 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -21,7 +21,7 @@ import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import classNames from 'classnames'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import DMRoomMap from '../../../utils/DMRoomMap'; import * as sdk from '../../../index'; diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 233bb110be..5ea979a8ef 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -16,7 +16,7 @@ limitations under the License. */ import React from 'react'; import PropTypes from 'prop-types'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import EditorModel from '../../../editor/model'; import { htmlSerializeIfNeeded, diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index 9d91ab04b3..fc6e80fc61 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -18,7 +18,7 @@ import {_t, _td} from '../../../languageHandler'; import AppTile from '../elements/AppTile'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import AccessibleButton from '../elements/AccessibleButton'; import WidgetUtils from '../../../utils/WidgetUtils'; import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.js b/src/components/views/rooms/ThirdPartyMemberInfo.js index 3e6ed16aa4..3a7042ebd2 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.js +++ b/src/components/views/rooms/ThirdPartyMemberInfo.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {MatrixEvent} from "matrix-js-sdk"; import {_t} from "../../../languageHandler"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import * as sdk from "../../../index"; import Modal from "../../../Modal"; import {isValid3pidInvite} from "../../../RoomInvite"; diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index 7c88573e9c..c7eccf2145 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -20,7 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import AccessibleButton from '../elements/AccessibleButton'; import { _t } from '../../../languageHandler'; import * as sdk from "../../../index"; diff --git a/src/components/views/settings/EnableNotificationsButton.js b/src/components/views/settings/EnableNotificationsButton.js index 9ca591f30e..e4b348dfbd 100644 --- a/src/components/views/settings/EnableNotificationsButton.js +++ b/src/components/views/settings/EnableNotificationsButton.js @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; import createReactClass from 'create-react-class'; import Notifier from "../../../Notifier"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import { _t } from '../../../languageHandler'; export default createReactClass({ diff --git a/src/components/views/settings/IntegrationManager.js b/src/components/views/settings/IntegrationManager.js index a5150e3777..fd6a62d73a 100644 --- a/src/components/views/settings/IntegrationManager.js +++ b/src/components/views/settings/IntegrationManager.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import {Key} from "../../../Keyboard"; export default class IntegrationManager extends React.Component { diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.js index cb37271452..23e72e2352 100644 --- a/src/components/views/settings/SetIdServer.js +++ b/src/components/views/settings/SetIdServer.js @@ -21,7 +21,7 @@ import {_t} from "../../../languageHandler"; import * as sdk from '../../../index'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import Modal from '../../../Modal'; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import { getThreepidsWithBindStatus } from '../../../boundThreepids'; import IdentityAuthClient from "../../../IdentityAuthClient"; import {abbreviateUrl, unabbreviateUrl} from "../../../utils/UrlUtils"; diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js index 9ee9c8d130..f57d5d3798 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.js @@ -21,7 +21,7 @@ import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; import AccessibleButton from "../../../elements/AccessibleButton"; import Modal from "../../../../../Modal"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; export default class AdvancedRoomSettingsTab extends React.Component { static propTypes = { diff --git a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js index 99882ae400..1f12396413 100644 --- a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js +++ b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.js @@ -20,7 +20,7 @@ import {_t} from "../../../../../languageHandler"; import RoomProfileSettings from "../../../room_settings/RoomProfileSettings"; import * as sdk from "../../../../.."; import AccessibleButton from "../../../elements/AccessibleButton"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; import MatrixClientContext from "../../../../../contexts/MatrixClientContext"; export default class GeneralRoomSettingsTab extends React.Component { diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 867982ad2b..216bffc3ed 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -32,7 +32,7 @@ import PlatformPeg from "../../../../../PlatformPeg"; import {MatrixClientPeg} from "../../../../../MatrixClientPeg"; import * as sdk from "../../../../.."; import Modal from "../../../../../Modal"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; import {Service, startTermsFlow} from "../../../../../Terms"; import {SERVICE_TYPES} from "matrix-js-sdk"; import IdentityAuthClient from "../../../../../IdentityAuthClient"; diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 5dd6475e6e..bed057f03d 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -25,7 +25,7 @@ import Analytics from "../../../../../Analytics"; import Modal from "../../../../../Modal"; import * as sdk from "../../../../.."; import {sleep} from "../../../../../utils/promise"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; export class IgnoredUser extends React.Component { static propTypes = { diff --git a/src/components/views/toasts/BulkUnverifiedSessionsToast.js b/src/components/views/toasts/BulkUnverifiedSessionsToast.js index 0c40e56858..99ff529c35 100644 --- a/src/components/views/toasts/BulkUnverifiedSessionsToast.js +++ b/src/components/views/toasts/BulkUnverifiedSessionsToast.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import DeviceListener from '../../../DeviceListener'; import FormButton from '../elements/FormButton'; diff --git a/src/components/views/toasts/VerificationRequestToast.js b/src/components/views/toasts/VerificationRequestToast.js index 6447e87627..421dd7bea1 100644 --- a/src/components/views/toasts/VerificationRequestToast.js +++ b/src/components/views/toasts/VerificationRequestToast.js @@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; import {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver"; -import dis from "../../../dispatcher"; +import dis from "../../../dispatcher/dispatcher"; import ToastStore from "../../../stores/ToastStore"; import Modal from "../../../Modal"; diff --git a/src/components/views/voip/CallPreview.js b/src/components/views/voip/CallPreview.js index 049dd8a3c6..c465170950 100644 --- a/src/components/views/voip/CallPreview.js +++ b/src/components/views/voip/CallPreview.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import RoomViewStore from '../../../stores/RoomViewStore'; import CallHandler from '../../../CallHandler'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import * as sdk from '../../../index'; export default createReactClass({ diff --git a/src/components/views/voip/CallView.js b/src/components/views/voip/CallView.js index 4a5f3923e2..a0a566dfac 100644 --- a/src/components/views/voip/CallView.js +++ b/src/components/views/voip/CallView.js @@ -17,7 +17,7 @@ limitations under the License. import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import CallHandler from '../../../CallHandler'; import * as sdk from '../../../index'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js index 53e829b784..bf28fa0157 100644 --- a/src/components/views/voip/IncomingCallBox.js +++ b/src/components/views/voip/IncomingCallBox.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import createReactClass from 'create-react-class'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; diff --git a/src/components/views/voip/VideoView.js b/src/components/views/voip/VideoView.js index 51be6db81d..a51ab70da9 100644 --- a/src/components/views/voip/VideoView.js +++ b/src/components/views/voip/VideoView.js @@ -22,7 +22,7 @@ import createReactClass from 'create-react-class'; import classNames from 'classnames'; import * as sdk from '../../../index'; -import dis from '../../../dispatcher'; +import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; diff --git a/src/createRoom.js b/src/createRoom.js index a39d2c2216..18fc787e1c 100644 --- a/src/createRoom.js +++ b/src/createRoom.js @@ -19,7 +19,7 @@ import {MatrixClientPeg} from './MatrixClientPeg'; import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import * as Rooms from "./Rooms"; import DMRoomMap from "./utils/DMRoomMap"; import {getAddressType} from "./UserAddress"; diff --git a/src/cryptodevices.js b/src/cryptodevices.js index f56a80e1e4..86b97364f9 100644 --- a/src/cryptodevices.js +++ b/src/cryptodevices.js @@ -16,7 +16,7 @@ limitations under the License. import Resend from './Resend'; import * as sdk from './index'; -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import Modal from './Modal'; import { _t } from './languageHandler'; diff --git a/src/dispatcher.ts b/src/dispatcher/dispatcher.ts similarity index 100% rename from src/dispatcher.ts rename to src/dispatcher/dispatcher.ts diff --git a/src/mjolnir/Mjolnir.js b/src/mjolnir/Mjolnir.js index 5836ffd57a..9876cb1f7f 100644 --- a/src/mjolnir/Mjolnir.js +++ b/src/mjolnir/Mjolnir.js @@ -18,7 +18,7 @@ import {MatrixClientPeg} from "../MatrixClientPeg"; import {ALL_RULE_TYPES, BanList} from "./BanList"; import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; import {_t} from "../languageHandler"; -import dis from "../dispatcher"; +import dis from "../dispatcher/dispatcher"; // TODO: Move this and related files to the js-sdk or something once finalized. diff --git a/src/settings/SettingsStore.js b/src/settings/SettingsStore.js index 0122916bc3..36111dd46f 100644 --- a/src/settings/SettingsStore.js +++ b/src/settings/SettingsStore.js @@ -24,7 +24,7 @@ import RoomSettingsHandler from "./handlers/RoomSettingsHandler"; import ConfigSettingsHandler from "./handlers/ConfigSettingsHandler"; import {_t} from '../languageHandler'; import SdkConfig from "../SdkConfig"; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {SETTINGS} from "./Settings"; import LocalEchoWrapper from "./handlers/LocalEchoWrapper"; import {WatchManager} from "./WatchManager"; diff --git a/src/settings/controllers/CustomStatusController.js b/src/settings/controllers/CustomStatusController.js index 0fc6619d92..031387bb6a 100644 --- a/src/settings/controllers/CustomStatusController.js +++ b/src/settings/controllers/CustomStatusController.js @@ -15,7 +15,7 @@ limitations under the License. */ import SettingController from "./SettingController"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; export default class CustomStatusController extends SettingController { onChange(level, roomId, newValue) { diff --git a/src/stores/CustomRoomTagStore.js b/src/stores/CustomRoomTagStore.js index 909282c085..c67868e2c6 100644 --- a/src/stores/CustomRoomTagStore.js +++ b/src/stores/CustomRoomTagStore.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import * as RoomNotifs from '../RoomNotifs'; import RoomListStore from './RoomListStore'; import EventEmitter from 'events'; diff --git a/src/stores/GroupStore.js b/src/stores/GroupStore.js index 78a144f755..d4097184a1 100644 --- a/src/stores/GroupStore.js +++ b/src/stores/GroupStore.js @@ -18,7 +18,7 @@ import EventEmitter from 'events'; import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups'; import FlairStore from './FlairStore'; import {MatrixClientPeg} from '../MatrixClientPeg'; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; function parseMembersResponse(response) { return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember)); diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 904f29f7b3..a12bac7dd6 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -15,7 +15,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {Store} from 'flux/utils'; const INITIAL_STATE = { diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.js index 3a5605ba3f..a73f3befbb 100644 --- a/src/stores/RightPanelStore.js +++ b/src/stores/RightPanelStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {pendingVerificationRequestForUser} from '../verification'; import {Store} from 'flux/utils'; import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; diff --git a/src/stores/RoomListStore.js b/src/stores/RoomListStore.js index 89edc9a8ef..d7b6759195 100644 --- a/src/stores/RoomListStore.js +++ b/src/stores/RoomListStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import {Store} from 'flux/utils'; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import DMRoomMap from '../utils/DMRoomMap'; import * as Unread from '../Unread'; import SettingsStore from "../settings/SettingsStore"; diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 841734dfb7..3d82d086d7 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -15,7 +15,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {Store} from 'flux/utils'; import {MatrixClientPeg} from '../MatrixClientPeg'; import * as sdk from '../index'; diff --git a/src/stores/SessionStore.js b/src/stores/SessionStore.js index f38bc046d0..096811940c 100644 --- a/src/stores/SessionStore.js +++ b/src/stores/SessionStore.js @@ -14,7 +14,7 @@ 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 dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import {Store} from 'flux/utils'; const INITIAL_STATE = { diff --git a/src/stores/TagOrderStore.js b/src/stores/TagOrderStore.js index c05728e497..2acf531d86 100644 --- a/src/stores/TagOrderStore.js +++ b/src/stores/TagOrderStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import {Store} from 'flux/utils'; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import GroupStore from './GroupStore'; import Analytics from '../Analytics'; import * as RoomNotifs from "../RoomNotifs"; diff --git a/src/theme.js b/src/theme.js index 2ccce81a8d..1da39d50fa 100644 --- a/src/theme.js +++ b/src/theme.js @@ -19,7 +19,7 @@ import {_t} from "./languageHandler"; export const DEFAULT_THEME = "light"; import Tinter from "./Tinter"; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; import ThemeController from "./settings/controllers/ThemeController"; diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index ad4c02887e..35e23f0429 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -18,7 +18,7 @@ limitations under the License. import {MatrixClientPeg} from '../MatrixClientPeg'; import SdkConfig from "../SdkConfig"; -import dis from '../dispatcher'; +import dis from '../dispatcher/dispatcher'; import * as url from "url"; import WidgetEchoStore from '../stores/WidgetEchoStore'; diff --git a/src/verification.js b/src/verification.js index f488b2ebeb..289ac9544b 100644 --- a/src/verification.js +++ b/src/verification.js @@ -15,7 +15,7 @@ limitations under the License. */ import {MatrixClientPeg} from './MatrixClientPeg'; -import dis from "./dispatcher"; +import dis from "./dispatcher/dispatcher"; import Modal from './Modal'; import * as sdk from './index'; import { _t } from './languageHandler'; diff --git a/test/components/views/rooms/RoomList-test.js b/test/components/views/rooms/RoomList-test.js index 8dc4647920..235ed61016 100644 --- a/test/components/views/rooms/RoomList-test.js +++ b/test/components/views/rooms/RoomList-test.js @@ -9,7 +9,7 @@ import {MatrixClientPeg} from '../../../../src/MatrixClientPeg'; import sdk from '../../../skinned-sdk'; import { DragDropContext } from 'react-beautiful-dnd'; -import dis from '../../../../src/dispatcher'; +import dis from '../../../../src/dispatcher/dispatcher'; import DMRoomMap from '../../../../src/utils/DMRoomMap.js'; import GroupStore from '../../../../src/stores/GroupStore.js'; diff --git a/test/test-utils.js b/test/test-utils.js index d7aa9d5de9..2d7c1bd62c 100644 --- a/test/test-utils.js +++ b/test/test-utils.js @@ -2,7 +2,7 @@ import React from 'react'; import {MatrixClientPeg as peg} from '../src/MatrixClientPeg'; -import dis from '../src/dispatcher'; +import dis from '../src/dispatcher/dispatcher'; import {makeType} from "../src/utils/TypeUtils"; import {ValidatedServerConfig} from "../src/utils/AutoDiscoveryUtils"; import ShallowRenderer from 'react-test-renderer/shallow'; From 8c72c27da9afe371e15f2f010d61fe4608c788c1 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Wed, 13 May 2020 20:46:00 -0600 Subject: [PATCH 138/196] Break out actions and payloads to their own files The definitions take up a lot of space which makes it hard to see the dispatcher class, so break them out. --- src/actions/RoomListActions.ts | 2 +- src/actions/TagOrderActions.ts | 2 +- src/actions/actionCreators.ts | 2 +- src/dispatcher/actions.ts | 23 +++++++++++++ src/dispatcher/dispatcher.ts | 51 ++--------------------------- src/dispatcher/payloads.ts | 59 ++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+), 52 deletions(-) create mode 100644 src/dispatcher/actions.ts create mode 100644 src/dispatcher/payloads.ts diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts index 0cdd3a86d8..eb9831ec47 100644 --- a/src/actions/RoomListActions.ts +++ b/src/actions/RoomListActions.ts @@ -23,7 +23,7 @@ import { _t } from '../languageHandler'; 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/dispatcher"; +import { AsyncActionPayload } from "../dispatcher/payloads"; export default class RoomListActions { /** diff --git a/src/actions/TagOrderActions.ts b/src/actions/TagOrderActions.ts index fa86e3a6b1..fa95ea1037 100644 --- a/src/actions/TagOrderActions.ts +++ b/src/actions/TagOrderActions.ts @@ -18,7 +18,7 @@ limitations under the License. import Analytics from '../Analytics'; import { asyncAction } from './actionCreators'; import TagOrderStore from '../stores/TagOrderStore'; -import { AsyncActionPayload } from "../dispatcher/dispatcher"; +import { AsyncActionPayload } from "../dispatcher/payloads"; import { MatrixClient } from "matrix-js-sdk/src/client"; export default class TagOrderActions { diff --git a/src/actions/actionCreators.ts b/src/actions/actionCreators.ts index 78525a9509..d84868a141 100644 --- a/src/actions/actionCreators.ts +++ b/src/actions/actionCreators.ts @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { AsyncActionPayload } from "../dispatcher/dispatcher"; +import { AsyncActionPayload } from "../dispatcher/payloads"; /** * Create an action thunk that will dispatch actions indicating the current diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts new file mode 100644 index 0000000000..ff1650e4e5 --- /dev/null +++ b/src/dispatcher/actions.ts @@ -0,0 +1,23 @@ +/* +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. +*/ + +// Dispatcher actions also extend into any arbitrary string, so support that. +export type DispatcherAction = Action | string; + +export enum Action { + // TODO: Populate with actual actions +} + diff --git a/src/dispatcher/dispatcher.ts b/src/dispatcher/dispatcher.ts index fce99df055..6ef165c485 100644 --- a/src/dispatcher/dispatcher.ts +++ b/src/dispatcher/dispatcher.ts @@ -17,55 +17,8 @@ limitations under the License. */ import { Dispatcher } from "flux"; - -export enum Action { - // TODO: Populate with actual actions -} - -// Dispatcher actions also extend into any arbitrary string, so support that. -export type DispatcherAction = Action | string; - -/** - * The base dispatch type exposed by our dispatcher. - */ -export interface ActionPayload { - [property: string]: any; // effectively makes this 'extends Object' - action: DispatcherAction; -} - -/** - * The function the dispatcher calls when ready for an AsyncActionPayload. The - * single argument is used to start a dispatch. First the dispatcher calls the - * outer function, then when the called function is ready it calls the cb - * function to issue the dispatch. It may call the callback repeatedly if needed. - */ -export type AsyncActionFn = (cb: (action: ActionPayload) => void) => void; - -/** - * An async version of ActionPayload - */ -export class AsyncActionPayload implements ActionPayload { - /** - * The function the dispatcher should call. - */ - public readonly fn: AsyncActionFn; - - /** - * @deprecated Not used on AsyncActionPayload. - */ - public get action(): DispatcherAction { - return "NOT_USED"; - } - - /** - * Create a new AsyncActionPayload with the given ready function. - * @param {AsyncActionFn} readyFn The function to be called when the - * dispatcher is ready. - */ - public constructor(readyFn: AsyncActionFn) { - this.fn = readyFn; - } -} +import { Action } from "./actions"; +import { ActionPayload, AsyncActionPayload } from "./payloads"; /** * A dispatcher for ActionPayloads (the default within the SDK). diff --git a/src/dispatcher/payloads.ts b/src/dispatcher/payloads.ts new file mode 100644 index 0000000000..fa45b30623 --- /dev/null +++ b/src/dispatcher/payloads.ts @@ -0,0 +1,59 @@ +/* +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 { DispatcherAction } from "./actions"; + +/** + * The base dispatch type exposed by our dispatcher. + */ +export interface ActionPayload { + [property: string]: any; // effectively makes this 'extends Object' + action: DispatcherAction; +} + +/** + * The function the dispatcher calls when ready for an AsyncActionPayload. The + * single argument is used to start a dispatch. First the dispatcher calls the + * outer function, then when the called function is ready it calls the cb + * function to issue the dispatch. It may call the callback repeatedly if needed. + */ +export type AsyncActionFn = (cb: (action: ActionPayload) => void) => void; + +/** + * An async version of ActionPayload + */ +export class AsyncActionPayload implements ActionPayload { + /** + * The function the dispatcher should call. + */ + public readonly fn: AsyncActionFn; + + /** + * @deprecated Not used on AsyncActionPayload. + */ + public get action(): DispatcherAction { + return "NOT_USED"; + } + + /** + * Create a new AsyncActionPayload with the given ready function. + * @param {AsyncActionFn} readyFn The function to be called when the + * dispatcher is ready. + */ + public constructor(readyFn: AsyncActionFn) { + this.fn = readyFn; + } +} From a3b4c2dfa02d1ae5ee959eaaa59e6544a0cea6d1 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Wed, 13 May 2020 20:48:49 -0600 Subject: [PATCH 139/196] Convert more async actions to AsyncActionPayload --- src/actions/GroupActions.js | 34 ---------------------------------- src/actions/GroupActions.ts | 34 ++++++++++++++++++++++++++++++++++ src/actions/TagOrderActions.ts | 3 +-- src/actions/actionCreators.ts | 5 ++--- 4 files changed, 37 insertions(+), 39 deletions(-) delete mode 100644 src/actions/GroupActions.js create mode 100644 src/actions/GroupActions.ts diff --git a/src/actions/GroupActions.js b/src/actions/GroupActions.js deleted file mode 100644 index 006c2da5b8..0000000000 --- a/src/actions/GroupActions.js +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2017 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. -*/ - -import { asyncAction } from './actionCreators'; - -const GroupActions = {}; - -/** - * Creates an action thunk that will do an asynchronous request to fetch - * the groups to which a user is joined. - * - * @param {MatrixClient} matrixClient the matrix client to query. - * @returns {function} an action thunk that will dispatch actions - * indicating the status of the request. - * @see asyncAction - */ -GroupActions.fetchJoinedGroups = function(matrixClient) { - return asyncAction('GroupActions.fetchJoinedGroups', () => matrixClient.getJoinedGroups()); -}; - -export default GroupActions; diff --git a/src/actions/GroupActions.ts b/src/actions/GroupActions.ts new file mode 100644 index 0000000000..81470d1221 --- /dev/null +++ b/src/actions/GroupActions.ts @@ -0,0 +1,34 @@ +/* +Copyright 2017 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { asyncAction } from './actionCreators'; +import { AsyncActionPayload } from "../dispatcher/payloads"; +import { MatrixClient } from "matrix-js-sdk/src/client"; + +export default class GroupActions { + /** + * Creates an action thunk that will do an asynchronous request to fetch + * the groups to which a user is joined. + * + * @param {MatrixClient} matrixClient the matrix client to query. + * @returns {AsyncActionPayload} An async action payload. + * @see asyncAction + */ + public static fetchJoinedGroups(matrixClient: MatrixClient): AsyncActionPayload { + return asyncAction('GroupActions.fetchJoinedGroups', () => matrixClient.getJoinedGroups(), null); + } +} diff --git a/src/actions/TagOrderActions.ts b/src/actions/TagOrderActions.ts index fa95ea1037..bf1820d5d1 100644 --- a/src/actions/TagOrderActions.ts +++ b/src/actions/TagOrderActions.ts @@ -90,8 +90,7 @@ export default class TagOrderActions { if (removedTags.includes(tag)) { // Return a thunk that doesn't do anything, we don't even need // an asynchronous action here, the tag is already removed. - return () => { - }; + return new AsyncActionPayload(() => {}); } removedTags.push(tag); diff --git a/src/actions/actionCreators.ts b/src/actions/actionCreators.ts index d84868a141..c789e3cd07 100644 --- a/src/actions/actionCreators.ts +++ b/src/actions/actionCreators.ts @@ -44,12 +44,11 @@ import { AsyncActionPayload } from "../dispatcher/payloads"; * result is the result of the promise returned by * `fn`. */ -export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () => any): AsyncActionPayload { +export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () => any | null): AsyncActionPayload { const helper = (dispatch) => { dispatch({ action: id + '.pending', - request: - typeof pendingFn === 'function' ? pendingFn() : undefined, + request: typeof pendingFn === 'function' ? pendingFn() : undefined, }); fn().then((result) => { dispatch({action: id + '.success', result}); From a5f3318f3bec7d66c20a862669c3c1b938b362a6 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Wed, 13 May 2020 21:03:12 -0600 Subject: [PATCH 140/196] Convert view_user dispatch to prove the conversion works This is a relatively obvious dispatch action that doesn't require a lot of complicated type definitions, so should be a good candidate to prove the thing works. If for some reason the thing stops working, we've done something wrong. This also adds a bit of generic types to the dispatch call so we don't confuse the tsx parser by using `dis.dispatch(<ViewUserPayload>{...})` as it thinks that's supposed to be a component. We still get type safety, and the thing remains happy with the generics approach. --- src/SlashCommands.tsx | 8 +++-- src/components/structures/MatrixChat.tsx | 26 ++++++++--------- src/components/structures/RightPanel.js | 5 ++-- src/components/views/avatars/MemberAvatar.js | 5 ++-- src/components/views/elements/Pill.js | 3 +- .../views/groups/GroupMemberInfo.js | 5 ++-- .../views/right_panel/GroupHeaderButtons.js | 6 ++-- .../views/right_panel/RoomHeaderButtons.js | 6 ++-- src/components/views/right_panel/UserInfo.js | 3 +- src/components/views/rooms/MemberInfo.js | 3 +- src/components/views/rooms/MemberTile.js | 3 +- src/dispatcher/actions.ts | 14 +++++++++ src/dispatcher/dispatcher.ts | 2 +- src/dispatcher/payloads/ViewUserPayload.ts | 29 +++++++++++++++++++ 14 files changed, 88 insertions(+), 30 deletions(-) create mode 100644 src/dispatcher/payloads/ViewUserPayload.ts diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index fd157b2b4c..d81da80e8d 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -41,6 +41,8 @@ import { parseFragment as parseHtml } from "parse5"; import sendBugReport from "./rageshake/submit-rageshake"; import SdkConfig from "./SdkConfig"; import { ensureDMExists } from "./createRoom"; +import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload"; +import { Action } from "./dispatcher/actions"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -943,8 +945,10 @@ export const Commands = [ } const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId); - dis.dispatch({ - action: 'view_user', + dis.dispatch<ViewUserPayload>({ + action: Action.ViewUser, + // XXX: We should be using a real member object and not assuming what the + // receiver wants. member: member || {userId}, }); return success(); diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 973d301420..d368b2c23a 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -17,12 +17,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {createRef} from 'react'; -import {InvalidStoreError} from "matrix-js-sdk/src/errors"; -import {RoomMember} from "matrix-js-sdk/src/models/room-member"; -import {MatrixEvent} from "matrix-js-sdk/src/models/event"; +import React, { createRef } from 'react'; +import { InvalidStoreError } from "matrix-js-sdk/src/errors"; +import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { isCryptoAvailable } from 'matrix-js-sdk/src/crypto'; - // focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss import 'focus-visible'; // what-input helps improve keyboard accessibility @@ -30,7 +29,7 @@ import 'what-input'; import Analytics from "../../Analytics"; import { DecryptionFailureTracker } from "../../DecryptionFailureTracker"; -import {MatrixClientPeg} from "../../MatrixClientPeg"; +import { MatrixClientPeg } from "../../MatrixClientPeg"; import PlatformPeg from "../../PlatformPeg"; import SdkConfig from "../../SdkConfig"; import * as RoomListSorter from "../../RoomListSorter"; @@ -40,7 +39,7 @@ import Notifier from '../../Notifier'; import Modal from "../../Modal"; import Tinter from "../../Tinter"; import * as sdk from '../../index'; -import { showStartChatInviteDialog, showRoomInviteDialog } from '../../RoomInvite'; +import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite'; import * as Rooms from '../../Rooms'; import linkifyMatrix from "../../linkify-matrix"; import * as Lifecycle from '../../Lifecycle'; @@ -52,21 +51,22 @@ import { getHomePageUrl } from '../../utils/pages'; import createRoom from "../../createRoom"; import KeyRequestHandler from '../../KeyRequestHandler'; import { _t, getCurrentLanguage } from '../../languageHandler'; -import SettingsStore, {SettingLevel} from "../../settings/SettingsStore"; +import SettingsStore, { SettingLevel } from "../../settings/SettingsStore"; import ThemeController from "../../settings/controllers/ThemeController"; import { startAnyRegistrationFlow } from "../../Registration.js"; import { messageForSyncError } from '../../utils/ErrorUtils'; import ResizeNotifier from "../../utils/ResizeNotifier"; -import { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils"; -import AutoDiscoveryUtils from "../../utils/AutoDiscoveryUtils"; +import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils"; import DMRoomMap from '../../utils/DMRoomMap'; import { countRoomsWithNotif } from '../../RoomNotifs'; import { ThemeWatcher } from "../../theme"; import { storeRoomAliasInCache } from '../../RoomAliasCache'; -import {defer, IDeferred} from "../../utils/promise"; +import { defer, IDeferred } from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; import type LoggedInViewType from "./LoggedInView"; +import { ViewUserPayload } from "../../dispatcher/payloads/ViewUserPayload"; +import { Action } from "../../dispatcher/actions"; /** constants for MatrixChat.state.view */ export enum Views { @@ -1755,8 +1755,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { const member = new RoomMember(null, userId); if (!member) { return; } - dis.dispatch({ - action: 'view_user', + dis.dispatch<ViewUserPayload>({ + action: Action.ViewUser, member: member, }); } diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index c6c330a202..56cc92a8f8 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -30,6 +30,7 @@ import SettingsStore from "../../settings/SettingsStore"; import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases"; import RightPanelStore from "../../stores/RightPanelStore"; import MatrixClientContext from "../../contexts/MatrixClientContext"; +import {Action} from "../../dispatcher/actions"; export default class RightPanel extends React.Component { static get propTypes() { @@ -237,7 +238,7 @@ export default class RightPanel extends React.Component { // within a room, so go back to the member panel if we were in the encryption panel, // or the member list if we were in the member panel... phew. dis.dispatch({ - action: "view_user", + action: Action.ViewUser, member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ? this.state.member : null, }); @@ -266,7 +267,7 @@ export default class RightPanel extends React.Component { if (SettingsStore.getValue("feature_cross_signing")) { const onClose = () => { dis.dispatch({ - action: "view_user", + action: Action.ViewUser, member: null, }); }; diff --git a/src/components/views/avatars/MemberAvatar.js b/src/components/views/avatars/MemberAvatar.js index 89314cfef7..d28d80e62a 100644 --- a/src/components/views/avatars/MemberAvatar.js +++ b/src/components/views/avatars/MemberAvatar.js @@ -21,6 +21,7 @@ import createReactClass from 'create-react-class'; import * as Avatar from '../../../Avatar'; import * as sdk from "../../../index"; import dis from "../../../dispatcher/dispatcher"; +import {Action} from "../../../dispatcher/actions"; export default createReactClass({ displayName: 'MemberAvatar', @@ -33,7 +34,7 @@ export default createReactClass({ resizeMethod: PropTypes.string, // The onClick to give the avatar onClick: PropTypes.func, - // Whether the onClick of the avatar should be overriden to dispatch 'view_user' + // Whether the onClick of the avatar should be overriden to dispatch `Action.ViewUser` viewUserOnClick: PropTypes.bool, title: PropTypes.string, }, @@ -85,7 +86,7 @@ export default createReactClass({ if (viewUserOnClick) { onClick = () => { dis.dispatch({ - action: 'view_user', + action: Action.ViewUser, member: this.props.member, }); }; diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index 3e4ddb99e8..03a1aeed85 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -26,6 +26,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg'; import FlairStore from "../../../stores/FlairStore"; import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import {Action} from "../../../dispatcher/actions"; // For URLs of matrix.to links in the timeline which have been reformatted by // HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`) @@ -191,7 +192,7 @@ const Pill = createReactClass({ onUserPillClicked: function() { dis.dispatch({ - action: 'view_user', + action: Action.ViewUser, member: this.state.member, }); }, diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js index 0d08771676..2582cab573 100644 --- a/src/components/views/groups/GroupMemberInfo.js +++ b/src/components/views/groups/GroupMemberInfo.js @@ -28,6 +28,7 @@ import GroupStore from '../../../stores/GroupStore'; import AccessibleButton from '../elements/AccessibleButton'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; +import {Action} from "../../../dispatcher/actions"; export default createReactClass({ displayName: 'GroupMemberInfo', @@ -103,7 +104,7 @@ export default createReactClass({ ).then(() => { // return to the user list dis.dispatch({ - action: "view_user", + action: Action.ViewUser, member: null, }); }).catch((e) => { @@ -124,7 +125,7 @@ export default createReactClass({ _onCancel: function(e) { // Go back to the user list dis.dispatch({ - action: "view_user", + action: Action.ViewUser, member: null, }); }, diff --git a/src/components/views/right_panel/GroupHeaderButtons.js b/src/components/views/right_panel/GroupHeaderButtons.js index f164b6c578..33d9325433 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.js +++ b/src/components/views/right_panel/GroupHeaderButtons.js @@ -23,6 +23,8 @@ import { _t } from '../../../languageHandler'; import HeaderButton from './HeaderButton'; import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons'; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import {Action} from "../../../dispatcher/actions"; +import {ActionPayload} from "../../../dispatcher/payloads"; const GROUP_PHASES = [ RIGHT_PANEL_PHASES.GroupMemberInfo, @@ -40,10 +42,10 @@ export default class GroupHeaderButtons extends HeaderButtons { this._onRoomsClicked = this._onRoomsClicked.bind(this); } - onAction(payload) { + onAction(payload: ActionPayload) { super.onAction(payload); - if (payload.action === "view_user") { + if (payload.action === Action.ViewUser) { if (payload.member) { this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member}); } else { diff --git a/src/components/views/right_panel/RoomHeaderButtons.js b/src/components/views/right_panel/RoomHeaderButtons.js index bad89e2dbe..838727981d 100644 --- a/src/components/views/right_panel/RoomHeaderButtons.js +++ b/src/components/views/right_panel/RoomHeaderButtons.js @@ -23,6 +23,8 @@ import { _t } from '../../../languageHandler'; import HeaderButton from './HeaderButton'; import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons'; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; +import {Action} from "../../../dispatcher/actions"; +import {ActionPayload} from "../../../dispatcher/payloads"; const MEMBER_PHASES = [ RIGHT_PANEL_PHASES.RoomMemberList, @@ -39,9 +41,9 @@ export default class RoomHeaderButtons extends HeaderButtons { this._onNotificationsClicked = this._onNotificationsClicked.bind(this); } - onAction(payload) { + onAction(payload: ActionPayload) { super.onAction(payload); - if (payload.action === "view_user") { + if (payload.action === Action.ViewUser) { if (payload.member) { this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member}); } else { diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index daf1a9490d..0392746c94 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -44,6 +44,7 @@ import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; import EncryptionPanel from "./EncryptionPanel"; import { useAsyncMemo } from '../../../hooks/useAsyncMemo'; import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification'; +import {Action} from "../../../dispatcher/actions"; const _disambiguateDevices = (devices) => { const names = Object.create(null); @@ -841,7 +842,7 @@ const GroupAdminToolsSection = ({children, groupId, groupMember, startUpdating, cli.removeUserFromGroup(groupId, groupMember.userId).then(() => { // return to the user list dis.dispatch({ - action: "view_user", + action: Action.ViewUser, member: null, }); }).catch((e) => { diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 5d8e03a050..ed6c4ad748 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -48,6 +48,7 @@ import E2EIcon from "./E2EIcon"; import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import {Action} from "../../../dispatcher/actions"; export default createReactClass({ displayName: 'MemberInfo', @@ -724,7 +725,7 @@ export default createReactClass({ onCancel: function(e) { dis.dispatch({ - action: "view_user", + action: Action.ViewUser, member: null, }); }, diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 6842e27ed3..2d290564c3 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -23,6 +23,7 @@ import * as sdk from "../../../index"; import dis from "../../../dispatcher/dispatcher"; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import {Action} from "../../../dispatcher/actions"; export default createReactClass({ displayName: 'MemberTile', @@ -185,7 +186,7 @@ export default createReactClass({ onClick: function(e) { dis.dispatch({ - action: 'view_user', + action: Action.ViewUser, member: this.props.member, }); }, diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index ff1650e4e5..880e7e62cd 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -19,5 +19,19 @@ export type DispatcherAction = Action | string; export enum Action { // TODO: Populate with actual actions + // This is lazily generated as it also includes fixing a bunch of references. Work + // that we don't really want to take on in a giant chunk. We should always define + // new actions here, and ideally when we touch existing ones we take some time to + // define them correctly. + + // When defining a new action, please use lower_scored_case with an optional class + // name prefix. For example, `RoomListStore.view_room` or `view_user_settings`. + // New definitions should also receive an accompanying interface in the payloads + // directory. + + /** + * View a user's profile. Should be used with a ViewUserPayload. + */ + ViewUser = "view_user", } diff --git a/src/dispatcher/dispatcher.ts b/src/dispatcher/dispatcher.ts index 6ef165c485..8330e5cd19 100644 --- a/src/dispatcher/dispatcher.ts +++ b/src/dispatcher/dispatcher.ts @@ -32,7 +32,7 @@ export class MatrixDispatcher extends Dispatcher<ActionPayload> { * an operation that the browser requires user interaction * for. Default false (async). */ - dispatch(payload: ActionPayload, sync = false) { + dispatch<T extends ActionPayload>(payload: T, sync = false) { if (payload instanceof AsyncActionPayload) { payload.fn((action: ActionPayload) => { this.dispatch(action, sync); diff --git a/src/dispatcher/payloads/ViewUserPayload.ts b/src/dispatcher/payloads/ViewUserPayload.ts new file mode 100644 index 0000000000..ed602d4e24 --- /dev/null +++ b/src/dispatcher/payloads/ViewUserPayload.ts @@ -0,0 +1,29 @@ +/* +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 { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { ActionPayload } from "../payloads"; +import { Action } from "../actions"; + +export interface ViewUserPayload extends ActionPayload { + action: Action.ViewUser, + + /** + * The member to view. May be null or falsy to indicate that no member + * should be shown (hide whichever relevant components). + */ + member?: RoomMember; +} From e4835c4b03f7f3bb1451f75a613a90a5d3b53ce9 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Wed, 13 May 2020 21:07:19 -0600 Subject: [PATCH 141/196] Demonstrate dis.fire() with view_user_settings Like a5f3318f3bec7d66c20a862669c3c1b938b362a6, this proves that the new dispatcher conversion works for fire-and-forget style dispatches too. This is another obvious-if-broken and generally safe conversion to make. Other actions which can be dispatched this way have been excluded for reasons mentioned in the Action enum's comments. --- .../views/dialogs/eventindex/DisableEventIndexDialog.js | 3 ++- .../views/dialogs/keybackup/NewRecoveryMethodDialog.js | 3 ++- .../dialogs/keybackup/RecoveryMethodRemovedDialog.js | 3 ++- src/components/structures/MatrixChat.tsx | 8 +++----- src/components/views/context_menus/TopLeftMenu.js | 3 ++- src/components/views/dialogs/AddressPickerDialog.js | 3 ++- .../views/dialogs/IntegrationsDisabledDialog.js | 3 ++- src/components/views/dialogs/InviteDialog.js | 3 ++- src/components/views/room_settings/UrlPreviewSettings.js | 3 ++- src/dispatcher/actions.ts | 5 +++++ 10 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js index 8af0bf278e..ec4b88f759 100644 --- a/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/DisableEventIndexDialog.js @@ -22,6 +22,7 @@ import { _t } from '../../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; +import {Action} from "../../../../dispatcher/actions"; /* * Allows the user to disable the Event Index. @@ -47,7 +48,7 @@ export default class DisableEventIndexDialog extends React.Component { await SettingsStore.setValue('enableEventIndexing', null, SettingLevel.DEVICE, false); await EventIndexPeg.deleteEventIndex(); this.props.onFinished(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); } render() { diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js index f00f2d9c3c..74552a5c08 100644 --- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js +++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js @@ -22,6 +22,7 @@ import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; +import {Action} from "../../../../dispatcher/actions"; export default class NewRecoveryMethodDialog extends React.PureComponent { static propTypes = { @@ -36,7 +37,7 @@ export default class NewRecoveryMethodDialog extends React.PureComponent { onGoToSettingsClick = () => { this.props.onFinished(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); } onSetupClick = async () => { diff --git a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js index 722334cd70..cda353e717 100644 --- a/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js +++ b/src/async-components/views/dialogs/keybackup/RecoveryMethodRemovedDialog.js @@ -21,6 +21,7 @@ import * as sdk from "../../../../index"; import dis from "../../../../dispatcher/dispatcher"; import { _t } from "../../../../languageHandler"; import Modal from "../../../../Modal"; +import {Action} from "../../../../dispatcher/actions"; export default class RecoveryMethodRemovedDialog extends React.PureComponent { static propTypes = { @@ -29,7 +30,7 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent { onGoToSettingsClick = () => { this.props.onFinished(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); } onSetupClick = () => { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index d368b2c23a..cf0388c490 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -107,7 +107,7 @@ export enum Views { // re-dispatched. NOTE: some actions are non-trivial and would require // re-factoring to be included in this list in future. const ONBOARDING_FLOW_STARTERS = [ - 'view_user_settings', + Action.ViewUserSettings, 'view_create_chat', 'view_create_room', 'view_create_group', @@ -613,7 +613,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { case 'view_indexed_room': this.viewIndexedRoom(payload.roomIndex); break; - case 'view_user_settings': { + case Action.ViewUserSettings: { const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); @@ -1621,9 +1621,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { action: 'view_create_room', }); } else if (screen === 'settings') { - dis.dispatch({ - action: 'view_user_settings', - }); + dis.fire(Action.ViewUserSettings); } else if (screen === 'welcome') { dis.dispatch({ action: 'view_welcome_page', diff --git a/src/components/views/context_menus/TopLeftMenu.js b/src/components/views/context_menus/TopLeftMenu.js index 3ec857be2f..ec99c63724 100644 --- a/src/components/views/context_menus/TopLeftMenu.js +++ b/src/components/views/context_menus/TopLeftMenu.js @@ -27,6 +27,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg'; import {MenuItem} from "../../structures/ContextMenu"; import * as sdk from "../../../index"; import {getHomePageUrl} from "../../../utils/pages"; +import {Action} from "../../../dispatcher/actions"; export default class TopLeftMenu extends React.Component { static propTypes = { @@ -134,7 +135,7 @@ export default class TopLeftMenu extends React.Component { } openSettings() { - dis.dispatch({action: 'view_user_settings'}); + dis.fire(Action.ViewUserSettings); this.closeMenu(); } diff --git a/src/components/views/dialogs/AddressPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js index 41a819c005..8ddd89dc65 100644 --- a/src/components/views/dialogs/AddressPickerDialog.js +++ b/src/components/views/dialogs/AddressPickerDialog.js @@ -33,6 +33,7 @@ import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../ import { abbreviateUrl } from '../../../utils/UrlUtils'; import {sleep} from "../../../utils/promise"; import {Key} from "../../../Keyboard"; +import {Action} from "../../../dispatcher/actions"; const TRUNCATE_QUERY_LIST = 40; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; @@ -615,7 +616,7 @@ export default createReactClass({ onManageSettingsClick(e) { e.preventDefault(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); this.onCancel(); }, diff --git a/src/components/views/dialogs/IntegrationsDisabledDialog.js b/src/components/views/dialogs/IntegrationsDisabledDialog.js index 2677963281..7c996fbeab 100644 --- a/src/components/views/dialogs/IntegrationsDisabledDialog.js +++ b/src/components/views/dialogs/IntegrationsDisabledDialog.js @@ -19,6 +19,7 @@ import PropTypes from 'prop-types'; import {_t} from "../../../languageHandler"; import * as sdk from "../../../index"; import dis from '../../../dispatcher/dispatcher'; +import {Action} from "../../../dispatcher/actions"; export default class IntegrationsDisabledDialog extends React.Component { static propTypes = { @@ -31,7 +32,7 @@ export default class IntegrationsDisabledDialog extends React.Component { _onOpenSettingsClick = () => { this.props.onFinished(); - dis.dispatch({action: "view_user_settings"}); + dis.fire(Action.ViewUserSettings); }; render() { diff --git a/src/components/views/dialogs/InviteDialog.js b/src/components/views/dialogs/InviteDialog.js index e62c417a70..1e624f7545 100644 --- a/src/components/views/dialogs/InviteDialog.js +++ b/src/components/views/dialogs/InviteDialog.js @@ -36,6 +36,7 @@ import {inviteMultipleToRoom} from "../../../RoomInvite"; import SettingsStore from '../../../settings/SettingsStore'; import RoomListStore, {TAG_DM} from "../../../stores/RoomListStore"; import {Key} from "../../../Keyboard"; +import {Action} from "../../../dispatcher/actions"; export const KIND_DM = "dm"; export const KIND_INVITE = "invite"; @@ -902,7 +903,7 @@ export default class InviteDialog extends React.PureComponent { _onManageSettingsClick = (e) => { e.preventDefault(); - dis.dispatch({ action: 'view_user_settings' }); + dis.fire(Action.ViewUserSettings); this.props.onFinished(); }; diff --git a/src/components/views/room_settings/UrlPreviewSettings.js b/src/components/views/room_settings/UrlPreviewSettings.js index 16dffd857b..cd00e5048c 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.js +++ b/src/components/views/room_settings/UrlPreviewSettings.js @@ -25,6 +25,7 @@ import { _t, _td } from '../../../languageHandler'; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import dis from "../../../dispatcher/dispatcher"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; +import {Action} from "../../../dispatcher/actions"; export default createReactClass({ @@ -37,7 +38,7 @@ export default createReactClass({ _onClickUserSettings: (e) => { e.preventDefault(); e.stopPropagation(); - dis.dispatch({action: 'view_user_settings'}); + dis.fire(Action.ViewUserSettings); }, render: function() { diff --git a/src/dispatcher/actions.ts b/src/dispatcher/actions.ts index 880e7e62cd..a2f9c3efe3 100644 --- a/src/dispatcher/actions.ts +++ b/src/dispatcher/actions.ts @@ -33,5 +33,10 @@ export enum Action { * View a user's profile. Should be used with a ViewUserPayload. */ ViewUser = "view_user", + + /** + * Open the user settings. No additional payload information required. + */ + ViewUserSettings = "view_user_settings", } From d1c6f3099c0fbcbb8e2d94f19660a99d8a378c2b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 11:20:06 +0100 Subject: [PATCH 142/196] move styling to QRCode scss Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 1 + res/css/views/dialogs/_ShareDialog.scss | 6 ------ res/css/views/elements/_QRCode.scss | 21 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 res/css/views/elements/_QRCode.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 428a28ac3a..c05e2cabc6 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -108,6 +108,7 @@ @import "./views/elements/_ManageIntegsButton.scss"; @import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; +@import "./views/elements/_QRCode.scss"; @import "./views/elements/_ReplyThread.scss"; @import "./views/elements/_ResizeHandle.scss"; @import "./views/elements/_RichText.scss"; diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index 3c984bbd1b..e08469ec6d 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -67,12 +67,6 @@ limitations under the License. height: 256px; width: 256px; margin-right: 64px; - - img { - width: 100%; - height: 100%; - border-radius: 8px; - } } .mx_ShareDialog_social_container { diff --git a/res/css/views/elements/_QRCode.scss b/res/css/views/elements/_QRCode.scss new file mode 100644 index 0000000000..96d9114b54 --- /dev/null +++ b/res/css/views/elements/_QRCode.scss @@ -0,0 +1,21 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_QRCode { + img { + border-radius: 8px; + } +} From 4c7d7032755f7a3e144efe696e5e60279b876fdf Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 11:20:27 +0100 Subject: [PATCH 143/196] Reuse QRCode for VerificationQRCode and specify widths Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/dialogs/ShareDialog.tsx | 2 +- src/components/views/elements/QRCode.tsx | 9 ++--- .../elements/crypto/VerificationQRCode.js | 36 ++++--------------- 3 files changed, 13 insertions(+), 34 deletions(-) diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx index 3924fd42ec..271f754fd2 100644 --- a/src/components/views/dialogs/ShareDialog.tsx +++ b/src/components/views/dialogs/ShareDialog.tsx @@ -221,7 +221,7 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> { <div className="mx_ShareDialog_split"> <div className="mx_ShareDialog_qrcode_container"> - <QRCode data={matrixToUrl} /> + <QRCode data={matrixToUrl} width={256} /> </div> <div className="mx_ShareDialog_social_container"> { socials.map((social) => ( diff --git a/src/components/views/elements/QRCode.tsx b/src/components/views/elements/QRCode.tsx index 9c867058c0..f70ab48fa3 100644 --- a/src/components/views/elements/QRCode.tsx +++ b/src/components/views/elements/QRCode.tsx @@ -16,19 +16,21 @@ limitations under the License. import * as React from "react"; import {toDataURL, QRCodeSegment, QRCodeToDataURLOptions} from "qrcode"; +import classNames from "classnames"; import {_t} from "../../../languageHandler"; import Spinner from "./Spinner"; interface IProps extends QRCodeToDataURLOptions { data: string | QRCodeSegment[]; + className?: string; } const defaultOptions: QRCodeToDataURLOptions = { errorCorrectionLevel: 'L', // we want it as trivial-looking as possible }; -const QRCode: React.FC<IProps> = ({data, ...options}) => { +const QRCode: React.FC<IProps> = ({data, className, ...options}) => { const [dataUri, setUri] = React.useState<string>(null); React.useEffect(() => { let cancelled = false; @@ -39,10 +41,9 @@ const QRCode: React.FC<IProps> = ({data, ...options}) => { return () => { cancelled = true; }; - }, [data, options]); + }, [JSON.stringify(data), options]); - - return <div className="mx_QRCode"> + return <div className={classNames("mx_QRCode", className)}> { dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> } </div>; }; diff --git a/src/components/views/elements/crypto/VerificationQRCode.js b/src/components/views/elements/crypto/VerificationQRCode.js index 08cd0c772e..3bbfb004c6 100644 --- a/src/components/views/elements/crypto/VerificationQRCode.js +++ b/src/components/views/elements/crypto/VerificationQRCode.js @@ -17,8 +17,7 @@ limitations under the License. import React from "react"; import PropTypes from "prop-types"; import {replaceableComponent} from "../../../../utils/replaceableComponent"; -import Spinner from "../Spinner"; -import * as QRCode from "qrcode"; +import QRCode from "../QRCode"; @replaceableComponent("views.elements.crypto.VerificationQRCode") export default class VerificationQRCode extends React.PureComponent { @@ -26,33 +25,12 @@ export default class VerificationQRCode extends React.PureComponent { qrCodeData: PropTypes.object.isRequired, }; - constructor(props) { - super(props); - this.state = { - dataUri: null, - }; - this.generateQrCode(); - } - - componentDidUpdate(prevProps): void { - if (JSON.stringify(this.props) === JSON.stringify(prevProps)) return; // No prop change - - this.generateQRCode(); - } - - async generateQrCode() { - // Now actually assemble the QR code's data URI - const uri = await QRCode.toDataURL([{data: this.props.qrCodeData.buffer, mode: 'byte'}], { - errorCorrectionLevel: 'L', // we want it as trivial-looking as possible - }); - this.setState({dataUri: uri}); - } - render() { - if (!this.state.dataUri) { - return <div className='mx_VerificationQRCode'><Spinner /></div>; - } - - return <img src={this.state.dataUri} className='mx_VerificationQRCode' />; + return ( + <QRCode + data={[{data: this.props.qrCodeData.buffer, mode: 'byte'}]} + className="mx_VerificationQRCode" + width={196} /> + ); } } From edba204408483ad41db5abaeb04768e945a233a5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 13:04:30 +0100 Subject: [PATCH 144/196] accept and linkify local domains like those from mDNS Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/linkify-matrix.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index ee9f703136..77c62ce84d 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -44,7 +44,7 @@ function matrixLinkify(linkify) { const S_HASH = S_START.jump(TT.POUND); const S_HASH_NAME = new linkify.parser.State(); const S_HASH_NAME_COLON = new linkify.parser.State(); - const S_HASH_NAME_COLON_DOMAIN = new linkify.parser.State(); + const S_HASH_NAME_COLON_DOMAIN = new linkify.parser.State(ROOMALIAS); const S_HASH_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); const S_ROOMALIAS = new linkify.parser.State(ROOMALIAS); const S_ROOMALIAS_COLON = new linkify.parser.State(); @@ -92,7 +92,7 @@ function matrixLinkify(linkify) { const S_AT = S_START.jump(TT.AT); const S_AT_NAME = new linkify.parser.State(); const S_AT_NAME_COLON = new linkify.parser.State(); - const S_AT_NAME_COLON_DOMAIN = new linkify.parser.State(); + const S_AT_NAME_COLON_DOMAIN = new linkify.parser.State(USERID); const S_AT_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); const S_USERID = new linkify.parser.State(USERID); const S_USERID_COLON = new linkify.parser.State(); @@ -138,7 +138,7 @@ function matrixLinkify(linkify) { const S_PLUS = S_START.jump(TT.PLUS); const S_PLUS_NAME = new linkify.parser.State(); const S_PLUS_NAME_COLON = new linkify.parser.State(); - const S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State(); + const S_PLUS_NAME_COLON_DOMAIN = new linkify.parser.State(GROUPID); const S_PLUS_NAME_COLON_DOMAIN_DOT = new linkify.parser.State(); const S_GROUPID = new linkify.parser.State(GROUPID); const S_GROUPID_COLON = new linkify.parser.State(); From cae28b48d6ed5de74e676e2a216637f5e73619d7 Mon Sep 17 00:00:00 2001 From: Travis Ralston <travpc@gmail.com> Date: Thu, 14 May 2020 09:43:34 -0600 Subject: [PATCH 145/196] Remove debugging that causes email addresses to load forever This was left in by accident for https://github.com/matrix-org/matrix-react-sdk/pull/4557 --- .../views/settings/tabs/user/GeneralUserSettingsTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 867982ad2b..0f82ce3a32 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -348,7 +348,7 @@ export default class GeneralUserSettingsTab extends React.Component { // For newer homeservers with separate 3PID add and bind methods (MSC2290), // there is no such concern, so we can always show the HS account 3PIDs. if (this.state.haveIdServer || this.state.serverSupportsSeparateAddAndBind === true) { - const emails = this.state.loading3pids || true + const emails = this.state.loading3pids ? <Spinner /> : <EmailAddresses emails={this.state.emails} From 777040208aa676cfe43eb69a9d9cbf92e0ce4edd Mon Sep 17 00:00:00 2001 From: Tirifto <tirifto@posteo.cz> Date: Thu, 14 May 2020 15:57:39 +0000 Subject: [PATCH 146/196] Translated using Weblate (Esperanto) Currently translated at 100.0% (2308 of 2308 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 9e8641a6e9..11239303f8 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -172,7 +172,7 @@ "New passwords don't match": "Novaj pasvortoj ne akordas", "Passwords can't be empty": "Pasvortoj ne povas esti malplenaj", "Continue": "Daŭrigi", - "Export E2E room keys": "Elporti ĝiscele ĉifrajn ŝlosilojn de la ĉambro", + "Export E2E room keys": "Elporti tutvoje ĉifrajn ŝlosilojn de la ĉambro", "Do you want to set an email address?": "Ĉu vi volas agordi retpoŝtadreson?", "Current password": "Nuna pasvorto", "Password": "Pasvorto", @@ -538,8 +538,8 @@ "Are you sure you want to leave the room '%(roomName)s'?": "Ĉu vi certe volas forlasi la ĉambron '%(roomName)s'?", "Failed to leave room": "Malsukcesis forlasi la ĉambron", "Signed Out": "Adiaŭinta", - "Old cryptography data detected": "Malnovaj kriptografiaj datumoj troviĝis", - "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Datumoj el malnova versio de Riot troviĝis. Ĉi tio malfunkciigos ĝiscelan ĉifradon en la malnova versio. Ĝiscele ĉifritaj mesaĝoj interŝanĝitaj freŝtempe per la malnova versio eble ne malĉifreblos. Tio povas kaŭzi malsukceson ankaŭ al mesaĝoj interŝanĝitaj kun tiu ĉi versio. Se vin trafos problemoj, adiaŭu kaj resalutu. Por reteni mesaĝan historion, elportu kaj reenportu viajn ŝlosilojn.", + "Old cryptography data detected": "Malnovaj datumoj de ĉifroteĥnikaro troviĝis", + "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Datumoj el malnova versio de Riot troviĝis. Ĉi tio malfunkciigos tutvojan ĉifradon en la malnova versio. Tutvoje ĉifritaj mesaĝoj interŝanĝitaj freŝtempe per la malnova versio eble ne malĉifreblos. Tio povas kaŭzi malsukceson ankaŭ al mesaĝoj interŝanĝitaj kun tiu ĉi versio. Se vin trafos problemoj, adiaŭu kaj resalutu. Por reteni mesaĝan historion, elportu kaj reenportu viajn ŝlosilojn.", "Logout": "Adiaŭi", "Your Communities": "Viaj komunumoj", "Error whilst fetching joined communities": "Akirado de viaj komunumoj eraris", @@ -576,8 +576,8 @@ "Success": "Sukceso", "Unable to remove contact information": "Ne povas forigi kontaktajn informojn", "<not supported>": "<nesubtenata>", - "Import E2E room keys": "Enporti ĝiscele ĉifrajn ĉambrajn ŝlosilojn", - "Cryptography": "Kriptografio", + "Import E2E room keys": "Enporti tutvoje ĉifrajn ĉambrajn ŝlosilojn", + "Cryptography": "Ĉifroteĥnikaro", "Analytics": "Analizo", "Riot collects anonymous analytics to allow us to improve the application.": "Riot kolektas sennomaj analizajn datumojn por helpi plibonigadon de la programo.", "Labs": "Eksperimentaj funkcioj", @@ -981,7 +981,7 @@ "Room version": "Ĉambra versio", "Room version:": "Ĉambra versio:", "Developer options": "Programistaj elektebloj", - "Room Addresses": "Ĉambra adresoj", + "Room Addresses": "Adresoj de ĉambro", "Change room avatar": "Ŝanĝi profilbildon de ĉambro", "Change room name": "Ŝanĝi nomon de ĉambro", "Change main address for the room": "Ŝanĝi ĉefan adreson de la ĉambro", @@ -2400,5 +2400,10 @@ "Opens chat with the given user": "Malfermas babilon kun la uzanto", "Sends a message to the given user": "Sendas mesaĝon al la uzanto", "Waiting for your other session to verify…": "Atendante kontrolon de via alia salutaĵo…", - "You've successfully verified your device!": "Vi sukcese kontrolis vian aparaton!" + "You've successfully verified your device!": "Vi sukcese kontrolis vian aparaton!", + "To continue, use Single Sign On to prove your identity.": "Por daŭrigi, pruvu vian identecon per ununura saluto.", + "Confirm to continue": "Konfirmu por daŭrigi", + "Click the button below to confirm your identity.": "Klaku sube la butonon por konfirmi vian identecon.", + "Confirm encryption setup": "Konfirmi agordon de ĉifrado", + "Click the button below to confirm setting up encryption.": "Klaku sube la butonon por konfirmi agordon de ĉifrado." } From e96fa6db33f1817bc368f3c9a75e70a666ec9f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com> Date: Thu, 14 May 2020 16:30:32 +0000 Subject: [PATCH 147/196] Translated using Weblate (Estonian) Currently translated at 46.9% (1082 of 2308 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 948055c35a..b6e4830bf7 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1082,5 +1082,8 @@ "Send a Direct Message": "Saada otsesõnum", "Are you sure you want to leave the room '%(roomName)s'?": "Kas oled kindel, et soovid lahkuda jututoast '%(roomName)s'?", "Unknown error": "Teadmata viga", - "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Selleka et jätkata koduserveri %(homeserverDomain)s kasutamist sa pead üle vaatama ja nõustuma meie kasutamistingimustega." + "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Selleka et jätkata koduserveri %(homeserverDomain)s kasutamist sa pead üle vaatama ja nõustuma meie kasutamistingimustega.", + "Permissions": "Õigused", + "Select the roles required to change various parts of the room": "Vali rollid, mis on vajalikud jututoa eri osade muutmiseks", + "Enable encryption?": "Kas võtame krüptimise kasutusele?" } From 9c04188ceb6ee66d492e79b5323f84f3f2ab4b93 Mon Sep 17 00:00:00 2001 From: Christian Paul <info@jaller.de> Date: Thu, 14 May 2020 16:21:20 +0000 Subject: [PATCH 148/196] Translated using Weblate (German) Currently translated at 99.7% (2300 of 2308 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 7fdaf20e40..a7b7da4146 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -2365,5 +2365,8 @@ "Activate selected button": "Ausgewählten Button aktivieren", "Toggle right panel": "Rechtes Panel ein-/ausblenden", "Toggle this dialog": "Diesen Dialog ein-/ausblenden", - "Move autocomplete selection up/down": "Auto-Vervollständigung nach oben/unten verschieben" + "Move autocomplete selection up/down": "Auto-Vervollständigung nach oben/unten verschieben", + "Opens chat with the given user": "Öffnet einen Chat mit diesem Benutzer", + "Sends a message to the given user": "Sendet diesem Benutzer eine Nachricht", + "Waiting for your other session to verify…": "Warte auf die Verifikation deiner anderen Sitzungen…" } From f35a8bffc93f91ce11a513070b7cd9feb28a4f04 Mon Sep 17 00:00:00 2001 From: Christian Paul <info@jaller.de> Date: Thu, 14 May 2020 16:32:26 +0000 Subject: [PATCH 149/196] Translated using Weblate (German) Currently translated at 100.0% (2309 of 2309 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index a7b7da4146..1166e7710a 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1794,7 +1794,7 @@ "Confirm adding this email address by using Single Sign On to prove your identity.": "Bestätige das Hinzufügen dieser E-Mail-Adresse mit „Single Sign-On“, um deine Identität nachzuweisen.", "Single Sign On": "Single Sign-On", "Confirm adding email": "Bestätige das Hinzfugen der Email-Addresse", - "Confirm adding this phone number by using Single Sign On to prove your identity.": "Bestätige das Hinzufügen dieser Telefonnumer, indem du deine Identität mittels Single Sign-On nachweist.", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Bestätige das Hinzufügen dieser Telefonnummer, indem du deine Identität mittels „Single Sign-On“ nachweist.", "Click the button below to confirm adding this phone number.": "Betätige unten die Schaltfläche um das Hinzufügen dieser Telefonnummer zu bestätigen.", "If you cancel now, you won't complete your operation.": "Wenn du jetzt abbrichst, wirst du deinen Vorgang nicht fertigstellen.", "%(name)s is requesting verification": "%(name)s fordert eine Verifizierung an", @@ -2368,5 +2368,12 @@ "Move autocomplete selection up/down": "Auto-Vervollständigung nach oben/unten verschieben", "Opens chat with the given user": "Öffnet einen Chat mit diesem Benutzer", "Sends a message to the given user": "Sendet diesem Benutzer eine Nachricht", - "Waiting for your other session to verify…": "Warte auf die Verifikation deiner anderen Sitzungen…" + "Waiting for your other session to verify…": "Warte auf die Verifikation deiner anderen Sitzungen…", + "You've successfully verified your device!": "Du hast dein Gerät erfolgreich verifiziert!", + "QR Code": "QR-Code", + "To continue, use Single Sign On to prove your identity.": "Zum Fortfahren, nutze Single Sign On um deine Identität zu bestätigen.", + "Confirm to continue": "Bestätige um fortzufahren", + "Click the button below to confirm your identity.": "Klicke den Button unten um deine Identität zu bestätigen.", + "Confirm encryption setup": "Bestätige die Einrichtung der Verschlüsselung", + "Click the button below to confirm setting up encryption.": "Klick die Schaltfläche unten um die Einstellungen der Verschlüsselung zu bestätigen." } From 77df85b0ac2774d42b842c323c2f16822762cca3 Mon Sep 17 00:00:00 2001 From: Tentarial <webmaster@rene-mail.de> Date: Thu, 14 May 2020 16:36:58 +0000 Subject: [PATCH 150/196] Translated using Weblate (German) Currently translated at 100.0% (2309 of 2309 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 1166e7710a..3600cb5148 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1029,7 +1029,7 @@ "Please review and accept all of the homeserver's policies": "Bitte prüfen und akzeptieren Sie alle Richtlinien des Heimservers", "Failed to load group members": "Konnte Gruppenmitglieder nicht laden", "That doesn't look like a valid email address": "Sieht nicht nach einer validen E-Mail-Adresse aus", - "Unable to load commit detail: %(msg)s": "Konnte Commit-Details nicht laden: %(msg)s", + "Unable to load commit detail: %(msg)s": "Konnte Commit-Details nicht laden: %(msg)", "Checking...": "Überprüfe...", "Unable to load backup status": "Konnte Backupstatus nicht laden", "Failed to decrypt %(failedCount)s sessions!": "Konnte %(failedCount)s Sitzungen nicht entschlüsseln!", From 09a4af49f35f750ecb66021e35c0540ec227d018 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 18:49:55 +0100 Subject: [PATCH 151/196] Consolidate zxcvbn progress bars into a component and add dynamic colour Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 1 + res/css/views/auth/_AuthBody.scss | 18 ------- .../views/elements/_ZxcvbnProgressBar.scss | 52 +++++++++++++++++++ .../keybackup/CreateKeyBackupDialog.js | 3 +- .../CreateSecretStorageDialog.js | 3 +- src/components/views/auth/RegistrationForm.js | 7 +-- .../views/elements/ZxcvbnProgressBar.tsx | 30 +++++++++++ 7 files changed, 89 insertions(+), 25 deletions(-) create mode 100644 res/css/views/elements/_ZxcvbnProgressBar.scss create mode 100644 src/components/views/elements/ZxcvbnProgressBar.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 428a28ac3a..671e156585 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -120,6 +120,7 @@ @import "./views/elements/_Tooltip.scss"; @import "./views/elements/_TooltipButton.scss"; @import "./views/elements/_Validation.scss"; +@import "./views/elements/_ZxcvbnProgressBar.scss"; @import "./views/emojipicker/_EmojiPicker.scss"; @import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index 4b2d6b1bf1..f4967ce202 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -148,25 +148,7 @@ limitations under the License. } .mx_AuthBody_passwordScore { - width: 100%; - appearance: none; height: 4px; - border: 0; - border-radius: 2px; position: absolute; top: -12px; - - &::-moz-progress-bar { - border-radius: 2px; - background-color: $accent-color; - } - - &::-webkit-progress-bar, - &::-webkit-progress-value { - border-radius: 2px; - } - - &::-webkit-progress-value { - background-color: $accent-color; - } } diff --git a/res/css/views/elements/_ZxcvbnProgressBar.scss b/res/css/views/elements/_ZxcvbnProgressBar.scss new file mode 100644 index 0000000000..f7786348db --- /dev/null +++ b/res/css/views/elements/_ZxcvbnProgressBar.scss @@ -0,0 +1,52 @@ +/* +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. +*/ + +$PassphraseStrengthHigh: $accent-color; +$PassphraseStrengthMedium: $username-variant5-color; +$PassphraseStrengthLow: $notice-primary-color; + +@define-mixin ProgressBarColour $colour { + color: $colour; + &::-moz-progress-bar { + background-color: $colour; + } + &::-webkit-progress-value { + background-color: $colour; + } +} + +progress.mx_ZxcvbnProgressBar { + appearance: none; + width: 100%; + border: 0; + + border-radius: 2px; + &::-moz-progress-bar { + border-radius: 2px; + } + &::-webkit-progress-bar, + &::-webkit-progress-value { + border-radius: 2px; + } + + @mixin ProgressBarColour $PassphraseStrengthLow; + &[value="2"], &[value="3"] { + @mixin ProgressBarColour $PassphraseStrengthMedium; + } + &[value="4"] { + @mixin ProgressBarColour $PassphraseStrengthHigh; + } +} diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index e4e39400f6..df2a81263d 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -26,6 +26,7 @@ import { accessSecretStorage } from '../../../../CrossSigningManager'; import SettingsStore from '../../../../settings/SettingsStore'; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import {copyNode} from "../../../../utils/strings"; +import ZxcvbnProgressBar from "../../../../components/views/elements/ZxcvbnProgressBar"; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; @@ -276,7 +277,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { </div>; } strengthMeter = <div> - <progress max={PASSWORD_MIN_SCORE} value={this.state.zxcvbnResult.score} /> + <ZxcvbnProgressBar value={this.state.zxcvbnResult.score} /> </div>; } diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index c24623e30e..4c1faa3e6a 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -26,6 +26,7 @@ import Modal from '../../../../Modal'; import { promptForBackupPassphrase } from '../../../../CrossSigningManager'; import {copyNode} from "../../../../utils/strings"; import {SSOAuthEntry} from "../../../../components/views/auth/InteractiveAuthEntryComponents"; +import ZxcvbnProgressBar from "../../../../components/views/elements/ZxcvbnProgressBar"; const PHASE_LOADING = 0; const PHASE_LOADERROR = 1; @@ -529,7 +530,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { </div>; } strengthMeter = <div> - <progress max={PASSWORD_MIN_SCORE} value={this.state.zxcvbnResult.score} /> + <ZxcvbnProgressBar value={this.state.zxcvbnResult.score} /> </div>; } diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 2a79bb8588..663e30d44d 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -29,6 +29,7 @@ import SdkConfig from '../../../SdkConfig'; import { SAFE_LOCALPART_REGEX } from '../../../Registration'; import withValidation from '../elements/Validation'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; +import ZxcvbnProgressBar from "../elements/ZxcvbnProgressBar"; const FIELD_EMAIL = 'field_email'; const FIELD_PHONE_NUMBER = 'field_phone_number'; @@ -274,11 +275,7 @@ export default createReactClass({ description: function() { const complexity = this.state.passwordComplexity; const score = complexity ? complexity.score : 0; - return <progress - className="mx_AuthBody_passwordScore" - max={PASSWORD_MIN_SCORE} - value={score} - />; + return <ZxcvbnProgressBar value={score} className="mx_AuthBody_passwordScore" />; }, rules: [ { diff --git a/src/components/views/elements/ZxcvbnProgressBar.tsx b/src/components/views/elements/ZxcvbnProgressBar.tsx new file mode 100644 index 0000000000..339149b400 --- /dev/null +++ b/src/components/views/elements/ZxcvbnProgressBar.tsx @@ -0,0 +1,30 @@ +/* +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 classNames from "classnames"; + +interface IProps { + value: 0 | 1 | 2 | 3 | 4; + className?: string; +} + +const ZxcvbnProgressBar: React.FC<IProps> = ({value, className}) => { + const classes = classNames("mx_ZxcvbnProgressBar", className); + return <progress className={classes} max={4} value={value} />; +}; + +export default ZxcvbnProgressBar; From 7ef456230479abd914f6aafbd54716007ad0a14f Mon Sep 17 00:00:00 2001 From: Tirifto <tirifto@posteo.cz> Date: Thu, 14 May 2020 16:39:20 +0000 Subject: [PATCH 152/196] Translated using Weblate (Esperanto) Currently translated at 100.0% (2309 of 2309 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 11239303f8..6b3881339b 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -177,7 +177,7 @@ "Current password": "Nuna pasvorto", "Password": "Pasvorto", "New Password": "Nova pasvorto", - "Confirm password": "Konfirmi pasvorton", + "Confirm password": "Konfirmu pasvorton", "Change Password": "Ŝanĝi pasvorton", "Authentication": "Aŭtentikigo", "Device ID": "Aparata identigilo", @@ -298,7 +298,7 @@ "Only people who have been invited": "Nur invititaj uzantoj", "Anyone who knows the room's link, apart from guests": "Iu ajn kun la ligilo, krom gastoj", "Anyone who knows the room's link, including guests": "Iu ajn kun la ligilo, inkluzive gastojn", - "Publish this room to the public in %(domain)s's room directory?": "Ĉu publikigi ĉi tiun ĉambron al la publika ĉambrujo de %(domain)s?", + "Publish this room to the public in %(domain)s's room directory?": "Ĉu publikigi ĉi tiun ĉambron al la publika listo de ĉambroj de %(domain)s?", "Who can read history?": "Kiu povas legi la historion?", "Anyone": "Iu ajn", "Members only (since the point in time of selecting this option)": "Nur ĉambranoj (ekde ĉi tiu elekto)", @@ -441,7 +441,7 @@ "collapse": "maletendi", "expand": "etendi", "Custom level": "Propra nivelo", - "Room directory": "Ĉambra dosierujo", + "Room directory": "Listo de ĉambroj", "Username not available": "Uzantonomo ne disponeblas", "Username invalid: %(errMessage)s": "Uzantonomo ne validas: %(errMessage)s", "Username available": "Uzantonomo disponeblas", @@ -732,7 +732,7 @@ "No update available.": "Neniuj ĝisdatigoj haveblas.", "Resend": "Resendi", "Collecting app version information": "Kolektante informon pri versio de la aplikaĵo", - "Delete the room alias %(alias)s and remove %(name)s from the directory?": "Ĉu forigi la ĉambran kromnomon %(alias)s kaj forigi %(name)s de la ujo?", + "Delete the room alias %(alias)s and remove %(name)s from the directory?": "Ĉu forigi la ĉambran kromnomon %(alias)s kaj forigi %(name)s de la listo de ĉambroj?", "Enable notifications for this account": "Ŝalti sciigojn por tiu ĉi konto", "Invite to this community": "Inviti al tiu ĉi komunumo", "Messages containing <span>keywords</span>": "Mesaĝoj enhavantaj <span>ŝlosilovortojn</span>", @@ -1485,7 +1485,7 @@ "Homeserver URL does not appear to be a valid Matrix homeserver": "URL por hejmservilo ŝajne ne ligas al valida hejmservilo de Matrix", "Invalid identity server discovery response": "Nevalida eltrova respondo de identiga servilo", "Identity server URL does not appear to be a valid identity server": "URL por identiga servilo ŝajne ne ligas al valida identiga servilo", - "Sign in with single sign-on": "Salutu per ununura saluto", + "Sign in with single sign-on": "Saluti per ununura saluto", "Failed to re-authenticate due to a homeserver problem": "Malsukcesis reaŭtentikigi pro hejmservila problemo", "Failed to re-authenticate": "Malsukcesis reaŭtentikigi", "Enter your password to sign in and regain access to your account.": "Enigu vian pasvorton por saluti kaj rehavi aliron al via konto.", @@ -2405,5 +2405,6 @@ "Confirm to continue": "Konfirmu por daŭrigi", "Click the button below to confirm your identity.": "Klaku sube la butonon por konfirmi vian identecon.", "Confirm encryption setup": "Konfirmi agordon de ĉifrado", - "Click the button below to confirm setting up encryption.": "Klaku sube la butonon por konfirmi agordon de ĉifrado." + "Click the button below to confirm setting up encryption.": "Klaku sube la butonon por konfirmi agordon de ĉifrado.", + "QR Code": "Rapidresponda kodo" } From 2565f751839c65ac506c8d2e524030c2bfba1d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com> Date: Thu, 14 May 2020 16:34:16 +0000 Subject: [PATCH 153/196] Translated using Weblate (Estonian) Currently translated at 48.2% (1113 of 2309 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index b6e4830bf7..2ab308b469 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1085,5 +1085,36 @@ "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.": "Selleka et jätkata koduserveri %(homeserverDomain)s kasutamist sa pead üle vaatama ja nõustuma meie kasutamistingimustega.", "Permissions": "Õigused", "Select the roles required to change various parts of the room": "Vali rollid, mis on vajalikud jututoa eri osade muutmiseks", - "Enable encryption?": "Kas võtame krüptimise kasutusele?" + "Enable encryption?": "Kas võtame krüptimise kasutusele?", + "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. <a>Learn more about encryption.</a>": "Kui kord juba kasutusele võetud, siis krüptimist enam hiljem ära lõpetada ei saa. Krüptitud sõnumeid ei saa lugeda ei vaheapealses veebiliikluses ega serveris ja vaid jututoa liikmed saavad neid lugeda. Krüptimise kasutusele võtmine takistada nii robotite kui sõnumisildade tööd. <a>Lisateave krüptimise kohta.</a>", + "Guests cannot join this room even if explicitly invited.": "Külalised ei saa selle jututoaga liituda ka siis, kui neid on otseselt kutsutud.", + "Click here to fix": "Parandamiseks klõpsi siia", + "Server error": "Serveri viga", + "Command error": "Käsu viga", + "Server unavailable, overloaded, or something else went wrong.": "Kas server pole saadaval, on üle koormatud või midagi muud läks viltu.", + "Unknown Command": "Tundmatu käsk", + "Unrecognised command: %(commandText)s": "Tundmatu käsk: %(commandText)s", + "Send as message": "Saada sõnumina", + "Failed to connect to integration manager": "Ühendus integratsioonihalduriga ei õnnestunud", + "You don't currently have any stickerpacks enabled": "Sul pole ühtegi kleepsupakki kasutusel", + "Add some now": "Lisa nüüd mõned", + "Stickerpack": "Kleepsupakk", + "Hide Stickers": "Peida kleepsud", + "Show Stickers": "Näita kleepse", + "Failed to revoke invite": "Kutse tühistamine ei õnnestunud", + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.": "Kutse tühistamine ei õnnestunud. Serveri töös võib olla ajutine tõrge või sul pole piisavalt õigusi kutse tühistamiseks.", + "Revoke invite": "Tühista kutse", + "Invited by %(sender)s": "Kutsutud %(sender)s poolt", + "Jump to first unread message.": "Mine esimese lugemata sõnumi juurde.", + "Mark all as read": "Märgi kõik loetuks", + "Error updating main address": "Viga põhiaadressi uuendamisel", + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.": "Jututoa põhiaadressi uuendamisel tekkis viga. See kas pole serveris lubatud või tekkis mingi ajutine viga.", + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.": "Jututoa lisaaadressi uuendamisel tekkis viga. See kas pole serveris lubatud või tekkis mingi ajutine viga.", + "Error creating alias": "Viga aliase loomisel", + "There was an error creating that alias. It may not be allowed by the server or a temporary failure occurred.": "Aliase loomisel tekkis viga. See kas pole serveris lubatud või tekkis mingi ajutine viga.", + "You don't have permission to delete the alias.": "Sul pole õigusi aliase kustutamiseks.", + "There was an error removing that alias. It may no longer exist or a temporary error occurred.": "Selle aliase eemaldamisel tekkis viga. Teda kas pole enam olemas või tekkis mingi ajutine viga.", + "Error removing alias": "Viga aliase eemaldamisel", + "Main address": "Põhiaadress", + "not specified": "määratlemata" } From 13922a71d3fd15f44a0d10e4fcd6a232081c8f14 Mon Sep 17 00:00:00 2001 From: Christian Paul <info@jaller.de> Date: Thu, 14 May 2020 16:48:38 +0000 Subject: [PATCH 154/196] Translated using Weblate (German) Currently translated at 100.0% (2309 of 2309 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/de/ --- src/i18n/strings/de_DE.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 3600cb5148..1166e7710a 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1029,7 +1029,7 @@ "Please review and accept all of the homeserver's policies": "Bitte prüfen und akzeptieren Sie alle Richtlinien des Heimservers", "Failed to load group members": "Konnte Gruppenmitglieder nicht laden", "That doesn't look like a valid email address": "Sieht nicht nach einer validen E-Mail-Adresse aus", - "Unable to load commit detail: %(msg)s": "Konnte Commit-Details nicht laden: %(msg)", + "Unable to load commit detail: %(msg)s": "Konnte Commit-Details nicht laden: %(msg)s", "Checking...": "Überprüfe...", "Unable to load backup status": "Konnte Backupstatus nicht laden", "Failed to decrypt %(failedCount)s sessions!": "Konnte %(failedCount)s Sitzungen nicht entschlüsseln!", From 93a608a6446c6717a8b88d9f47ac71a096da0c20 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 19:31:40 +0100 Subject: [PATCH 155/196] flatten out passwordSafe as it was a derived state value Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/auth/RegistrationForm.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 663e30d44d..4e2860c0cf 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -79,7 +79,6 @@ export default createReactClass({ password: this.props.defaultPassword || "", passwordConfirm: this.props.defaultPassword || "", passwordComplexity: null, - passwordSafe: false, }; }, @@ -291,22 +290,21 @@ export default createReactClass({ } const { scorePassword } = await import('../../../utils/PasswordScorer'); const complexity = scorePassword(value); - const safe = complexity.score >= PASSWORD_MIN_SCORE; - const allowUnsafe = SdkConfig.get()["dangerously_allow_unsafe_and_insecure_passwords"]; this.setState({ passwordComplexity: complexity, - passwordSafe: safe, }); + const safe = complexity.score >= PASSWORD_MIN_SCORE; + const allowUnsafe = SdkConfig.get()["dangerously_allow_unsafe_and_insecure_passwords"]; return allowUnsafe || safe; }, valid: function() { // Unsafe passwords that are valid are only possible through a // configuration flag. We'll print some helper text to signal // to the user that their password is allowed, but unsafe. - if (!this.state.passwordSafe) { - return _t("Password is allowed, but unsafe"); + if (this.state.passwordComplexity.score >= PASSWORD_MIN_SCORE) { + return _t("Nice, strong password!"); } - return _t("Nice, strong password!"); + return _t("Password is allowed, but unsafe"); }, invalid: function() { const complexity = this.state.passwordComplexity; From 8dd561d28a8a1fc39663a0bc138ac7e317e5f03b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 19:33:17 +0100 Subject: [PATCH 156/196] Convert Validation to TypeScript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../{Validation.js => Validation.tsx} | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) rename src/components/views/elements/{Validation.js => Validation.tsx} (87%) diff --git a/src/components/views/elements/Validation.js b/src/components/views/elements/Validation.tsx similarity index 87% rename from src/components/views/elements/Validation.js rename to src/components/views/elements/Validation.tsx index 2be526a3c3..09d6ec12f7 100644 --- a/src/components/views/elements/Validation.js +++ b/src/components/views/elements/Validation.tsx @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,11 +16,34 @@ limitations under the License. */ /* eslint-disable babel/no-invalid-this */ +import React from "react"; +import classNames from "classnames"; -import classNames from 'classnames'; +type Data = Pick<IValidateArgs, "value" | "allowEmpty">; + +interface IRule<T> { + key: string; + final?: boolean; + skip?(this: T, data: Data): boolean; + test(this: T, data: Data): boolean | Promise<boolean>; + valid?(this: T): string; + invalid?(this: T): string; +} + +interface IArgs<T> { + rules: IRule<T>[]; + description(): React.ReactChild; +} + +interface IValidateArgs { + value: string; + focused: boolean; + allowEmpty: boolean; +} /** * Creates a validation function from a set of rules describing what to validate. + * Generic T is the "this" type passed to the rule methods * * @param {Function} description * Function that returns a string summary of the kind of value that will @@ -37,8 +61,8 @@ import classNames from 'classnames'; * A validation function that takes in the current input value and returns * the overall validity and a feedback UI that can be rendered for more detail. */ -export default function withValidation({ description, rules }) { - return async function onValidate({ value, focused, allowEmpty = true }) { +export default function withValidation<T = undefined>({ description, rules }: IArgs<T>) { + return async function onValidate({ value, focused, allowEmpty = true }: IValidateArgs) { if (!value && allowEmpty) { return { valid: null, From eb6796bd0eef258bbd278b247e3fd6d773efd6fe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 19:53:09 +0100 Subject: [PATCH 157/196] Migrate PasswordScorer to TypeScript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/utils/{PasswordScorer.js => PasswordScorer.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/utils/{PasswordScorer.js => PasswordScorer.ts} (98%) diff --git a/src/utils/PasswordScorer.js b/src/utils/PasswordScorer.ts similarity index 98% rename from src/utils/PasswordScorer.js rename to src/utils/PasswordScorer.ts index 9d89942bf5..d8f3b0fb96 100644 --- a/src/utils/PasswordScorer.js +++ b/src/utils/PasswordScorer.ts @@ -63,7 +63,7 @@ _td("Short keyboard patterns are easy to guess"); * @param {string} password Password to score * @returns {object} Score result with `score` and `feedback` properties */ -export function scorePassword(password) { +export function scorePassword(password: string) { if (password.length === 0) return null; const userInputs = ZXCVBN_USER_INPUTS.slice(); From cf3c4d9e5f53a387a60cad742b6304b7c39f5b14 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 20:19:15 +0100 Subject: [PATCH 158/196] Extract Password field from Registration into a reusable component Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 1 + res/css/_components.scss | 1 + res/css/views/auth/_AuthBody.scss | 6 - .../_PassphraseField.scss} | 7 +- src/components/views/auth/PassphraseField.tsx | 121 ++++++++++++++++++ src/components/views/auth/RegistrationForm.js | 63 +-------- src/components/views/elements/Validation.tsx | 13 +- yarn.lock | 5 + 8 files changed, 148 insertions(+), 69 deletions(-) rename res/css/views/{elements/_ZxcvbnProgressBar.scss => auth/_PassphraseField.scss} (92%) create mode 100644 src/components/views/auth/PassphraseField.tsx diff --git a/package.json b/package.json index 92d228a812..797b57d306 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "@types/classnames": "^2.2.10", "@types/modernizr": "^3.5.3", "@types/react": "16.9", + "@types/zxcvbn": "^4.4.0", "babel-eslint": "^10.0.3", "babel-jest": "^24.9.0", "chokidar": "^3.3.1", diff --git a/res/css/_components.scss b/res/css/_components.scss index 671e156585..b871045fa1 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -41,6 +41,7 @@ @import "./views/auth/_CountryDropdown.scss"; @import "./views/auth/_InteractiveAuthEntryComponents.scss"; @import "./views/auth/_LanguageSelector.scss"; +@import "./views/auth/_PassphraseField.scss"; @import "./views/auth/_ServerConfig.scss"; @import "./views/auth/_ServerTypeSelector.scss"; @import "./views/auth/_Welcome.scss"; diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index f4967ce202..120da4c4f1 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -146,9 +146,3 @@ limitations under the License. .mx_AuthBody_spinner { margin: 1em 0; } - -.mx_AuthBody_passwordScore { - height: 4px; - position: absolute; - top: -12px; -} diff --git a/res/css/views/elements/_ZxcvbnProgressBar.scss b/res/css/views/auth/_PassphraseField.scss similarity index 92% rename from res/css/views/elements/_ZxcvbnProgressBar.scss rename to res/css/views/auth/_PassphraseField.scss index f7786348db..d810198213 100644 --- a/res/css/views/elements/_ZxcvbnProgressBar.scss +++ b/res/css/views/auth/_PassphraseField.scss @@ -18,6 +18,8 @@ $PassphraseStrengthHigh: $accent-color; $PassphraseStrengthMedium: $username-variant5-color; $PassphraseStrengthLow: $notice-primary-color; +.mx_PassphraseField {} + @define-mixin ProgressBarColour $colour { color: $colour; &::-moz-progress-bar { @@ -28,10 +30,13 @@ $PassphraseStrengthLow: $notice-primary-color; } } -progress.mx_ZxcvbnProgressBar { +progress.mx_PassphraseField_progress { appearance: none; width: 100%; border: 0; + height: 4px; + position: absolute; + top: -12px; border-radius: 2px; &::-moz-progress-bar { diff --git a/src/components/views/auth/PassphraseField.tsx b/src/components/views/auth/PassphraseField.tsx new file mode 100644 index 0000000000..425921cd7c --- /dev/null +++ b/src/components/views/auth/PassphraseField.tsx @@ -0,0 +1,121 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, {PureComponent, RefCallback, RefObject} from "react"; +import classNames from "classnames"; +import zxcvbn from "zxcvbn"; + +import SdkConfig from "../../../SdkConfig"; +import withValidation, {IFieldState, IValidationResult} from "../elements/Validation"; +import {_t, _td} from "../../../languageHandler"; +import Field from "../elements/Field"; + +interface IProps { + id?: string; + className?: string; + minScore: 0 | 1 | 2 | 3 | 4; + value: string; + fieldRef: RefCallback<Field> | RefObject<Field>; + + label?: string; + labelEnterPassword?: string; + labelStrongPassword?: string; + labelAllowedButUnsafe?: string; + + onChange(ev: KeyboardEvent); + onValidate(result: IValidationResult); +} + +interface IState { + complexity: zxcvbn.ZXCVBNResult; +} + +class PassphraseField extends PureComponent<IProps, IState> { + static defaultProps = { + label: _td("Password"), + labelEnterPassword: _td("Enter password"), + labelStrongPassword: _td("Nice, strong password!"), + labelAllowedButUnsafe: _td("Password is allowed, but unsafe"), + }; + + public readonly validate = withValidation<this>({ + description: function() { + const complexity = this.state.complexity; + const score = complexity ? complexity.score : 0; + return <progress className="mx_PassphraseField_progress" max={4} value={score} />; + }, + rules: [ + { + key: "required", + test: ({ value, allowEmpty }) => allowEmpty || !!value, + invalid: () => _t(this.props.labelEnterPassword), + }, + { + key: "complexity", + test: async function({ value }) { + if (!value) { + return false; + } + const { scorePassword } = await import('../../../utils/PasswordScorer'); + const complexity = scorePassword(value); + this.setState({ complexity }); + const safe = complexity.score >= this.props.minScore; + const allowUnsafe = SdkConfig.get()["dangerously_allow_unsafe_and_insecure_passwords"]; + return allowUnsafe || safe; + }, + valid: function() { + // Unsafe passwords that are valid are only possible through a + // configuration flag. We'll print some helper text to signal + // to the user that their password is allowed, but unsafe. + if (this.state.complexity.score >= this.props.minScore) { + return _t(this.props.labelStrongPassword); + } + return _t(this.props.labelAllowedButUnsafe); + }, + invalid: function() { + const complexity = this.state.complexity; + if (!complexity) { + return null; + } + const { feedback } = complexity; + return feedback.warning || feedback.suggestions[0] || _t("Keep going..."); + }, + }, + ], + }); + + onValidate = async (fieldState: IFieldState) => { + const result = await this.validate(fieldState); + this.props.onValidate(result); + return result; + }; + + render() { + return <Field + id={this.props.id} + className={classNames("mx_PassphraseField", this.props.className)} + ref={this.props.fieldRef} + type="password" + autoComplete="new-password" + label={_t(this.props.label)} + value={this.props.value} + onChange={this.props.onChange} + onValidate={this.onValidate} + /> + } +} + +export default PassphraseField; diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index 4e2860c0cf..7bbd15d8d3 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -29,7 +29,7 @@ import SdkConfig from '../../../SdkConfig'; import { SAFE_LOCALPART_REGEX } from '../../../Registration'; import withValidation from '../elements/Validation'; import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils"; -import ZxcvbnProgressBar from "../elements/ZxcvbnProgressBar"; +import PassphraseField from "./PassphraseField"; const FIELD_EMAIL = 'field_email'; const FIELD_PHONE_NUMBER = 'field_phone_number'; @@ -264,60 +264,10 @@ export default createReactClass({ }); }, - async onPasswordValidate(fieldState) { - const result = await this.validatePasswordRules(fieldState); + onPasswordValidate(result) { this.markFieldValid(FIELD_PASSWORD, result.valid); - return result; }, - validatePasswordRules: withValidation({ - description: function() { - const complexity = this.state.passwordComplexity; - const score = complexity ? complexity.score : 0; - return <ZxcvbnProgressBar value={score} className="mx_AuthBody_passwordScore" />; - }, - rules: [ - { - key: "required", - test: ({ value, allowEmpty }) => allowEmpty || !!value, - invalid: () => _t("Enter password"), - }, - { - key: "complexity", - test: async function({ value }) { - if (!value) { - return false; - } - const { scorePassword } = await import('../../../utils/PasswordScorer'); - const complexity = scorePassword(value); - this.setState({ - passwordComplexity: complexity, - }); - const safe = complexity.score >= PASSWORD_MIN_SCORE; - const allowUnsafe = SdkConfig.get()["dangerously_allow_unsafe_and_insecure_passwords"]; - return allowUnsafe || safe; - }, - valid: function() { - // Unsafe passwords that are valid are only possible through a - // configuration flag. We'll print some helper text to signal - // to the user that their password is allowed, but unsafe. - if (this.state.passwordComplexity.score >= PASSWORD_MIN_SCORE) { - return _t("Nice, strong password!"); - } - return _t("Password is allowed, but unsafe"); - }, - invalid: function() { - const complexity = this.state.passwordComplexity; - if (!complexity) { - return null; - } - const { feedback } = complexity; - return feedback.warning || feedback.suggestions[0] || _t("Keep going..."); - }, - }, - ], - }), - onPasswordConfirmChange(ev) { this.setState({ passwordConfirm: ev.target.value, @@ -479,13 +429,10 @@ export default createReactClass({ }, renderPassword() { - const Field = sdk.getComponent('elements.Field'); - return <Field + return <PassphraseField id="mx_RegistrationForm_password" - ref={field => this[FIELD_PASSWORD] = field} - type="password" - autoComplete="new-password" - label={_t("Password")} + fieldRef={field => this[FIELD_PASSWORD] = field} + minScore={PASSWORD_MIN_SCORE} value={this.state.password} onChange={this.onPasswordChange} onValidate={this.onPasswordValidate} diff --git a/src/components/views/elements/Validation.tsx b/src/components/views/elements/Validation.tsx index 09d6ec12f7..50544c9f51 100644 --- a/src/components/views/elements/Validation.tsx +++ b/src/components/views/elements/Validation.tsx @@ -19,7 +19,7 @@ limitations under the License. import React from "react"; import classNames from "classnames"; -type Data = Pick<IValidateArgs, "value" | "allowEmpty">; +type Data = Pick<IFieldState, "value" | "allowEmpty">; interface IRule<T> { key: string; @@ -32,15 +32,20 @@ interface IRule<T> { interface IArgs<T> { rules: IRule<T>[]; - description(): React.ReactChild; + description(this: T): React.ReactChild; } -interface IValidateArgs { +export interface IFieldState { value: string; focused: boolean; allowEmpty: boolean; } +export interface IValidationResult { + valid?: boolean; + feedback?: React.ReactChild; +} + /** * Creates a validation function from a set of rules describing what to validate. * Generic T is the "this" type passed to the rule methods @@ -62,7 +67,7 @@ interface IValidateArgs { * the overall validity and a feedback UI that can be rendered for more detail. */ export default function withValidation<T = undefined>({ description, rules }: IArgs<T>) { - return async function onValidate({ value, focused, allowEmpty = true }: IValidateArgs) { + return async function onValidate({ value, focused, allowEmpty = true }: IFieldState): Promise<IValidationResult> { if (!value && allowEmpty) { return { valid: null, diff --git a/yarn.lock b/yarn.lock index 520e976b17..b2c703c0d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1318,6 +1318,11 @@ dependencies: "@types/yargs-parser" "*" +"@types/zxcvbn@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.0.tgz#fbc1d941cc6d9d37d18405c513ba6b294f89b609" + integrity sha512-GQLOT+SN20a+AI51y3fAimhyTF4Y0RG+YP3gf91OibIZ7CJmPFgoZi+ZR5a+vRbS01LbQosITWum4ATmJ1Z6Pg== + "@typescript-eslint/experimental-utils@^2.5.0": version "2.27.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz#801a952c10b58e486c9a0b36cf21e2aab1e9e01a" From 865495dd695293fa5c8cc9ee17bb24dfdc66d097 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 20:33:50 +0100 Subject: [PATCH 159/196] replace zxcvbn field in CreateSecretStorageDialog with PassphraseField Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../_CreateSecretStorageDialog.scss | 11 -- .../CreateSecretStorageDialog.js | 106 +++++------------- src/components/views/auth/PassphraseField.tsx | 6 +- 3 files changed, 36 insertions(+), 87 deletions(-) diff --git a/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss b/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss index a9ebd54b31..63e5a3de09 100644 --- a/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss +++ b/res/css/views/dialogs/secretstorage/_CreateSecretStorageDialog.scss @@ -68,17 +68,6 @@ limitations under the License. margin-top: 0px; } -.mx_CreateSecretStorageDialog_passPhraseHelp { - flex: 1; - height: 64px; - margin-left: 20px; - font-size: 80%; -} - -.mx_CreateSecretStorageDialog_passPhraseHelp progress { - width: 100%; -} - .mx_CreateSecretStorageDialog_passPhraseMatch { width: 200px; margin-left: 20px; diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 4c1faa3e6a..b77c71d08c 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -15,18 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; -import { scorePassword } from '../../../../utils/PasswordScorer'; import FileSaver from 'file-saver'; -import { _t } from '../../../../languageHandler'; +import {_t, _td} from '../../../../languageHandler'; import Modal from '../../../../Modal'; import { promptForBackupPassphrase } from '../../../../CrossSigningManager'; import {copyNode} from "../../../../utils/strings"; import {SSOAuthEntry} from "../../../../components/views/auth/InteractiveAuthEntryComponents"; -import ZxcvbnProgressBar from "../../../../components/views/elements/ZxcvbnProgressBar"; +import PassphraseField from "../../../../components/views/auth/PassphraseField"; const PHASE_LOADING = 0; const PHASE_LOADERROR = 1; @@ -40,7 +39,6 @@ const PHASE_DONE = 8; const PHASE_CONFIRM_SKIP = 9; const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms. /* * Walks the user through the process of creating a passphrase to guard Secure @@ -69,10 +67,10 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this.state = { phase: PHASE_LOADING, passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', copied: false, downloaded: false, - zxcvbnResult: null, backupInfo: null, backupSigStatus: null, // does the server offer a UI auth flow with just m.login.password @@ -84,6 +82,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { useKeyBackup: true, }; + this._passphraseField = createRef(); + this._fetchBackupInfo(); if (this.state.accountPassword) { // If we have an account password in memory, let's simplify and @@ -365,22 +365,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _onPassPhraseNextClick = async (e) => { e.preventDefault(); + if (!this._passphraseField.current) return; // unmounting - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + await this._passphraseField.current.validate({ allowEmpty: false }); + if (!this._passphraseField.current.state.valid) { + this._passphraseField.current.focus(); + this._passphraseField.current.validate({ allowEmpty: false, focused: true }); + return; } + + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); }; _onPassPhraseConfirmNextClick = async (e) => { @@ -400,9 +394,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _onSetAgainClick = () => { this.setState({ passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', phase: PHASE_PASSPHRASE, - zxcvbnResult: null, }); } @@ -412,23 +406,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } + _onPassPhraseValidate = (result) => { + this.setState({ + passPhraseValid: result.valid, + }); + }; + _onPassPhraseChange = (e) => { this.setState({ passPhrase: e.target.value, }); - - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - this._setZxcvbnResultTimeout = setTimeout(() => { - this._setZxcvbnResultTimeout = null; - this.setState({ - // precompute this and keep it in state: zxcvbn is fast but - // we use it in a couple of different places so no point recomputing - // it unnecessarily. - zxcvbnResult: scorePassword(this.state.passPhrase), - }); - }, PASSPHRASE_FEEDBACK_DELAY); } _onPassPhraseConfirmChange = (e) => { @@ -437,10 +424,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { }); } - _passPhraseIsValid() { - return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE; - } - _onAccountPasswordChange = (e) => { this.setState({ accountPassword: e.target.value, @@ -503,37 +486,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { _renderPhasePassPhrase() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const Field = sdk.getComponent('views.elements.Field'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const LabelledToggleSwitch = sdk.getComponent('views.elements.LabelledToggleSwitch'); - let strengthMeter; - let helpText; - if (this.state.zxcvbnResult) { - if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) { - helpText = _t("Great! This recovery passphrase looks strong enough."); - } else { - // We take the warning from zxcvbn or failing that, the first - // suggestion. In practice The first is generally the most relevant - // and it's probably better to present the user with one thing to - // improve about their password than a whole collection - it can - // spit out a warning and multiple suggestions which starts getting - // very information-dense. - const suggestion = ( - this.state.zxcvbnResult.feedback.warning || - this.state.zxcvbnResult.feedback.suggestions[0] - ); - const suggestionBlock = <div>{suggestion || _t("Keep going...")}</div>; - - helpText = <div> - {suggestionBlock} - </div>; - } - strengthMeter = <div> - <ZxcvbnProgressBar value={this.state.zxcvbnResult.score} /> - </div>; - } - return <form onSubmit={this._onPassPhraseNextClick}> <p>{_t( "Set a recovery passphrase to secure encrypted information and recover it if you log out. " + @@ -541,19 +496,20 @@ export default class CreateSecretStorageDialog extends React.PureComponent { )}</p> <div className="mx_CreateSecretStorageDialog_passPhraseContainer"> - <Field - type="password" + <PassphraseField className="mx_CreateSecretStorageDialog_passPhraseField" onChange={this._onPassPhraseChange} + minScore={PASSWORD_MIN_SCORE} value={this.state.passPhrase} - label={_t("Enter a recovery passphrase")} + onValidate={this._onPassPhraseValidate} + fieldRef={this._passphraseField} autoFocus={true} - autoComplete="new-password" + + label={_td("Enter a recovery passphrase")} + labelEnterPassword={_td("Enter a recovery passphrase")} + labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")} + labelAllowedButUnsafe={_td("Great! This recovery passphrase looks strong enough.")} /> - <div className="mx_CreateSecretStorageDialog_passPhraseHelp"> - {strengthMeter} - {helpText} - </div> </div> <LabelledToggleSwitch @@ -565,7 +521,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { primaryButton={_t('Continue')} onPrimaryButtonClick={this._onPassPhraseNextClick} hasCancel={false} - disabled={!this._passPhraseIsValid()} + disabled={!this.state.passPhraseValid} > <button type="button" onClick={this._onSkipSetupClick} diff --git a/src/components/views/auth/PassphraseField.tsx b/src/components/views/auth/PassphraseField.tsx index 425921cd7c..c1781406d9 100644 --- a/src/components/views/auth/PassphraseField.tsx +++ b/src/components/views/auth/PassphraseField.tsx @@ -24,11 +24,12 @@ import {_t, _td} from "../../../languageHandler"; import Field from "../elements/Field"; interface IProps { + autoFocus?: boolean; id?: string; className?: string; minScore: 0 | 1 | 2 | 3 | 4; value: string; - fieldRef: RefCallback<Field> | RefObject<Field>; + fieldRef?: RefCallback<Field> | RefObject<Field>; label?: string; labelEnterPassword?: string; @@ -51,6 +52,8 @@ class PassphraseField extends PureComponent<IProps, IState> { labelAllowedButUnsafe: _td("Password is allowed, but unsafe"), }; + state = { complexity: null }; + public readonly validate = withValidation<this>({ description: function() { const complexity = this.state.complexity; @@ -106,6 +109,7 @@ class PassphraseField extends PureComponent<IProps, IState> { render() { return <Field id={this.props.id} + autoFocus={this.props.autoFocus} className={classNames("mx_PassphraseField", this.props.className)} ref={this.props.fieldRef} type="password" From f2979f3fd8f7233c121d4738c1ba037814eddfd7 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 20:59:46 +0100 Subject: [PATCH 160/196] replace zxcvbn field in CreateKeyBackupDialog with PassphraseField Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 1 - .../keybackup/_CreateKeyBackupDialog.scss | 11 -- .../keybackup/CreateKeyBackupDialog.js | 108 ++++++------------ .../CreateSecretStorageDialog.js | 5 - src/i18n/strings/en_EN.json | 11 +- 5 files changed, 37 insertions(+), 99 deletions(-) diff --git a/res/css/_components.scss b/res/css/_components.scss index b871045fa1..0faf971786 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -121,7 +121,6 @@ @import "./views/elements/_Tooltip.scss"; @import "./views/elements/_TooltipButton.scss"; @import "./views/elements/_Validation.scss"; -@import "./views/elements/_ZxcvbnProgressBar.scss"; @import "./views/emojipicker/_EmojiPicker.scss"; @import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; diff --git a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss index b9babd05f5..9be98e25b2 100644 --- a/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss +++ b/res/css/views/dialogs/keybackup/_CreateKeyBackupDialog.scss @@ -35,17 +35,6 @@ limitations under the License. align-items: flex-start; } -.mx_CreateKeyBackupDialog_passPhraseHelp { - flex: 1; - height: 85px; - margin-left: 20px; - font-size: 80%; -} - -.mx_CreateKeyBackupDialog_passPhraseHelp progress { - width: 100%; -} - .mx_CreateKeyBackupDialog_passPhraseInput { flex: none; width: 250px; diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index df2a81263d..532b2f960f 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -15,18 +15,17 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React, {createRef} from 'react'; import FileSaver from 'file-saver'; import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import PropTypes from 'prop-types'; -import { scorePassword } from '../../../../utils/PasswordScorer'; -import { _t } from '../../../../languageHandler'; +import {_t, _td} from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../CrossSigningManager'; import SettingsStore from '../../../../settings/SettingsStore'; import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import {copyNode} from "../../../../utils/strings"; -import ZxcvbnProgressBar from "../../../../components/views/elements/ZxcvbnProgressBar"; +import PassphraseField from "../../../../components/views/auth/PassphraseField"; const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE_CONFIRM = 1; @@ -37,7 +36,6 @@ const PHASE_DONE = 5; const PHASE_OPTOUT_CONFIRM = 6; const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc. -const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms. /* * Walks the user through the process of creating an e2e key backup @@ -53,17 +51,18 @@ export default class CreateKeyBackupDialog extends React.PureComponent { this._recoveryKeyNode = null; this._keyBackupInfo = null; - this._setZxcvbnResultTimeout = null; this.state = { secureSecretStorage: null, phase: PHASE_PASSPHRASE, passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', copied: false, downloaded: false, - zxcvbnResult: null, }; + + this._passphraseField = createRef(); } async componentDidMount() { @@ -82,12 +81,6 @@ export default class CreateKeyBackupDialog extends React.PureComponent { } } - componentWillUnmount() { - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - } - _collectRecoveryKeyNode = (n) => { this._recoveryKeyNode = n; } @@ -181,22 +174,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent { _onPassPhraseNextClick = async (e) => { e.preventDefault(); + if (!this._passphraseField.current) return; // unmounting - // If we're waiting for the timeout before updating the result at this point, - // skip ahead and do it now, otherwise we'll deny the attempt to proceed - // even if the user entered a valid passphrase - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - this._setZxcvbnResultTimeout = null; - await new Promise((resolve) => { - this.setState({ - zxcvbnResult: scorePassword(this.state.passPhrase), - }, resolve); - }); - } - if (this._passPhraseIsValid()) { - this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); + await this._passphraseField.current.validate({ allowEmpty: false }); + if (!this._passphraseField.current.state.valid) { + this._passphraseField.current.focus(); + this._passphraseField.current.validate({ allowEmpty: false, focused: true }); + return; } + + this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); }; _onPassPhraseConfirmNextClick = async (e) => { @@ -215,9 +202,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent { _onSetAgainClick = () => { this.setState({ passPhrase: '', + passPhraseValid: false, passPhraseConfirm: '', phase: PHASE_PASSPHRASE, - zxcvbnResult: null, }); } @@ -227,23 +214,16 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } + _onPassPhraseValidate = (result) => { + this.setState({ + passPhraseValid: result.valid, + }); + }; + _onPassPhraseChange = (e) => { this.setState({ passPhrase: e.target.value, }); - - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } - this._setZxcvbnResultTimeout = setTimeout(() => { - this._setZxcvbnResultTimeout = null; - this.setState({ - // precompute this and keep it in state: zxcvbn is fast but - // we use it in a couple of different places so no point recomputing - // it unnecessarily. - zxcvbnResult: scorePassword(this.state.passPhrase), - }); - }, PASSPHRASE_FEEDBACK_DELAY); } _onPassPhraseConfirmChange = (e) => { @@ -252,35 +232,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent { }); } - _passPhraseIsValid() { - return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE; - } - _renderPhasePassPhrase() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - let strengthMeter; - let helpText; - if (this.state.zxcvbnResult) { - if (this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE) { - helpText = _t("Great! This recovery passphrase looks strong enough."); - } else { - const suggestions = []; - for (let i = 0; i < this.state.zxcvbnResult.feedback.suggestions.length; ++i) { - suggestions.push(<div key={i}>{this.state.zxcvbnResult.feedback.suggestions[i]}</div>); - } - const suggestionBlock = <div>{suggestions.length > 0 ? suggestions : _t("Keep going...")}</div>; - - helpText = <div> - {this.state.zxcvbnResult.feedback.warning} - {suggestionBlock} - </div>; - } - strengthMeter = <div> - <ZxcvbnProgressBar value={this.state.zxcvbnResult.score} /> - </div>; - } - return <form onSubmit={this._onPassPhraseNextClick}> <p>{_t( "<b>Warning</b>: You should only set up key backup from a trusted computer.", {}, @@ -294,17 +248,19 @@ export default class CreateKeyBackupDialog extends React.PureComponent { <div className="mx_CreateKeyBackupDialog_primaryContainer"> <div className="mx_CreateKeyBackupDialog_passPhraseContainer"> - <input type="password" - onChange={this._onPassPhraseChange} - value={this.state.passPhrase} + <PassphraseField className="mx_CreateKeyBackupDialog_passPhraseInput" - placeholder={_t("Enter a recovery passphrase...")} + onChange={this._onPassPhraseChange} + minScore={PASSWORD_MIN_SCORE} + value={this.state.passPhrase} + onValidate={this._onPassPhraseValidate} + fieldRef={this._passphraseField} autoFocus={true} + label={_td("Enter a recovery passphrase")} + labelEnterPassword={_td("Enter a recovery passphrase")} + labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")} + labelAllowedButUnsafe={_td("Great! This recovery passphrase looks strong enough.")} /> - <div className="mx_CreateKeyBackupDialog_passPhraseHelp"> - {strengthMeter} - {helpText} - </div> </div> </div> @@ -312,7 +268,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent { primaryButton={_t('Next')} onPrimaryButtonClick={this._onPassPhraseNextClick} hasCancel={false} - disabled={!this._passPhraseIsValid()} + disabled={!this.state.passPhraseValid} /> <details> diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index b77c71d08c..12b71206d0 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -61,7 +61,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this._recoveryKey = null; this._recoveryKeyNode = null; - this._setZxcvbnResultTimeout = null; this._backupKey = null; this.state = { @@ -100,9 +99,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { componentWillUnmount() { MatrixClientPeg.get().removeListener('crypto.keyBackupStatus', this._onKeyBackupStatusChange); - if (this._setZxcvbnResultTimeout !== null) { - clearTimeout(this._setZxcvbnResultTimeout); - } } async _fetchBackupInfo() { @@ -504,7 +500,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { onValidate={this._onPassPhraseValidate} fieldRef={this._passphraseField} autoFocus={true} - label={_td("Enter a recovery passphrase")} labelEnterPassword={_td("Enter a recovery passphrase")} labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 82b2a752ea..ec018d5e84 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1888,6 +1888,10 @@ "Your Modular server": "Your Modular server", "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.": "Enter the location of your Modular homeserver. It may use your own domain name or be a subdomain of <a>modular.im</a>.", "Server Name": "Server Name", + "Enter password": "Enter password", + "Nice, strong password!": "Nice, strong password!", + "Password is allowed, but unsafe": "Password is allowed, but unsafe", + "Keep going...": "Keep going...", "The email field must not be blank.": "The email field must not be blank.", "The username field must not be blank.": "The username field must not be blank.", "The phone number field must not be blank.": "The phone number field must not be blank.", @@ -1902,10 +1906,6 @@ "Use an email address to recover your account": "Use an email address to recover your account", "Enter email address (required on this homeserver)": "Enter email address (required on this homeserver)", "Doesn't look like a valid email address": "Doesn't look like a valid email address", - "Enter password": "Enter password", - "Password is allowed, but unsafe": "Password is allowed, but unsafe", - "Nice, strong password!": "Nice, strong password!", - "Keep going...": "Keep going...", "Passwords don't match": "Passwords don't match", "Other users can invite you to rooms using your contact details": "Other users can invite you to rooms using your contact details", "Enter phone number (required on this homeserver)": "Enter phone number (required on this homeserver)", @@ -2199,9 +2199,9 @@ "Restore": "Restore", "You'll need to authenticate with the server to confirm the upgrade.": "You'll need to authenticate with the server to confirm the upgrade.", "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.": "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.", - "Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.", "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:": "Set a recovery passphrase to secure encrypted information and recover it if you log out. This should be different to your account password:", "Enter a recovery passphrase": "Enter a recovery passphrase", + "Great! This recovery passphrase looks strong enough.": "Great! This recovery passphrase looks strong enough.", "Back up encrypted message keys": "Back up encrypted message keys", "Set up with a recovery key": "Set up with a recovery key", "That matches!": "That matches!", @@ -2229,7 +2229,6 @@ "Unable to set up secret storage": "Unable to set up secret storage", "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.": "We'll store an encrypted copy of your keys on our server. Secure your backup with a recovery passphrase.", "For maximum security, this should be different from your account password.": "For maximum security, this should be different from your account password.", - "Enter a recovery passphrase...": "Enter a recovery passphrase...", "Please enter your recovery passphrase a second time to confirm.": "Please enter your recovery passphrase a second time to confirm.", "Repeat your recovery passphrase...": "Repeat your recovery passphrase...", "Your keys are being backed up (the first backup could take a few minutes).": "Your keys are being backed up (the first backup could take a few minutes).", From 8bab8be3dd14afd1eca49079ccd3a41987138e36 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 21:01:49 +0100 Subject: [PATCH 161/196] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/views/auth/_PassphraseField.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/views/auth/_PassphraseField.scss b/res/css/views/auth/_PassphraseField.scss index d810198213..d1b8c47d00 100644 --- a/res/css/views/auth/_PassphraseField.scss +++ b/res/css/views/auth/_PassphraseField.scss @@ -18,8 +18,6 @@ $PassphraseStrengthHigh: $accent-color; $PassphraseStrengthMedium: $username-variant5-color; $PassphraseStrengthLow: $notice-primary-color; -.mx_PassphraseField {} - @define-mixin ProgressBarColour $colour { color: $colour; &::-moz-progress-bar { From 89c16603fccfafd2a5503385c819d064d07d9c28 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 May 2020 21:06:18 +0100 Subject: [PATCH 162/196] Remove the now inlined ZxcvbnProgressBar.tsx Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/elements/ZxcvbnProgressBar.tsx | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 src/components/views/elements/ZxcvbnProgressBar.tsx diff --git a/src/components/views/elements/ZxcvbnProgressBar.tsx b/src/components/views/elements/ZxcvbnProgressBar.tsx deleted file mode 100644 index 339149b400..0000000000 --- a/src/components/views/elements/ZxcvbnProgressBar.tsx +++ /dev/null @@ -1,30 +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. -*/ - -import React from "react"; -import classNames from "classnames"; - -interface IProps { - value: 0 | 1 | 2 | 3 | 4; - className?: string; -} - -const ZxcvbnProgressBar: React.FC<IProps> = ({value, className}) => { - const classes = classNames("mx_ZxcvbnProgressBar", className); - return <progress className={classes} max={4} value={value} />; -}; - -export default ZxcvbnProgressBar; From 51165dc6695fe53300cb4b41fdcea74ffb2dd5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com> Date: Thu, 14 May 2020 18:56:29 +0000 Subject: [PATCH 163/196] Translated using Weblate (Estonian) Currently translated at 50.3% (1162 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 51 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 2ab308b469..738b863122 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1116,5 +1116,54 @@ "There was an error removing that alias. It may no longer exist or a temporary error occurred.": "Selle aliase eemaldamisel tekkis viga. Teda kas pole enam olemas või tekkis mingi ajutine viga.", "Error removing alias": "Viga aliase eemaldamisel", "Main address": "Põhiaadress", - "not specified": "määratlemata" + "not specified": "määratlemata", + "Rejecting invite …": "Hülgan kutset …", + "Join the conversation with an account": "Liitu vestlusega kasutades oma kontot", + "Sign Up": "Registreeru", + "Loading room preview": "Laen jututoa eelvaadet", + "You were kicked from %(roomName)s by %(memberName)s": "%(memberName)s müksas sind välja jututoast %(roomName)s", + "Reason: %(reason)s": "Põhjus: %(reason)s", + "Forget this room": "Unusta see jututuba", + "Re-join": "Liitu uuesti", + "You were banned from %(roomName)s by %(memberName)s": "%(memberName)s keelas sulle ligipääsu jututuppa %(roomName)s", + "Something went wrong with your invite to %(roomName)s": "Midagi läks viltu sinu kutsega %(roomName)s jututuppa", + "An error (%(errcode)s) was returned while trying to validate your invite. You could try to pass this information on to a room admin.": "Sinu kutse kontrollimisel tekkis viga (%(errcode)s). Kui võimalik, siis proovi see info edastada jututoa haldurile.", + "unknown error code": "tundmatu veakood", + "You can only join it with a working invite.": "Sa võid liituda vaid toimiva kutse alusel.", + "Try to join anyway": "Proovi siiski liituda", + "You can still join it because this is a public room.": "Kuna tegemist on avaliku jututoaga, siis võid ikkagi liituda.", + "Join the discussion": "Liitu vestlusega", + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account": "See kutse jututuppa %(roomName)s saadeti e-posti aadressile %(email)s, mis ei ole seotud sinu kontoga", + "Link this email with your account in Settings to receive invites directly in Riot.": "Selleks et saada kutseid otse Riot'isse, seosta see e-posti aadress seadete all oma kontoga.", + "This invite to %(roomName)s was sent to %(email)s": "Kutse %(roomName)s jututuppa saadeti %(email)s e-posti aadressile", + "Use an identity server in Settings to receive invites directly in Riot.": "Selleks et saada kutseid otse Riot'isse peab seadistustes olema määratud isikutuvastusserver.", + "Share this email in Settings to receive invites directly in Riot.": "Selleks, et saada kutseid otse Riot'isse, jaga oma seadetes seda e-posti aadressi.", + "Start chatting": "Alusta vestlust", + "Do you want to join %(roomName)s?": "Kas sa soovid liitud jututoaga %(roomName)s?", + "<userName/> invited you": "<userName/> kutsus sind", + "Reject": "Hülga", + "You're previewing %(roomName)s. Want to join it?": "Sa vaatad jututoa %(roomName)s eelvaadet. Kas soovid sellega liituda?", + "%(roomName)s can't be previewed. Do you want to join it?": "Jututoal %(roomName)s puudub eelvaate võimalus. Kas sa soovid sellega liituda?", + "%(roomName)s does not exist.": "Jututuba %(roomName)s ei ole olemas.", + "This room doesn't exist. Are you sure you're at the right place?": "Seda jututuba ei ole olemas. Kas sa oled kindlasti õiges kohas?", + "%(roomName)s is not accessible at this time.": "Jututuba %(roomName)s ei ole parasjagu kättesaadav.", + "Try again later, or ask a room admin to check if you have access.": "Proovi hiljem uuesti või küsi jututoa haldurilt, kas sul on ligipääs olemas.", + "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.": "Astumisel jututuppa tekkis viga %(errcode)s. Kui sa arvad, et sellise põhjusega viga ei tohiks tekkida, siis palun <issueLink>koosta veateade</issueLink>.", + "Never lose encrypted messages": "Ära kunagi kaota krüptitud sõnumeid", + "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Sõnumid siis jututoas kasutavad läbivat krüptimist. Ainult sinul ja saaja(te)l on võtmed selliste sõnumite lugemiseks.", + "Securely back up your keys to avoid losing them. <a>Learn more.</a>": "Vältimaks nende kaotamist, varunda turvaliselt oma võtmed. <a>Loe lisateavet.</a>", + "Not now": "Mitte praegu", + "Don't ask me again": "Ära küsi minult uuesti", + "%(count)s unread messages including mentions.|other": "%(count)s lugemata sõnumit kaasa arvatud mainimised.", + "%(count)s unread messages.|other": "%(count)s lugemata teadet.", + "%(count)s unread messages.|one": "1 lugemata teade.", + "Unread mentions.": "Lugemata mainimised.", + "Unread messages.": "Lugemata sõnumid.", + "Add a topic": "Lisa teema", + "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Selle jututoa versiooni uuendamine sulgeb tema praeguse instantsi ja loob sama nimega uuendatud jututoa.", + "This room has already been upgraded.": "See jututuba on juba uuendatud.", + "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "Selle jututoa versioon on <roomVersion /> ning see koduserver on tema märkinud <i>ebastabiilseks</i>.", + "Only room administrators will see this warning": "Vaid administraatorid näevad seda hoiatust", + "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "Kirjutades <code>/help</code> saad vaadata käskude loendit. Või soovisid seda saata sõnumina?", + "Hint: Begin your message with <code>//</code> to start it with a slash.": "Vihje: kui soovid alustada sõnumit kaldkriipsuga, siis kirjuta <code>//</code>." } From 523067e5f9ec90ec9283b9988822d5fd90d342cd Mon Sep 17 00:00:00 2001 From: David Baker <dave@matrix.org> Date: Fri, 15 May 2020 13:32:12 +0100 Subject: [PATCH 164/196] Make email auth component fail better if server claims email isn't validated https://github.com/matrix-org/synapse/issues/7512 means that (at least) sometimes after clicking on the email validation link and being redirected to riot, the server will claim the email identity auth stage is still incomplete. This meant that we displayed the email identity UIA component but with an empty email address, because we don't know that in the new session. Work around this by assuming that if the email UIA component is being displayed but we don't have an email address input, the link has been clicked and we're just waiting for the poll. Also don't fire off an initial register request if we're already mid-UI-auth, because that's confusing and unnecessary. Also also remove unused requestingToken state. Fixes https://github.com/vector-im/riot-web/issues/13434 --- src/components/structures/auth/Registration.js | 13 +++++++++---- .../views/auth/InteractiveAuthEntryComponents.js | 14 +++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 7818496e71..a25b532447 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -243,10 +243,15 @@ export default createReactClass({ }); }; try { - await this._makeRegisterRequest({}); - // This should never succeed since we specified an empty - // auth object. - console.log("Expecting 401 from register request but got success!"); + // We do the first registration request ourselves to discover whether we need to + // do SSO instead. If we've already started the UI Auth process though, we don't + // need to. + if (!this.state.doingUIAuth) { + await this._makeRegisterRequest({}); + // This should never succeed since we specified an empty + // auth object. + console.log("Expecting 401 from register request but got success!"); + } } catch (e) { if (e.httpStatus === 401) { this.setState({ diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index 327451be17..655452fcee 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -412,14 +412,14 @@ export const EmailIdentityAuthEntry = createReactClass({ this.props.onPhaseChange(DEFAULT_PHASE); }, - getInitialState: function() { - return { - requestingToken: false, - }; - }, - render: function() { - if (this.state.requestingToken) { + // This component is now only displayed once the token has been requested, + // so we know the email has been sent. It can also get loaded after the user + // has clicked the validation link if the server takes a while to propagate + // the validation internally. If we're in the session spawned from clicking + // the validation link, we won't know the email address, so if we don't have it, + // assume that the link has been clicked and the server will realise when we poll. + if (this.props.inputs.emailAddress === undefined) { const Loader = sdk.getComponent("elements.Spinner"); return <Loader />; } else { From 4ef32598edc874af554bf3773e573a75c1aa0d6f Mon Sep 17 00:00:00 2001 From: Besnik Bleta <besnik@programeshqip.org> Date: Fri, 15 May 2020 07:36:23 +0000 Subject: [PATCH 165/196] Translated using Weblate (Albanian) Currently translated at 99.9% (2309 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sq/ --- src/i18n/strings/sq.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index ee2781f445..7058ad67b0 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2408,5 +2408,21 @@ "or another cross-signing capable Matrix client": "ose një tjetër klient Matrix i aftë për <em>cross-signing</em", "Use Recovery Passphrase or Key": "Përdorni Frazëkalim Rikthimesh ose Kyç", "Unable to query secret storage status": "S’u arrit të merret gjendje depozite të fshehtë", - "Currently indexing: %(currentRoom)s": "Indeksim aktual: %(currentRoom)s" + "Currently indexing: %(currentRoom)s": "Indeksim aktual: %(currentRoom)s", + "Opens chat with the given user": "Hap fjalosje me përdoruesin e dhënë", + "Sends a message to the given user": "I dërgon një mesazh përdoruesit të dhënë", + "Waiting for your other session to verify…": "Po pritet për verifikimin e sesionit tuaj tjetër…", + "You've successfully verified your device!": "E verifikuat me sukses pajisjen tuaj!", + "Message deleted": "Mesazhi u fshi", + "Message deleted by %(name)s": "Mesazh i fshirë nga %(name)s", + "QR Code": "Kod QR", + "To continue, use Single Sign On to prove your identity.": "Që të vazhdohet, përdorni Hyrje Njëshe, që të provoni identitetin tuaj.", + "Confirm to continue": "Ripohojeni që të vazhdohet", + "Click the button below to confirm your identity.": "Klikoni mbi butonin më poshtë që të ripohoni identitetin tuaj.", + "Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Ftoni dikë duke përdorur emrin e tij, emrin e përdoruesit (bie fjala, <userId/>), adresën email ose <a>duke ndarë me të këtë dhomë</a>.", + "Confirm encryption setup": "Ripohoni ujdisje fshehtëzimi", + "Click the button below to confirm setting up encryption.": "Klikoni mbi butonin më poshtë që të ripohoni ujdisjen e fshehtëzimit.", + "Dismiss read marker and jump to bottom": "Mos merr parasysh piketë leximi dhe hidhu te fundi", + "Jump to oldest unread message": "Hidhu te mesazhi më i vjetër i palexuar", + "Upload a file": "Ngarkoni një kartelë" } From 7ae10d0ba8deaf54f2db30d1f3e3e1b76724ee54 Mon Sep 17 00:00:00 2001 From: Jeff Huang <s8321414@gmail.com> Date: Fri, 15 May 2020 01:55:37 +0000 Subject: [PATCH 166/196] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2312 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hant/ --- src/i18n/strings/zh_Hant.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 0ca6cfc586..4995eeccb0 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2430,5 +2430,9 @@ "Confirm to continue": "確認以繼續", "Click the button below to confirm your identity.": "點擊下方按鈕以確認您的身份。", "Confirm encryption setup": "確認加密設定", - "Click the button below to confirm setting up encryption.": "點擊下方按鈕以確認設定加密。" + "Click the button below to confirm setting up encryption.": "點擊下方按鈕以確認設定加密。", + "QR Code": "QR Code", + "Dismiss read marker and jump to bottom": "取消讀取標記並跳至底部", + "Jump to oldest unread message": "跳至最舊的未讀訊息", + "Upload a file": "上傳檔案" } From acfacb9b95faa384f98887e54f0e6e67edef61ea Mon Sep 17 00:00:00 2001 From: Tirifto <tirifto@posteo.cz> Date: Thu, 14 May 2020 21:10:34 +0000 Subject: [PATCH 167/196] Translated using Weblate (Esperanto) Currently translated at 100.0% (2312 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/eo/ --- src/i18n/strings/eo.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 6b3881339b..3f749ebe0f 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -2406,5 +2406,8 @@ "Click the button below to confirm your identity.": "Klaku sube la butonon por konfirmi vian identecon.", "Confirm encryption setup": "Konfirmi agordon de ĉifrado", "Click the button below to confirm setting up encryption.": "Klaku sube la butonon por konfirmi agordon de ĉifrado.", - "QR Code": "Rapidresponda kodo" + "QR Code": "Rapidresponda kodo", + "Dismiss read marker and jump to bottom": "Forigi legomarkon kaj iri al fundo", + "Jump to oldest unread message": "Iri al plej malnova nelegita mesaĝo", + "Upload a file": "Alŝuti dosieron" } From 9542851c25bf233cfa133ec62bc8b4ec969c10ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com> Date: Thu, 14 May 2020 23:00:36 +0000 Subject: [PATCH 168/196] Translated using Weblate (Estonian) Currently translated at 50.9% (1176 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 738b863122..286bd9ec5c 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -1165,5 +1165,19 @@ "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "Selle jututoa versioon on <roomVersion /> ning see koduserver on tema märkinud <i>ebastabiilseks</i>.", "Only room administrators will see this warning": "Vaid administraatorid näevad seda hoiatust", "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "Kirjutades <code>/help</code> saad vaadata käskude loendit. Või soovisid seda saata sõnumina?", - "Hint: Begin your message with <code>//</code> to start it with a slash.": "Vihje: kui soovid alustada sõnumit kaldkriipsuga, siis kirjuta <code>//</code>." + "Hint: Begin your message with <code>//</code> to start it with a slash.": "Vihje: kui soovid alustada sõnumit kaldkriipsuga, siis kirjuta <code>//</code>.", + "To link to this room, please add an alias.": "Viitamaks sellele jututoale, palun määra talle alias.", + "Only people who have been invited": "Vaid kutsutud kasutajad", + "Anyone who knows the room's link, apart from guests": "Kõik, kes teavad jututoa viidet, välja arvatud külalised", + "Anyone who knows the room's link, including guests": "Kõik, kes teavad jututoa viidet, kaasa arvatud külalised", + "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Kui muudad seda, kes saavad selle jututoa ajalugu lugeda, siis kehtib see vaid tulevaste sõnumite kohta. Senise ajaloo nähtavus sellega ei muutu.", + "Unable to revoke sharing for email address": "Ei õnnestu tagasi võtta otsust e-posti aadressi jagamise kohta", + "Unable to share email address": "Ei õnnestu jagada e-posti aadressi", + "Your email address hasn't been verified yet": "Sinu e-posti aadress pole veel verifitseeritud", + "Click the link in the email you received to verify and then click continue again.": "Klõpsi saabunud e-kirjas olevat verifitseerimisviidet ning seejärel klõpsi siin uuesti nuppu \"Jätka\".", + "Unable to verify email address.": "E-posti aadressi verifitseerimine ei õnnestunud.", + "Verify the link in your inbox": "Verifitseeri klõpsides viidet saabunud e-kirjas", + "Complete": "Valmis", + "Revoke": "Tühista", + "Share": "Jaga" } From 7d15fea5f975d9b99062022ba7a7bc208630e3f9 Mon Sep 17 00:00:00 2001 From: Tuomas Hietala <tuomas.hietala@iki.fi> Date: Fri, 15 May 2020 12:01:35 +0000 Subject: [PATCH 169/196] Translated using Weblate (Finnish) Currently translated at 91.2% (2108 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fi/ --- src/i18n/strings/fi.json | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/fi.json b/src/i18n/strings/fi.json index e8eef891be..ae08572eb2 100644 --- a/src/i18n/strings/fi.json +++ b/src/i18n/strings/fi.json @@ -1282,7 +1282,7 @@ "Premium hosting for organisations <a>Learn more</a>": "Premium-ylläpitoa organisaatioille. <a>Lue lisää</a>", "Other": "Muut", "Find other public servers or use a custom server": "Etsi muita julkisia palvelimia tai käytä mukautettua palvelinta", - "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Parhaan käyttökokemuksen saa <chromeLink>Chromella</chromeLink>, <firefoxLink>Firefoxilla</firefoxLink> tai <safariLink>Safarilla</safariLink>.", + "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Asenna <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> tai <safariLink>Safari</safariLink>, jotta kaikki toimii parhaiten.", "<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n": "<h1>HTML-kuvaus yhteisösi etusivulle</h1>\n<p>\n Käytä pitkää kuvausta esitelläksesi yhteisöäsi uusille jäsenille tai jakaaksesi tärkeitä\n <a href=\"foo\">linkkejä</a>\n</p>\n<p>\n Voit jopa käyttää 'img'-tageja\n</p>\n", "Unable to join community": "Yhteisöön liittyminen epäonnistui", "You are an administrator of this community. You will not be able to rejoin without an invite from another administrator.": "Olet tämän yhteisön ylläpitäjä. Et voi liittyä uudelleen ilman kutsua toiselta ylläpitäjältä.", @@ -2167,5 +2167,27 @@ "Sends a message as html, without interpreting it as markdown": "Lähettää viestin HTML-muodossa, tulkitsematta sitä Markdowniksi", "Please supply a widget URL or embed code": "Anna sovelman osoite tai upotettava koodinpätkä", "You signed in to a new session without verifying it:": "Olet kirjautunut uuteen istuntoon varmentamatta sitä:", - "Verify your other session using one of the options below.": "Varmenna toinen istuntosi käyttämällä yhtä seuraavista tavoista." + "Verify your other session using one of the options below.": "Varmenna toinen istuntosi käyttämällä yhtä seuraavista tavoista.", + "Click the button below to confirm deleting these sessions.|other": "Napsauta alla olevaa painiketta vahvistaaksesi näiden istuntojen poistamisen.", + "Click the button below to confirm deleting these sessions.|one": "Napsauta alla olevaa painiketta vahvistaaksesi tämän istunnon poistamisen.", + "Backup has a signature from <verify>unknown</verify> session with ID %(deviceId)s": "Varmuuskopiossa on allekirjoitus <verify>tuntemattomasta</verify> istunnosta tunnuksella %(deviceId)s", + "Error downloading theme information.": "Virhe ladattaessa teematietoa.", + "Waiting for you to accept on your other session…": "Odotetaan, että hyväksyt toisen istunnon…", + "Almost there! Is your other session showing the same shield?": "Melkein valmista! Näyttääkö toinen istuntosi saman kilven?", + "Almost there! Is %(displayName)s showing the same shield?": "Melkein valmista! Näyttääkö %(displayName)s saman kilven?", + "Message deleted": "Viesti poistettu", + "Message deleted by %(name)s": "%(name)s poisti viestin", + "QR Code": "QR-koodi", + "To continue, use Single Sign On to prove your identity.": "Todista henkilöllisyytesi kertakirjautumisen avulla jatkaaksesi.", + "If they don't match, the security of your communication may be compromised.": "Jos ne eivät täsmää, viestinnän turvallisuus saattaa olla vaarantunut.", + "This session, or the other session": "Tämä tai toinen istunto", + "We recommend you change your password and recovery key in Settings immediately": "Suosittelemme, että vaihdat salasanasi ja palautusavaimesi asetuksissa välittömästi", + "Restoring keys from backup": "Palautetaan avaimia varmuuskopiosta", + "Fetching keys from server...": "Noudetaan avaimia palvelimelta...", + "%(completed)s of %(total)s keys restored": "%(completed)s / %(total)s avainta palautettu", + "Keys restored": "Avaimet palautettu", + "Successfully restored %(sessionCount)s keys": "%(sessionCount)s avaimen palautus onnistui", + "This requires the latest Riot on your other devices:": "Tämä vaatii uusimman Riotin muilla laitteillasi:", + "Currently indexing: %(currentRoom)s": "Indeksoidaan huonetta: %(currentRoom)s", + "Jump to oldest unread message": "Siirry vanhimpaan lukemattomaan viestiin" } From b27ac716808bb5039291477fe84aa3c45d1776d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= <zecakeh@pm.me> Date: Fri, 15 May 2020 06:26:53 +0000 Subject: [PATCH 170/196] Translated using Weblate (French) Currently translated at 100.0% (2312 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/fr/ --- src/i18n/strings/fr.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 3fac2168a2..c231769f27 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2431,5 +2431,9 @@ "Confirm to continue": "Confirmer pour continuer", "Click the button below to confirm your identity.": "Cliquez sur le bouton ci-dessous pour confirmer votre identité.", "Confirm encryption setup": "Confirmer la configuration du chiffrement", - "Click the button below to confirm setting up encryption.": "Cliquez sur le bouton ci-dessous pour confirmer la configuration du chiffrement." + "Click the button below to confirm setting up encryption.": "Cliquez sur le bouton ci-dessous pour confirmer la configuration du chiffrement.", + "QR Code": "Code QR", + "Dismiss read marker and jump to bottom": "Ignorer le signet de lecture et aller en bas", + "Jump to oldest unread message": "Aller au plus vieux message non lu", + "Upload a file": "Envoyer un fichier" } From ae9d700c936a7ed0f51748205d0254e661e264f6 Mon Sep 17 00:00:00 2001 From: Szimszon <github@oregpreshaz.eu> Date: Fri, 15 May 2020 11:25:44 +0000 Subject: [PATCH 171/196] Translated using Weblate (Hungarian) Currently translated at 99.4% (2299 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index afad390192..b3bc468da6 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2409,5 +2409,7 @@ "Successfully restored %(sessionCount)s keys": "Kulcsok (%(sessionCount)s) sikeresen visszaállítva", "You signed in to a new session without verifying it:": "Ellenőrzés nélkül jelentkeztél be egy új munkamenetbe:", "Verify your other session using one of the options below.": "Ellenőrizd a másik munkameneted a lenti lehetőségek egyikével.", - "Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Hívj meg valakit a nevével, felhasználónevével (mint <userId/>), e-mail címével vagy <a>oszd meg ezt a szobát</a>." + "Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Hívj meg valakit a nevével, felhasználónevével (mint <userId/>), e-mail címével vagy <a>oszd meg ezt a szobát</a>.", + "Opens chat with the given user": "Beszélgetés megnyitása a megadott felhasználóval", + "Sends a message to the given user": "Üzenet küldése a megadott felhasználónak" } From 16bdbfd67dcc118b668369ce60508722a7d51eda Mon Sep 17 00:00:00 2001 From: random <dictionary@tutamail.com> Date: Fri, 15 May 2020 08:07:07 +0000 Subject: [PATCH 172/196] Translated using Weblate (Italian) Currently translated at 100.0% (2312 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/it/ --- src/i18n/strings/it.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index e79db1b061..014d6015b3 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2426,5 +2426,9 @@ "Confirm to continue": "Conferma per continuare", "Click the button below to confirm your identity.": "Clicca il pulsante sotto per confermare la tua identità.", "Confirm encryption setup": "Conferma impostazione cifratura", - "Click the button below to confirm setting up encryption.": "Clicca il pulsante sotto per confermare l'impostazione della cifratura." + "Click the button below to confirm setting up encryption.": "Clicca il pulsante sotto per confermare l'impostazione della cifratura.", + "QR Code": "Codice QR", + "Dismiss read marker and jump to bottom": "Scarta il segno di lettura e salta alla fine", + "Jump to oldest unread message": "Salta al messaggio non letto più vecchio", + "Upload a file": "Invia un file" } From c09745e1833d100ca3dabe715c1420b89eb4c740 Mon Sep 17 00:00:00 2001 From: Szimszon <github@oregpreshaz.eu> Date: Fri, 15 May 2020 15:44:38 +0000 Subject: [PATCH 173/196] Translated using Weblate (Hungarian) Currently translated at 100.0% (2312 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/hu/ --- src/i18n/strings/hu.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index b3bc468da6..17254a77db 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2411,5 +2411,18 @@ "Verify your other session using one of the options below.": "Ellenőrizd a másik munkameneted a lenti lehetőségek egyikével.", "Invite someone using their name, username (like <userId/>), email address or <a>share this room</a>.": "Hívj meg valakit a nevével, felhasználónevével (mint <userId/>), e-mail címével vagy <a>oszd meg ezt a szobát</a>.", "Opens chat with the given user": "Beszélgetés megnyitása a megadott felhasználóval", - "Sends a message to the given user": "Üzenet küldése a megadott felhasználónak" + "Sends a message to the given user": "Üzenet küldése a megadott felhasználónak", + "Waiting for your other session to verify…": "A másik munkameneted ellenőrzésére várunk…", + "You've successfully verified your device!": "Sikeresen ellenőrizted az eszközödet!", + "Message deleted": "Üzenet törölve", + "Message deleted by %(name)s": "Üzenetet törölte: %(name)s", + "QR Code": "QR kód", + "To continue, use Single Sign On to prove your identity.": "A folytatáshoz a személyazonosságod megerősítéséhez használd az egyszeri bejelentkezést.", + "Confirm to continue": "Erősítsd meg a továbblépéshez", + "Click the button below to confirm your identity.": "A személyazonosságod megerősítéséhez kattints az alábbi gombra.", + "Confirm encryption setup": "Erősítsd meg a titkosítási beállításokat", + "Click the button below to confirm setting up encryption.": "Az alábbi gomb megnyomásával erősítsd meg, hogy megadod a titkosítási beállításokat.", + "Dismiss read marker and jump to bottom": "Az olvasottak jel eltűntetése és ugrás a végére", + "Jump to oldest unread message": "A legrégebbi olvasatlan üzenetre ugrás", + "Upload a file": "Fájl feltöltése" } From 0feb55ff4f8fed692d7041e1d16d7fa1ded29297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com> Date: Sat, 16 May 2020 12:47:57 +0000 Subject: [PATCH 174/196] Translated using Weblate (Estonian) Currently translated at 51.4% (1188 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 286bd9ec5c..2ec4a14d3a 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -262,7 +262,7 @@ "Expand room list section": "Laienda jututubade loendi valikut", "Create Account": "Loo konto", "Sign In": "Logi sisse", - "Send a bug report with logs": "Saada veakirjeldus koos logidega", + "Send a bug report with logs": "Saada veakirjeldus koos logikirjetega", "Group & filter rooms by custom tags (refresh to apply changes)": "Rühmita ja filtreeri jututubasid kohandatud siltide alusel (muudatuste rakendamiseks värskenda vaade)", "Try out new ways to ignore people (experimental)": "Proovi uusi kasutajate eiramise viise (katseline)", "Uploading report": "Laen üles veakirjeldust", @@ -1179,5 +1179,17 @@ "Verify the link in your inbox": "Verifitseeri klõpsides viidet saabunud e-kirjas", "Complete": "Valmis", "Revoke": "Tühista", - "Share": "Jaga" + "Share": "Jaga", + "Session already verified!": "Sessioon on juba verifitseeritud!", + "WARNING: Session already verified, but keys do NOT MATCH!": "HOIATUS: Sessioon on juba verifitseeritud, aga võtmed ei klapi!", + "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "HOIATUS: VÕTMETE VERIFITSEERIMINE EI ÕNNESTUNUD! Kasutaja %(userId)s ja sessiooni %(deviceId)s allkirjastamise võti on \"%(fprint)s\", aga see ei vasta antud sõrmejäljele \"%(fingerprint)s\". See võib tähendada, et sinu kasutatavad ühendused võivad olla kolmanda osapoole poolt vahelt lõigatud!", + "Verified key": "Verifitseeritud võti", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Sinu antud allkirjavõti vastab allkirjavõtmele, mille sa said kasutaja %(userId)s sessioonist %(deviceId)s. Sessioon on märgitud verifitseerituks.", + "Sends the given message coloured as a rainbow": "Saadab selle sõnumi vikerkaarevärvilisena", + "Sends the given emote coloured as a rainbow": "Saadab antud emote vikerkaarevärvides", + "Displays list of commands with usages and descriptions": "Näitab käskude loendit koos kirjeldustega", + "Logs sent": "Logikirjed saadetud", + "Thank you!": "Suur tänu!", + "Opens chat with the given user": "Avab vestluse näidatud kasutajaga", + "Sends a message to the given user": "Saadab sõnumi näidatud kasutajale" } From 3ffa333f31eefb57f76ad5bca7d17598d9bc8c46 Mon Sep 17 00:00:00 2001 From: "Pepper.Cabbit.Snoopy" <miranda_zhang@live.cn> Date: Sun, 17 May 2020 09:23:01 +0000 Subject: [PATCH 175/196] Translated using Weblate (Chinese (Simplified)) Currently translated at 62.7% (1450 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/zh_Hans/ --- src/i18n/strings/zh_Hans.json | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hans.json b/src/i18n/strings/zh_Hans.json index dc3cc6d176..481167a645 100644 --- a/src/i18n/strings/zh_Hans.json +++ b/src/i18n/strings/zh_Hans.json @@ -1468,5 +1468,31 @@ "%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s 发起了视频通话。(此浏览器不支持)", "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的用户的规则", "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的服务器的规则", - "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的规则" + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s 移除了禁止匹配 %(glob)s 的规则", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s 删除了禁止聊天室匹配%(glob)s的规则", + "%(senderName)s updated an invalid ban rule": "%(senderName)s 更新了一个无效的禁止规则", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s 而禁止用户匹配%(glob)s的规则", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s而禁止聊天室匹配%(glob)s的规则", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s而禁止服务器匹配%(glob)s的规则", + "%(senderName)s updated a ban rule matching %(glob)s for %(reason)s": "%(senderName)s 更新了由于%(reason)s而禁止匹配%(glob)s的规则", + "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s 创建了因为%(reason)s而禁止用户匹配%(glob)s的规则", + "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s 创建了由于%(reason)s而禁止聊天室匹配%(glob)s的规则", + "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s 创建了由于%(reason)s而禁止服务器匹配%(glob)s的规则", + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s 创建了由于%(reason)s而禁止匹配%(glob)s的股则", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 更改了一个由于%(reason)s而禁止用户%(oldGlob)s跟%(newGlob)s匹配的规则", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s更改了一个由于%(reason)s而禁止聊天室%(oldGlob)s跟%(newGlob)s匹配的规则", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 更新了一个由于%(reason)s而禁止服务器%(oldGlob)s跟%(newGlob)s匹配的规则", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s 更新了一个由于%(reason)s而禁止%(oldGlob)s跟%(newGlob)s匹配的规则", + "You signed in to a new session without verifying it:": "您登陆了未经过验证的新会话:", + "Verify your other session using one of the options below.": "使用以下选项之一验证您的其他会话。", + "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s(%(userId)s)登陆到未验证的新会话:", + "Ask this user to verify their session, or manually verify it below.": "要求该用户验证其会话,或在下面手动进行验证。", + "Not Trusted": "不可信任", + "Manually Verify by Text": "手动验证文字", + "Interactively verify by Emoji": "通过表情符号进行交互式验证", + "Done": "完成", + "Cannot reach homeserver": "不可连接到主服务器", + "Ensure you have a stable internet connection, or get in touch with the server admin": "确保您的网络连接稳定,或与服务器管理员联系", + "Ask your Riot admin to check <a>your config</a> for incorrect or duplicate entries.": "跟您的Riot管理员确认<a>您的配置</a>不正确或重复的条目。", + "Cannot reach identity server": "不可连接到身份服务器" } From 0662c231a52917a288713e1069cdeb07035a1658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= <riot@joeruut.com> Date: Sat, 16 May 2020 18:13:05 +0000 Subject: [PATCH 176/196] Translated using Weblate (Estonian) Currently translated at 53.3% (1233 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/et/ --- src/i18n/strings/et.json | 53 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/et.json b/src/i18n/strings/et.json index 2ec4a14d3a..3fb069055c 100644 --- a/src/i18n/strings/et.json +++ b/src/i18n/strings/et.json @@ -148,7 +148,7 @@ "Remove": "Eemalda", "You should <b>remove your personal data</b> from identity server <idserver /> before disconnecting. Unfortunately, identity server <idserver /> is currently offline or cannot be reached.": "Sa peaksid enne ühenduse katkestamisst <b>eemaldama isiklikud andmed</b> id-serverist <idserver />. Kahjuks id-server <idserver /> ei ole hetkel võrgus või pole kättesaadav.", "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Me soovitame, et eemaldad enne ühenduse katkestamist oma e-posti aadressi ja telefoninumbrid id-serverist.", - "Remove messages": "Kustuta sõnumid", + "Remove messages": "Kustuta sõnumeid", "Unable to remove contact information": "Kontaktiinfo eemaldamine ebaõnnestus", "Remove %(email)s?": "Eemalda %(email)s?", "Remove %(phone)s?": "Eemalda %(phone)s?", @@ -334,7 +334,7 @@ "Once enabled, encryption cannot be disabled.": "Kui krüptimine on juba kasutusele võetud, siis ei saa seda enam eemaldada.", "Encrypted": "Krüptitud", "Who can access this room?": "Kes pääsevad ligi siia jututuppa?", - "Who can read history?": "Kes võib lugeda ajalugu?", + "Who can read history?": "Kes võivad lugeda ajalugu?", "Encrypted by an unverified session": "Krüptitud verifitseerimata sessiooni poolt", "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Sõnumid selles jututoas on läbivalt krüptitud. Uuri lisaks ja verifitseeri see kasutaja tema kasutajaprofiilis.", "Encryption not enabled": "Krüptimine ei ole kasutusel", @@ -685,7 +685,7 @@ "Room version:": "Jututoa versioon:", "Developer options": "Valikud arendajale", "Open Devtools": "Ava arendusvahendid", - "Change room name": "Muuda jututoa nimi", + "Change room name": "Muuda jututoa nime", "Roles & Permissions": "Rollid ja õigused", "Room Name": "Jututoa nimi", "Room Topic": "Jututoa teema", @@ -1191,5 +1191,50 @@ "Logs sent": "Logikirjed saadetud", "Thank you!": "Suur tänu!", "Opens chat with the given user": "Avab vestluse näidatud kasutajaga", - "Sends a message to the given user": "Saadab sõnumi näidatud kasutajale" + "Sends a message to the given user": "Saadab sõnumi näidatud kasutajale", + "Short keyboard patterns are easy to guess": "Lühikesi klahvijärjestusi on lihtne ära arvata", + "Keyboard Shortcuts": "Kiirklahvid", + "This room is bridging messages to the following platforms. <a>Learn more.</a>": "See jututuba kasutab sõnumisildasid liidestamiseks järgmiste süsteemidega. <a>Lisateave.</a>", + "This room isn’t bridging messages to any platforms. <a>Learn more.</a>": "See jututuba ei kasuta sõnumisildasid liidestamiseks muude süsteemidega. <a>Lisateave.</a>", + "Bridges": "Sõnumisillad", + "Room Addresses": "Jututubade aadressid", + "Browse": "Sirvi", + "Change room avatar": "Muuda jututoa profiilipilti ehk avatari", + "Change main address for the room": "Muuda jututoa põhiaadressi", + "Change history visibility": "Muuda vestlusajaloo nähtavust", + "Change permissions": "Muuda õigusi", + "Change topic": "Muuda teemat", + "Upgrade the room": "Uuenda jututuba uue versioonini", + "Enable room encryption": "Võta jututoas kasutusele krüptimine", + "Modify widgets": "Muuda vidinaid", + "Default role": "Vaikimisi roll", + "Send messages": "Saada sõnumeid", + "Invite users": "Kutsu kasutajaid", + "Change settings": "Muuda seadistusi", + "Kick users": "Müksa kasutajaid välja", + "Ban users": "Määra kasutajatele suhtluskeeld", + "Notify everyone": "Teavita kõiki", + "No users have specific privileges in this room": "Mitte ühelgi kasutajal pole siin jututoas eelisõigusi", + "Privileged Users": "Eelisõigustega kasutajad", + "Banned users": "Suhtluskeelu saanud kasutajad", + "Send %(eventType)s events": "Saada %(eventType)s-sündmusi", + "Unable to revoke sharing for phone number": "Telefoninumbri jagamist ei õnnestunud tühistada", + "Unable to share phone number": "Telefoninumbri jagamine ei õnnestunud", + "Unable to verify phone number.": "Telefoninumbri verifitseerimine ei õnnestunud.", + "Incorrect verification code": "Vigane verifikatsioonikood", + "Please enter verification code sent via text.": "Palun sisesta verifikatsioonikood, mille said telefoni tekstisõnumina.", + "Verification code": "Verifikatsioonikood", + "Invalid Email Address": "Vigane e-posti aadress", + "This doesn't appear to be a valid email address": "See ei tundu olema e-posti aadressi moodi", + "Preparing to send logs": "Valmistun logikirjete saatmiseks", + "Failed to send logs: ": "Logikirjete saatmine ei õnnestunud: ", + "Verify session": "Verifitseeri sessioon", + "Use Legacy Verification (for older clients)": "Kasuta vanematüübilist verifitseerimist (vähem-moodsa klient-tarkvara jaoks)", + "Verify by comparing a short text string.": "Verifitseeri kasutades lühikeste sõnede võrdlemist.", + "Begin Verifying": "Alusta verifitseerimist", + "Waiting for partner to accept...": "Ootan, et teine osapool nõustuks…", + "Nothing appearing? Not all clients support interactive verification yet. <button>Use legacy verification</button>.": "Mitte midagi ei kuvata? Kõik Matrix'i kliendid ei toeta veel interaktiivset verifitseerimist. <button>Kasuta vana kooli verifitseerimismeetodit</button>.", + "Waiting for %(userId)s to confirm...": "Ootan kinnitust kasutajalt %(userId)s…", + "Skip": "Jäta vahele", + "Token incorrect": "Vigane tunnusluba" } From 7c5842c208fe08b9e48964214663a447216bc928 Mon Sep 17 00:00:00 2001 From: Xose M <xosem@disroot.org> Date: Sun, 17 May 2020 09:37:45 +0000 Subject: [PATCH 177/196] Translated using Weblate (Galician) Currently translated at 40.0% (925 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/gl/ --- src/i18n/strings/gl.json | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/i18n/strings/gl.json b/src/i18n/strings/gl.json index 346dda09f2..3328292be0 100644 --- a/src/i18n/strings/gl.json +++ b/src/i18n/strings/gl.json @@ -698,9 +698,9 @@ "Failed to remove tag %(tagName)s from room": "Fallo ao eliminar a etiqueta %(tagName)s da sala", "Failed to add tag %(tagName)s to room": "Fallo ao engadir a etiqueta %(tagName)s a sala", "Key request sent.": "Petición de chave enviada.", - "Flair": "Aura", - "Showing flair for these communities:": "Mostrar a aura para estas comunidades:", - "Display your community flair in rooms configured to show it.": "Mostrar a aura da súa comunidade nas salas configuradas para que a mostren.", + "Flair": "Popularidade", + "Showing flair for these communities:": "Mostrar a popularidade destas comunidades:", + "Display your community flair in rooms configured to show it.": "Mostrar a popularidade da túa comunidade nas salas configuradas para que a mostren.", "Did you know: you can use communities to filter your Riot.im experience!": "Sabía que pode utilizar as comunidades para mellorar a súa experiencia con Riot.im!", "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "Para establecer un filtro, arrastre un avatar da comunidade sobre o panel de filtros na parte esquerda da pantalla. Pode pulsar nun avatar no panel de filtrado en calquera momento para ver só salas e xente asociada a esa comunidade.", "Deops user with given id": "Degradar o usuario con esa ID", @@ -822,7 +822,7 @@ "Debug logs contain application usage data including your username, the IDs or aliases of the rooms or groups you have visited and the usernames of other users. They do not contain messages.": "Os informes de depuración conteñen datos de utilización do aplicativo como o seu nome de usuario, os IDs ou alcumes de salas e grupos que vostede visitou e os nomes de usuarios doutras usuarias. Non conteñen mensaxes.", "Unhide Preview": "Desagochar a vista previa", "Unable to join network": "Non se puido conectar a rede", - "You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Pode que os configurase nun cliente diferente de Riot. Non pode establecelos desde Riot pero aínda así aplicaranse", + "You might have configured them in a client other than Riot. You cannot tune them in Riot but they still apply": "Pode que os configurases nun cliente diferente de Riot. Non podes establecelos desde Riot pero aínda así aplicaranse", "Sorry, your browser is <b>not</b> able to run Riot.": "Desculpe, o seu navegador <b>non pode</b> executar Riot.", "Uploaded on %(date)s by %(user)s": "Subido a %(date)s por %(user)s", "Messages in group chats": "Mensaxes en grupos de chat", @@ -919,5 +919,23 @@ "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.": "A súa mensaxe non foi enviada porque este servidor acadou o Límite Mensual de Usuaria Activa. Por favor <a>contacte coa administración do servizo</a> para continuar utilizando o servizo.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.": "A súa mensaxe non foi enviada porque o servidor superou o límite de recursos. Por favor <a>contacte coa administración do servizo</a> para continuar utilizando o servizo.", "Legal": "Legal", - "Please <a>contact your service administrator</a> to continue using this service.": "Por favor <a>contacte coa administración do servizo</a> para continuar utilizando o servizo." + "Please <a>contact your service administrator</a> to continue using this service.": "Por favor <a>contacte coa administración do servizo</a> para continuar utilizando o servizo.", + "Use Single Sign On to continue": "Usar Single Sign On para continuar", + "Confirm adding this email address by using Single Sign On to prove your identity.": "Confirma que queres engadir este email usando Single Sign On como proba de identidade.", + "Single Sign On": "Single Sign On", + "Confirm adding email": "Confirma novo email", + "Click the button below to confirm adding this email address.": "Preme no botón inferior para confirmar que queres engadir o email.", + "Confirm": "Confirmar", + "Add Email Address": "Engadir email", + "Confirm adding this phone number by using Single Sign On to prove your identity.": "Confirma que queres engadir este teléfono usando Single Sign On como proba de identidade.", + "Confirm adding phone number": "Confirma a adición do teléfono", + "Click the button below to confirm adding this phone number.": "Preme no botón inferior para confirmar que engades este número.", + "Add Phone Number": "Engadir novo Número", + "The version of Riot": "A versión de Riot", + "Whether or not you're logged in (we don't record your username)": "Se estás conectada ou non (non rexistramos o teu nome de usuaria)", + "Whether you're using Riot on a device where touch is the primary input mechanism": "Se estás conectada utilizando Riot nun dispositivo maiormente táctil", + "Whether you're using Riot as an installed Progressive Web App": "Se estás a usar Riot como unha Progressive Web App instalada", + "Your user agent": "User Agent do navegador", + "The information being sent to us to help make Riot better includes:": "Información que nos envías para mellorar Riot inclúe:", + "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Instala <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ou <safariLink>Safari</safariLink> para ter unha mellor experiencia." } From 5ea4cc1c5b209b5ea8b3401ea3cabf80a20da11a Mon Sep 17 00:00:00 2001 From: yuuki-san <yuuki-san@protonmail.com> Date: Mon, 18 May 2020 08:31:13 +0000 Subject: [PATCH 178/196] Translated using Weblate (Slovak) Currently translated at 62.3% (1441 of 2312 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/sk/ --- src/i18n/strings/sk.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/sk.json b/src/i18n/strings/sk.json index fbecaa4845..e85732ed82 100644 --- a/src/i18n/strings/sk.json +++ b/src/i18n/strings/sk.json @@ -628,7 +628,7 @@ "Export room keys": "Exportovať kľúče miestností", "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.": "Tento proces vás prevedie exportom kľúčov určených na dešifrovanie správ, ktoré ste dostali v šifrovaných miestnostiach do lokálneho súboru. Tieto kľúče zo súboru môžete neskôr importovať do iného Matrix klienta, aby ste v ňom mohli dešifrovať vaše šifrované správy.", "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a passphrase below, which will be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.": "Tento súbor umožní komukoľvek, k to má ku nemu prístup dešifrovať všetky vami viditeľné šifrované správy, mali by ste teda byť opatrní a tento súbor si bezpečne uchovať. Aby bolo toto pre vás jednoduchšie, nižšie zadajte heslo, ktorým budú údaje v súbore zašifrované. Importovať údaje zo súboru bude možné len po zadaní tohoto istého hesla.", - "Enter passphrase": "Zadajte heslo", + "Enter passphrase": "Zadajte (dlhé) heslo", "Confirm passphrase": "Potvrďte heslo", "Export": "Exportovať", "Import room keys": "Importovať kľúče miestností", @@ -1519,5 +1519,17 @@ "Whether you're using Riot on a device where touch is the primary input mechanism": "Či používate Riot na zariadení, ktorého hlavným vstupným mechanizmom je dotyk (mobil, tablet,...)", "Whether you're using Riot as an installed Progressive Web App": "Či používate Riot ako nainštalovanú Progresívnu Webovú Aplikáciu", "Your user agent": "Identifikátor vášho prehliadača", - "The information being sent to us to help make Riot better includes:": "Informácie, ktoré nám posielate, aby sme zlepšili Riot, zahŕňajú:" + "The information being sent to us to help make Riot better includes:": "Informácie, ktoré nám posielate, aby sme zlepšili Riot, zahŕňajú:", + "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "V miestnosti je neznáma relácia: pokiaľ budete pokračovať bez jej overenia, bude schopná odpočúvať váš hovor.", + "Review Sessions": "Overiť reláciu", + "If you cancel now, you won't complete verifying the other user.": "Pokiaľ teraz proces zrušíte, nedokončíte overenie druhého používateľa.", + "If you cancel now, you won't complete verifying your other session.": "Pokiaľ teraz proces zrušíte, nedokončíte overenie vašej druhej relácie.", + "If you cancel now, you won't complete your operation.": "Pokiaľ teraz proces zrušíte, nedokončíte ho.", + "Cancel entering passphrase?": "Zrušiť zadávanie (dlhého) hesla.", + "Setting up keys": "Príprava kľúčov", + "Verify this session": "Overiť túto reláciu", + "Keep recovery passphrase in memory for this session": "Ponechať (dlhé) heslo pre obnovu zálohy v pamäti pre túto reláciu", + "Enter recovery passphrase": "Zadajte (dlhé) heslo pre obnovu zálohy", + "Unable to access secret storage. Please verify that you entered the correct recovery passphrase.": "Nemožno sa dostať do tajného úložiska. Prosím, overte, že ste zadali správne (dlhé) heslo pre obnovu zálohy.", + "Access your secure message history and your cross-signing identity for verifying other sessions by entering your recovery passphrase.": "Získajte prístup k vašej zabezpečenej histórií správ a vašemu krížom-podpísanej identite na potvrdenie iných relácií zadaním vášho (dlhého) hesla na obnovu zálohy." } From a864643d98197ae6bc3bf34c8ee3da4c4a94a013 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 18 May 2020 10:37:02 +0100 Subject: [PATCH 179/196] Label the create room button something better than "Add room" Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/RoomList.js | 1 + src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 289a89a206..c46b946b5c 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -785,6 +785,7 @@ export default createReactClass({ label: _t('Rooms'), incomingCall: incomingCallIfTaggedAs('im.vector.fake.recent'), onAddRoom: () => {dis.dispatch({action: 'view_create_room'});}, + addRoomLabel: _t("Create room"), }, ]; const tagSubLists = Object.keys(this.state.lists) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f16a0d7755..c9ac768b3e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1113,6 +1113,7 @@ "Direct Messages": "Direct Messages", "Start chat": "Start chat", "Rooms": "Rooms", + "Create room": "Create room", "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", From 17f535e5f878337854b7599ca8da2b3e8528c81b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 18 May 2020 16:37:10 +0100 Subject: [PATCH 180/196] Fix review problems - removed superfluous position and classes - fixed compact view - fixed event list summary avatar and text overlap - fixed a problem where the mention list refuses to load. --- res/css/views/rooms/_EventTile.scss | 79 ------------------ res/css/views/rooms/_GroupLayout.scss | 82 ++++++++++++++++++- res/css/views/rooms/_IRCLayout.scss | 4 +- src/components/structures/MessagePanel.js | 2 +- .../views/elements/ErrorBoundary.js | 2 +- .../elements/IRCTimelineProfileResizer.tsx | 4 +- src/components/views/rooms/EventTile.js | 2 - 7 files changed, 88 insertions(+), 87 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index b9a41c4310..c278f813d0 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -37,7 +37,6 @@ limitations under the License. } .mx_EventTile_avatar { - position: absolute; top: 14px; left: 8px; cursor: pointer; @@ -576,84 +575,6 @@ div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody { /* end of overrides */ -.mx_MatrixChat_useCompactLayout { - .mx_EventTile { - padding-top: 4px; - } - - .mx_EventTile.mx_EventTile_info { - // same as the padding for non-compact .mx_EventTile.mx_EventTile_info - padding-top: 0px; - font-size: $font-13px; - .mx_EventTile_line, .mx_EventTile_reply { - line-height: $font-20px; - } - .mx_EventTile_avatar { - top: 4px; - } - } - - .mx_EventTile .mx_SenderProfile { - font-size: $font-13px; - } - - .mx_EventTile.mx_EventTile_emote { - // add a bit more space for emotes so that avatars don't collide - padding-top: 8px; - .mx_EventTile_avatar { - top: 2px; - } - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 1px; - } - } - - .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { - padding-top: 0; - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - } - - .mx_EventTile_line, .mx_EventTile_reply { - padding-top: 0px; - padding-bottom: 0px; - } - - .mx_EventTile_avatar { - top: 2px; - } - - .mx_EventTile_e2eIcon { - top: 3px; - } - - .mx_EventTile_readAvatars { - top: 27px; - } - - .mx_EventTile_continuation .mx_EventTile_readAvatars, - .mx_EventTile_emote .mx_EventTile_readAvatars { - top: 5px; - } - - .mx_EventTile_info .mx_EventTile_readAvatars { - top: 4px; - } - - .mx_RoomView_MessageList h2 { - margin-top: 6px; - } - - .mx_EventTile_content .markdown-body { - p, ul, ol, dl, blockquote, pre, table { - margin-bottom: 4px; // 1/4 of the non-compact margin-bottom - } - } -} - .mx_EventTile_tileError { color: red; text-align: center; diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 6528d6c6cd..bfe463ed49 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -47,6 +47,86 @@ $left-gutter: 65px; } .mx_EventTile_info .mx_EventTile_line { - padding-left: 83px; + padding-left: calc($left-gutter + 18px); } } + +/* Compact layout overrides */ + +.mx_MatrixChat_useCompactLayout { + .mx_EventTile { + padding-top: 4px; + } + + .mx_EventTile.mx_EventTile_info { + // same as the padding for non-compact .mx_EventTile.mx_EventTile_info + padding-top: 0px; + font-size: $font-13px; + .mx_EventTile_line, .mx_EventTile_reply { + line-height: $font-20px; + } + .mx_EventTile_avatar { + top: 4px; + } + } + + .mx_EventTile .mx_SenderProfile { + font-size: $font-13px; + } + + .mx_EventTile.mx_EventTile_emote { + // add a bit more space for emotes so that avatars don't collide + padding-top: 8px; + .mx_EventTile_avatar { + top: 2px; + } + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 1px; + } + } + + .mx_EventTile.mx_EventTile_emote.mx_EventTile_continuation { + padding-top: 0; + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + } + + .mx_EventTile_line, .mx_EventTile_reply { + padding-top: 0px; + padding-bottom: 0px; + } + + .mx_EventTile_avatar { + top: 2px; + } + + .mx_EventTile_e2eIcon { + top: 3px; + } + + .mx_EventTile_readAvatars { + top: 27px; + } + + .mx_EventTile_continuation .mx_EventTile_readAvatars, + .mx_EventTile_emote .mx_EventTile_readAvatars { + top: 5px; + } + + .mx_EventTile_info .mx_EventTile_readAvatars { + top: 4px; + } + + .mx_RoomView_MessageList h2 { + margin-top: 6px; + } + + .mx_EventTile_content .markdown-body { + p, ul, ol, dl, blockquote, pre, table { + margin-bottom: 4px; // 1/4 of the non-compact margin-bottom + } + } +} \ No newline at end of file diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss index f2a616f9c9..5f88473c5f 100644 --- a/res/css/views/rooms/_IRCLayout.scss +++ b/res/css/views/rooms/_IRCLayout.scss @@ -138,7 +138,7 @@ $irc-line-height: $font-18px; .mx_EventListSummary_avatars { padding: 0; - margin: 0; + margin: 0 9px 0 0; } } @@ -185,7 +185,7 @@ $irc-line-height: $font-18px; .mx_SenderProfile_hover:hover { overflow: visible; - width: auto; + width: max(auto, 100%); z-index: 10; } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 1c10efb346..f875467e4f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -822,7 +822,7 @@ export default class MessagePanel extends React.Component { let ircResizer = null; if (this.state.useIRCLayout) { - ircResizer = <IRCTimelineProfileResizer minWidth={20} maxWidth={600} roomId={this.props.room.roomId} />; + ircResizer = <IRCTimelineProfileResizer minWidth={20} maxWidth={600} roomId={this.props.room ? this.props.roomroomId : null} />; } return ( diff --git a/src/components/views/elements/ErrorBoundary.js b/src/components/views/elements/ErrorBoundary.js index 1abd11f838..a043b350ab 100644 --- a/src/components/views/elements/ErrorBoundary.js +++ b/src/components/views/elements/ErrorBoundary.js @@ -73,7 +73,7 @@ export default class ErrorBoundary extends React.PureComponent { if (this.state.error) { const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new"; - return <div className="mx_ErrorBoundary mx_IRCLayout"> + return <div className="mx_ErrorBoundary"> <div className="mx_ErrorBoundary_body"> <h1>{_t("Something went wrong!")}</h1> <p>{_t( diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 44ceeb9b7b..596d46bf36 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -77,7 +77,9 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I } private onMoueUp(event: MouseEvent) { - SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width); + if (this.props.roomId) { + SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width); + } } render() { diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index b2daa5667f..4ff3a5ccfc 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -659,8 +659,6 @@ export default createReactClass({ const classes = classNames({ mx_EventTile_bubbleContainer: isBubbleMessage, mx_EventTile: true, - mx_EventTile_irc: this.props.useIRCLayout, - mx_EventTile_group: !this.props.useIRCLayout, mx_EventTile_isEditing: isEditing, mx_EventTile_info: isInfoMessage, mx_EventTile_12hr: this.props.isTwelveHour, From 5380e76b7b95b952b9ec764dbf03baf227851bc0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 18 May 2020 16:43:47 +0100 Subject: [PATCH 181/196] lint --- res/css/views/rooms/_GroupLayout.scss | 2 +- src/components/structures/MessagePanel.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index bfe463ed49..40440f7d49 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -129,4 +129,4 @@ $left-gutter: 65px; margin-bottom: 4px; // 1/4 of the non-compact margin-bottom } } -} \ No newline at end of file +} diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index f875467e4f..b3f9b40ada 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -822,7 +822,11 @@ export default class MessagePanel extends React.Component { let ircResizer = null; if (this.state.useIRCLayout) { - ircResizer = <IRCTimelineProfileResizer minWidth={20} maxWidth={600} roomId={this.props.room ? this.props.roomroomId : null} />; + ircResizer = <IRCTimelineProfileResizer + minWidth={20} + maxWidth={600} + roomId={this.props.room ? this.props.roomroomId : null} + />; } return ( From 4deeef5fca9afcaf35d3c76f20ea84c8ac4808aa Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 18 May 2020 16:57:00 +0100 Subject: [PATCH 182/196] Remove ability to remove avatars --- src/components/structures/MessagePanel.js | 13 ------------- src/components/views/elements/ReplyThread.js | 7 ++----- src/components/views/rooms/EventTile.js | 6 +----- src/settings/Settings.js | 6 ------ 4 files changed, 3 insertions(+), 29 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index b3f9b40ada..404e950d7f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -174,11 +174,6 @@ export default class MessagePanel extends React.Component { SettingsStore.watchSetting("showTypingNotifications", null, this.onShowTypingNotificationsChange); this._layoutWatcherRef = SettingsStore.watchSetting("feature_irc_ui", null, this.onLayoutChange); - this._displayAvatarsWatcherRef = SettingsStore.watchSetting( - "feature_no_timeline_avatars", - null, - this.onDisplayAvatarsChange, - ); } componentDidMount() { @@ -189,7 +184,6 @@ export default class MessagePanel extends React.Component { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); SettingsStore.unwatchSetting(this._layoutWatcherRef); - SettingsStore.unwatchSetting(this._displayAvatarsWatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -214,12 +208,6 @@ export default class MessagePanel extends React.Component { }); } - onDisplayAvatarsChange = () => { - this.setState({ - displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), - }); - } - /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { @@ -622,7 +610,6 @@ export default class MessagePanel extends React.Component { getRelationsForEvent={this.props.getRelationsForEvent} showReactions={this.props.showReactions} useIRCLayout={this.state.useIRCLayout} - displayAvatars={this.state.displayAvatars} /> </TileErrorBoundary> </li>, diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 6bfda5dd94..04e31d6490 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -39,8 +39,6 @@ export default class ReplyThread extends React.Component { permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired, // Specifies which layout to use. useIRCLayout: PropTypes.bool, - // Specifies whether to display avatars. - displayAvatars: PropTypes.bool, }; static contextType = MatrixClientContext; @@ -180,7 +178,7 @@ export default class ReplyThread extends React.Component { }; } - static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout, displayAvatars) { + static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, useIRCLayout) { if (!ReplyThread.getParentEventId(parentEv)) { return <div className="mx_ReplyThread_wrapper_empty" />; } @@ -190,7 +188,7 @@ export default class ReplyThread extends React.Component { ref={ref} permalinkCreator={permalinkCreator} useIRCLayout={useIRCLayout} - displayAvatars={displayAvatars} />; + />; } componentDidMount() { @@ -342,7 +340,6 @@ export default class ReplyThread extends React.Component { isRedacted={ev.isRedacted()} isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} useIRCLayout={this.props.useIRCLayout} - displayAvatars={this.props.displayAvatars} /> </blockquote>; }); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 4ff3a5ccfc..1cb632b2e8 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -209,9 +209,6 @@ export default createReactClass({ // whether to use the irc layout useIRCLayout: PropTypes.bool, - - // whether to display avatars - displayAvatars: PropTypes.bool, }, getDefaultProps: function() { @@ -714,7 +711,7 @@ export default createReactClass({ needsSenderProfile = true; } - if (this.props.mxEvent.sender && avatarSize && this.props.displayAvatars) { + if (this.props.mxEvent.sender && avatarSize) { avatar = ( <div className="mx_EventTile_avatar"> <MemberAvatar member={this.props.mxEvent.sender} @@ -898,7 +895,6 @@ export default createReactClass({ this.props.permalinkCreator, this._replyThread, this.props.useIRCLayout, - this.props.displayAvatars, ); // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 6d741ba3a5..f54b32a8b5 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -143,12 +143,6 @@ export const SETTINGS = { default: false, isFeature: true, }, - "feature_no_timeline_avatars": { - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td('Display user avatars on messages'), - default: true, - isFeature: true, - }, "mjolnirRooms": { supportedLevels: ['account'], default: [], From bba6b2c4413b47d1d3ae95d350df99d2826668eb Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 18 May 2020 17:06:54 +0100 Subject: [PATCH 183/196] Appease i18n --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 37b9c1dfc8..12838968f7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -406,7 +406,6 @@ "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Support adding custom themes": "Support adding custom themes", "Use IRC layout": "Use IRC layout", - "Display user avatars on messages": "Display user avatars on messages", "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", From 7bb7f30b8f4ca9c8bd3d02e69ff2928317626fa9 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Mon, 18 May 2020 22:02:22 +0100 Subject: [PATCH 184/196] missed one --- src/components/structures/MessagePanel.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 404e950d7f..cac04d84f1 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -119,7 +119,6 @@ export default class MessagePanel extends React.Component { ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), useIRCLayout: SettingsStore.getValue("feature_irc_ui"), - displayAvatars: SettingsStore.getValue("feature_no_timeline_avatars"), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker From 833eccf5cd901d04ddfcc06a7f95e2e2ebe6f5d5 Mon Sep 17 00:00:00 2001 From: RiotRobot <releases@riot.im> Date: Tue, 19 May 2020 11:25:07 +0100 Subject: [PATCH 185/196] Reset matrix-js-sdk back to develop branch --- package.json | 2 +- yarn.lock | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 3bdda8a583..7c008d5ccc 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "is-ip": "^2.0.0", "linkifyjs": "^2.1.6", "lodash": "^4.17.14", - "matrix-js-sdk": "6.1.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "minimist": "^1.2.0", "pako": "^1.0.5", "parse5": "^5.1.1", diff --git a/yarn.lock b/yarn.lock index b7e5017fd7..93118dab22 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1285,6 +1285,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== +"@types/qrcode@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b" + integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg== + dependencies: + "@types/node" "*" + "@types/react@*": version "16.9.35" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.35.tgz#a0830d172e8aadd9bd41709ba2281a3124bbd368" @@ -1293,13 +1300,6 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/qrcode@^1.3.4": - version "1.3.4" - resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.3.4.tgz#984d97bb72caa558d470158701081ccb712f616b" - integrity sha512-aILE5yvKaqQXlY0YPMEYwK/KwdD43fwQTyagj0ffBBTQj8h//085Zp8LUrOnZ9FT69x64f5UgDo0EueY4BPAdg== - dependencies: - "@types/node" "*" - "@types/react@16.9": version "16.9.32" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.32.tgz#f6368625b224604148d1ddf5920e4fefbd98d383" @@ -5753,10 +5753,9 @@ mathml-tag-names@^2.0.1: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@6.1.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "6.1.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-6.1.0.tgz#c28ad67c113c4aa9c8bce409c7ba550170bdc2ee" - integrity sha512-N+vCgxWORvhh7AGyWZlU5Z2brojbbnHnWlMkBF6JjWe6a+pfpjmRKp5/jeQpOz6yfe56sIQvU7ikBZl3JjlMiw== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e3c6a0e1a08a3812ba988e60eb5a2a013bb27404" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From 4bf2e5fd8eaafe22533e5e3a02752a9d2251d129 Mon Sep 17 00:00:00 2001 From: David Baker <dave@matrix.org> Date: Tue, 19 May 2020 15:40:26 +0100 Subject: [PATCH 186/196] Remove SSSS key upgrade check from rageshake This code doesn't exist anymore as the SSSS symmetric upgrade stuff has been removed. Fixes https://github.com/vector-im/riot-web/issues/13715 --- src/rageshake/submit-rageshake.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index e5027e0d37..9f9d7898cb 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -133,7 +133,6 @@ export default async function sendBugReport(bugReportEndpoint: string, opts: IOp body.append("cross_signing_supported_by_hs", String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"))); body.append("cross_signing_ready", String(await client.isCrossSigningReady())); - body.append("ssss_key_needs_upgrade", String(await client.secretStorageKeyNeedsUpgrade())); } } From 286828b3bb8a3556726c625be4b6034c1e83d87b Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Tue, 19 May 2020 16:15:13 +0100 Subject: [PATCH 187/196] Disable irc mode in notifiactions panel --- src/components/structures/MessagePanel.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index cac04d84f1..93e4668f66 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -110,15 +110,16 @@ export default class MessagePanel extends React.Component { showReactions: PropTypes.bool, }; - constructor() { - super(); + // Force props to be loaded for useIRCLayout + constructor(props) { + super(props); this.state = { // previous positions the read marker has been in, so we can // display 'ghost' read markers that are animating away ghostReadMarkers: [], showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), - useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }; // opaque readreceipt info for each userId; used by ReadReceiptMarker @@ -203,10 +204,15 @@ export default class MessagePanel extends React.Component { onLayoutChange = () => { this.setState({ - useIRCLayout: SettingsStore.getValue("feature_irc_ui"), + useIRCLayout: this.useIRCLayout(SettingsStore.getValue("feature_irc_ui")), }); } + useIRCLayout(ircLayoutSelected) { + // if room is null we are not in a normal room list + return ircLayoutSelected && this.props.room; + } + /* get the DOM node representing the given event */ getNodeForEventId(eventId) { if (!this.eventNodes) { From 86ad6de41ee455285695270242a5d0983b014899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= <poljar@termina.org.uk> Date: Wed, 20 May 2020 11:01:56 +0200 Subject: [PATCH 188/196] EventIndex: Handle null tokens in the crawler loop as well. This is similar to 5eb510387c9aa08a061d5fd17bed3efc104b48eb. But now the checkpoint arrived during a crawl. --- src/indexing/EventIndex.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 02151f8474..16b0183da1 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -489,14 +489,20 @@ export default class EventIndex extends EventEmitter { return object; }); - // Create a new checkpoint so we can continue crawling the room for - // messages. - const newCheckpoint = { - roomId: checkpoint.roomId, - token: res.end, - fullCrawl: checkpoint.fullCrawl, - direction: checkpoint.direction, - }; + let newCheckpoint; + + // The token can be null for some reason. Don't create a checkpoint + // in that case since adding it to the db will fail. + if (res.end) { + // Create a new checkpoint so we can continue crawling the room + // for messages. + newCheckpoint = { + roomId: checkpoint.roomId, + token: res.end, + fullCrawl: checkpoint.fullCrawl, + direction: checkpoint.direction, + }; + } try { for (let i = 0; i < redactionEvents.length; i++) { @@ -506,6 +512,15 @@ export default class EventIndex extends EventEmitter { const eventsAlreadyAdded = await indexManager.addHistoricEvents( events, newCheckpoint, checkpoint); + + // We didn't get a valid new checkpoint from the server, nothing + // to do here anymore. + if (!newCheckpoint) { + console.log("EventIndex: The server didn't return a valid ", + "new checkpoint, not continuing the crawl.", checkpoint); + continue + } + // If all events were already indexed we assume that we catched // up with our index and don't need to crawl the room further. // Let us delete the checkpoint in that case, otherwise push From db72c645bae492a5a3daba70695ef137ef07d485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= <poljar@termina.org.uk> Date: Wed, 20 May 2020 11:14:37 +0200 Subject: [PATCH 189/196] EventIndex: Add a missing semicolon. --- src/indexing/EventIndex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 16b0183da1..d372c38405 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -518,7 +518,7 @@ export default class EventIndex extends EventEmitter { if (!newCheckpoint) { console.log("EventIndex: The server didn't return a valid ", "new checkpoint, not continuing the crawl.", checkpoint); - continue + continue; } // If all events were already indexed we assume that we catched From 3e30df17fbc9a3a5efeb4c7bbf24ea62c828b235 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 20 May 2020 13:07:33 +0100 Subject: [PATCH 190/196] Slider is more responsive --- res/css/views/elements/_Slider.scss | 5 ++++- src/components/views/elements/Slider.tsx | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_Slider.scss b/res/css/views/elements/_Slider.scss index 06c3c4c98b..58ba2813b4 100644 --- a/res/css/views/elements/_Slider.scss +++ b/res/css/views/elements/_Slider.scss @@ -38,7 +38,9 @@ limitations under the License. .mx_Slider_bar > hr { width: 100%; - border: 0.2em solid $slider-background-color; + height: 0.4em; + background-color: $slider-background-color; + border: 0; } .mx_Slider_selection { @@ -47,6 +49,7 @@ limitations under the License. width: calc(100% - 1em); // 2 * half the width of a dot height: 1em; position: absolute; + pointer-events: none; } .mx_Slider_selectionDot { diff --git a/src/components/views/elements/Slider.tsx b/src/components/views/elements/Slider.tsx index e181f0d9e3..f76a4684d3 100644 --- a/src/components/views/elements/Slider.tsx +++ b/src/components/views/elements/Slider.tsx @@ -93,7 +93,7 @@ export default class Slider extends React.Component<IProps> { return <div className="mx_Slider"> <div> <div className="mx_Slider_bar"> - <hr /> + <hr onClick={this.props.disabled ? () => {} : this.onClick.bind(this)}/> { selection } </div> <div className="mx_Slider_dotContainer"> @@ -102,6 +102,15 @@ export default class Slider extends React.Component<IProps> { </div> </div>; } + + onClick(event: React.MouseEvent) { + const width = (event.target as HTMLElement).clientWidth; + // nativeEvent is safe to use because https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/offsetX + // is supported by all modern browsers + const relativeClick = (event.nativeEvent.offsetX / width); + const nearestValue = this.props.values[Math.round(relativeClick * (this.props.values.length - 1))]; + this.props.onSelectionChange(nearestValue); + } } interface IDotProps { From 55e72dd5bf826fd8fb9cfb3f7b769ad682247b67 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 20 May 2020 13:45:54 +0100 Subject: [PATCH 191/196] Remove min and max font setting --- src/FontWatcher.js | 9 +++++---- .../settings/tabs/user/AppearanceUserSettingsTab.js | 6 +++--- src/settings/Settings.js | 10 ---------- 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index 561edc4662..1128ac1bd5 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -18,6 +18,10 @@ import dis from './dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { + + static minSize = 13; + static maxSize = 20; + constructor() { this._dispatcherRef = null; } @@ -38,10 +42,7 @@ export class FontWatcher { }; _setRootFontSize = (size) => { - const min = SettingsStore.getValue("fontSizeMin"); - const max = SettingsStore.getValue("fontSizeMax"); - - const fontSize = Math.max(Math.min(max, size), min); + const fontSize = Math.max(Math.min(this.maxSize, size), this.minSize); if (fontSize != size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 63857ed9c2..308b7098d1 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -25,7 +25,7 @@ import Field from "../../../elements/Field"; import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; import dis from "../../../../../dispatcher"; -import _range from "lodash/range"; +import { FontWatcher } from "../../../../../FontWatcher"; export default class AppearanceUserSettingsTab extends React.Component { constructor() { @@ -109,8 +109,8 @@ export default class AppearanceUserSettingsTab extends React.Component { console.log({value}); const parsedSize = parseFloat(value); - const min = SettingsStore.getValue("fontSizeMin"); - const max = SettingsStore.getValue("fontSizeMax"); + const min = FontWatcher.minSize; + const max = FontWatcher.maxSize; if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; diff --git a/src/settings/Settings.js b/src/settings/Settings.js index afe8d2cecc..5f093f6a70 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -171,16 +171,6 @@ export const SETTINGS = { default: 16, controller: new FontSizeController(), }, - "fontSizeMin": { - displayName: _td("Min font size"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 13, - }, - "fontSizeMax": { - displayName: _td("Max font size"), - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - default: 20, - }, "useCustomFontSize": { displayName: _td("Custom font size"), supportedLevels: LEVELS_ACCOUNT_SETTINGS, From 82b76192aee6fe64865c1bc4ff456d0c4614dad0 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 20 May 2020 14:44:56 +0100 Subject: [PATCH 192/196] Fixes, lints and i18n --- src/FontWatcher.js | 4 ++-- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 2 +- src/i18n/strings/en_EN.json | 2 -- src/settings/controllers/FontSizeController.js | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index 1128ac1bd5..b45d9065ce 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import dis from './dispatcher'; +import dis from './dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { @@ -42,7 +42,7 @@ export class FontWatcher { }; _setRootFontSize = (size) => { - const fontSize = Math.max(Math.min(this.maxSize, size), this.minSize); + const fontSize = Math.max(Math.min(FontWatcher.maxSize, size), FontWatcher.minSize); if (fontSize != size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 308b7098d1..3d04e10df7 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -24,7 +24,7 @@ import {enumerateThemes, ThemeWatcher} from "../../../../../theme"; import Field from "../../../elements/Field"; import Slider from "../../../elements/Slider"; import AccessibleButton from "../../../elements/AccessibleButton"; -import dis from "../../../../../dispatcher"; +import dis from "../../../../../dispatcher/dispatcher"; import { FontWatcher } from "../../../../../FontWatcher"; export default class AppearanceUserSettingsTab extends React.Component { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d6670e85b3..b7417762f1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -412,8 +412,6 @@ "Enable cross-signing to verify per-user instead of per-session": "Enable cross-signing to verify per-user instead of per-session", "Show info about bridges in room settings": "Show info about bridges in room settings", "Font size": "Font size", - "Min font size": "Min font size", - "Max font size": "Max font size", "Custom font size": "Custom font size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Use compact timeline layout": "Use compact timeline layout", diff --git a/src/settings/controllers/FontSizeController.js b/src/settings/controllers/FontSizeController.js index 8e855e31ec..3ef01ab99b 100644 --- a/src/settings/controllers/FontSizeController.js +++ b/src/settings/controllers/FontSizeController.js @@ -15,7 +15,7 @@ limitations under the License. */ import SettingController from "./SettingController"; -import dis from "../../dispatcher"; +import dis from "../../dispatcher/dispatcher"; export default class FontSizeController extends SettingController { constructor() { From 4e9a139e8b45a8597b3fa51c357fca5ea1691fa7 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 20 May 2020 14:49:18 +0100 Subject: [PATCH 193/196] lint --- src/FontWatcher.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index b45d9065ce..c547ef8ce0 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -18,7 +18,6 @@ import dis from './dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { - static minSize = 13; static maxSize = 20; From adec5a4f921bb9e24d7f0d52240bbcc84959b871 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 20 May 2020 15:09:10 +0100 Subject: [PATCH 194/196] fix test --- test/components/views/messages/TextualBody-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index 59671327ce..4e93b3bb64 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -206,7 +206,7 @@ describe("<TextualBody />", () => { 'Hey <span>' + '<a class="mx_Pill mx_UserPill" title="@user:server">' + '<img class="mx_BaseAvatar mx_BaseAvatar_image" src="mxc://avatar.url/image.png" ' + - 'style="width: 1.0666666666666667rem; height: 1.0666666666666667rem;" ' + + 'style="width: 16px; height: 16px;" ' + 'title="@member:domain.bla" alt="" aria-hidden="true">Member</a>' + '</span></span>'); }); From 50a44405f012f9f987709025d1ea4c0e1e275eac Mon Sep 17 00:00:00 2001 From: Jorik Schellekens <joriksch@gmail.com> Date: Wed, 20 May 2020 15:17:47 +0100 Subject: [PATCH 195/196] CONSTANT_CASING --- src/FontWatcher.js | 6 +++--- .../views/settings/tabs/user/AppearanceUserSettingsTab.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FontWatcher.js b/src/FontWatcher.js index c547ef8ce0..006df202ad 100644 --- a/src/FontWatcher.js +++ b/src/FontWatcher.js @@ -18,8 +18,8 @@ import dis from './dispatcher/dispatcher'; import SettingsStore, {SettingLevel} from './settings/SettingsStore'; export class FontWatcher { - static minSize = 13; - static maxSize = 20; + static MIN_SIZE = 13; + static MAX_SIZE = 20; constructor() { this._dispatcherRef = null; @@ -41,7 +41,7 @@ export class FontWatcher { }; _setRootFontSize = (size) => { - const fontSize = Math.max(Math.min(FontWatcher.maxSize, size), FontWatcher.minSize); + const fontSize = Math.max(Math.min(FontWatcher.MAX_SIZE, size), FontWatcher.MIN_SIZE); if (fontSize != size) { SettingsStore.setValue("fontSize", null, SettingLevel.Device, fontSize); diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js index 3d04e10df7..5b49dd0abd 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.js @@ -109,8 +109,8 @@ export default class AppearanceUserSettingsTab extends React.Component { console.log({value}); const parsedSize = parseFloat(value); - const min = FontWatcher.minSize; - const max = FontWatcher.maxSize; + const min = FontWatcher.MIN_SIZE; + const max = FontWatcher.MAX_SIZE; if (isNaN(parsedSize)) { return {valid: false, feedback: _t("Size must be a number")}; From 5d1c01fd6fd006443bbf4d6f1e0e731a99ad042e Mon Sep 17 00:00:00 2001 From: David Baker <dave@matrix.org> Date: Thu, 21 May 2020 12:26:27 +0100 Subject: [PATCH 196/196] Fix key backup restore with SSSS The room / session ID params come after the backupInfo for restoring from SSSS so the options object was being passed into the wrong param. Roll on TypeScript. This meant restoring backups worked fine when the key was cached but failed when it wasn't. Regressed in https://github.com/matrix-org/matrix-react-sdk/pull/4507 --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 7e51e76f6c..a16202ed93 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -201,7 +201,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { // `accessSecretStorage` may prompt for storage access as needed. const recoverInfo = await accessSecretStorage(async () => { return MatrixClientPeg.get().restoreKeyBackupWithSecretStorage( - this.state.backupInfo, + this.state.backupInfo, undefined, undefined, { progressCallback: this._progressCallback }, ); });