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 01/34] 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 = ; - return (
); + return (); } else { return (
); } From 777040208aa676cfe43eb69a9d9cbf92e0ce4edd Mon Sep 17 00:00:00 2001 From: Tirifto Date: Thu, 14 May 2020 15:57:39 +0000 Subject: [PATCH 02/34] 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", "": "", - "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?= Date: Thu, 14 May 2020 16:30:32 +0000 Subject: [PATCH 03/34] 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 Date: Thu, 14 May 2020 16:21:20 +0000 Subject: [PATCH 04/34] 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 Date: Thu, 14 May 2020 16:32:26 +0000 Subject: [PATCH 05/34] 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 Date: Thu, 14 May 2020 16:36:58 +0000 Subject: [PATCH 06/34] 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 07/34] 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 {
; } strengthMeter =
- +
; } 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 {
; } strengthMeter =
- +
; } 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 ; + return ; }, 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 = ({value, className}) => { + const classes = classNames("mx_ZxcvbnProgressBar", className); + return ; +}; + +export default ZxcvbnProgressBar; From 7ef456230479abd914f6aafbd54716007ad0a14f Mon Sep 17 00:00:00 2001 From: Tirifto Date: Thu, 14 May 2020 16:39:20 +0000 Subject: [PATCH 08/34] 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 keywords": "Mesaĝoj enhavantaj ŝlosilovortojn", @@ -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?= Date: Thu, 14 May 2020 16:34:16 +0000 Subject: [PATCH 09/34] 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. Learn more about encryption.": "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. Lisateave krüptimise kohta.", + "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 Date: Thu, 14 May 2020 16:48:38 +0000 Subject: [PATCH 10/34] 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 11/34] 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 12/34] 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; + +interface IRule { + key: string; + final?: boolean; + skip?(this: T, data: Data): boolean; + test(this: T, data: Data): boolean | Promise; + valid?(this: T): string; + invalid?(this: T): string; +} + +interface IArgs { + rules: IRule[]; + 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({ description, rules }: IArgs) { + 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 13/34] 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 14/34] 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 | RefObject; + + label?: string; + labelEnterPassword?: string; + labelStrongPassword?: string; + labelAllowedButUnsafe?: string; + + onChange(ev: KeyboardEvent); + onValidate(result: IValidationResult); +} + +interface IState { + complexity: zxcvbn.ZXCVBNResult; +} + +class PassphraseField extends PureComponent { + 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({ + description: function() { + const complexity = this.state.complexity; + const score = complexity ? complexity.score : 0; + return ; + }, + 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 + } +} + +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 ; - }, - 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 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; +type Data = Pick; interface IRule { key: string; @@ -32,15 +32,20 @@ interface IRule { interface IArgs { rules: IRule[]; - 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({ description, rules }: IArgs) { - return async function onValidate({ value, focused, allowEmpty = true }: IValidateArgs) { + return async function onValidate({ value, focused, allowEmpty = true }: IFieldState): Promise { 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 15/34] 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 =
{suggestion || _t("Keep going...")}
; - - helpText =
- {suggestionBlock} -
; - } - strengthMeter =
- -
; - } - return

{_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 { )}

- -
- {strengthMeter} - {helpText} -
.": "Mitte midagi ei kuvata? Kõik Matrix'i kliendid ei toeta veel interaktiivset verifitseerimist. .", + "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 Date: Sun, 17 May 2020 09:37:45 +0000 Subject: [PATCH 33/34] 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 not able to run Riot.": "Desculpe, o seu navegador non pode 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 contact your service administrator 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 contacte coa administración do servizo para continuar utilizando o servizo.", "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.": "A súa mensaxe non foi enviada porque o servidor superou o límite de recursos. Por favor contacte coa administración do servizo para continuar utilizando o servizo.", "Legal": "Legal", - "Please contact your service administrator to continue using this service.": "Por favor contacte coa administración do servizo para continuar utilizando o servizo." + "Please contact your service administrator to continue using this service.": "Por favor contacte coa administración do servizo 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 Chrome, Firefox, or Safari for the best experience.": "Instala Chrome, Firefox, ou Safari para ter unha mellor experiencia." } From 5ea4cc1c5b209b5ea8b3401ea3cabf80a20da11a Mon Sep 17 00:00:00 2001 From: yuuki-san Date: Mon, 18 May 2020 08:31:13 +0000 Subject: [PATCH 34/34] 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." }