From d0afd65652bbfcc2888b73219310bb02794be8e5 Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Sat, 21 Mar 2020 10:12:55 +0100 Subject: [PATCH 001/114] fix view community link icon contrast Signed-off-by: thobyv-kismat --- 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 472831c0d9..c1c5d92d3c 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -137,7 +137,7 @@ limitations under the License. top: -8px; border-radius: 8px; background-color: $neutral-badge-color; - color: #ffffff; + color: #000; font-weight: 600; font-size: 10px; text-align: center; From d7953dfa6dfc18ce0161ab8cf1a46359bab83068 Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Tue, 24 Mar 2020 13:09:06 +0100 Subject: [PATCH 002/114] fix formatbar not hidden on highlighted message sent --- src/components/views/rooms/BasicMessageComposer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 147f3c0af8..899b3360d8 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -152,6 +152,7 @@ export default class BasicMessageEditor extends React.Component { if (this.props.placeholder) { const {isEmpty} = this.props.model; if (isEmpty) { + this._formatBarRef.hide(); this._showPlaceholder(); } else { this._hidePlaceholder(); From a43281b9a6fe207480580cfd24fa3425c452e870 Mon Sep 17 00:00:00 2001 From: catborise Date: Tue, 24 Mar 2020 08:16:30 +0000 Subject: [PATCH 003/114] Translated using Weblate (Turkish) Currently translated at 77.5% (1724 of 2224 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 4728cdd319..6e46c7ad81 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -538,9 +538,9 @@ "View Source": "Kaynağı Görüntüle", "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "Geçerli tarayıcınız ile birlikte , uygulamanın görünüş ve kullanım hissi tamamen hatalı olabilir ve bazı ya da tüm özellikler çalışmayabilir. Yine de denemek isterseniz devam edebilirsiniz ancak karşılaşabileceğiniz sorunlar karşısında kendi başınasınız !", "There are advanced notifications which are not shown here": "Burada gösterilmeyen gelişmiş bildirimler var", - "The platform you're on": "Bulunduğun platform", + "The platform you're on": "Platformunuz", "The version of Riot.im": "Riot.im'in sürümü", - "Your language of choice": "Dil seçeneği", + "Your language of choice": "Dil seçiminiz", "Which officially provided instance you are using, if any": "Hangi resmi destekli örneği(eğer varsa) kullanmaktasınız", "Add Email Address": "E-posta Adresi Ekle", "Add Phone Number": "Telefon Numarası Ekle", From 1d665bcba425e16b51a0811afbc8a83d4201a1e1 Mon Sep 17 00:00:00 2001 From: roket1428 Date: Tue, 24 Mar 2020 14:18:53 +0000 Subject: [PATCH 004/114] Translated using Weblate (Turkish) Currently translated at 77.4% (1724 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 6e46c7ad81..493deb9008 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -602,7 +602,7 @@ "%(names)s and %(count)s others are typing …|one": "%(names)s ve bir diğeri yazıyor…", "%(names)s and %(lastPerson)s are typing …": "%(names)s ve %(lastPerson)s yazıyor…", "Cannot reach homeserver": "Ana sunucuya erişilemiyor", - "Your Riot is misconfigured": "Rioutunuz hatalı yapılandırılmış", + "Your Riot is misconfigured": "Riot hatalı yapılandırılmış", "Cannot reach identity server": "Kimlik sunucu erişilemiyor", "No homeserver URL provided": "Ana sunucu adresi belirtilmemiş", "Unexpected error resolving homeserver configuration": "Ana sunucu yapılandırması çözümlenirken beklenmeyen hata", From f7fa9613e0fa2441e214155a46b577cc32726a8b Mon Sep 17 00:00:00 2001 From: catborise Date: Tue, 24 Mar 2020 14:21:04 +0000 Subject: [PATCH 005/114] Translated using Weblate (Turkish) Currently translated at 77.4% (1724 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 493deb9008..6015eb4ab7 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -542,7 +542,7 @@ "The version of Riot.im": "Riot.im'in sürümü", "Your language of choice": "Dil seçiminiz", "Which officially provided instance you are using, if any": "Hangi resmi destekli örneği(eğer varsa) kullanmaktasınız", - "Add Email Address": "E-posta Adresi Ekle", + "Add Email Address": "E-posta Adres Ekle", "Add Phone Number": "Telefon Numarası Ekle", "Your identity server's URL": "Kimlik sunucunuzun linki", "e.g. %(exampleValue)s": "örn.%(exampleValue)s", From 30db3f2a4cbc817c2873692c8f1d07b7040fcd54 Mon Sep 17 00:00:00 2001 From: roket1428 Date: Tue, 24 Mar 2020 14:21:21 +0000 Subject: [PATCH 006/114] Translated using Weblate (Turkish) Currently translated at 77.4% (1724 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 6015eb4ab7..493deb9008 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -542,7 +542,7 @@ "The version of Riot.im": "Riot.im'in sürümü", "Your language of choice": "Dil seçiminiz", "Which officially provided instance you are using, if any": "Hangi resmi destekli örneği(eğer varsa) kullanmaktasınız", - "Add Email Address": "E-posta Adres Ekle", + "Add Email Address": "E-posta Adresi Ekle", "Add Phone Number": "Telefon Numarası Ekle", "Your identity server's URL": "Kimlik sunucunuzun linki", "e.g. %(exampleValue)s": "örn.%(exampleValue)s", From 483bd509f5d36c177a79cdf40ee916a052515af1 Mon Sep 17 00:00:00 2001 From: Ege Date: Tue, 24 Mar 2020 14:22:00 +0000 Subject: [PATCH 007/114] Translated using Weblate (Turkish) Currently translated at 77.4% (1724 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 493deb9008..ce600e49b4 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -602,7 +602,7 @@ "%(names)s and %(count)s others are typing …|one": "%(names)s ve bir diğeri yazıyor…", "%(names)s and %(lastPerson)s are typing …": "%(names)s ve %(lastPerson)s yazıyor…", "Cannot reach homeserver": "Ana sunucuya erişilemiyor", - "Your Riot is misconfigured": "Riot hatalı yapılandırılmış", + "Your Riot is misconfigured": "Riot hatalı ayarlanmış", "Cannot reach identity server": "Kimlik sunucu erişilemiyor", "No homeserver URL provided": "Ana sunucu adresi belirtilmemiş", "Unexpected error resolving homeserver configuration": "Ana sunucu yapılandırması çözümlenirken beklenmeyen hata", From aa904761eeb04c636f6e81d7cb9fb1a48c157daa Mon Sep 17 00:00:00 2001 From: vejetaryenvampir Date: Tue, 24 Mar 2020 14:22:46 +0000 Subject: [PATCH 008/114] Translated using Weblate (Turkish) Currently translated at 77.4% (1724 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index ce600e49b4..daefb766b3 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -398,7 +398,7 @@ "Please check your email to continue registration.": "Kayıt işlemine devam etmek için lütfen e-postanızı kontrol edin.", "Token incorrect": "Belirteç(Token) hatalı", "Please enter the code it contains:": "Lütfen içerdiği kodu girin:", - "powered by Matrix": "Matrix'den besleniyor", + "powered by Matrix": "Matrix tarafından dest", "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Eğer bir e-posta adresi belirtmezseniz , şifrenizi sıfırlayamazsınız . Emin misiniz ?", "Error decrypting audio": "Ses şifre çözme hatası", "Error decrypting image": "Resim şifre çözme hatası", From ca51690bbfdaf7031f53ad4ccaf50c70c36e3ea5 Mon Sep 17 00:00:00 2001 From: random Date: Tue, 24 Mar 2020 14:23:17 +0000 Subject: [PATCH 009/114] Translated using Weblate (Italian) Currently translated at 100.0% (2227 of 2227 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 | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/it.json b/src/i18n/strings/it.json index a18aa8f760..4829d4cf7a 100644 --- a/src/i18n/strings/it.json +++ b/src/i18n/strings/it.json @@ -2276,5 +2276,17 @@ "Esc": "Esc", "Enter": "Invio", "Space": "Barra spaziatrice", - "End": "Fine" + "End": "Fine", + "Manually Verify by Text": "Verifica manualmente con testo", + "Interactively verify by Emoji": "Verifica interattivamente con emoji", + "Secret Storage key format:": "Formato chiave di archivio segreto:", + "outdated": "non aggiornato", + "up to date": "aggiornato", + "Confirm by comparing the following with the User Settings in your other session:": "Conferma confrontando il seguente con le impostazioni utente nell'altra sessione:", + "Confirm this user's session by comparing the following with their User Settings:": "Conferma questa sessione confrontando il seguente con le sue impostazioni utente:", + "If they don't match, the security of your communication may be compromised.": "Se non corrispondono, la sicurezza delle tue comunicazioni potrebbe essere compromessa.", + "Navigate composer history": "Naviga cronologia compositore", + "Previous/next unread room or DM": "Stanza o msg non letti successivi/precedenti", + "Previous/next room or DM": "Stanza o msg successivi/precedenti", + "Toggle right panel": "Apri/chiudi pannello a destra" } From 8ed9feb5053ee6423d2918a2fb85719259420651 Mon Sep 17 00:00:00 2001 From: roket1428 Date: Tue, 24 Mar 2020 14:23:58 +0000 Subject: [PATCH 010/114] Translated using Weblate (Turkish) Currently translated at 77.8% (1733 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index daefb766b3..8f90bfe936 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -398,7 +398,7 @@ "Please check your email to continue registration.": "Kayıt işlemine devam etmek için lütfen e-postanızı kontrol edin.", "Token incorrect": "Belirteç(Token) hatalı", "Please enter the code it contains:": "Lütfen içerdiği kodu girin:", - "powered by Matrix": "Matrix tarafından dest", + "powered by Matrix": "Matrix'den besleniyor", "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "Eğer bir e-posta adresi belirtmezseniz , şifrenizi sıfırlayamazsınız . Emin misiniz ?", "Error decrypting audio": "Ses şifre çözme hatası", "Error decrypting image": "Resim şifre çözme hatası", @@ -839,7 +839,7 @@ "Set a new password": "Yeni bir şifre belirle", "General failure": "Genel başarısızlık", "This homeserver does not support login using email address.": "Bu ana sunucu e-posta adresiyle oturum açmayı desteklemiyor.", - "This account has been deactivated.": "Bu hesap pasifleştirilmiş.", + "This account has been deactivated.": "Hesap devre dışı bırakıldı.", "Create account": "Yeni hesap", "Unable to query for supported registration methods.": "Desteklenen kayıt yöntemleri için sorgulama yapılamıyor.", "Continue with previous account": "Önceki hesapla devam et", @@ -983,7 +983,7 @@ "Cat": "Kedi", "Lion": "Aslan", "Horse": "At", - "Unicorn": "Tek boynuzlu at", + "Unicorn": "Midilli", "Pig": "Domuz", "Elephant": "Fil", "Rabbit": "Tavşan", @@ -1752,5 +1752,11 @@ "Failed to re-authenticate due to a homeserver problem": "Anasunucu problemi yüzünden yeniden kimlik doğrulama başarısız", "Failed to re-authenticate": "Yeniden kimlik doğrulama başarısız", "A new recovery passphrase and key for Secure Messages have been detected.": "Yeni bir kurtarma parolası ve Güvenli Mesajlar için anahtar tespit edildi.", - "Not currently indexing messages for any room.": "Şu an hiç bir odada mesaj indeksleme yapılmıyor." + "Not currently indexing messages for any room.": "Şu an hiç bir odada mesaj indeksleme yapılmıyor.", + "Whether you're using Riot on a device where touch is the primary input mechanism": "Riot'u ana giriş yöntemi dokunma olan bir cihazda kullanıyor olsanızda", + "Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)": "'Breadcrumbs' özelliğini kullanıp kullanmadığınız (oda listesi üzerinde avatarlar)", + "Whether you're using Riot as an installed Progressive Web App": "Riot'u gelişmiş web uygulaması olarak yükleyip yüklemediğinizi", + "The information being sent to us to help make Riot better includes:": "Riot'u geliştirmemizde bize yardım etmek için gönderdiğiniz bilgiler şunları içeriyor:", + "A call is currently being placed!": "Bir çağrı şu anda başlatılıyor!", + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Şu anda dosya ile birlikte mesaj yollamak mümkün değil. Dosyayı mesajsız yüklemek ister misiniz?" } From 4ff847c8a32fcefef52a0e872704e6f4f9caeba3 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 24 Mar 2020 15:57:04 +0100 Subject: [PATCH 011/114] put CompleteSecurity state management in store and split off a child component (SetupCrossSigningBody) that can be reused from the "Verify this session" toast. --- .../structures/auth/CompleteSecurity.js | 235 ++---------------- .../structures/auth/SetupEncryptionBody.js | 196 +++++++++++++++ src/stores/SetupEncryptionStore.js | 147 +++++++++++ 3 files changed, 366 insertions(+), 212 deletions(-) create mode 100644 src/components/structures/auth/SetupEncryptionBody.js create mode 100644 src/stores/SetupEncryptionStore.js diff --git a/src/components/structures/auth/CompleteSecurity.js b/src/components/structures/auth/CompleteSecurity.js index 3154564cd3..06cece0af2 100644 --- a/src/components/structures/auth/CompleteSecurity.js +++ b/src/components/structures/auth/CompleteSecurity.js @@ -18,13 +18,14 @@ import React from 'react'; import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; -import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import { accessSecretStorage, AccessCancelledError } from '../../../CrossSigningManager'; - -const PHASE_INTRO = 0; -const PHASE_BUSY = 1; -const PHASE_DONE = 2; -const PHASE_CONFIRM_SKIP = 3; +import { + SetupEncryptionStore, + PHASE_INTRO, + PHASE_BUSY, + PHASE_DONE, + PHASE_CONFIRM_SKIP, +} from '../../../stores/SetupEncryptionStore'; +import SetupEncryptionBody from "./SetupEncryptionBody"; export default class CompleteSecurity extends React.Component { static propTypes = { @@ -33,232 +34,42 @@ export default class CompleteSecurity extends React.Component { constructor() { super(); - - this.state = { - phase: PHASE_INTRO, - // this serves dual purpose as the object for the request logic and - // the presence of it insidicating that we're in 'verify mode'. - // Because of the latter, it lives in the state. - verificationRequest: null, - backupInfo: null, - }; - MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequest); + const store = SetupEncryptionStore.sharedInstance(); + store.on("update", this._onStoreUpdate); + store.start(); + this.state = {phase: store.phase}; } + _onStoreUpdate = () => { + const store = SetupEncryptionStore.sharedInstance(); + this.setState({phase: store.phase}); + }; + componentWillUnmount() { - if (this.state.verificationRequest) { - this.state.verificationRequest.off("change", this.onVerificationRequestChange); - } - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest); - } - } - - _onUsePassphraseClick = async () => { - this.setState({ - phase: PHASE_BUSY, - }); - const cli = MatrixClientPeg.get(); - try { - const backupInfo = await cli.getKeyBackupVersion(); - this.setState({backupInfo}); - - // The control flow is fairly twisted here... - // For the purposes of completing security, we only wait on getting - // as far as the trust check and then show a green shield. - // We also begin the key backup restore as well, which we're - // awaiting inside `accessSecretStorage` only so that it keeps your - // passphase cached for that work. This dialog itself will only wait - // on the first trust check, and the key backup restore will happen - // in the background. - await new Promise((resolve, reject) => { - try { - accessSecretStorage(async () => { - await cli.checkOwnCrossSigningTrust(); - resolve(); - if (backupInfo) { - // A complete restore can take many minutes for large - // accounts / slow servers, so we allow the dialog - // to advance before this. - await cli.restoreKeyBackupWithSecretStorage(backupInfo); - } - }); - } catch (e) { - console.error(e); - reject(e); - } - }); - - if (cli.getCrossSigningId()) { - this.setState({ - phase: PHASE_DONE, - }); - } - } catch (e) { - if (!(e instanceof AccessCancelledError)) { - console.log(e); - } - // this will throw if the user hits cancel, so ignore - this.setState({ - phase: PHASE_INTRO, - }); - } - } - - onVerificationRequest = async (request) => { - if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return; - - if (this.state.verificationRequest) { - this.state.verificationRequest.off("change", this.onVerificationRequestChange); - } - await request.accept(); - request.on("change", this.onVerificationRequestChange); - this.setState({ - verificationRequest: request, - }); - } - - onVerificationRequestChange = () => { - if (this.state.verificationRequest.cancelled) { - this.state.verificationRequest.off("change", this.onVerificationRequestChange); - this.setState({ - verificationRequest: null, - }); - } - } - - onSkipClick = () => { - this.setState({ - phase: PHASE_CONFIRM_SKIP, - }); - } - - onSkipConfirmClick = () => { - this.props.onFinished(); - } - - onSkipBackClick = () => { - this.setState({ - phase: PHASE_INTRO, - }); - } - - onDoneClick = () => { - this.props.onFinished(); + const store = SetupEncryptionStore.sharedInstance(); + store.off("update", this._onStoreUpdate); + store.stop(); } render() { const AuthPage = sdk.getComponent("auth.AuthPage"); const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody"); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - - const { - phase, - } = this.state; - + const {phase} = this.state; let icon; let title; - let body; - - if (this.state.verificationRequest) { - const EncryptionPanel = sdk.getComponent("views.right_panel.EncryptionPanel"); - body = ; - } else if (phase === PHASE_INTRO) { - const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); + if (phase === PHASE_INTRO) { icon = ; title = _t("Complete security"); - body = ( -
-

{_t( - "Open an existing session & use it to verify this one, " + - "granting it access to encrypted messages.", - )}

-

{_t("Waiting…")}

-

{_t( - "If you can’t access one, ", - {}, { - button: sub => - {sub} - , - })}

-
- - {_t("Skip")} - -
-
- ); } else if (phase === PHASE_DONE) { icon = ; title = _t("Session verified"); - let message; - if (this.state.backupInfo) { - message =

{_t( - "Your new session is now verified. It has access to your " + - "encrypted messages, and other users will see it as trusted.", - )}

; - } else { - message =

{_t( - "Your new session is now verified. Other users will see it as trusted.", - )}

; - } - body = ( -
-
- {message} -
- - {_t("Done")} - -
-
- ); } else if (phase === PHASE_CONFIRM_SKIP) { icon = ; title = _t("Are you sure?"); - body = ( -
-

{_t( - "Without completing security on this session, it won’t have " + - "access to encrypted messages.", - )}

-
- - {_t("Skip")} - - - {_t("Go Back")} - -
-
- ); } else if (phase === PHASE_BUSY) { - const Spinner = sdk.getComponent('views.elements.Spinner'); icon = ; title = _t("Complete security"); - body = ; } else { throw new Error(`Unknown phase ${phase}`); } @@ -271,7 +82,7 @@ export default class CompleteSecurity extends React.Component { {title}
- {body} +
diff --git a/src/components/structures/auth/SetupEncryptionBody.js b/src/components/structures/auth/SetupEncryptionBody.js new file mode 100644 index 0000000000..a59fa08b32 --- /dev/null +++ b/src/components/structures/auth/SetupEncryptionBody.js @@ -0,0 +1,196 @@ +/* +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 PropTypes from 'prop-types'; +import { _t } from '../../../languageHandler'; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; +import * as sdk from '../../../index'; +import { + SetupEncryptionStore, + PHASE_INTRO, + PHASE_BUSY, + PHASE_DONE, + PHASE_CONFIRM_SKIP, + PHASE_FINISHED, +} from '../../../stores/SetupEncryptionStore'; + +export default class SetupEncryptionBody extends React.Component { + static propTypes = { + onFinished: PropTypes.func.isRequired, + }; + + constructor() { + super(); + const store = SetupEncryptionStore.sharedInstance(); + store.on("update", this._onStoreUpdate); + store.start(); + this.state = { + phase: store.phase, + // this serves dual purpose as the object for the request logic and + // the presence of it insidicating that we're in 'verify mode'. + // Because of the latter, it lives in the state. + verificationRequest: store.verificationRequest, + backupInfo: store.backupInfo, + }; + } + + _onStoreUpdate = () => { + const store = SetupEncryptionStore.sharedInstance(); + if (store.phase === PHASE_FINISHED) { + this.props.onFinished(); + return; + } + this.setState({ + phase: store.phase, + verificationRequest: store.verificationRequest, + backupInfo: store.backupInfo, + }); + }; + + componentWillUnmount() { + const store = SetupEncryptionStore.sharedInstance(); + store.off("update", this._onStoreUpdate); + store.stop(); + } + + _onUsePassphraseClick = async () => { + const store = SetupEncryptionStore.sharedInstance(); + store.usePassPhrase(); + } + + onSkipClick = () => { + const store = SetupEncryptionStore.sharedInstance(); + store.skip(); + } + + onSkipConfirmClick = () => { + const store = SetupEncryptionStore.sharedInstance(); + store.skipConfirm(); + } + + onSkipBackClick = () => { + const store = SetupEncryptionStore.sharedInstance(); + store.returnAfterSkip(); + } + + onDoneClick = () => { + const store = SetupEncryptionStore.sharedInstance(); + store.done(); + } + + render() { + const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); + + const { + phase, + } = this.state; + + if (this.state.verificationRequest) { + const EncryptionPanel = sdk.getComponent("views.right_panel.EncryptionPanel"); + return ; + } else if (phase === PHASE_INTRO) { + const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); + return ( +
+

{_t( + "Open an existing session & use it to verify this one, " + + "granting it access to encrypted messages.", + )}

+

{_t("Waiting…")}

+

{_t( + "If you can’t access one, ", + {}, { + button: sub => + {sub} + , + })}

+
+ + {_t("Skip")} + +
+
+ ); + } else if (phase === PHASE_DONE) { + let message; + if (this.state.backupInfo) { + message =

{_t( + "Your new session is now verified. It has access to your " + + "encrypted messages, and other users will see it as trusted.", + )}

; + } else { + message =

{_t( + "Your new session is now verified. Other users will see it as trusted.", + )}

; + } + return ( +
+
+ {message} +
+ + {_t("Done")} + +
+
+ ); + } else if (phase === PHASE_CONFIRM_SKIP) { + return ( +
+

{_t( + "Without completing security on this session, it won’t have " + + "access to encrypted messages.", + )}

+
+ + {_t("Skip")} + + + {_t("Go Back")} + +
+
+ ); + } else if (phase === PHASE_BUSY) { + const Spinner = sdk.getComponent('views.elements.Spinner'); + return ; + } else { + throw new Error(`Unknown phase ${phase}`); + } + } +} diff --git a/src/stores/SetupEncryptionStore.js b/src/stores/SetupEncryptionStore.js new file mode 100644 index 0000000000..93c1770b1f --- /dev/null +++ b/src/stores/SetupEncryptionStore.js @@ -0,0 +1,147 @@ +/* +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 EventEmitter from 'events'; +import { MatrixClientPeg } from '../MatrixClientPeg'; +import { accessSecretStorage, AccessCancelledError } from '../CrossSigningManager'; + +export const PHASE_INTRO = 0; +export const PHASE_BUSY = 1; +export const PHASE_DONE = 2; //final done stage, but still showing UX +export const PHASE_CONFIRM_SKIP = 3; +export const PHASE_FINISHED = 4; //UX can be closed + +/** + * Holds the active "Complete Security" session + */ +export class SetupEncryptionStore extends EventEmitter { + static sharedInstance() { + if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore(); + return global.mx_SetupEncryptionStore; + } + + start() { + if (this._started) { + return; + } + this._started = true; + this.phase = PHASE_INTRO; + this.verificationRequest = null; + this.backupInfo = null; + MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequest); + } + + stop() { + if (!this._started) { + return; + } + this._started = false; + if (this.verificationRequest) { + this.verificationRequest.off("change", this.onVerificationRequestChange); + } + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener("crypto.verification.request", this.onVerificationRequest); + } + } + + async usePassPhrase() { + this.phase = PHASE_BUSY; + this.emit("update"); + const cli = MatrixClientPeg.get(); + try { + const backupInfo = await cli.getKeyBackupVersion(); + this.backupInfo = backupInfo; + this.emit("update"); + // The control flow is fairly twisted here... + // For the purposes of completing security, we only wait on getting + // as far as the trust check and then show a green shield. + // We also begin the key backup restore as well, which we're + // awaiting inside `accessSecretStorage` only so that it keeps your + // passphase cached for that work. This dialog itself will only wait + // on the first trust check, and the key backup restore will happen + // in the background. + await new Promise((resolve, reject) => { + try { + accessSecretStorage(async () => { + await cli.checkOwnCrossSigningTrust(); + resolve(); + if (backupInfo) { + // A complete restore can take many minutes for large + // accounts / slow servers, so we allow the dialog + // to advance before this. + await cli.restoreKeyBackupWithSecretStorage(backupInfo); + } + }).catch(reject); + } catch (e) { + console.error(e); + reject(e); + } + }); + + if (cli.getCrossSigningId()) { + this.phase = PHASE_DONE; + this.emit("update"); + } + } catch (e) { + if (!(e instanceof AccessCancelledError)) { + console.log(e); + } + // this will throw if the user hits cancel, so ignore + this.phase = PHASE_INTRO; + this.emit("update"); + } + } + + onVerificationRequest = async (request) => { + if (request.otherUserId !== MatrixClientPeg.get().getUserId()) return; + + if (this.verificationRequest) { + this.verificationRequest.off("change", this.onVerificationRequestChange); + } + this.verificationRequest = request; + await request.accept(); + request.on("change", this.onVerificationRequestChange); + this.emit("update"); + } + + onVerificationRequestChange = () => { + if (this.verificationRequest.cancelled) { + this.verificationRequest.off("change", this.onVerificationRequestChange); + this.verificationRequest = null; + this.emit("update"); + } + } + + skip() { + this.phase = PHASE_CONFIRM_SKIP; + this.emit("update"); + } + + skipConfirm() { + this.phase = PHASE_FINISHED; + this.emit("update"); + } + + returnAfterSkip() { + this.phase = PHASE_INTRO; + this.emit("update"); + } + + done() { + this.phase = PHASE_FINISHED; + this.emit("update"); + } +} From 3e59127d12a83e55def0f7003c39c7e82cc33a2f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 24 Mar 2020 16:10:43 +0100 Subject: [PATCH 012/114] use SetupEncryptionBody to show a dialog from "Verify this session" --- .../views/dialogs/SetupEncryptionDialog.js | 29 +++++++++++++++++++ .../views/toasts/SetupEncryptionToast.js | 9 +++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 src/components/views/dialogs/SetupEncryptionDialog.js diff --git a/src/components/views/dialogs/SetupEncryptionDialog.js b/src/components/views/dialogs/SetupEncryptionDialog.js new file mode 100644 index 0000000000..f32a289a29 --- /dev/null +++ b/src/components/views/dialogs/SetupEncryptionDialog.js @@ -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 SetupEncryptionBody from '../../structures/auth/SetupEncryptionBody'; +import BaseDialog from './BaseDialog'; +import { _t } from '../../../languageHandler'; + +export default function SetupEncryptionDialog({onFinished}) { + return + + ; +} diff --git a/src/components/views/toasts/SetupEncryptionToast.js b/src/components/views/toasts/SetupEncryptionToast.js index 9016e4c6d7..ad6488a9bb 100644 --- a/src/components/views/toasts/SetupEncryptionToast.js +++ b/src/components/views/toasts/SetupEncryptionToast.js @@ -18,7 +18,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; +import Modal from '../../../Modal'; import DeviceListener from '../../../DeviceListener'; +import SetupEncryptionDialog from "../dialogs/SetupEncryptionDialog"; import { accessSecretStorage } from '../../../CrossSigningManager'; export default class SetupEncryptionToast extends React.PureComponent { @@ -32,7 +34,12 @@ export default class SetupEncryptionToast extends React.PureComponent { }; _onSetupClick = async () => { - accessSecretStorage(); + if (this.props.kind === "verify_this_session") { + Modal.createTrackedDialog('Verify session', 'Verify session', SetupEncryptionDialog, + {}, null, /* priority = */ false, /* static = */ true); + } else { + accessSecretStorage(); + } }; getDescription() { From c53b07a35a73d8a83c0e1bfaebdb0284e03b398d Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 24 Mar 2020 15:49:51 +0000 Subject: [PATCH 013/114] Add logging when secrets are missing from cache --- src/CrossSigningManager.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js index 5c254bbd00..097464ee43 100644 --- a/src/CrossSigningManager.js +++ b/src/CrossSigningManager.js @@ -149,9 +149,15 @@ const onSecretRequested = async function({ if (!callbacks.getCrossSigningKeyCache) return; if (name === "m.cross_signing.self_signing") { const key = await callbacks.getCrossSigningKeyCache("self_signing"); + if (!key) { + console.log(`self_signing requested by ${deviceId}, but not found in cache`); + } return key && encodeBase64(key); } else if (name === "m.cross_signing.user_signing") { const key = await callbacks.getCrossSigningKeyCache("user_signing"); + if (!key) { + console.log(`user_signing requested by ${deviceId}, but not found in cache`); + } return key && encodeBase64(key); } console.warn("onSecretRequested didn't recognise the secret named ", name); From 1c802cc6afd6e27effc791238e07da13e8dcbf86 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 24 Mar 2020 15:50:08 +0000 Subject: [PATCH 014/114] Show private key cache state in debug panel --- .../views/settings/CrossSigningPanel.js | 17 +++++++++++++++++ src/i18n/strings/en_EN.json | 4 ++++ 2 files changed, 21 insertions(+) diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index cf47c797fc..b960434ca1 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -32,6 +32,8 @@ export default class CrossSigningPanel extends React.PureComponent { error: null, crossSigningPublicKeysOnDevice: false, crossSigningPrivateKeysInStorage: false, + selfSigningPrivateKeyCached: false, + userSigningPrivateKeyCached: false, secretStorageKeyInAccount: false, secretStorageKeyNeedsUpgrade: null, }; @@ -71,10 +73,13 @@ export default class CrossSigningPanel extends React.PureComponent { async _getUpdatedStatus() { const cli = MatrixClientPeg.get(); + const pkCache = cli.getCrossSigningCacheCallbacks(); const crossSigning = cli._crypto._crossSigningInfo; const secretStorage = cli._crypto._secretStorage; const crossSigningPublicKeysOnDevice = crossSigning.getId(); const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage); + const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")); + const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing")); const secretStorageKeyInAccount = await secretStorage.hasKey(); const homeserverSupportsCrossSigning = await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"); @@ -84,6 +89,8 @@ export default class CrossSigningPanel extends React.PureComponent { this.setState({ crossSigningPublicKeysOnDevice, crossSigningPrivateKeysInStorage, + selfSigningPrivateKeyCached, + userSigningPrivateKeyCached, secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, @@ -130,6 +137,8 @@ export default class CrossSigningPanel extends React.PureComponent { error, crossSigningPublicKeysOnDevice, crossSigningPrivateKeysInStorage, + selfSigningPrivateKeyCached, + userSigningPrivateKeyCached, secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, @@ -209,6 +218,14 @@ export default class CrossSigningPanel extends React.PureComponent { {_t("Cross-signing private keys:")} {crossSigningPrivateKeysInStorage ? _t("in secret storage") : _t("not found")} + + {_t("Self signing private key:")} + {selfSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")} + + + {_t("User signing private key:")} + {userSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")} + {_t("Secret storage public key:")} {secretStorageKeyInAccount ? _t("in account data") : _t("not found")} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 57b39309b0..12bd462937 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -582,6 +582,10 @@ "not found": "not found", "Cross-signing private keys:": "Cross-signing private keys:", "in secret storage": "in secret storage", + "Self signing private key:": "Self signing private key:", + "cached locally": "cached locally", + "not found locally": "not found locally", + "User signing private key:": "User signing private key:", "Secret storage public key:": "Secret storage public key:", "in account data": "in account data", "Homeserver feature support:": "Homeserver feature support:", From bdcb65de77107a45875c0854441594720439cb84 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Mar 2020 09:55:54 -0600 Subject: [PATCH 015/114] Support and send the config over to capable widgets For https://github.com/vector-im/riot-web/pull/12845 --- src/FromWidgetPostMessageApi.js | 11 +++++++- src/WidgetMessaging.js | 11 ++++++++ src/components/views/elements/AppTile.js | 6 +++++ src/utils/WidgetUtils.js | 8 ++++-- src/widgets/WidgetApi.ts | 34 +++++++++++++++++++++--- 5 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/FromWidgetPostMessageApi.js b/src/FromWidgetPostMessageApi.js index 64caba0fdf..ea76c85643 100644 --- a/src/FromWidgetPostMessageApi.js +++ b/src/FromWidgetPostMessageApi.js @@ -24,6 +24,8 @@ import {MatrixClientPeg} from "./MatrixClientPeg"; import RoomViewStore from "./stores/RoomViewStore"; import {IntegrationManagers} from "./integrations/IntegrationManagers"; import SettingsStore from "./settings/SettingsStore"; +import {Capability, KnownWidgetActions} from "./widgets/WidgetApi"; +import SdkConfig from "./SdkConfig"; const WIDGET_API_VERSION = '0.0.2'; // Current API version const SUPPORTED_WIDGET_API_VERSIONS = [ @@ -213,11 +215,18 @@ export default class FromWidgetPostMessageApi { const data = event.data.data; const val = data.value; - if (ActiveWidgetStore.widgetHasCapability(widgetId, 'm.always_on_screen')) { + if (ActiveWidgetStore.widgetHasCapability(widgetId, Capability.AlwaysOnScreen)) { ActiveWidgetStore.setWidgetPersistence(widgetId, val); } } else if (action === 'get_openid') { // Handled by caller + } else if (action === KnownWidgetActions.GetRiotWebConfig) { + if (ActiveWidgetStore.widgetHasCapability(widgetId, Capability.GetRiotWebConfig)) { + this.sendResponse(event, { + api: INBOUND_API_NAME, + config: SdkConfig.get(), + }); + } } else { console.warn('Widget postMessage event unhandled'); this.sendError(event, {message: 'The postMessage was unhandled'}); diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index d40a8ab637..b0cfe963f3 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -27,6 +27,7 @@ import {MatrixClientPeg} from "./MatrixClientPeg"; import SettingsStore from "./settings/SettingsStore"; import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog"; import WidgetUtils from "./utils/WidgetUtils"; +import {KnownWidgetActions} from "./widgets/WidgetApi"; if (!global.mxFromWidgetMessaging) { global.mxFromWidgetMessaging = new FromWidgetPostMessageApi(); @@ -75,6 +76,16 @@ export default class WidgetMessaging { }); } + /** + * Tells the widget that the client is ready to handle further widget requests. + */ + flagReadyToContinue() { + return this.messageToWidget({ + api: OUTBOUND_API_NAME, + action: KnownWidgetActions.ClientReady, + }); + } + /** * Request a screenshot from a widget * @return {Promise} To be resolved with screenshot data when it has been generated diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index a26478c461..0a8bf7443b 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -419,6 +419,12 @@ export default class AppTile extends React.Component { if (this.props.onCapabilityRequest) { this.props.onCapabilityRequest(requestedCapabilities); } + + // We only tell Jitsi widgets that we're ready because they're realistically the only ones + // using this custom extension to the widget API. + if (this.props.type === 'jitsi') { + widgetMessaging.flagReadyToContinue(); + } }).catch((err) => { console.log(`Failed to get capabilities for widget type ${this.props.type}`, this.props.id, err); }); diff --git a/src/utils/WidgetUtils.js b/src/utils/WidgetUtils.js index 74e5f82c35..eea995cfea 100644 --- a/src/utils/WidgetUtils.js +++ b/src/utils/WidgetUtils.js @@ -28,6 +28,7 @@ const WIDGET_WAIT_TIME = 20000; import SettingsStore from "../settings/SettingsStore"; import ActiveWidgetStore from "../stores/ActiveWidgetStore"; import {IntegrationManagers} from "../integrations/IntegrationManagers"; +import {Capability} from "../widgets/WidgetApi"; /** * Encodes a URI according to a set of template variables. Variables will be @@ -454,12 +455,15 @@ export default class WidgetUtils { static getCapWhitelistForAppTypeInRoomId(appType, roomId) { const enableScreenshots = SettingsStore.getValue("enableWidgetScreenshots", roomId); - const capWhitelist = enableScreenshots ? ["m.capability.screenshot"] : []; + const capWhitelist = enableScreenshots ? [Capability.Screenshot] : []; // Obviously anyone that can add a widget can claim it's a jitsi widget, // so this doesn't really offer much over the set of domains we load // widgets from at all, but it probably makes sense for sanity. - if (appType == 'jitsi') capWhitelist.push("m.always_on_screen"); + if (appType === 'jitsi') { + capWhitelist.push(Capability.AlwaysOnScreen); + capWhitelist.push(Capability.GetRiotWebConfig); + } return capWhitelist; } diff --git a/src/widgets/WidgetApi.ts b/src/widgets/WidgetApi.ts index c19e34ae43..d6d1c79a99 100644 --- a/src/widgets/WidgetApi.ts +++ b/src/widgets/WidgetApi.ts @@ -23,6 +23,7 @@ export enum Capability { Screenshot = "m.capability.screenshot", Sticker = "m.sticker", AlwaysOnScreen = "m.always_on_screen", + GetRiotWebConfig = "im.vector.web.riot_config", } export enum KnownWidgetActions { @@ -33,7 +34,10 @@ export enum KnownWidgetActions { UpdateVisibility = "visibility", ReceiveOpenIDCredentials = "openid_credentials", SetAlwaysOnScreen = "set_always_on_screen", + GetRiotWebConfig = "im.vector.web.riot_config", + ClientReady = "im.vector.ready", } + export type WidgetAction = KnownWidgetActions | string; export enum WidgetApiType { @@ -63,10 +67,15 @@ export interface FromWidgetRequest extends WidgetRequest { */ export class WidgetApi { private origin: string; - private inFlightRequests: {[requestId: string]: (reply: FromWidgetRequest) => void} = {}; + private inFlightRequests: { [requestId: string]: (reply: FromWidgetRequest) => void } = {}; private readyPromise: Promise; private readyPromiseResolve: () => void; + /** + * Set this to true if your widget is expecting a ready message from the client. False otherwise (default). + */ + public expectingExplicitReady = false; + constructor(currentUrl: string, private widgetId: string, private requestedCapabilities: string[]) { this.origin = new URL(currentUrl).origin; @@ -83,7 +92,14 @@ export class WidgetApi { if (payload.action === KnownWidgetActions.GetCapabilities) { this.onCapabilitiesRequest(payload); + if (!this.expectingExplicitReady) { + this.readyPromiseResolve(); + } + } else if (payload.action === KnownWidgetActions.ClientReady) { this.readyPromiseResolve(); + + // Automatically acknowledge so we can move on + this.replyToRequest(payload, {}); } else { console.warn(`[WidgetAPI] Got unexpected action: ${payload.action}`); } @@ -126,7 +142,10 @@ export class WidgetApi { data: payload, response: {}, // Not used at this layer - it's used when the client responds }; - this.inFlightRequests[request.requestId] = callback; + + if (callback) { + this.inFlightRequests[request.requestId] = callback; + } console.log(`[WidgetAPI] Sending request: `, request); window.parent.postMessage(request, "*"); @@ -134,7 +153,16 @@ export class WidgetApi { public setAlwaysOnScreen(onScreen: boolean): Promise { return new Promise(resolve => { - this.callAction(KnownWidgetActions.SetAlwaysOnScreen, {value: onScreen}, resolve); + this.callAction(KnownWidgetActions.SetAlwaysOnScreen, {value: onScreen}, null); + resolve(); // SetAlwaysOnScreen is currently fire-and-forget, but that could change. + }); + } + + public getRiotConfig(): Promise { + return new Promise(resolve => { + this.callAction(KnownWidgetActions.GetRiotWebConfig, {}, response => { + resolve(response.response.config); + }); }); } } From 7cded53cdb118c3fee1bc21e60148d60a88f0c74 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 24 Mar 2020 17:02:36 +0100 Subject: [PATCH 016/114] fix i18n --- src/i18n/strings/en_EN.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 57b39309b0..9dcea47cc8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2006,14 +2006,7 @@ "Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other", "Could not load user profile": "Could not load user profile", "Complete security": "Complete security", - "Open an existing session & use it to verify this one, granting it access to encrypted messages.": "Open an existing session & use it to verify this one, granting it access to encrypted messages.", - "Waiting…": "Waiting…", - "If you can’t access one, ": "If you can’t access one, ", "Session verified": "Session verified", - "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.", - "Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.", - "Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.", - "Go Back": "Go Back", "Failed to send email": "Failed to send email", "The email address linked to your account must be entered.": "The email address linked to your account must be entered.", "A new password must be entered.": "A new password must be entered.", @@ -2063,6 +2056,13 @@ "You can now close this window or log in to your new account.": "You can now close this window or log in to your new account.", "Registration Successful": "Registration Successful", "Create your account": "Create your account", + "Open an existing session & use it to verify this one, granting it access to encrypted messages.": "Open an existing session & use it to verify this one, granting it access to encrypted messages.", + "Waiting…": "Waiting…", + "If you can’t access one, ": "If you can’t access one, ", + "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.", + "Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.", + "Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.", + "Go Back": "Go Back", "Failed to re-authenticate due to a homeserver problem": "Failed to re-authenticate due to a homeserver problem", "Failed to re-authenticate": "Failed to re-authenticate", "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.": "Regain access to your account and recover encryption keys stored in this session. Without them, you won’t be able to read all of your secure messages in any session.", From 7d4e4982575d9bc97f5ba8a2eb47a927f36ecf4f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 24 Mar 2020 17:03:40 +0100 Subject: [PATCH 017/114] fix lint --- src/stores/SetupEncryptionStore.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/stores/SetupEncryptionStore.js b/src/stores/SetupEncryptionStore.js index 93c1770b1f..7b42e1552d 100644 --- a/src/stores/SetupEncryptionStore.js +++ b/src/stores/SetupEncryptionStore.js @@ -24,9 +24,6 @@ export const PHASE_DONE = 2; //final done stage, but still showing UX export const PHASE_CONFIRM_SKIP = 3; export const PHASE_FINISHED = 4; //UX can be closed -/** - * Holds the active "Complete Security" session - */ export class SetupEncryptionStore extends EventEmitter { static sharedInstance() { if (!global.mx_SetupEncryptionStore) global.mx_SetupEncryptionStore = new SetupEncryptionStore(); From 7ea61e41055fef1ba199e78af6420dbd7405dce4 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 24 Mar 2020 10:05:57 -0600 Subject: [PATCH 018/114] Appease the linter --- src/WidgetMessaging.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index b0cfe963f3..30c2389b1e 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -78,6 +78,7 @@ export default class WidgetMessaging { /** * Tells the widget that the client is ready to handle further widget requests. + * @returns {Promise<*>} Resolves after the widget has acknowledged the ready message. */ flagReadyToContinue() { return this.messageToWidget({ From 91b9a04ede6ebb18bd6a53e756a1f2f6a24399d4 Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Tue, 24 Mar 2020 19:00:43 +0100 Subject: [PATCH 019/114] refactor:consider checking if no placeholder --- src/components/views/rooms/BasicMessageComposer.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 899b3360d8..707dc12a97 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -149,15 +149,17 @@ export default class BasicMessageEditor extends React.Component { const position = selection.end || selection; this._setLastCaretFromPosition(position); } + const {isEmpty} = this.props.model; if (this.props.placeholder) { - const {isEmpty} = this.props.model; if (isEmpty) { - this._formatBarRef.hide(); this._showPlaceholder(); } else { this._hidePlaceholder(); } } + if(isEmpty) { + this._formatBarRef.hide(); + } this.setState({autoComplete: this.props.model.autoComplete}); this.historyManager.tryPush(this.props.model, selection, inputType, diff); TypingStore.sharedInstance().setSelfTyping(this.props.room.roomId, !this.props.model.isEmpty); From b817c06c6e6259b0f23c21c1329a061490ae3c10 Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Tue, 24 Mar 2020 19:09:50 +0100 Subject: [PATCH 020/114] fix failing linter tests --- src/components/views/rooms/BasicMessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 707dc12a97..75455518a1 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -157,7 +157,7 @@ export default class BasicMessageEditor extends React.Component { this._hidePlaceholder(); } } - if(isEmpty) { + if (isEmpty) { this._formatBarRef.hide(); } this.setState({autoComplete: this.props.model.autoComplete}); From be50f803145455aa37d25fc95478c67348c12771 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Mar 2020 19:02:57 +0000 Subject: [PATCH 021/114] Wait for SSSS upgrade to complete and show a spinner while it completes --- src/DeviceListener.js | 2 +- .../views/toasts/SetupEncryptionToast.js | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/DeviceListener.js b/src/DeviceListener.js index 7878a1a670..0151a45c18 100644 --- a/src/DeviceListener.js +++ b/src/DeviceListener.js @@ -169,7 +169,7 @@ export default class DeviceListener { key: THIS_DEVICE_TOAST_KEY, title: _t("Encryption upgrade available"), icon: "verification_warning", - props: {kind: 'upgrade_encryption'}, + props: {kind: 'upgrade_ssss'}, component: sdk.getComponent("toasts.SetupEncryptionToast"), }); } else { diff --git a/src/components/views/toasts/SetupEncryptionToast.js b/src/components/views/toasts/SetupEncryptionToast.js index 9016e4c6d7..f25d8fd506 100644 --- a/src/components/views/toasts/SetupEncryptionToast.js +++ b/src/components/views/toasts/SetupEncryptionToast.js @@ -16,6 +16,8 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; +import Modal from '../../../Modal'; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; import DeviceListener from '../../../DeviceListener'; @@ -24,21 +26,52 @@ import { accessSecretStorage } from '../../../CrossSigningManager'; export default class SetupEncryptionToast extends React.PureComponent { static propTypes = { toastKey: PropTypes.string.isRequired, - kind: PropTypes.oneOf(['set_up_encryption', 'verify_this_session', 'upgrade_encryption']).isRequired, + kind: PropTypes.oneOf([ + 'set_up_encryption', + 'verify_this_session', + 'upgrade_encryption', + 'upgrade_ssss', + ]).isRequired, }; _onLaterClick = () => { DeviceListener.sharedInstance().dismissEncryptionSetup(); }; + async _waitForCompletion() { + if (this.props.kind === 'upgrade_ssss') { + return new Promise(resolve => { + const recheck = async () => { + const needsUpgrade = await MatrixClientPeg.get().secretStorageKeyNeedsUpgrade(); + if (!needsUpgrade) { + MatrixClientPeg.get().removeListener('accountData', recheck); + resolve(); + } + }; + MatrixClientPeg.get().on('accountData', recheck); + recheck(); + }); + } else { + return; + } + } + _onSetupClick = async () => { - accessSecretStorage(); + const Spinner = sdk.getComponent("elements.Spinner"); + const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner'); + try { + await accessSecretStorage(); + await this._waitForCompletion(); + } finally { + modal.close(); + } }; getDescription() { switch (this.props.kind) { case 'set_up_encryption': case 'upgrade_encryption': + case 'upgrade_ssss': return _t('Verify yourself & others to keep your chats safe'); case 'verify_this_session': return _t('Other users may not trust it'); @@ -49,6 +82,7 @@ export default class SetupEncryptionToast extends React.PureComponent { switch (this.props.kind) { case 'set_up_encryption': case 'upgrade_encryption': + case 'upgrade_ssss': return _t('Upgrade'); case 'verify_this_session': return _t('Verify'); From 8f10223be8fe436396c4c06740ae65a208ca5591 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 24 Mar 2020 16:58:55 +0000 Subject: [PATCH 022/114] Translated using Weblate (Albanian) Currently translated at 99.7% (2220 of 2227 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 | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/sq.json b/src/i18n/strings/sq.json index 0dc35005ce..543b5fadb3 100644 --- a/src/i18n/strings/sq.json +++ b/src/i18n/strings/sq.json @@ -2265,5 +2265,20 @@ "Esc": "Esc", "Enter": "Enter", "Space": "Space", - "End": "End" + "End": "End", + "Manually Verify by Text": "Verifikojeni Dorazi përmes Teksti", + "Interactively verify by Emoji": "Verifikojeni në mënyrë ndërvepruese përmes Emoji-sh", + "Secret Storage key format:": "Format kyçesh Depozite të Fshehtë:", + "outdated": "e vjetruar", + "up to date": "e përditësuar", + "Start a conversation with someone using their name, username (like ) or email address.": "Nisni një bisedë me dikë duke përdorur emrin e tij, emrin e përdoruesit për të (bie fjala, ) ose adresë email.", + "Confirm by comparing the following with the User Settings in your other session:": "Ripohojeni duke krahasuar sa vijon me Rregullimet e Përdoruesit te sesioni juaj tjetër:", + "Confirm this user's session by comparing the following with their User Settings:": "Ripohojeni këtë sesion përdoruesi duke krahasuar sa vijon me Rregullimet e tij të Përdoruesit:", + "If they don't match, the security of your communication may be compromised.": "Nëse s’përputhen, siguria e komunikimeve tuaja mund të jetë komprometuar.", + "Super": "Super", + "Navigate composer history": "Lëvizni nëpër historikun e hartuesit", + "Toggle video on/off": "Aktivizoni/çaktivizoni videon", + "Previous/next unread room or DM": "Dhoma ose MD i palexuar i mëparshëm/pasues", + "Previous/next room or DM": "Dhoma ose MD i mëparshëm/pasues", + "Toggle right panel": "Hap/mbyll panelin djathtas" } From c23aba51c31bc6ff18356d039998900584943b3f Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Wed, 25 Mar 2020 04:19:03 +0000 Subject: [PATCH 023/114] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2227 of 2227 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/zh_Hant.json b/src/i18n/strings/zh_Hant.json index 6c60acc60f..99ed5d843e 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2287,5 +2287,8 @@ "Navigate composer history": "瀏覽編輯區歷史紀錄", "Previous/next unread room or DM": "上一下/下一個未讀聊天室或直接訊息", "Previous/next room or DM": "上一個/下一個聊天室或直接訊息", - "Toggle right panel": "切換右側面板" + "Toggle right panel": "切換右側面板", + "Secret Storage key format:": "秘密儲存金鑰格式:", + "outdated": "太舊了", + "up to date": "已為最新" } From 297abac07231a69babbb35edfb2d1a51a64f2bd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 25 Mar 2020 08:51:51 +0000 Subject: [PATCH 024/114] Translated using Weblate (French) Currently translated at 100.0% (2227 of 2227 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/fr.json b/src/i18n/strings/fr.json index 258d9950b5..941fd209f2 100644 --- a/src/i18n/strings/fr.json +++ b/src/i18n/strings/fr.json @@ -2288,5 +2288,8 @@ "Navigate composer history": "Explorer l’historique du compositeur", "Previous/next unread room or DM": "Salon ou message direct non lu précédent/suivant", "Previous/next room or DM": "Salon ou message direct précédent/suivant", - "Toggle right panel": "Afficher/masquer le panneau de droite" + "Toggle right panel": "Afficher/masquer le panneau de droite", + "Secret Storage key format:": "Format de clé du coffre secret :", + "outdated": "obsolète", + "up to date": "à jour" } From 718eeeade6d46a6c67305cdf328cc47cbab96844 Mon Sep 17 00:00:00 2001 From: Kilian Koeltzsch Date: Tue, 24 Mar 2020 19:48:03 +0000 Subject: [PATCH 025/114] Translated using Weblate (German) Currently translated at 75.4% (1679 of 2227 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 2eb24c29bf..0f19660c2e 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -701,7 +701,7 @@ "Failed to remove tag %(tagName)s from room": "Fehler beim Entfernen des \"%(tagName)s\"-Tags von dem Raum", "Failed to add tag %(tagName)s to room": "Fehler beim Hinzufügen des \"%(tagName)s\"-Tags an dem Raum", "Did you know: you can use communities to filter your Riot.im experience!": "Wusstest du: Du kannst Communities nutzen um deine Riot.im-Erfahrung zu filtern!", - "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.": "Um einen Filter zu setzen, siehe einen Community-Bild auf das Filter-Panel ganz links. Du kannst jederzeit auf einen Avatar im Filter-Panel klicken um nur die Räume und Personen aus der Community zu sehen.", + "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.": "Um einen Filter zu setzen, ziehe ein Community-Bild auf das Filter-Panel ganz links. Du kannst jederzeit auf einen Avatar im Filter-Panel klicken um nur die Räume und Personen aus der Community zu sehen.", "Clear filter": "Filter zurücksetzen", "Key request sent.": "Schlüssel-Anfragen gesendet.", "If you've submitted a bug via GitHub, debug logs can help us track down the problem. 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.": "Wenn du einen Fehler via GitHub gemeldet hast, können Fehlerberichte uns helfen um das Problem zu finden. Sie enthalten Anwendungsdaten wie deinen Nutzernamen, Raum- und Gruppen-ID's und Aliase die du besucht hast und Nutzernamen anderer Nutzer. Sie enthalten keine Nachrichten.", From 19b17db803bc4672d9331b125b325ab3930a7a99 Mon Sep 17 00:00:00 2001 From: roket1428 Date: Tue, 24 Mar 2020 14:57:50 +0000 Subject: [PATCH 026/114] Translated using Weblate (Turkish) Currently translated at 80.1% (1783 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 76 +++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 8f90bfe936..69f23032e5 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -81,7 +81,7 @@ "Direct chats": "Doğrudan Sohbetler", "Disable Notifications": "Bildirimleri Devre Dışı Bırak", "Disinvite": "Daveti İptal Et", - "Displays action": "Görünür eylem", + "Displays action": "Eylemi görüntüler", "Download %(text)s": "%(text)s metnini indir", "Drop File Here": "Dosyayı Buraya Bırak", "Ed25519 fingerprint": "Ed25519 parmak izi", @@ -117,7 +117,7 @@ "Failed to toggle moderator status": "Moderatör durumunu değiştirmek başarısız oldu", "Failed to unban": "Yasağı kaldırmak başarısız oldu", "Failed to upload profile picture!": "Profil resmi yükleme başarısız oldu!", - "Failed to verify email address: make sure you clicked the link in the email": "E-posta adresini doğrulama başarısız : e-postadaki bağlantıya tıkladığınızdan emin olun", + "Failed to verify email address: make sure you clicked the link in the email": "Eposta adresini doğrulamadı: epostadaki bağlantıya tıkladığınızdan emin olun", "Failure to create room": "Oda oluşturulamadı", "Favourite": "Favori", "Favourites": "Favoriler", @@ -249,7 +249,7 @@ "Submit": "Gönder", "Success": "Başarılı", "The phone number entered looks invalid": "Girilen telefon numarası geçersiz görünüyor", - "This email address is already in use": "Bu e-posta adresi zaten kullanımda", + "This email address is already in use": "Bu eposta adresi zaten kullanımda", "This email address was not found": "Bu e-posta adresi bulunamadı", "The email address linked to your account must be entered.": "Hesabınıza bağlı e-posta adresi girilmelidir.", "The remote side failed to pick up": "Uzak taraf toplanamadı(alınamadı)", @@ -541,12 +541,12 @@ "The platform you're on": "Platformunuz", "The version of Riot.im": "Riot.im'in sürümü", "Your language of choice": "Dil seçiminiz", - "Which officially provided instance you are using, if any": "Hangi resmi destekli örneği(eğer varsa) kullanmaktasınız", - "Add Email Address": "E-posta Adresi Ekle", + "Which officially provided instance you are using, if any": "Hangi resmi destekli platformu kullanmaktasınız (eğer varsa)", + "Add Email Address": "Eposta Adresi Ekle", "Add Phone Number": "Telefon Numarası Ekle", "Your identity server's URL": "Kimlik sunucunuzun linki", "e.g. %(exampleValue)s": "örn.%(exampleValue)s", - "Every page you use in the app": "uygulamadaki kullandığınız tüm sayfalar", + "Every page you use in the app": "Uygulamadaki kullandığınız tüm sayfalar", "e.g. ": "örn. ", "Your User Agent": "Kullanıcı Ajanınız", "Your device resolution": "Cihazınızın çözünürlüğü", @@ -643,7 +643,7 @@ "Power level": "Güç düzeyi", "e.g. my-room": "örn. odam", "Some characters not allowed": "Bazı karakterlere izin verilmiyor", - "Matrix ID": "Matrix ID", + "Matrix ID": "Matrix Kimliği", "Matrix Room ID": "Matrix Oda ID", "email address": "e-posta adresi", "That doesn't look like a valid email address": "Geçerli bir e-posta adresi gibi gözükmüyor", @@ -848,8 +848,8 @@ "Create your account": "Hesabınızı oluşturun", "Forgotten your password?": "Parolanızı mı unuttunuz?", "Sign in and regain access to your account.": "Oturum açın ve yeniden hesabınıza ulaşın.", - "Whether or not you're logged in (we don't record your username)": "İster oturum açın yasa açmayın (biz kullanıcı adınızı kaydetmiyoruz)", - "Whether or not you're using the Richtext mode of the Rich Text Editor": "Zengin Metin Düzenleyicinin Zengin metin modunu kullanıyor ya da kullanmıyorsunuz", + "Whether or not you're logged in (we don't record your username)": "İster oturum açın yada açmayın (biz kullanıcı adınızı kaydetmiyoruz)", + "Whether or not you're using the Richtext mode of the Rich Text Editor": "Zengin Metin Düzenleyicisinin Zengin metin modunu kullanıyor ya da kullanmıyorsunuz", "Your homeserver's URL": "Ana sunucunuzun URL’i", "The information being sent to us to help make Riot.im better includes:": "Riot.im i daha iyi yapmamıza yardımcı olacak bize gönderdiğiniz bilgilerin içeriği:", "Try using turn.matrix.org": "turn.matrix.org i kullanarak dene", @@ -1230,7 +1230,7 @@ "Failed to copy": "Kopyalama başarısız", "edited": "düzenlendi", "Message removed by %(userId)s": "Mesaj %(userId)s tarafından silindi", - "You are still sharing your personal data on the identity server .": "Kimlik sunucusu üzerinde hala kişisel veri paylaşımı yapıyorsunuz.", + "You are still sharing your personal data on the identity server .": "Kimlik sunucusu üzerinde hala kişisel veri paylaşımı yapıyorsunuz \n.", "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.": "Kimlik sunucusundan bağlantıyı kesmeden önce telefon numaranızı ve e-posta adreslerinizi silmenizi tavsiye ederiz.", "Set a new account password...": "Yeni bir hesap parolası belirle...", "Deactivating your account is a permanent action - be careful!": "Hesabınızı pasifleştirmek bir kalıcı eylemdir - dikkat edin!", @@ -1363,7 +1363,7 @@ "Members only (since the point in time of selecting this option)": "Sadece üyeler ( bu seçeneği seçtiğinizden itibaren)", "Unable to revoke sharing for email address": "E-posta adresi paylaşımı kaldırılamadı", "Unable to revoke sharing for phone number": "Telefon numarası paylaşımı kaldırılamıyor", - "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Bu sayfadaki oda, kullanıcı veya grup ID si gibi betimleyici bilgiler sunucuya gönderilmeden önce silindi.", + "Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Bu sayfadaki oda, kullanıcı veya grup kimliği gibi betimleyici bilgiler sunucuya gönderilmeden önce silindi.", "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.": "Çağrıların sağlıklı bir şekide yapılabilmesi için lütfen anasunucunuzun (%(homeserverDomain)s) yöneticisinden bir TURN sunucusu yapılandırmasını isteyin.", "%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s kullanıcıları isimlerini %(count)s kez değiştirdiler", "%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s ismini %(count)s kez değiştirdi", @@ -1538,7 +1538,7 @@ "Some sessions for this user are not trusted": "Bu kullanıcı için bazı oturumlar güvenilir değil", "All sessions for this user are trusted": "Bu kullanıcı için tüm oturumlar güvenilir", "The version of Riot": "Riot sürümü", - "Your user agent": "Kullanıcı ajanınız", + "Your user agent": "Kullanıcı aracınız", "If you cancel now, you won't complete verifying the other user.": "Şimdi iptal ederseniz, diğer kullanıcıyı doğrulamayı tamamlamış olmayacaksınız.", "If you cancel now, you won't complete verifying your other session.": "Şimdi iptal ederseniz, diğer oturumu doğrulamış olmayacaksınız.", "Setting up keys": "Anahtarları ayarla", @@ -1758,5 +1758,55 @@ "Whether you're using Riot as an installed Progressive Web App": "Riot'u gelişmiş web uygulaması olarak yükleyip yüklemediğinizi", "The information being sent to us to help make Riot better includes:": "Riot'u geliştirmemizde bize yardım etmek için gönderdiğiniz bilgiler şunları içeriyor:", "A call is currently being placed!": "Bir çağrı şu anda başlatılıyor!", - "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Şu anda dosya ile birlikte mesaj yollamak mümkün değil. Dosyayı mesajsız yüklemek ister misiniz?" + "At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Şu anda dosya ile birlikte mesaj yollamak mümkün değil. Dosyayı mesajsız yüklemek ister misiniz?", + "PM": "24:00", + "AM": "12:00", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.": "Bu eylem, bir e-posta adresini veya telefon numarasını doğrulamak için varsayılan kimlik sunucusuna erişilmesini gerektirir, ancak sunucunun herhangi bir hizmet şartı yoktur.", + "Sends a message as plain text, without interpreting it as markdown": "Mesajı markdown kullanmadan basit metin olarak iletir", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.": "E-posta ile davet etmek için kimlik sunucusu kullan. Varsayılan kimlik sunucusunu (%(defaultIdentityServerName)s) kullanmak için devam edin ya da ayarlardan değiştirin.", + "Unignored user": "Reddedilmemiş kullanıcı", + "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!": "UYARI: ANAHTAR DOĞRULAMASI BAŞARISIZ! %(userld)s'nin/nın %(deviceld)s oturumu için imza anahtarı \"%(fprint)s\" verilen anahtar ile uyumsuz \"%(fingerprint)s\". Bu iletişiminizin engellendiği anlamına gelebilir!", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "Verilen imza anahtarı %(userld)s'nin/nın %(deviceld)s oturumundan gelen anahtar ile uyumlu. Oturum doğrulanmış olarak işaretlendi.", + "Forces the current outbound group session in an encrypted room to be discarded": "Şifreli bir odadaki geçerli giden grup oturumunun atılmasını zorlar", + "%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.": "%(senderDisplayName)s oda ismini %(oldRoomName)s bununla değiştirdi %(newRoomName)s.", + "%(senderDisplayName)s enabled flair for %(groups)s in this room.": "%(senderDisplayName)s bu odada %(groups)s için etiketleri etkinleştirdi.", + "%(senderDisplayName)s disabled flair for %(groups)s in this room.": "%(senderDisplayName)s bu odada %(groups)s için etiketleri devre dışı bıraktı.", + "%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for %(oldGroups)s in this room.": "%(senderDisplayName)s bu odada etiketleri %(newGroups)s için etkinleştirdi ve %(oldGroups)s için devre dışı bıraktı.", + "%(senderName)s added the alternative addresses %(addresses)s for this room.|one": "%(senderName)s bu oda için alternatif adres %(addresses)s ekledi.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|other": "%(senderName)s bu oda için alternatif adresleri %(addresses)s sildi.", + "%(senderName)s removed the alternative addresses %(addresses)s for this room.|one": "%(senderName)s bu oda için alternatif adresi %(addresses)s sildi.", + "%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.": "%(senderName)s, %(targetDisplayName)s'nin odaya katılması için daveti iptal etti.", + "%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s %(glob)s ile eşleşen kullanıcıları banlama kuralını kaldırdı", + "%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s %(glob)s ile eşleşen odaları banlama kuralını kaldırdı", + "%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s %(glob)s ile eşleşen sunucuları banlama kuralını kaldırdı", + "%(senderName)s removed a ban rule matching %(glob)s": "%(senderName)s %(glob)s ile eşleşen banlama kuralını kaldırdı", + "%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s": "%(senderName)s %(glob)s ile eşleşen kullanıcıları banlama kuralını bu sebepten dolayı güncelledi %(reason)s", + "%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s %(glob)s ile eşleşen odaları banlama kuralını bu sebepten dolayı güncelledi %(reason)s", + "%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s %(glob)s ile eşleşen sunucuları banlama kuralını bu sebepten dolayı güncelledi %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s %(oldGlob)s ile eşleşen kullanıcıları banlama kuralını %(newGlob)s ile eşleşen olarak değiştirdi sebebi %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s %(oldGlob)s ile eşleşen odaları banlama kuralını %(newGlob)s ile eşleşen olarak değiştirdi sebebi %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s %(oldGlob)s ile eşleşen sunucuları banlama kuralını %(newGlob)s ile eşleşen olarak değiştirdi sebebi %(reason)s", + "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s %(oldGlob)s ile eşleşen banlama kuralını %(newGlob)s ile eşleşen olarak değiştirdi sebebi %(reason)s", + "%(name)s (%(userId)s) signed in to a new session without verifying it:": "%(name)s (%(userId)s) yeni oturuma doğrulamadan giriş yaptı:", + "Ask this user to verify their session, or manually verify it below.": "Kullanıcıya oturumunu doğrulamasını söyle, ya da aşağıdan doğrula.", + "Manually Verify by Text": "Metin ile Doğrula", + "Interactively verify by Emoji": "Emoji ile etkileşimli olarak doğrula", + "Use a longer keyboard pattern with more turns": "Daha karmaşık ve uzun bir klavye deseni kullan", + "Predictable substitutions like '@' instead of 'a' don't help very much": "Tahmin edilebilir harf değişimleri örneğin 'a' yerine '@' pek yardımcı olmuyor", + "A word by itself is easy to guess": "Kelime zaten kolay tahmin edilir", + "Straight rows of keys are easy to guess": "Aynı klavye satırındaki ardışık tuşlar kolay tahmin edilir", + "Short keyboard patterns are easy to guess": "Kısa klavye desenleri kolay tahmin edilir", + "Show a presence dot next to DMs in the room list": "Oda listesinde DM'lerin yanında varlık noktası göster", + "Support adding custom themes": "Özel tema eklemeyi destekle", + "Enable cross-signing to verify per-user instead of per-session (in development)": "Oturum başına doğrulamak yerine kullanıcı başına doğrulama yapmak için çapraz giriş yapmayı etkinleştir (geliştirmede)", + "Show padlocks on invite only rooms": "Sadece davetle girilen odalarda kilit işareti göster", + "Show read receipts sent by other users": "Diğer kullanıcılar tarafından gönderilen okundu bilgisini göster", + "Show a reminder to enable Secure Message Recovery in encrypted rooms": "Şifrelenmiş odalarda güvenli mesaj kurtarmayı etkinleştirmek için hatırlatıcı göster", + "Enable automatic language detection for syntax highlighting": "Sözdizimi vurgularken otomatik dil algılamayı etkinleştir", + "Show avatars in user and room mentions": "Kullanıcı veya odadan bahsedilirken avatarlarını göster", + "Automatically replace plain text Emoji": "Düz metini otomatik olarak emoji ile değiştir", + "Never send encrypted messages to unverified sessions from this session": "Şifreli mesajları asla bu oturumdaki doğrulanmamış oturumlara iletme", + "Never send encrypted messages to unverified sessions in this room from this session": "Şifreli mesajları asla oturumdaki bu odadaki doğrulanmamış oturumlara iletme", + "Prompt before sending invites to potentially invalid matrix IDs": "Potansiyel olarak geçersiz matrix kimliği olanlara davet gönderirken uyarı ver", + "Show shortcuts to recently viewed rooms above the room list": "Oda listesinin üzerinde en son kullanılan odaları göster" } From f6e9c32c48081be68a6b9bbf5ff3b86a0d56704a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 12:04:09 +0100 Subject: [PATCH 027/114] fall back to non-standard persisted api for Safari --- src/rageshake/submit-rageshake.js | 6 ++++++ src/utils/StorageManager.js | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/rageshake/submit-rageshake.js b/src/rageshake/submit-rageshake.js index 53e9f24788..5a7039a913 100644 --- a/src/rageshake/submit-rageshake.js +++ b/src/rageshake/submit-rageshake.js @@ -119,6 +119,12 @@ export default async function sendBugReport(bugReportEndpoint, opts) { body.append("storageManager_persisted", await navigator.storage.persisted()); } catch (e) {} } + // Safari + if (document.hasStorageAccess) { + try { + body.append("storageManager_persisted", await document.hasStorageAccess()); + } catch (e) {} + } if (navigator.storage && navigator.storage.estimate) { try { const estimate = await navigator.storage.estimate(); diff --git a/src/utils/StorageManager.js b/src/utils/StorageManager.js index 4ed118da8a..175772903d 100644 --- a/src/utils/StorageManager.js +++ b/src/utils/StorageManager.js @@ -48,6 +48,11 @@ export function tryPersistStorage() { navigator.storage.persist().then(persistent => { console.log("StorageManager: Persistent?", persistent); }); + } else if (document.requestStorageAccess) { //Safari + document.requestStorageAccess().then( + () => console.log("StorageManager: Persistent?", true), + () => console.log("StorageManager: Persistent?", false), + ); } else { console.log("StorageManager: Persistence unsupported"); } From 9a5f4d9b22b2dadaddcdca3e0e17acbd969bc9dd Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 12:07:11 +0100 Subject: [PATCH 028/114] fall back, don't do both on FF --- src/rageshake/submit-rageshake.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rageshake/submit-rageshake.js b/src/rageshake/submit-rageshake.js index 5a7039a913..00ef87f89c 100644 --- a/src/rageshake/submit-rageshake.js +++ b/src/rageshake/submit-rageshake.js @@ -118,9 +118,7 @@ export default async function sendBugReport(bugReportEndpoint, opts) { try { body.append("storageManager_persisted", await navigator.storage.persisted()); } catch (e) {} - } - // Safari - if (document.hasStorageAccess) { + } else if (document.hasStorageAccess) { // Safari try { body.append("storageManager_persisted", await document.hasStorageAccess()); } catch (e) {} From 8b2ae3e20ff2eb67ff508afd97e40d24c6a2cb9d Mon Sep 17 00:00:00 2001 From: Zoe Date: Tue, 24 Mar 2020 12:24:12 +0000 Subject: [PATCH 029/114] If cached keys are present in the key backup dialog, use them --- .../keybackup/RestoreKeyBackupDialog.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 8e4a4e1e60..5f6f08453d 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -200,6 +200,24 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { } } + async _restoreWithCachedKey(backupInfo) { + if (!backupInfo) return false; + try { + const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache( + undefined, /* targetRoomId */ + undefined, /* targetSessionId */ + backupInfo + ); + this.setState({ + recoverInfo + }); + return true; + } catch (e) { + console.log("restoreWithCachedKey failed:", e); + return false; + } + } + async _loadBackupStatus() { this.setState({ loading: true, @@ -213,6 +231,15 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { backupKeyStored, }); + const gotCache = await this._restoreWithCachedKey(backupInfo); + if (gotCache) { + console.log("RestoreKeyBackupDialog: found cached backup key"); + this.setState({ + loading: false, + }); + return; + } + // If the backup key is stored, we can proceed directly to restore. if (backupKeyStored) { return this._restoreWithSecretStorage(); From 936a4a00225871442e82a3bb99ff9002f2196316 Mon Sep 17 00:00:00 2001 From: Zoe Date: Wed, 25 Mar 2020 11:47:07 +0000 Subject: [PATCH 030/114] lint --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 5f6f08453d..aecfa9bfd1 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -206,10 +206,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent { const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithCache( undefined, /* targetRoomId */ undefined, /* targetSessionId */ - backupInfo + backupInfo, ); this.setState({ - recoverInfo + recoverInfo, }); return true; } catch (e) { From 792a7b395394f5bffe7d0009b4d9089509a7d03e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 13:07:07 +0100 Subject: [PATCH 031/114] don't throw here --- src/components/structures/auth/SetupEncryptionBody.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/auth/SetupEncryptionBody.js b/src/components/structures/auth/SetupEncryptionBody.js index a59fa08b32..e8c15bd1af 100644 --- a/src/components/structures/auth/SetupEncryptionBody.js +++ b/src/components/structures/auth/SetupEncryptionBody.js @@ -190,7 +190,7 @@ export default class SetupEncryptionBody extends React.Component { const Spinner = sdk.getComponent('views.elements.Spinner'); return ; } else { - throw new Error(`Unknown phase ${phase}`); + console.log(`SetupEncryptionBody: Unknown phase ${phase}`); } } } From 78b167a7ea4f6a055c248c1d3e24c01f71345108 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 13:40:09 +0100 Subject: [PATCH 032/114] fix typo --- src/components/structures/auth/SetupEncryptionBody.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/auth/SetupEncryptionBody.js b/src/components/structures/auth/SetupEncryptionBody.js index e8c15bd1af..c7c73cd616 100644 --- a/src/components/structures/auth/SetupEncryptionBody.js +++ b/src/components/structures/auth/SetupEncryptionBody.js @@ -41,7 +41,7 @@ export default class SetupEncryptionBody extends React.Component { this.state = { phase: store.phase, // this serves dual purpose as the object for the request logic and - // the presence of it insidicating that we're in 'verify mode'. + // the presence of it indicating that we're in 'verify mode'. // Because of the latter, it lives in the state. verificationRequest: store.verificationRequest, backupInfo: store.backupInfo, From 9dd81216fb6678fa48d651c1e1d859ea56b9f5ef Mon Sep 17 00:00:00 2001 From: Zoe Date: Wed, 25 Mar 2020 14:21:17 +0000 Subject: [PATCH 033/114] rename secret storage force-reset variable to avoid confusion --- src/CrossSigningManager.js | 8 ++++---- src/components/views/settings/CrossSigningPanel.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js index 5c254bbd00..f5a6663dfd 100644 --- a/src/CrossSigningManager.js +++ b/src/CrossSigningManager.js @@ -195,19 +195,19 @@ export async function promptForBackupPassphrase() { * * @param {Function} [func] An operation to perform once secret storage has been * bootstrapped. Optional. - * @param {bool} [force] Reset secret storage even if it's already set up + * @param {bool} [forceReset] Reset secret storage even if it's already set up */ -export async function accessSecretStorage(func = async () => { }, force = false) { +export async function accessSecretStorage(func = async () => { }, forceReset = false) { const cli = MatrixClientPeg.get(); secretStorageBeingAccessed = true; try { - if (!await cli.hasSecretStorageKey() || force) { + if (!await cli.hasSecretStorageKey() || forceReset) { // This dialog calls bootstrap itself after guiding the user through // passphrase creation. const { finished } = Modal.createTrackedDialogAsync('Create Secret Storage dialog', '', import("./async-components/views/dialogs/secretstorage/CreateSecretStorageDialog"), { - force, + force: forceReset, }, null, /* priority = */ false, /* static = */ true, ); diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index cf47c797fc..649ca4a950 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -98,12 +98,12 @@ export default class CrossSigningPanel extends React.PureComponent { * 2. Access existing secret storage by requesting passphrase and accessing * cross-signing keys as needed. * 3. All keys are loaded and there's nothing to do. - * @param {bool} [force] Bootstrap again even if keys already present + * @param {bool} [forceReset] Bootstrap again even if keys already present */ - _bootstrapSecureSecretStorage = async (force=false) => { + _bootstrapSecureSecretStorage = async (forceReset=false) => { this.setState({ error: null }); try { - await accessSecretStorage(() => undefined, force); + await accessSecretStorage(() => undefined, forceReset); } catch (e) { this.setState({ error: e }); console.error("Error bootstrapping secret storage", e); From 2b600e74c4d092e34f5de3348bc7ab946dacc506 Mon Sep 17 00:00:00 2001 From: catborise Date: Wed, 25 Mar 2020 13:23:47 +0000 Subject: [PATCH 034/114] Translated using Weblate (Turkish) Currently translated at 80.9% (1801 of 2227 strings) Translation: Riot Web/matrix-react-sdk Translate-URL: https://translate.riot.im/projects/riot-web/matrix-react-sdk/tr/ --- src/i18n/strings/tr.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/tr.json b/src/i18n/strings/tr.json index 69f23032e5..7b5a0ac3ec 100644 --- a/src/i18n/strings/tr.json +++ b/src/i18n/strings/tr.json @@ -1808,5 +1808,23 @@ "Never send encrypted messages to unverified sessions from this session": "Şifreli mesajları asla bu oturumdaki doğrulanmamış oturumlara iletme", "Never send encrypted messages to unverified sessions in this room from this session": "Şifreli mesajları asla oturumdaki bu odadaki doğrulanmamış oturumlara iletme", "Prompt before sending invites to potentially invalid matrix IDs": "Potansiyel olarak geçersiz matrix kimliği olanlara davet gönderirken uyarı ver", - "Show shortcuts to recently viewed rooms above the room list": "Oda listesinin üzerinde en son kullanılan odaları göster" + "Show shortcuts to recently viewed rooms above the room list": "Oda listesinin üzerinde en son kullanılan odaları göster", + "Secret Storage key format:": "Sır Depolama anahtar biçemi:", + "Error downloading theme information.": "Tema bilgisi indirilirken hata.", + "Theme added!": "Tema eklendi!", + "Add theme": "Tema ekle", + "Keyboard Shortcuts": "Klavye Kısayolları", + "%(count)s unread messages including mentions.|other": "anmalar dahil okunmayan %(count)s mesaj.", + "Local address": "Yerel adres", + "Local Addresses": "Yerel Adresler", + "Yours, or the other users’ session": "Sizin yada diğer kullanıcıların oturumları", + "Trusted": "Güvenilir", + "Not trusted": "Güvenilir değil", + "Hide sessions": "Oturumları gizle", + "Verify by scanning": "Taramayla doğrula", + "Verify by emoji": "Emojiyle doğrula", + "Verify by comparing unique emoji.": "Eşsiz emoji eşleştirme ile doğrulama.", + "Edited at %(date)s. Click to view edits.": "%(date)s tarihinde düzenlendi. Düzenlemeleri görmek için tıkla.", + "Failed to load group members": "Grup üyeleri yüklenirken başarısız", + "Visibility in Room List": "Oda Listesindeki Görünürlük" } From 4d63c11f260f78e93d01f025d55ae028a2897cd8 Mon Sep 17 00:00:00 2001 From: Zoe Date: Wed, 25 Mar 2020 14:06:47 +0000 Subject: [PATCH 035/114] Respond to backup key sharing requests --- src/CrossSigningManager.js | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js index 097464ee43..5def8d9fd5 100644 --- a/src/CrossSigningManager.js +++ b/src/CrossSigningManager.js @@ -145,18 +145,33 @@ const onSecretRequested = async function({ console.log(`CrossSigningManager: Ignoring request from untrusted device ${deviceId}`); return; } - const callbacks = client.getCrossSigningCacheCallbacks(); - if (!callbacks.getCrossSigningKeyCache) return; - if (name === "m.cross_signing.self_signing") { - const key = await callbacks.getCrossSigningKeyCache("self_signing"); - if (!key) { - console.log(`self_signing requested by ${deviceId}, but not found in cache`); + if (name.startsWith("m.cross_signing")) { + const callbacks = client.getCrossSigningCacheCallbacks(); + if (!callbacks.getCrossSigningKeyCache) return; + /* Explicit enumeration here is deliberate – never share the master key! */ + if (name === "m.cross_signing.self_signing") { + const key = await callbacks.getCrossSigningKeyCache("self_signing"); + if (!key) { + console.log( + `self_signing requested by ${deviceId}, but not found in cache` + ); + } + return key && encodeBase64(key); + } else if (name === "m.cross_signing.user_signing") { + const key = await callbacks.getCrossSigningKeyCache("user_signing"); + if (!key) { + console.log( + `user_signing requested by ${deviceId}, but not found in cache` + ); + } + return key && encodeBase64(key); } - return key && encodeBase64(key); - } else if (name === "m.cross_signing.user_signing") { - const key = await callbacks.getCrossSigningKeyCache("user_signing"); + } else if (name === "m.megolm_backup.v1") { + const key = await client._crypto.getSessionBackupPrivateKey(); if (!key) { - console.log(`user_signing requested by ${deviceId}, but not found in cache`); + console.log( + `session backup key requested by ${deviceId}, but not found in cache` + ); } return key && encodeBase64(key); } From f891f3e9fa70ffc83fb679d5364d5dfc909ad42b Mon Sep 17 00:00:00 2001 From: Zoe Date: Wed, 25 Mar 2020 16:08:26 +0000 Subject: [PATCH 036/114] lint --- src/CrossSigningManager.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CrossSigningManager.js b/src/CrossSigningManager.js index 5def8d9fd5..29eb3cb8be 100644 --- a/src/CrossSigningManager.js +++ b/src/CrossSigningManager.js @@ -153,7 +153,7 @@ const onSecretRequested = async function({ const key = await callbacks.getCrossSigningKeyCache("self_signing"); if (!key) { console.log( - `self_signing requested by ${deviceId}, but not found in cache` + `self_signing requested by ${deviceId}, but not found in cache`, ); } return key && encodeBase64(key); @@ -161,7 +161,7 @@ const onSecretRequested = async function({ const key = await callbacks.getCrossSigningKeyCache("user_signing"); if (!key) { console.log( - `user_signing requested by ${deviceId}, but not found in cache` + `user_signing requested by ${deviceId}, but not found in cache`, ); } return key && encodeBase64(key); @@ -170,7 +170,7 @@ const onSecretRequested = async function({ const key = await client._crypto.getSessionBackupPrivateKey(); if (!key) { console.log( - `session backup key requested by ${deviceId}, but not found in cache` + `session backup key requested by ${deviceId}, but not found in cache`, ); } return key && encodeBase64(key); From 0097134ade4c3953d57fa64e33c70d3b45d92c5f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 17:56:35 +0000 Subject: [PATCH 037/114] Update src/utils/StorageManager.js Co-Authored-By: J. Ryan Stinnett --- src/utils/StorageManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/StorageManager.js b/src/utils/StorageManager.js index 175772903d..e29b6d9b0e 100644 --- a/src/utils/StorageManager.js +++ b/src/utils/StorageManager.js @@ -48,7 +48,7 @@ export function tryPersistStorage() { navigator.storage.persist().then(persistent => { console.log("StorageManager: Persistent?", persistent); }); - } else if (document.requestStorageAccess) { //Safari + } else if (document.requestStorageAccess) { // Safari document.requestStorageAccess().then( () => console.log("StorageManager: Persistent?", true), () => console.log("StorageManager: Persistent?", false), From 0fb4c51b2c55f30391b6a6c1c9107ebe37653cd9 Mon Sep 17 00:00:00 2001 From: Max Klenk Date: Wed, 25 Mar 2020 17:17:09 +0000 Subject: [PATCH 038/114] Translated using Weblate (German) Currently translated at 75.5% (1685 of 2231 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 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 0f19660c2e..306a1a1f69 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1733,5 +1733,9 @@ "%(senderName)s created a rule banning users matching %(glob)s for %(reason)s": "%(senderName)s erstellte eine Ausschluss-Regel für Nutzer, die wegen %(reason)s %(glob)s entspricht", "%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s": "%(senderName)s erstellte eine Ausschluss-Regel für Räume, die wegen %(reason)s %(glob)s entspricht", "%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s": "%(senderName)s erstellte eine Ausschluss-Regel für Server, die aufgrund von %(reason)s %(glob)s entsprechen", - "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s erstellt eine Ausschluss-Regel, die aufgrund von %(reason)s %(glob)s entsprechen" + "%(senderName)s created a ban rule matching %(glob)s for %(reason)s": "%(senderName)s erstellt eine Ausschluss-Regel, die aufgrund von %(reason)s %(glob)s entsprechen", + "Do you want to chat with %(user)s?": "Möchtest du mit %(user)s chatten?", + " wants to chat": " möchte mit dir chatten", + "Start chatting": "Chat starten", + "Reject & Ignore user": "Ablehnen & Nutzer ignorieren" } From 2c1fbfb69c88506ed804606c679d55a49528748e Mon Sep 17 00:00:00 2001 From: "@a2sc:matrix.org" Date: Wed, 25 Mar 2020 17:25:02 +0000 Subject: [PATCH 039/114] Translated using Weblate (German) Currently translated at 75.5% (1685 of 2231 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 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/de_DE.json b/src/i18n/strings/de_DE.json index 306a1a1f69..9565299b8d 100644 --- a/src/i18n/strings/de_DE.json +++ b/src/i18n/strings/de_DE.json @@ -1737,5 +1737,7 @@ "Do you want to chat with %(user)s?": "Möchtest du mit %(user)s chatten?", " wants to chat": " möchte mit dir chatten", "Start chatting": "Chat starten", - "Reject & Ignore user": "Ablehnen & Nutzer ignorieren" + "Reject & Ignore user": "Ablehnen & Nutzer ignorieren", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ändert eine Ausschluss-Regel von %(oldGlob)s nach %(newGlob)s, wegen %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s": "%(senderName)s ändert eine Ausschluss-Regel für Räume von %(oldGlob)s nach %(newGlob)s, wegen %(reason)s" } From 25e7bde7bcfb1fbacd1b3a489cac20171a4ff116 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 25 Mar 2020 18:38:12 +0000 Subject: [PATCH 040/114] Add a flag to control whether cross-signing signatures are trusted Fixes: https://github.com/vector-im/riot-web/issues/12616 --- res/css/_components.scss | 1 + src/MatrixClientPeg.js | 3 +++ src/components/views/rooms/MemberTile.js | 7 +++++++ .../settings/tabs/user/SecurityUserSettingsTab.js | 3 +++ src/i18n/strings/en_EN.json | 2 ++ src/settings/Settings.js | 11 +++++++++++ 6 files changed, 27 insertions(+) diff --git a/res/css/_components.scss b/res/css/_components.scss index 6890a1ffd1..b959b1f1cd 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -186,6 +186,7 @@ @import "./views/settings/_AvatarSetting.scss"; @import "./views/settings/_CrossSigningPanel.scss"; @import "./views/settings/_DevicesPanel.scss"; +@import "./views/settings/_E2eAdvancedPanel.scss"; @import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_IntegrationManager.scss"; @import "./views/settings/_KeyBackupPanel.scss"; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 98fcc85d60..21f05b9759 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -148,6 +148,9 @@ class _MatrixClientPeg { // check that we have a version of the js-sdk which includes initCrypto if (!SettingsStore.getValue("lowBandwidth") && this.matrixClient.initCrypto) { await this.matrixClient.initCrypto(); + this.matrixClient.setCryptoTrustCrossSignedDevices( + !SettingsStore.getValue('e2ee.manuallyVerifyAllSessions'), + ); StorageManager.setCryptoInitialised(true); } } catch (e) { diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 1f1d8389b1..a0e900b5fc 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -65,6 +65,7 @@ export default createReactClass({ }); if (isRoomEncrypted) { cli.on("userTrustStatusChanged", this.onUserTrustStatusChanged); + cli.on("deviceVerificationChanged", this.onDeviceVerificationChanged); this.updateE2EStatus(); } else { // Listen for room to become encrypted @@ -88,6 +89,7 @@ export default createReactClass({ if (cli) { cli.removeListener("RoomState.events", this.onRoomStateEvents); cli.removeListener("userTrustStatusChanged", this.onUserTrustStatusChanged); + cli.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); } }, @@ -110,6 +112,11 @@ export default createReactClass({ this.updateE2EStatus(); }, + onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) { + if (userId !== this.props.member.userId) return; + this.updateE2EStatus(); + }, + updateE2EStatus: async function() { const cli = MatrixClientPeg.get(); const { userId } = this.props.member; diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js index 2e35a6bf6f..3dca6e2490 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.js @@ -281,6 +281,8 @@ export default class SecurityUserSettingsTab extends React.Component { ); } + const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel'); + return (
{_t("Security & Privacy")}
@@ -311,6 +313,7 @@ export default class SecurityUserSettingsTab extends React.Component {
{this._renderIgnoredUsers()} {this._renderManageInvites()} + ); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 57b39309b0..3602134f08 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -432,6 +432,7 @@ "Enable message search in encrypted rooms": "Enable message search in encrypted rooms", "Keep secret storage passphrase in memory for this session": "Keep secret storage 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", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading report": "Uploading report", @@ -598,6 +599,7 @@ "Public Name": "Public Name", "Last seen": "Last seen", "Failed to set display name": "Failed to set display name", + "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.", "Disable Notifications": "Disable Notifications", "Enable Notifications": "Enable Notifications", "Securely cache encrypted messages locally for them to appear in search results, using ": "Securely cache encrypted messages locally for them to appear in search results, using ", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 461761dfa2..0d72017878 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -16,6 +16,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import {MatrixClient} from 'matrix-js-sdk'; + import {_td} from '../languageHandler'; import { AudioNotificationsEnabledController, @@ -24,6 +26,7 @@ import { } from "./controllers/NotificationControllers"; import CustomStatusController from "./controllers/CustomStatusController"; import ThemeController from './controllers/ThemeController'; +import PushToMatrixClientController from './controllers/PushToMatrixClientController'; import ReloadOnChangeController from "./controllers/ReloadOnChangeController"; import {RIGHT_PANEL_PHASES} from "../stores/RightPanelStorePhases"; @@ -525,4 +528,12 @@ export const SETTINGS = { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: true, }, + "e2ee.manuallyVerifyAllSessions": { + supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS, + displayName: _td("Manually verify all remote sessions"), + default: false, + controller: new PushToMatrixClientController( + MatrixClient.prototype.setCryptoTrustCrossSignedDevices, true, + ), + }, }; From 327b3c860b4f0c622654f83e08454a75a4d5e195 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 25 Mar 2020 18:47:57 +0000 Subject: [PATCH 041/114] Adding the files would help --- res/css/views/settings/_E2eAdvancedPanel.scss | 20 ++++++++++ .../views/settings/E2eAdvancedPanel.js | 39 +++++++++++++++++++ .../PushToMatrixClientController.js | 37 ++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 res/css/views/settings/_E2eAdvancedPanel.scss create mode 100644 src/components/views/settings/E2eAdvancedPanel.js create mode 100644 src/settings/controllers/PushToMatrixClientController.js diff --git a/res/css/views/settings/_E2eAdvancedPanel.scss b/res/css/views/settings/_E2eAdvancedPanel.scss new file mode 100644 index 0000000000..9e32685d12 --- /dev/null +++ b/res/css/views/settings/_E2eAdvancedPanel.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_E2eAdvancedPanel_settingLongDescription { + margin-right: 150px; +} + diff --git a/src/components/views/settings/E2eAdvancedPanel.js b/src/components/views/settings/E2eAdvancedPanel.js new file mode 100644 index 0000000000..709465bcb0 --- /dev/null +++ b/src/components/views/settings/E2eAdvancedPanel.js @@ -0,0 +1,39 @@ +/* +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 * as sdk from '../../../index'; +import {_t} from "../../../languageHandler"; +import {SettingLevel} from "../../../settings/SettingsStore"; + +const SETTING_MANUALLY_VERIFY_ALL_SESSIONS = "e2ee.manuallyVerifyAllSessions"; + +const E2eAdvancedPanel = props => { + const SettingsFlag = sdk.getComponent('views.elements.SettingsFlag'); + return
+ {_t("Advanced")} + + +
{_t( + "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.", + )}
+
; +}; + +export default E2eAdvancedPanel; diff --git a/src/settings/controllers/PushToMatrixClientController.js b/src/settings/controllers/PushToMatrixClientController.js new file mode 100644 index 0000000000..b7c285227f --- /dev/null +++ b/src/settings/controllers/PushToMatrixClientController.js @@ -0,0 +1,37 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { MatrixClientPeg } from '../../MatrixClientPeg'; + +/** + * When the value changes, call a setter function on the matrix client with the new value + */ +export default class PushToMatrixClientController { + constructor(setter, inverse) { + this._setter = setter; + this._inverse = inverse; + } + + getValueOverride(level, roomId, calculatedValue, calculatedAtLevel) { + return null; // no override + } + + onChange(level, roomId, newValue) { + // XXX does this work? This surely isn't necessarily the effective value, + // but it's what NotificationsEnabledController does... + this._setter.call(MatrixClientPeg.get(), this._inverse ? !newValue : newValue); + } +} From d098c91c3043d319a89ce047e9439cc92f05695c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 25 Mar 2020 22:38:11 +0000 Subject: [PATCH 042/114] Update cross-signing verification copy and fix i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/right_panel/EncryptionInfo.js | 22 +++++++++++++++---- .../views/right_panel/EncryptionPanel.js | 5 ++++- src/components/views/right_panel/UserInfo.js | 5 ++--- .../views/right_panel/VerificationPanel.js | 16 ++++++++++---- src/i18n/strings/en_EN.json | 8 ++++++- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/components/views/right_panel/EncryptionInfo.js b/src/components/views/right_panel/EncryptionInfo.js index bbedc9b303..610ea99511 100644 --- a/src/components/views/right_panel/EncryptionInfo.js +++ b/src/components/views/right_panel/EncryptionInfo.js @@ -28,7 +28,7 @@ export const PendingActionSpinner = ({text}) => { ; }; -const EncryptionInfo = ({waitingForOtherParty, waitingForNetwork, member, onStartVerification}) => { +const EncryptionInfo = ({waitingForOtherParty, waitingForNetwork, member, onStartVerification, isRoomEncrypted}) => { let content; if (waitingForOtherParty || waitingForNetwork) { let text; @@ -49,13 +49,27 @@ const EncryptionInfo = ({waitingForOtherParty, waitingForNetwork, member, onStar ); } - return -
-

{_t("Encryption")}

+ let description; + if (isRoomEncrypted) { + description = (

{_t("Messages in this room are end-to-end encrypted.")}

{_t("Your messages are secured and only you and the recipient have the unique keys to unlock them.")}

+ ); + } else { + description = ( +
+

{_t("Messages in this room are not end-to-end encrypted.")}

+

{_t("In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.")}

+
+ ); + } + + return +
+

{_t("Encryption")}

+ { description }

{_t("Verify User")}

diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index a14d4a2b7d..3a4aa2edc3 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -30,7 +30,8 @@ import {_t} from "../../../languageHandler"; // cancellation codes which constitute a key mismatch const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"]; -const EncryptionPanel = ({verificationRequest, verificationRequestPromise, member, onClose, layout}) => { +const EncryptionPanel = (props) => { + const {verificationRequest, verificationRequestPromise, member, onClose, layout, isRoomEncrypted} = props; const [request, setRequest] = useState(verificationRequest); // state to show a spinner immediately after clicking "start verification", // before we have a request @@ -98,6 +99,7 @@ const EncryptionPanel = ({verificationRequest, verificationRequestPromise, membe if (!request || requested) { const initiatedByMe = (!request && isRequesting) || (request && request.initiatedByMe); return { const userVerified = userTrust.isCrossSigningVerified(); const isMe = member.userId === cli.getUserId(); const canVerify = SettingsStore.isFeatureEnabled("feature_cross_signing") && - homeserverSupportsCrossSigning && - isRoomEncrypted && !userVerified && !isMe; + homeserverSupportsCrossSigning && !userVerified && !isMe; const setUpdating = (updating) => { setPendingUpdateCount(count => count + (updating ? 1 : -1)); @@ -1496,7 +1495,7 @@ const UserInfo = ({user, groupId, roomId, onClose, phase=RIGHT_PANEL_PHASES.Room case RIGHT_PANEL_PHASES.EncryptionPanel: classes.push("mx_UserInfo_smallAvatar"); content = ( - + ); break; } diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index 38ee31c8b7..a5cb796531 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -48,6 +48,7 @@ export default class VerificationPanel extends React.PureComponent { PHASE_DONE, ]).isRequired, onClose: PropTypes.func.isRequired, + isRoomEncrypted: PropTypes.bool, }; constructor(props) { @@ -174,15 +175,22 @@ export default class VerificationPanel extends React.PureComponent { renderVerifiedPhase() { const {member} = this.props; + let text; + if (this.props.isRoomEncrypted) { + text = _t("Verify all users in a room to ensure it's secure."); + } else { + text = _t("In encrypted rooms, verify all users in a room to ensure it’s secure."); + } + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); return (
-

Verified

+

{_t("Verified")}

{_t("You've successfully verified %(displayName)s!", { displayName: member.displayName || member.name || member.userId, })}

-

Verify all users in a room to ensure it's secure.

+

{ text }

{_t("Got it")} @@ -209,7 +217,7 @@ export default class VerificationPanel extends React.PureComponent { return (
-

Verification cancelled

+

{_t("Verification cancelled")}

{ text }

@@ -231,7 +239,7 @@ export default class VerificationPanel extends React.PureComponent { if (this.state.sasEvent) { const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas'); return
-

Compare emoji

+

{_t("Compare emoji")}

%(role)s in %(roomName)s": "%(role)s in %(roomName)s", "This client does not support end-to-end encryption.": "This client does not support end-to-end encryption.", - "Messages in this room are not end-to-end encrypted.": "Messages in this room are not end-to-end encrypted.", "Security": "Security", "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what Riot supports. Try with a different client.", "Verify by scanning": "Verify by scanning", @@ -1233,11 +1234,16 @@ "Verify by emoji": "Verify by emoji", "If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.", "Verify by comparing unique emoji.": "Verify by comparing unique emoji.", + "Verify all users in a room to ensure it's secure.": "Verify all users in a room to ensure it's secure.", + "In encrypted rooms, verify all users in a room to ensure it’s secure.": "In encrypted rooms, verify all users in a room to ensure it’s secure.", + "Verified": "Verified", "You've successfully verified %(displayName)s!": "You've successfully verified %(displayName)s!", "Got it": "Got it", "Verification timed out. Start verification again from their profile.": "Verification timed out. Start verification again from their profile.", "%(displayName)s cancelled verification. Start verification again from their profile.": "%(displayName)s cancelled verification. Start verification again from their profile.", "You cancelled verification. Start verification again from their profile.": "You cancelled verification. Start verification again from their profile.", + "Verification cancelled": "Verification cancelled", + "Compare emoji": "Compare emoji", "Sunday": "Sunday", "Monday": "Monday", "Tuesday": "Tuesday", From be3167c87042e65ae38aadd11655031670f3ea7c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 25 Mar 2020 22:44:09 +0000 Subject: [PATCH 043/114] always show Devices section Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/right_panel/UserInfo.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 3a415fa8a7..34d0047892 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1319,20 +1319,15 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => { ); } - let devicesSection; - if (isRoomEncrypted) { - devicesSection = ; - } - const securitySection = (

{ _t("Security") }

{ text }

{ verifyButton } - { devicesSection } +
); From 7ca6a4b7770e1b2940d60791cceff8432a851c8e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 25 Mar 2020 23:00:46 +0000 Subject: [PATCH 044/114] Iterate copy Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/right_panel/VerificationPanel.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index a5cb796531..1cb2737005 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -179,7 +179,7 @@ export default class VerificationPanel extends React.PureComponent { if (this.props.isRoomEncrypted) { text = _t("Verify all users in a room to ensure it's secure."); } else { - text = _t("In encrypted rooms, verify all users in a room to ensure it’s secure."); + text = _t("In encrypted rooms, verify all users to ensure it’s secure."); } const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7dec09d30a..ef6dc6d821 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1235,7 +1235,7 @@ "If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.", "Verify by comparing unique emoji.": "Verify by comparing unique emoji.", "Verify all users in a room to ensure it's secure.": "Verify all users in a room to ensure it's secure.", - "In encrypted rooms, verify all users in a room to ensure it’s secure.": "In encrypted rooms, verify all users in a room to ensure it’s secure.", + "In encrypted rooms, verify all users to ensure it’s secure.": "In encrypted rooms, verify all users to ensure it’s secure.", "Verified": "Verified", "You've successfully verified %(displayName)s!": "You've successfully verified %(displayName)s!", "Got it": "Got it", From c41b39488b46ac772d3b0c0ba5c9c3b060e495e6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 25 Mar 2020 23:35:36 +0000 Subject: [PATCH 045/114] Automatically redirect to SSO when the user clicks through to Login Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/auth/Login.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index bfabc34a62..36df08627e 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -53,6 +53,10 @@ _td("Invalid base_url for m.identity_server"); _td("Identity server URL does not appear to be a valid identity server"); _td("General failure"); +const M_LOGIN_CAS = "m.login.cas"; +const M_LOGIN_SSO = "m.login.sso"; +const SSO_FLOWS = [M_LOGIN_SSO, M_LOGIN_CAS]; + /** * A wire component which glues together login UI components and Login logic */ @@ -122,11 +126,11 @@ export default createReactClass({ 'm.login.password': this._renderPasswordStep, // CAS and SSO are the same thing, modulo the url we link to - 'm.login.cas': () => this._renderSsoStep("cas"), - 'm.login.sso': () => this._renderSsoStep("sso"), + [M_LOGIN_CAS]: () => this._renderSsoStep("cas"), + [M_LOGIN_SSO]: () => this._renderSsoStep("sso"), }; - this._initLoginLogic(); + this._initLoginLogic(true); }, componentWillUnmount: function() { @@ -138,7 +142,7 @@ export default createReactClass({ newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return; // Ensure that we end up actually logging in to the right place - this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); + this._initLoginLogic(false, newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); }, onPasswordLoginError: function(errorText) { @@ -342,12 +346,12 @@ export default createReactClass({ onTryRegisterClick: function(ev) { const step = this._getCurrentFlowStep(); - if (step === 'm.login.sso' || step === 'm.login.cas') { + if (SSO_FLOWS.includes(step)) { // If we're showing SSO it means that registration is also probably disabled, // so intercept the click and instead pretend the user clicked 'Sign in with SSO'. ev.preventDefault(); ev.stopPropagation(); - const ssoKind = step === 'm.login.sso' ? 'sso' : 'cas'; + const ssoKind = step === M_LOGIN_SSO ? 'sso' : 'cas'; PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind); } else { // Don't intercept - just go through to the register page @@ -369,7 +373,7 @@ export default createReactClass({ }); }, - _initLoginLogic: async function(hsUrl, isUrl) { + _initLoginLogic: async function(initial, hsUrl, isUrl) { hsUrl = hsUrl || this.props.serverConfig.hsUrl; isUrl = isUrl || this.props.serverConfig.isUrl; @@ -429,6 +433,13 @@ export default createReactClass({ continue; } + // if this is the initial render and the flow we choose is SSO/CAS then go to it automatically + // we do not do this when the user has changed to the server manually as that may be jarring. + if (initial && SSO_FLOWS.includes(flows[i].type)) { + const tmpCli = this._loginLogic.createTemporaryClient(); + PlatformPeg.get().startSingleSignOn(tmpCli, flows[i].type === M_LOGIN_SSO ? "sso": "cas"); + } + // we just pick the first flow where we support all the // steps. (we don't have a UI for multiple logins so let's skip // that for now). From f448f456e1f2f56eb461026e6c9da499237b825f Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Thu, 26 Mar 2020 02:37:38 +0000 Subject: [PATCH 046/114] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (2231 of 2231 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 99ed5d843e..eb4f94172e 100644 --- a/src/i18n/strings/zh_Hant.json +++ b/src/i18n/strings/zh_Hant.json @@ -2290,5 +2290,9 @@ "Toggle right panel": "切換右側面板", "Secret Storage key format:": "秘密儲存金鑰格式:", "outdated": "太舊了", - "up to date": "已為最新" + "up to date": "已為最新", + "Self signing private key:": "自行簽章私鑰:", + "cached locally": "本機快取", + "not found locally": "在本機找不到", + "User signing private key:": "使用者簽章私鑰:" } From 7d924308319fdb135aefd47d2748aeda330521f1 Mon Sep 17 00:00:00 2001 From: Tirifto Date: Wed, 25 Mar 2020 21:52:01 +0000 Subject: [PATCH 047/114] Translated using Weblate (Esperanto) Currently translated at 99.8% (2227 of 2231 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 | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/eo.json b/src/i18n/strings/eo.json index 4849862f99..f4bb6ec244 100644 --- a/src/i18n/strings/eo.json +++ b/src/i18n/strings/eo.json @@ -111,7 +111,7 @@ "Someone": "Iu", "(not supported by this browser)": "(nesubtenata de tiu ĉi foliumilo)", "%(senderName)s answered the call.": "%(senderName)s akceptis la vokon.", - "(could not connect media)": "(aŭdvidaĵoj ne kunigeblis)", + "(could not connect media)": "(ne povis kunigi aŭdovidaĵojn)", "(no answer)": "(sen respondo)", "(unknown failure: %(reason)s)": "(nekonata eraro: %(reason)s)", "%(senderName)s ended the call.": "%(senderName)s finis la vokon.", @@ -2210,5 +2210,69 @@ "Matrix rooms": "Ĉambroj de Matrix", "Open an existing session & use it to verify this one, granting it access to encrypted messages.": "Malfermi jaman salutaĵon kaj kontroli ĉi tiun per ĝi, permesante al ĝi aliron al ĉifritaj mesaĝoj.", "Waiting…": "Atendante…", - "If you can’t access one, ": "Se vi ne povas iun atingi, " + "If you can’t access one, ": "Se vi ne povas iun atingi, ", + "Manually Verify by Text": "Permane kontroli tekste", + "Interactively verify by Emoji": "Interage kontroli bildosigne", + "Self signing private key:": "Memsubskriba privata ŝlosilo", + "cached locally": "kaŝmemorita loke", + "not found locally": "ne trovita loke", + "User signing private key:": "Uzantosubskriba privata ŝlosilo:", + "Secret Storage key format:": "Ŝlosila formo de sekreta deponejo:", + "outdated": "eksdata", + "up to date": "ĝisdata", + "Keyboard Shortcuts": "Klavkombinoj", + "Start a conversation with someone using their name, username (like ) or email address.": "Komencu interparolon kun iu per ĝia nomo, uzantonomo (kiel ), aŭ retpoŝtadreso.", + "a new master key signature": "nova ĉefŝlosila subskribo", + "a new cross-signing key signature": "nova transire subskriba ŝlosila subskribo", + "a device cross-signing signature": "aparata transire subskriba ŝlosila subskribo", + "a key signature": "ŝlosila subskribo", + "Riot encountered an error during upload of:": "Riot eraris dum alŝuto de:", + "Upload completed": "Alŝuto finiĝis", + "Cancelled signature upload": "Alŝuto de subskribo nuliĝis", + "Unabled to upload": "Ne povas alŝuti", + "Signature upload success": "Alŝuto de subskribo sukcesis", + "Signature upload failed": "Alŝuto de subskribo malsukcesis", + "Confirm by comparing the following with the User Settings in your other session:": "Konfirmu per komparo de la sekva kun la agardoj de uzanto en via alia salutaĵo:", + "Confirm this user's session by comparing the following with their User Settings:": "Konfirmu la salutaĵon de ĉi tiu uzanto per komparo de la sekva kun ĝiaj agordoj de uzanto", + "If they don't match, the security of your communication may be compromised.": "Se ili ne akordas, la sekureco de via komunikado eble estas rompita.", + "Navigation": "Navigado", + "Calls": "Vokoj", + "Room List": "Listo de ĉambroj", + "Autocomplete": "Memkompletigo", + "Alt": "Alt-klavo", + "Alt Gr": "Alt-Gr-klavo", + "Shift": "Majuskliga klavo", + "Super": "Super-klavo", + "Ctrl": "Stir-klavo", + "Toggle Bold": "Ŝalti grason", + "Toggle Italics": "Ŝalti kursivon", + "Toggle Quote": "Ŝalti citaĵon", + "New line": "Nova linio", + "Navigate recent messages to edit": "Navigi freŝajn mesaĝojn redaktotajn", + "Jump to start/end of the composer": "Salti al komenco/fino de la komponilo", + "Navigate composer history": "Navigi historion de la komponilo", + "Toggle microphone mute": "Baskuligi silentigon de mikrofono", + "Toggle video on/off": "Baskuligi filmojn", + "Jump to room search": "Salti al serĉo de ĉambroj", + "Navigate up/down in the room list": "Navigi supren/malsupren en la listo de ĉambroj", + "Select room from the room list": "Elekti ĉambron el la listo de ĉambroj", + "Collapse room list section": "Maletendi parton kun listo de ĉambroj", + "Expand room list section": "Etendi parton kun listo de ĉambroj", + "Clear room list filter field": "Vakigi filtrilon de la listo de ĉambroj", + "Scroll up/down in the timeline": "Rulumi supren/suben en la historio", + "Previous/next unread room or DM": "Antaŭa/sekva nelegita ĉambro", + "Previous/next room or DM": "Antaŭa/sekva ĉambro", + "Toggle the top left menu": "Baskuligi la supran maldekstran menuon", + "Close dialog or context menu": "Fermi interagujon aŭ kuntekstan menuon", + "Activate selected button": "Aktivigi la elektitan butonon", + "Toggle right panel": "Baskuligi la dekstran panelon", + "Toggle this dialog": "Baskuligi ĉi tiun interagujon", + "Move autocomplete selection up/down": "Movi memkompletigan elekton supren/suben", + "Cancel autocomplete": "Nuligi memkompletigon", + "Page Up": "Paĝosupren-klavo", + "Page Down": "Paĝosuben-klavo", + "Esc": "Eskapa klavo", + "Enter": "Eniga klavo", + "Space": "Spaco", + "End": "Finen-klavo" } From 883b0afea0c3dbc5788582ba83570b44cc0bfedf Mon Sep 17 00:00:00 2001 From: Szimszon Date: Wed, 25 Mar 2020 19:42:58 +0000 Subject: [PATCH 048/114] Translated using Weblate (Hungarian) Currently translated at 97.1% (2167 of 2231 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 | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/i18n/strings/hu.json b/src/i18n/strings/hu.json index d1e7aeebe7..7e78c367f7 100644 --- a/src/i18n/strings/hu.json +++ b/src/i18n/strings/hu.json @@ -2199,5 +2199,32 @@ "Error downloading theme information.": "A téma információk letöltése sikertelen.", "Theme added!": "Téma hozzáadva!", "Custom theme URL": "Egyedi téma URL", - "Add theme": "Téma hozzáadása" + "Add theme": "Téma hozzáadása", + "Review Sessions": "Munkamenetek átnézése", + "Manually Verify by Text": "Manuális szöveges ellenőrzés", + "Interactively verify by Emoji": "Közös ellenőrzés Emodzsival", + "Self signing private key:": "Titkos önaláíró kulcs:", + "cached locally": "helyben gyorsítótárazott", + "not found locally": "helyben nem található", + "User signing private key:": "Titkos felhasználó aláíró kulcs:", + "Secret Storage key format:": "Biztonsági tároló kulcs formátum:", + "outdated": "lejárt", + "up to date": "friss", + "Keyboard Shortcuts": "Billentyűzet kombinációk", + "Scroll to most recent messages": "A legfrissebb üzenethez görget", + "Local address": "Helyi cím", + "Published Addresses": "Nyilvánosságra hozott cím", + "Other published addresses:": "Másik nyilvánosságra hozott cím:", + "No other published addresses yet, add one below": "Nincs másik nyilvánosságra hozott cím, alább adj hozzá egyet", + "New published address (e.g. #alias:server)": "Új nyilvános cím (pl.: #becenév:szerver)", + "Local Addresses": "Helyi címek", + "Enter a server name": "Add meg a szerver nevét", + "Looks good": "Jól néz ki", + "Can't find this server or its room list": "A szerver vagy a szoba listája nem található", + "All rooms": "Minden szoba", + "Your server": "Matrix szervered", + "Are you sure you want to remove %(serverName)s": "Biztos, hogy törölni szeretnéd: %(serverName)s", + "Remove server": "Szerver törlése", + "Matrix": "Matrix", + "Add a new server": "Új szerver hozzáadása" } From 0436507574704d700a73eccd0b82b9f7d0a6c02e Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 19:24:32 +0100 Subject: [PATCH 049/114] use transparent scrollbars on not hover to emulate scrolbar on hover allows us to get rid of the margin-right hack as well --- res/css/structures/_AutoHideScrollbar.scss | 78 +++++-------------- .../structures/AutoHideScrollbar.js | 57 +------------- 2 files changed, 21 insertions(+), 114 deletions(-) diff --git a/res/css/structures/_AutoHideScrollbar.scss b/res/css/structures/_AutoHideScrollbar.scss index 6e4484157c..3d91293c98 100644 --- a/res/css/structures/_AutoHideScrollbar.scss +++ b/res/css/structures/_AutoHideScrollbar.scss @@ -14,76 +14,34 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* This file has CSS for both native and non-native scrollbars in an order - * that's fairly logical to read but duplicates a selector to separate the - * hiding/showing from the sizing. - */ -/* stylelint-disable no-duplicate-selectors */ - -/* -1. for browsers that support native overlay auto-hiding scrollbars -*/ .mx_AutoHideScrollbar { overflow-x: hidden; overflow-y: auto; + overflow-y: overlay; // where supported -ms-overflow-style: -ms-autohiding-scrollbar; -} -/* -2. webkit also supports overflow:overlay where the scrollbars don't take any space -in the layout but they don't autohide, so do that only on hover -*/ -body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar { - overflow-y: hidden; -} -body.mx_scrollbar_overlay_noautohide .mx_AutoHideScrollbar:hover { - overflow-y: overlay; -} -/* -3. as a last fallback, compensate for the scrollbar taking up space in the layout -by having giving the child element (.mx_AutoHideScrollbar_offset) a -negative right margin of the width of the scrollbar when the container -is overflowing. This is what Firefox ends up using. Overflow is detected -in javascript, and adds the mx_AutoHideScrollbar_overflow class to the container. -This only works in Firefox, which should be fine as this fallback is only needed there. -*/ -body.mx_scrollbar_nooverlay { - .mx_AutoHideScrollbar { - overflow-y: hidden; + &::-webkit-scrollbar { + width: 6px; + height: 6px; + background-color: transparent; } - .mx_AutoHideScrollbar:hover { - overflow-y: auto; + &::-webkit-scrollbar-thumb { + border-radius: 3px; + background-color: transparent; } - /* - offset scrollbar width with negative margin-right - - include before and after psuedo-elements here so they can - be used to do something interesting like scroll-indicating - gradients (see IndicatorScrollBar) - */ - .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow > .mx_AutoHideScrollbar_offset, - .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::before, - .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::after { - margin-right: calc(-1 * var(--scrollbar-width)); - } -} - -// style the native scrollbars ... -// ... standard css scrollbars (firefox at time of writing) -.mx_AutoHideScrollbar { - scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color; + scrollbar-color: transparent transparent; scrollbar-width: thin; } -// or fallback for webkit browsers -::-webkit-scrollbar { - width: 6px; - height: 6px; - background-color: $scrollbar-track-color; -} -::-webkit-scrollbar-thumb { - background-color: $scrollbar-thumb-color; - border-radius: 3px; +.mx_AutoHideScrollbar:hover { + &::-webkit-scrollbar { + background-color: $scrollbar-track-color; + } + + &::-webkit-scrollbar-thumb { + background-color: $scrollbar-thumb-color; + } + scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color; } diff --git a/src/components/structures/AutoHideScrollbar.js b/src/components/structures/AutoHideScrollbar.js index 3f27f51f18..398b07e2d4 100644 --- a/src/components/structures/AutoHideScrollbar.js +++ b/src/components/structures/AutoHideScrollbar.js @@ -18,7 +18,7 @@ import React from "react"; // derived from code from github.com/noeldelgado/gemini-scrollbar // Copyright (c) Noel Delgado (pixelia.me) -function getScrollbarWidth(alternativeOverflow) { +function getScrollbarWidth() { const div = document.createElement('div'); div.className = 'mx_AutoHideScrollbar'; //to get width of css scrollbar div.style.position = 'absolute'; @@ -26,9 +26,6 @@ function getScrollbarWidth(alternativeOverflow) { div.style.width = '100px'; div.style.height = '100px'; div.style.overflow = "scroll"; - if (alternativeOverflow) { - div.style.overflow = alternativeOverflow; - } div.style.msOverflowStyle = '-ms-autohiding-scrollbar'; document.body.appendChild(div); const scrollbarWidth = (div.offsetWidth - div.clientWidth); @@ -39,18 +36,7 @@ function getScrollbarWidth(alternativeOverflow) { function install() { const scrollbarWidth = getScrollbarWidth(); if (scrollbarWidth !== 0) { - const hasForcedOverlayScrollbar = getScrollbarWidth('overlay') === 0; - // overflow: overlay on webkit doesn't auto hide the scrollbar - if (hasForcedOverlayScrollbar) { - document.body.classList.add("mx_scrollbar_overlay_noautohide"); - } else { - document.body.classList.add("mx_scrollbar_nooverlay"); - const style = document.createElement('style'); - style.type = 'text/css'; - style.innerText = - `body.mx_scrollbar_nooverlay { --scrollbar-width: ${scrollbarWidth}px; }`; - document.head.appendChild(style); - } + document.body.classList.add("mx_scrollbar_noautohide"); } } @@ -67,42 +53,7 @@ const installBodyClassesIfNeeded = (function() { export default class AutoHideScrollbar extends React.Component { constructor(props) { super(props); - this.onOverflow = this.onOverflow.bind(this); - this.onUnderflow = this.onUnderflow.bind(this); this._collectContainerRef = this._collectContainerRef.bind(this); - this._needsOverflowListener = null; - } - - onOverflow() { - this.containerRef.classList.add("mx_AutoHideScrollbar_overflow"); - this.containerRef.classList.remove("mx_AutoHideScrollbar_underflow"); - } - - onUnderflow() { - this.containerRef.classList.remove("mx_AutoHideScrollbar_overflow"); - this.containerRef.classList.add("mx_AutoHideScrollbar_underflow"); - } - - checkOverflow() { - if (!this._needsOverflowListener) { - return; - } - if (this.containerRef.scrollHeight > this.containerRef.clientHeight) { - this.onOverflow(); - } else { - this.onUnderflow(); - } - } - - componentDidUpdate() { - this.checkOverflow(); - } - - componentDidMount() { - installBodyClassesIfNeeded(); - this._needsOverflowListener = - document.body.classList.contains("mx_scrollbar_nooverlay"); - this.checkOverflow(); } _collectContainerRef(ref) { @@ -126,9 +77,7 @@ export default class AutoHideScrollbar extends React.Component { onScroll={this.props.onScroll} onWheel={this.props.onWheel} > -
- { this.props.children } -
+ { this.props.children }
); } } From 75cbc1dc07627a7c82e080bce8994e91fd6734a9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 19:26:15 +0100 Subject: [PATCH 050/114] Adjust IndicatorScrollbar to not rely on AutoHideScrollbar for overflow --- .../structures/IndicatorScrollbar.js | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js index f14d99f730..05ad4f7561 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.js @@ -66,6 +66,22 @@ export default class IndicatorScrollbar extends React.Component { this._autoHideScrollbar = autoHideScrollbar; } + + componentDidUpdate(prevProps) { + const prevLen = prevProps && prevProps.children && prevProps.children.length || 0; + const curLen = this.props.children && this.props.children.length || 0; + // check overflow only if amount of children changes. + // if we don't guard here, we end up with an infinite + // render > componentDidUpdate > checkOverflow > setState > render loop + if (prevLen !== curLen) { + this.checkOverflow(); + } + } + + componentDidMount() { + this.checkOverflow(); + } + checkOverflow() { const hasTopOverflow = this._scrollElement.scrollTop > 0; const hasBottomOverflow = this._scrollElement.scrollHeight > @@ -95,10 +111,6 @@ export default class IndicatorScrollbar extends React.Component { this._scrollElement.classList.remove("mx_IndicatorScrollbar_rightOverflow"); } - if (this._autoHideScrollbar) { - this._autoHideScrollbar.checkOverflow(); - } - if (this.props.trackHorizontalOverflow) { this.setState({ // Offset from absolute position of the container From 2186023e4334d5121a27f038cc1b3c205d5c4820 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 19:26:59 +0100 Subject: [PATCH 051/114] css adjustments because mx_AutoHideScrollBar_offset was removed --- res/css/structures/_GroupView.scss | 4 ++-- res/css/structures/_RoomSubList.scss | 22 +++------------------- res/css/views/rooms/_RoomBreadcrumbs.scss | 2 +- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/res/css/structures/_GroupView.scss b/res/css/structures/_GroupView.scss index 2575169664..72a1132c15 100644 --- a/res/css/structures/_GroupView.scss +++ b/res/css/structures/_GroupView.scss @@ -337,7 +337,7 @@ limitations under the License. display: none; } -.mx_GroupView_body .mx_AutoHideScrollbar_offset > * { +.mx_GroupView_body .mx_AutoHideScrollbar > * { margin: 11px 50px 50px 68px; } @@ -366,7 +366,7 @@ limitations under the License. padding: 40px 20px; } -.mx_GroupView .mx_MemberInfo .mx_AutoHideScrollbar_offset > :not(.mx_MemberInfo_avatar) { +.mx_GroupView .mx_MemberInfo .mx_AutoHideScrollbar > :not(.mx_MemberInfo_avatar) { padding-left: 16px; padding-right: 16px; } diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index be44563cfb..0a8ae00f1e 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -166,8 +166,7 @@ limitations under the License. // overflow indicators .mx_RoomSubList:not(.resized-all) > .mx_RoomSubList_scroll { - &.mx_IndicatorScrollbar_topOverflow::before, - &.mx_IndicatorScrollbar_bottomOverflow::after { + &.mx_IndicatorScrollbar_topOverflow::before { position: sticky; left: 0; right: 0; @@ -178,29 +177,14 @@ limitations under the License. pointer-events: none; } - &.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset { + + &.mx_IndicatorScrollbar_topOverflow { margin-top: -8px; } - &.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset { - margin-bottom: -8px; - } &.mx_IndicatorScrollbar_topOverflow::before { top: 0; transition: background-image 0.1s ease-in; background: linear-gradient(to top, $panel-gradient); } - - /* - // for now, we remove the bottomOverflow entirely as we don't want to - // lose the screen real-estate due to a bg-colored gradient, but we also - // don't want to use drop shadows and risk a confusing hierarchy of cards. - // so, instead, we hard-clip at the bottom but soft-clip at the top. - &.mx_IndicatorScrollbar_bottomOverflow::after { - bottom: 0; - transition: background-image 0.1s ease-in; - margin: 0px -8px; - background: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.0)); - } - */ } diff --git a/res/css/views/rooms/_RoomBreadcrumbs.scss b/res/css/views/rooms/_RoomBreadcrumbs.scss index 67350aac34..3858d836e6 100644 --- a/res/css/views/rooms/_RoomBreadcrumbs.scss +++ b/res/css/views/rooms/_RoomBreadcrumbs.scss @@ -41,7 +41,7 @@ limitations under the License. overflow-x: visible; } - .mx_AutoHideScrollbar_offset { + .mx_AutoHideScrollbar { display: flex; flex-direction: row; height: 100%; From aed85a2183eabdf64cc2cf2eeb2d89a613db1da8 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 25 Mar 2020 19:33:55 +0100 Subject: [PATCH 052/114] fix style lint --- res/css/structures/_RoomSubList.scss | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 0a8ae00f1e..1934e681c2 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -168,23 +168,20 @@ limitations under the License. .mx_RoomSubList:not(.resized-all) > .mx_RoomSubList_scroll { &.mx_IndicatorScrollbar_topOverflow::before { position: sticky; + content: ""; + top: 0; left: 0; right: 0; height: 8px; - content: ""; - display: block; z-index: 100; + display: block; pointer-events: none; + transition: background-image 0.1s ease-in; + background: linear-gradient(to top, $panel-gradient); } &.mx_IndicatorScrollbar_topOverflow { margin-top: -8px; } - - &.mx_IndicatorScrollbar_topOverflow::before { - top: 0; - transition: background-image 0.1s ease-in; - background: linear-gradient(to top, $panel-gradient); - } } From 785d0e99fb29d74c25ab8605a2686fa443ef4017 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 11:05:27 +0100 Subject: [PATCH 053/114] remove some remaining dead code --- .../structures/AutoHideScrollbar.js | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/src/components/structures/AutoHideScrollbar.js b/src/components/structures/AutoHideScrollbar.js index 398b07e2d4..04323bb548 100644 --- a/src/components/structures/AutoHideScrollbar.js +++ b/src/components/structures/AutoHideScrollbar.js @@ -1,5 +1,6 @@ /* 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. @@ -16,40 +17,6 @@ limitations under the License. import React from "react"; -// derived from code from github.com/noeldelgado/gemini-scrollbar -// Copyright (c) Noel Delgado (pixelia.me) -function getScrollbarWidth() { - const div = document.createElement('div'); - div.className = 'mx_AutoHideScrollbar'; //to get width of css scrollbar - div.style.position = 'absolute'; - div.style.top = '-9999px'; - div.style.width = '100px'; - div.style.height = '100px'; - div.style.overflow = "scroll"; - div.style.msOverflowStyle = '-ms-autohiding-scrollbar'; - document.body.appendChild(div); - const scrollbarWidth = (div.offsetWidth - div.clientWidth); - document.body.removeChild(div); - return scrollbarWidth; -} - -function install() { - const scrollbarWidth = getScrollbarWidth(); - if (scrollbarWidth !== 0) { - document.body.classList.add("mx_scrollbar_noautohide"); - } -} - -const installBodyClassesIfNeeded = (function() { - let installed = false; - return function() { - if (!installed) { - install(); - installed = true; - } - }; -})(); - export default class AutoHideScrollbar extends React.Component { constructor(props) { super(props); From 9c20bf22ce47de52796bd1d44db80554f56a856e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 26 Mar 2020 10:45:26 +0000 Subject: [PATCH 054/114] Fix soft-crash on bad permalinks Fixes https://github.com/vector-im/riot-web/issues/12880 --- src/linkify-matrix.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/linkify-matrix.js b/src/linkify-matrix.js index cff7a93d08..ee9f703136 100644 --- a/src/linkify-matrix.js +++ b/src/linkify-matrix.js @@ -200,13 +200,17 @@ matrixLinkify.options = { switch (type) { case "url": { // intercept local permalinks to users and show them like userids (in userinfo of current room) - const permalink = parsePermalink(href); - if (permalink && permalink.userId) { - return { - click: function(e) { - matrixLinkify.onUserClick(e, permalink.userId); - }, - }; + try { + const permalink = parsePermalink(href); + if (permalink && permalink.userId) { + return { + click: function(e) { + matrixLinkify.onUserClick(e, permalink.userId); + }, + }; + } + } catch (e) { + // OK fine, it's not actually a permalink } break; } From 8532c72fcecf873d014399d15cea03f24847c2c9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 26 Mar 2020 11:12:00 +0000 Subject: [PATCH 055/114] Update copy on SSSS symmetric upgrade toast Fixes https://github.com/vector-im/riot-web/issues/12881 --- src/components/views/toasts/SetupEncryptionToast.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/toasts/SetupEncryptionToast.js b/src/components/views/toasts/SetupEncryptionToast.js index f7d242caac..e24cb0b234 100644 --- a/src/components/views/toasts/SetupEncryptionToast.js +++ b/src/components/views/toasts/SetupEncryptionToast.js @@ -78,10 +78,11 @@ export default class SetupEncryptionToast extends React.PureComponent { switch (this.props.kind) { case 'set_up_encryption': case 'upgrade_encryption': - case 'upgrade_ssss': return _t('Verify yourself & others to keep your chats safe'); case 'verify_this_session': return _t('Other users may not trust it'); + case 'upgrade_ssss': + return _t('Update your secure storage'); } } From 95828ce751896ab01b5c30e6af030db848b98cca Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 26 Mar 2020 11:13:57 +0000 Subject: [PATCH 056/114] Remove duplicate import --- src/components/views/toasts/SetupEncryptionToast.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/toasts/SetupEncryptionToast.js b/src/components/views/toasts/SetupEncryptionToast.js index f7d242caac..fbd04ae443 100644 --- a/src/components/views/toasts/SetupEncryptionToast.js +++ b/src/components/views/toasts/SetupEncryptionToast.js @@ -20,7 +20,6 @@ import Modal from '../../../Modal'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import * as sdk from "../../../index"; import { _t } from '../../../languageHandler'; -import Modal from '../../../Modal'; import DeviceListener from '../../../DeviceListener'; import SetupEncryptionDialog from "../dialogs/SetupEncryptionDialog"; import { accessSecretStorage } from '../../../CrossSigningManager'; From f7e9cf4552a7d4345c6b6b7dd04c7d4efac5f78e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 26 Mar 2020 11:15:07 +0000 Subject: [PATCH 057/114] 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 5d923e0a24..d964d48e64 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -543,6 +543,7 @@ "Pin": "Pin", "Verify yourself & others to keep your chats safe": "Verify yourself & others to keep your chats safe", "Other users may not trust it": "Other users may not trust it", + "Update your secure storage": "Update your secure storage", "Upgrade": "Upgrade", "Verify": "Verify", "Later": "Later", From c95b23f3ec3ca2567d73db981bcb1c1896a53529 Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Tue, 24 Mar 2020 19:50:37 +0100 Subject: [PATCH 058/114] fix formatBar not hidden after highlight-backspacing text partially --- src/components/views/rooms/BasicMessageComposer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 75455518a1..8d46146e18 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -447,6 +447,8 @@ export default class BasicMessageEditor extends React.Component { } else if (event.key === Key.TAB) { this._tabCompleteName(); handled = true; + } else if (event.key === Key.BACKSPACE) { + this._formatBarRef.hide(); } } if (handled) { From 2bd112ca7cfddf0601556e17c4e6ea26ed843f86 Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Thu, 26 Mar 2020 08:37:46 +0100 Subject: [PATCH 059/114] add delete support and to keyboard shortcuts --- src/Keyboard.ts | 1 + src/components/views/rooms/BasicMessageComposer.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Keyboard.ts b/src/Keyboard.ts index 817d0a0b97..23e2bbf0d6 100644 --- a/src/Keyboard.ts +++ b/src/Keyboard.ts @@ -22,6 +22,7 @@ export const Key = { PAGE_UP: "PageUp", PAGE_DOWN: "PageDown", BACKSPACE: "Backspace", + DELETE: "Delete", ARROW_UP: "ArrowUp", ARROW_DOWN: "ArrowDown", ARROW_LEFT: "ArrowLeft", diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 8d46146e18..ab6a3b584d 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -447,7 +447,7 @@ export default class BasicMessageEditor extends React.Component { } else if (event.key === Key.TAB) { this._tabCompleteName(); handled = true; - } else if (event.key === Key.BACKSPACE) { + } else if (event.key === Key.BACKSPACE || Key.DELETE) { this._formatBarRef.hide(); } } From a1c83a4f0e56316bdcb3d98cc70f8346e26abb4a Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Thu, 26 Mar 2020 13:02:54 +0000 Subject: [PATCH 060/114] Upgrade matrix-js-sdk to 5.2.0-rc.1 --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1ff0fb6f55..29e4809e49 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": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "5.2.0-rc.1", "minimist": "^1.2.0", "pako": "^1.0.5", "png-chunks-extract": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 582d89137e..04dcab02ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5689,9 +5689,10 @@ 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@github:matrix-org/matrix-js-sdk#develop": - version "5.1.1" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/b2e154377a4268441a3b27b183dd7f7018187035" +matrix-js-sdk@5.2.0-rc.1: + version "5.2.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-5.2.0-rc.1.tgz#014239eb713ea3b648b7a573785d01edff1efd03" + integrity sha512-Jv/0kyZEtkfrD6mlq3g2YoFI2+eOBFOz5D186jJAvFg1W7UaQ5gn6iualR+2AKa1AIk2AT6lDcgPb1djZ7/3Vw== dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From 1713006958becbbad6996bd403aa98ca70e4f5cd Mon Sep 17 00:00:00 2001 From: thobyv-kismat Date: Thu, 26 Mar 2020 14:03:38 +0100 Subject: [PATCH 061/114] recommit bwindels commited changes to refactor check logic after rebase mistake --- src/components/views/rooms/BasicMessageComposer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index ab6a3b584d..df62823360 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -447,7 +447,7 @@ export default class BasicMessageEditor extends React.Component { } else if (event.key === Key.TAB) { this._tabCompleteName(); handled = true; - } else if (event.key === Key.BACKSPACE || Key.DELETE) { + } else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) { this._formatBarRef.hide(); } } From 91f706260b9f90984646555d2d444937765053c6 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Thu, 26 Mar 2020 13:21:10 +0000 Subject: [PATCH 062/114] Prepare changelog for v2.3.0-rc.1 --- CHANGELOG.md | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07e478fa02..378ba6f1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,153 @@ +Changes in [2.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0-rc.1) (2020-03-26) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3...v2.3.0-rc.1) + + * Upgrade JS SDK to 5.2.0-rc.1 + * Add a flag to control whether cross-signing signatures are trusted + [\#4277](https://github.com/matrix-org/matrix-react-sdk/pull/4277) + * Update from Weblate + [\#4282](https://github.com/matrix-org/matrix-react-sdk/pull/4282) + * Update copy on SSSS symmetric upgrade toast + [\#4281](https://github.com/matrix-org/matrix-react-sdk/pull/4281) + * Wait for SSSS upgrade to complete + [\#4270](https://github.com/matrix-org/matrix-react-sdk/pull/4270) + * Update cross-signing verification copy and fix i18n + [\#4278](https://github.com/matrix-org/matrix-react-sdk/pull/4278) + * Fix soft-crash on bad permalinks + [\#4280](https://github.com/matrix-org/matrix-react-sdk/pull/4280) + * Fix: make self-verification wait for incoming request + [\#4267](https://github.com/matrix-org/matrix-react-sdk/pull/4267) + * Fall back to non-standard persisted api for Safari + [\#4272](https://github.com/matrix-org/matrix-react-sdk/pull/4272) + * Respond to backup key sharing requests + [\#4275](https://github.com/matrix-org/matrix-react-sdk/pull/4275) + * Log and display secret sharing cache state + [\#4268](https://github.com/matrix-org/matrix-react-sdk/pull/4268) + * Support sending config and ready events to capable widgets (Jitsi) + [\#4266](https://github.com/matrix-org/matrix-react-sdk/pull/4266) + * If cached keys are present in the key backup dialog, use them + [\#4273](https://github.com/matrix-org/matrix-react-sdk/pull/4273) + * Fix formatbar not hidden on highlighted message sent + [\#4265](https://github.com/matrix-org/matrix-react-sdk/pull/4265) + * Support Jitsi conferences sent/received on Riot Mobile and older Riot Webs + [\#4252](https://github.com/matrix-org/matrix-react-sdk/pull/4252) + * Use unified function to check cross-signing is ready + [\#4263](https://github.com/matrix-org/matrix-react-sdk/pull/4263) + * Migrate SSSS to symmetric + [\#4224](https://github.com/matrix-org/matrix-react-sdk/pull/4224) + * Migration to symmetric SSSS + [\#4242](https://github.com/matrix-org/matrix-react-sdk/pull/4242) + * Always display verification request toasts on top + [\#4262](https://github.com/matrix-org/matrix-react-sdk/pull/4262) + * Fix: assume SAS is supported when starting request with .start + [\#4249](https://github.com/matrix-org/matrix-react-sdk/pull/4249) + * Fix logout when Olm failed to load. + [\#4261](https://github.com/matrix-org/matrix-react-sdk/pull/4261) + * Improve naming of Jitsi conferences + [\#4251](https://github.com/matrix-org/matrix-react-sdk/pull/4251) + * Handle matrix.to user permalink in-room rather than solo + [\#4245](https://github.com/matrix-org/matrix-react-sdk/pull/4245) + * Fix: filter room list (again) by canonical and alternative aliases + [\#4260](https://github.com/matrix-org/matrix-react-sdk/pull/4260) + * EventIndex: Add some logging to the file panel populating. + [\#4250](https://github.com/matrix-org/matrix-react-sdk/pull/4250) + * Update from Weblate + [\#4259](https://github.com/matrix-org/matrix-react-sdk/pull/4259) + * Migrate RoomView to React Contexts in the hope for better temporal stability + [\#4258](https://github.com/matrix-org/matrix-react-sdk/pull/4258) + * Update WidgetUtils.js fix Jitsi path + [\#4256](https://github.com/matrix-org/matrix-react-sdk/pull/4256) + * Fix local jitsi build url fail and missing argument + [\#4255](https://github.com/matrix-org/matrix-react-sdk/pull/4255) + * Add shortcut CmdOrCtrl+. to toggle right panel + [\#4244](https://github.com/matrix-org/matrix-react-sdk/pull/4244) + * Improve Keyboard Shortcuts. Add alt-arrows & alt-shift-arrows + [\#4241](https://github.com/matrix-org/matrix-react-sdk/pull/4241) + * Bring back legacy verification by comparing public device keys + [\#4240](https://github.com/matrix-org/matrix-react-sdk/pull/4240) + * Searching: Return an empty result if the search term is an empty string. + [\#4248](https://github.com/matrix-org/matrix-react-sdk/pull/4248) + * Break continuation on showHiddenEvents-rendered events + [\#4247](https://github.com/matrix-org/matrix-react-sdk/pull/4247) + * Watch for show-RR settings changes, use room-specific and fix margins + [\#4246](https://github.com/matrix-org/matrix-react-sdk/pull/4246) + * Register Mac electron specific Cmd+, shortcut to User Settings + [\#4243](https://github.com/matrix-org/matrix-react-sdk/pull/4243) + * Use a local wrapper for Jitsi calls + [\#4234](https://github.com/matrix-org/matrix-react-sdk/pull/4234) + * Invite Dialog fixes + [\#4233](https://github.com/matrix-org/matrix-react-sdk/pull/4233) + * RoomPreviewBar word-break the sender name too + [\#4239](https://github.com/matrix-org/matrix-react-sdk/pull/4239) + * Report to the user when a key signature upload fails + [\#4229](https://github.com/matrix-org/matrix-react-sdk/pull/4229) + * pre-send megolm keys when possible when a user starts typing + [\#4235](https://github.com/matrix-org/matrix-react-sdk/pull/4235) + * we don't do mx_fadable anymore so get rid of broken RightPanel disabling + [\#4238](https://github.com/matrix-org/matrix-react-sdk/pull/4238) + * Fix left left panel overflowing vertically + [\#4237](https://github.com/matrix-org/matrix-react-sdk/pull/4237) + * Fix custom tags causing left panel to over-expand + [\#4236](https://github.com/matrix-org/matrix-react-sdk/pull/4236) + * Add Keyboard shortcuts dialog + [\#4231](https://github.com/matrix-org/matrix-react-sdk/pull/4231) + * Don't use buildkite agent to upload logs + [\#4232](https://github.com/matrix-org/matrix-react-sdk/pull/4232) + * Remove Gemini Scrollbars + [\#4217](https://github.com/matrix-org/matrix-react-sdk/pull/4217) + * Room Directory Explore Servers redesign + [\#4209](https://github.com/matrix-org/matrix-react-sdk/pull/4209) + * Fix redo keyboard shortcut on macOS + [\#4110](https://github.com/matrix-org/matrix-react-sdk/pull/4110) + * Fix: ensure local state for aliases doesn't get garbled up + [\#4230](https://github.com/matrix-org/matrix-react-sdk/pull/4230) + * Rename 'jump to bottom' to avoid ublock block + [\#4208](https://github.com/matrix-org/matrix-react-sdk/pull/4208) + * Restore key backup in background after complete security + [\#4225](https://github.com/matrix-org/matrix-react-sdk/pull/4225) + * Fix key backup trust text for cross-signing + [\#4223](https://github.com/matrix-org/matrix-react-sdk/pull/4223) + * Add default on config setting to control call button in composer + [\#4227](https://github.com/matrix-org/matrix-react-sdk/pull/4227) + * Fix: make alternative addresses UX less confusing + [\#4221](https://github.com/matrix-org/matrix-react-sdk/pull/4221) + * Wait for verification request on login + [\#4222](https://github.com/matrix-org/matrix-react-sdk/pull/4222) + * EventIndex: Add support to delete events from the index. + [\#4204](https://github.com/matrix-org/matrix-react-sdk/pull/4204) + * EventIndex: Remove a checkpoint if the HTTP request returns a 403. + [\#4214](https://github.com/matrix-org/matrix-react-sdk/pull/4214) + * Move to composer when typing letters with Shift held + [\#4216](https://github.com/matrix-org/matrix-react-sdk/pull/4216) + * Wrap large room names when previewing them + [\#4213](https://github.com/matrix-org/matrix-react-sdk/pull/4213) + * Rename Review Devices to Review Sessions + [\#4219](https://github.com/matrix-org/matrix-react-sdk/pull/4219) + * Fix typo in tabIndex to make React happy + [\#4215](https://github.com/matrix-org/matrix-react-sdk/pull/4215) + * Proof of concept for custom theme adding + [\#4148](https://github.com/matrix-org/matrix-react-sdk/pull/4148) + * Remove stuff that yarn install doesn't think we need + [\#4205](https://github.com/matrix-org/matrix-react-sdk/pull/4205) + * Declare jsx in tsconfig for IDEs + [\#4207](https://github.com/matrix-org/matrix-react-sdk/pull/4207) + * Fix: best-effort to join room without canonical alias over federation from + room directory + [\#4210](https://github.com/matrix-org/matrix-react-sdk/pull/4210) + * Test for cross-signing homeserver support during login, toasts + [\#4206](https://github.com/matrix-org/matrix-react-sdk/pull/4206) + * Send verification request to a single device in a way compatible with non- + cross-signing + [\#4202](https://github.com/matrix-org/matrix-react-sdk/pull/4202) + * Fixes for removing local alias + [\#4199](https://github.com/matrix-org/matrix-react-sdk/pull/4199) + * yarn upgrade + [\#4201](https://github.com/matrix-org/matrix-react-sdk/pull/4201) + * Support TypeScript for React components + [\#4203](https://github.com/matrix-org/matrix-react-sdk/pull/4203) + * When room name is changed, show both the old and new name + [\#4183](https://github.com/matrix-org/matrix-react-sdk/pull/4183) + Changes in [2.2.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.2.3) (2020-03-17) =================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3-rc.1...v2.2.3) From 2fe0edd3eb7202fb352fa1f12ef9f12a0ce72ee0 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Thu, 26 Mar 2020 13:21:11 +0000 Subject: [PATCH 063/114] v2.3.0-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29e4809e49..7579cab714 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "2.2.3", + "version": "2.3.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 9b54d15fcd2395fe9b5465f630db3e1044c43664 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 16:51:24 +0100 Subject: [PATCH 064/114] add cancel button to verification panel --- .../views/right_panel/_VerificationPanel.scss | 15 +++++++++ .../views/right_panel/EncryptionPanel.js | 32 ++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/res/css/views/right_panel/_VerificationPanel.scss b/res/css/views/right_panel/_VerificationPanel.scss index 2a733d11a7..a717495e43 100644 --- a/res/css/views/right_panel/_VerificationPanel.scss +++ b/res/css/views/right_panel/_VerificationPanel.scss @@ -15,6 +15,21 @@ limitations under the License. */ .mx_UserInfo { + .mx_EncryptionPanel_cancel { + mask: url('$(res)/img/feather-customised/cancel.svg'); + mask-repeat: no-repeat; + mask-position: center; + mask-size: cover; + width: 14px; + height: 14px; + background-color: $dialog-close-fg-color; + cursor: pointer; + position: absolute; + z-index: 100; + top: 14px; + right: 14px; + } + .mx_VerificationPanel_verified_section .mx_E2EIcon { // Override general user info margin margin: 0 auto !important; diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index 3a4aa2edc3..7ee9225a38 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -84,6 +84,18 @@ const EncryptionPanel = (props) => { }, [onClose, request]); useEventEmitter(request, "change", changeHandler); + const onCancel = useCallback(function() { + if (request) { + request.cancel(); + } + }, [request]); + + let cancelButton; + if (request && request.pending) { + const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); + cancelButton = ; + } + const onStartVerification = useCallback(async () => { setRequesting(true); const cli = MatrixClientPeg.get(); @@ -98,14 +110,18 @@ const EncryptionPanel = (props) => { (request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined)); if (!request || requested) { const initiatedByMe = (!request && isRequesting) || (request && request.initiatedByMe); - return ; + return + {cancelButton} + + ; } else { - return ( + return + {cancelButton} { request={request} key={request.channel.transactionId} phase={phase} /> - ); + ; } }; EncryptionPanel.propTypes = { From 34b0c1b1e75907a6568ffdc7776c2fb98ff3cab5 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 16:54:31 +0100 Subject: [PATCH 065/114] fix lint & other cleanup --- .../views/right_panel/EncryptionPanel.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index 7ee9225a38..e67af0805a 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -93,7 +93,11 @@ const EncryptionPanel = (props) => { let cancelButton; if (request && request.pending) { const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - cancelButton = ; + cancelButton = (); } const onStartVerification = useCallback(async () => { @@ -110,7 +114,7 @@ const EncryptionPanel = (props) => { (request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined)); if (!request || requested) { const initiatedByMe = (!request && isRequesting) || (request && request.initiatedByMe); - return + return ( {cancelButton} { member={member} waitingForOtherParty={requested && initiatedByMe} waitingForNetwork={requested && !initiatedByMe} /> - ; + ); } else { - return + return ( {cancelButton} { request={request} key={request.channel.transactionId} phase={phase} /> - ; + ); } }; EncryptionPanel.propTypes = { From 4f3842d75e23e10f623509df0442bfe5911bbed6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 16:58:17 +0100 Subject: [PATCH 066/114] pick color similar to chevron for cancel X button --- res/css/views/right_panel/_VerificationPanel.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/right_panel/_VerificationPanel.scss b/res/css/views/right_panel/_VerificationPanel.scss index a717495e43..459622b277 100644 --- a/res/css/views/right_panel/_VerificationPanel.scss +++ b/res/css/views/right_panel/_VerificationPanel.scss @@ -22,7 +22,7 @@ limitations under the License. mask-size: cover; width: 14px; height: 14px; - background-color: $dialog-close-fg-color; + background-color: $settings-subsection-fg-color; cursor: pointer; position: absolute; z-index: 100; From a6fbb9825247faa2b93bf9019fb6fe9bc568891f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 17:02:32 +0100 Subject: [PATCH 067/114] only show close button when not in a dialog, which already has one --- src/components/views/right_panel/EncryptionPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/right_panel/EncryptionPanel.js b/src/components/views/right_panel/EncryptionPanel.js index e67af0805a..2c51662111 100644 --- a/src/components/views/right_panel/EncryptionPanel.js +++ b/src/components/views/right_panel/EncryptionPanel.js @@ -91,7 +91,7 @@ const EncryptionPanel = (props) => { }, [request]); let cancelButton; - if (request && request.pending) { + if (layout !== "dialog" && request && request.pending) { const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); cancelButton = ( Date: Thu, 26 Mar 2020 17:31:31 +0100 Subject: [PATCH 068/114] Show EncryptionPanel straight away when there is an ongoing verification request for a user --- src/stores/RightPanelStore.js | 22 ++++++++++++++++++---- src/verification.js | 15 +++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/stores/RightPanelStore.js b/src/stores/RightPanelStore.js index 814f54b454..8869d0be42 100644 --- a/src/stores/RightPanelStore.js +++ b/src/stores/RightPanelStore.js @@ -15,6 +15,7 @@ limitations under the License. */ import dis from '../dispatcher'; +import {pendingVerificationRequestForUser} from '../verification'; import {Store} from 'flux/utils'; import SettingsStore, {SettingLevel} from "../settings/SettingsStore"; import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "./RightPanelStorePhases"; @@ -135,7 +136,20 @@ export default class RightPanelStore extends Store { break; case 'set_right_panel_phase': { - const targetPhase = payload.phase; + let targetPhase = payload.phase; + let refireParams = payload.refireParams; + // redirect to EncryptionPanel if there is an ongoing verification request + if (targetPhase === RIGHT_PANEL_PHASES.RoomMemberInfo) { + const {member} = payload.refireParams; + const pendingRequest = pendingVerificationRequestForUser(member); + if (pendingRequest) { + targetPhase = RIGHT_PANEL_PHASES.EncryptionPanel; + refireParams = { + verificationRequest: pendingRequest, + member, + }; + } + } if (!RIGHT_PANEL_PHASES[targetPhase]) { console.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`); return; @@ -153,7 +167,7 @@ export default class RightPanelStore extends Store { }); } } else { - if (targetPhase === this._state.lastRoomPhase && !payload.refireParams) { + if (targetPhase === this._state.lastRoomPhase && !refireParams) { this._setState({ showRoomPanel: !this._state.showRoomPanel, }); @@ -161,7 +175,7 @@ export default class RightPanelStore extends Store { this._setState({ lastRoomPhase: targetPhase, showRoomPanel: true, - lastRoomPhaseParams: payload.refireParams || {}, + lastRoomPhaseParams: refireParams || {}, }); } } @@ -170,7 +184,7 @@ export default class RightPanelStore extends Store { dis.dispatch({ action: 'after_right_panel_phase_change', phase: targetPhase, - ...(payload.refireParams || {}), + ...(refireParams || {}), }); break; } diff --git a/src/verification.js b/src/verification.js index d0f6fd7806..2231346478 100644 --- a/src/verification.js +++ b/src/verification.js @@ -111,12 +111,7 @@ export async function verifyUser(user) { if (!await enable4SIfNeeded()) { return; } - const cli = MatrixClientPeg.get(); - const dmRoom = findDMForUser(cli, user.userId); - let existingRequest; - if (dmRoom) { - existingRequest = cli.findVerificationRequestDMInProgress(dmRoom.roomId); - } + const existingRequest = pendingVerificationRequestForUser(user); dis.dispatch({ action: "set_right_panel_phase", phase: RIGHT_PANEL_PHASES.EncryptionPanel, @@ -126,3 +121,11 @@ export async function verifyUser(user) { }, }); } + +export function pendingVerificationRequestForUser(user) { + const cli = MatrixClientPeg.get(); + const dmRoom = findDMForUser(cli, user.userId); + if (dmRoom) { + return cli.findVerificationRequestDMInProgress(dmRoom.roomId); + } +} From ce77593cf9aac0ee61743303004d5b3198142762 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 17:37:44 +0100 Subject: [PATCH 069/114] rename toast title --- src/DeviceListener.js | 2 +- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DeviceListener.js b/src/DeviceListener.js index 0151a45c18..9161a462eb 100644 --- a/src/DeviceListener.js +++ b/src/DeviceListener.js @@ -189,7 +189,7 @@ export default class DeviceListener { this._activeNagToasts.add(device.deviceId); ToastStore.sharedInstance().addOrReplaceToast({ key: toastKey(device.deviceId), - title: _t("Unverified session"), + title: _t("Unverified login. Was this you?"), icon: "verification_warning", props: { device }, component: sdk.getComponent("toasts.UnverifiedSessionToast"), diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c79717e352..85e382bdb6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -96,7 +96,7 @@ "Verify this session": "Verify this session", "Encryption upgrade available": "Encryption upgrade available", "Set up encryption": "Set up encryption", - "Unverified session": "Unverified session", + "Unverified login. Was this you?": "Unverified login. Was this you?", "Who would you like to add to this community?": "Who would you like to add to this community?", "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Warning: any person you add to a community will be publicly visible to anyone who knows the community ID", "Invite new community members": "Invite new community members", From e3e40ace819ff025d1c48f5b0e9ec60bc5f9b7a4 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 26 Mar 2020 17:45:59 +0100 Subject: [PATCH 070/114] Fix: pick last active DM for verification request --- src/createRoom.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/createRoom.js b/src/createRoom.js index 45e849065d..66d4d1908e 100644 --- a/src/createRoom.js +++ b/src/createRoom.js @@ -174,6 +174,9 @@ export function findDMForUser(client, userId) { return member && (member.membership === "invite" || member.membership === "join"); } return false; + }).sort((r1, r2) => { + return r2.getLastActiveTimestamp() - + r1.getLastActiveTimestamp(); }); if (suitableDMRooms.length) { return suitableDMRooms[0]; From ccb3283fd3e59c39ee67db79dbde4f082f688e73 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 26 Mar 2020 18:50:31 +0000 Subject: [PATCH 071/114] Show whether backup key is cached in cross signing debug panel --- src/components/views/settings/CrossSigningPanel.js | 8 ++++++++ src/i18n/strings/en_EN.json | 1 + 2 files changed, 9 insertions(+) diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index b960434ca1..1242c35df8 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -34,6 +34,7 @@ export default class CrossSigningPanel extends React.PureComponent { crossSigningPrivateKeysInStorage: false, selfSigningPrivateKeyCached: false, userSigningPrivateKeyCached: false, + sessionBackupKeyCached: false, secretStorageKeyInAccount: false, secretStorageKeyNeedsUpgrade: null, }; @@ -80,6 +81,7 @@ export default class CrossSigningPanel extends React.PureComponent { const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage); const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")); const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing")); + const sessionBackupKeyCached = !!(await cli._crypto.getSessionBackupPrivateKey()); const secretStorageKeyInAccount = await secretStorage.hasKey(); const homeserverSupportsCrossSigning = await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"); @@ -91,6 +93,7 @@ export default class CrossSigningPanel extends React.PureComponent { crossSigningPrivateKeysInStorage, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, + sessionBackupKeyCached, secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, @@ -139,6 +142,7 @@ export default class CrossSigningPanel extends React.PureComponent { crossSigningPrivateKeysInStorage, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, + sessionBackupKeyCached, secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, @@ -226,6 +230,10 @@ export default class CrossSigningPanel extends React.PureComponent { {_t("User signing private key:")} {userSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")} + + {_t("Session backup key:")} + {sessionBackupKeyCached ? _t("cached locally") : _t("not found locally")} + {_t("Secret storage public key:")} {secretStorageKeyInAccount ? _t("in account data") : _t("not found")} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c79717e352..c1f8df1371 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -588,6 +588,7 @@ "cached locally": "cached locally", "not found locally": "not found locally", "User signing private key:": "User signing private key:", + "Session backup key:": "Session backup key:", "Secret storage public key:": "Secret storage public key:", "in account data": "in account data", "Homeserver feature support:": "Homeserver feature support:", From 7fbfd73e11e03a192d2bb74e3146b9ea07189e5d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 01:17:42 +0000 Subject: [PATCH 072/114] Show modal on "instant SSO" Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.js | 19 ++++++++++++++++++- src/components/structures/auth/Login.js | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 5d809eb28f..489d9474a1 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -22,6 +22,11 @@ limitations under the License. import {MatrixClient} from "matrix-js-sdk"; import dis from './dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; +import Modal from "./Modal"; +import InfoDialog from "./components/views/dialogs/InfoDialog"; +import {_t} from "./languageHandler"; +import Spinner from "./components/views/elements/Spinner"; +import React from "react"; /** * Base class for classes that provide platform-specific functionality @@ -183,9 +188,21 @@ 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 {boolean} showModal whether or not to show the spinner modal. */ - startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") { + startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas", showModal: boolean) { const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl()); + if (showModal) { + Modal.createTrackedDialog('BasePlatform', 'SSO', InfoDialog, { + title: _t("Single sign-on"), + description: , + }); + } window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } } diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 36df08627e..6c6a8bedbd 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -437,7 +437,7 @@ export default createReactClass({ // we do not do this when the user has changed to the server manually as that may be jarring. if (initial && SSO_FLOWS.includes(flows[i].type)) { const tmpCli = this._loginLogic.createTemporaryClient(); - PlatformPeg.get().startSingleSignOn(tmpCli, flows[i].type === M_LOGIN_SSO ? "sso": "cas"); + PlatformPeg.get().startSingleSignOn(tmpCli, flows[i].type === M_LOGIN_SSO ? "sso": "cas", true); } // we just pick the first flow where we support all the From a974f0fe469fe6bf854701e6ce0061813d47b480 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 09:59:18 +0000 Subject: [PATCH 073/114] blank out UserInfo avatar when changing between members Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/right_panel/UserInfo.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index 4219da690e..112423a0f2 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -1388,6 +1388,7 @@ const UserInfoHeader = ({onClose, member, e2eStatus}) => {
Date: Fri, 27 Mar 2020 09:59:43 +0000 Subject: [PATCH 074/114] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 12bd462937..4677f40ce1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -25,6 +25,8 @@ "Error": "Error", "Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.", "Dismiss": "Dismiss", + "Single sign-on": "Single sign-on", + "Click here if you're not redirected automatically": "Click here if you're not redirected automatically", "Call Failed": "Call Failed", "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.", "Review Sessions": "Review Sessions", From 548e9184372d9a7c8a74a07841f97134ee55fbb4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 12:01:10 +0000 Subject: [PATCH 075/114] only auto-sso when disable_custom_urls is true Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/auth/Login.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 6c6a8bedbd..134f6fa6f3 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -435,7 +435,8 @@ export default createReactClass({ // if this is the initial render and the flow we choose is SSO/CAS then go to it automatically // we do not do this when the user has changed to the server manually as that may be jarring. - if (initial && SSO_FLOWS.includes(flows[i].type)) { + // Only allow it when disable_custom_urls is asserted so that we don't prevent user from changing HS URL + if (initial && SdkConfig.get()['disable_custom_urls'] && SSO_FLOWS.includes(flows[i].type)) { const tmpCli = this._loginLogic.createTemporaryClient(); PlatformPeg.get().startSingleSignOn(tmpCli, flows[i].type === M_LOGIN_SSO ? "sso": "cas", true); } From 0f2e2ea069e9cdd5f05319370744aa354d450758 Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 27 Mar 2020 11:58:54 +0000 Subject: [PATCH 076/114] Update shield display rules --- src/components/structures/RoomView.js | 8 +++++++- src/components/views/rooms/RoomTile.js | 7 ++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5fd5f42f78..dd453667e0 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -40,6 +40,7 @@ import rate_limited_func from '../../ratelimitedfunc'; import * as ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; import eventSearch from '../../Searching'; +import DMRoomMap from '../../utils/DMRoomMap'; import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard'; @@ -833,7 +834,12 @@ export default createReactClass({ /* Check all verified user devices. */ /* Don't alarm if no other users are verified */ - const targets = (verified.length > 0) ? [...verified, this.context.getUserId()] : verified; + const isDM = !!DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); + const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified + !isDM && // Don't alarm for self in DMs with other users + (e2eMembers.length != 2) || // Don't alarm for self in 1:1 chats with other users + (e2eMembers.length == 1); // Do alarm for self if we're alone in a room + const targets = includeUser ? [...verified, this.context.getUserId()] : verified; for (const userId of targets) { const devices = await this.context.getStoredDevicesForUser(userId); const anyDeviceNotVerified = devices.some(({deviceId}) => { diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 0f44f5077a..6f9cd7f1be 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -167,7 +167,12 @@ export default createReactClass({ /* Check all verified user devices. */ /* Don't alarm if no other users are verified */ - const targets = (verified.length > 0) ? [...verified, cli.getUserId()] : verified; + const isDM = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); + const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified + !isDM && // Don't alarm for self in DMs with other users + (e2eMembers.length != 2) || // Don't alarm for self in 1:1 chats with other users + (e2eMembers.length == 1); // Do alarm for self if we're alone in a room + const targets = includeUser ? [...verified, cli.getUserId()] : verified; for (const userId of targets) { const devices = await cli.getStoredDevicesForUser(userId); const allDevicesVerified = devices.every(({deviceId}) => { From 5d7adef0a2a7d5de0ca9a7d7931c90e5fb52a213 Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 27 Mar 2020 13:45:21 +0000 Subject: [PATCH 077/114] Factor out shield display rules into one file --- src/components/structures/RoomView.js | 39 ++-------------------- src/components/views/rooms/RoomTile.js | 36 ++------------------- src/utils/ShieldUtils.ts | 45 ++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 70 deletions(-) create mode 100644 src/utils/ShieldUtils.ts diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index dd453667e0..7ce50348c9 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -56,6 +56,7 @@ import RightPanelStore from "../../stores/RightPanelStore"; import {haveTileForEvent} from "../views/rooms/EventTile"; import RoomContext from "../../contexts/RoomContext"; import MatrixClientContext from "../../contexts/MatrixClientContext"; +import { shieldStatusForMembership } from '../../utils/ShieldUtils'; const DEBUG = false; let debuglog = function() {}; @@ -818,45 +819,9 @@ export default createReactClass({ return; } - // Duplication between here and _updateE2eStatus in RoomTile /* At this point, the user has encryption on and cross-signing on */ - const e2eMembers = await room.getEncryptionTargetMembers(); - const verified = []; - const unverified = []; - e2eMembers.map(({userId}) => userId) - .filter((userId) => userId !== this.context.getUserId()) - .forEach((userId) => { - (this.context.checkUserTrust(userId).isCrossSigningVerified() ? - verified : unverified).push(userId); - }); - - debuglog("e2e verified", verified, "unverified", unverified); - - /* Check all verified user devices. */ - /* Don't alarm if no other users are verified */ - const isDM = !!DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); - const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified - !isDM && // Don't alarm for self in DMs with other users - (e2eMembers.length != 2) || // Don't alarm for self in 1:1 chats with other users - (e2eMembers.length == 1); // Do alarm for self if we're alone in a room - const targets = includeUser ? [...verified, this.context.getUserId()] : verified; - for (const userId of targets) { - const devices = await this.context.getStoredDevicesForUser(userId); - const anyDeviceNotVerified = devices.some(({deviceId}) => { - return !this.context.checkDeviceTrust(userId, deviceId).isVerified(); - }); - if (anyDeviceNotVerified) { - this.setState({ - e2eStatus: "warning", - }); - debuglog("e2e status set to warning as not all users trust all of their sessions." + - " Aborted on user", userId); - return; - } - } - this.setState({ - e2eStatus: unverified.length === 0 ? "verified" : "normal", + e2eStatus: await shieldStatusForMembership(this.context, room), }); }, diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 6f9cd7f1be..478d6703b4 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -37,6 +37,7 @@ import E2EIcon from './E2EIcon'; import InviteOnlyIcon from './InviteOnlyIcon'; // eslint-disable-next-line camelcase import rate_limited_func from '../../../ratelimitedfunc'; +import { shieldStatusForMembership } from '../../../utils/ShieldUtils'; export default createReactClass({ displayName: 'RoomTile', @@ -154,40 +155,9 @@ export default createReactClass({ return; } - // Duplication between here and _updateE2eStatus in RoomView - const e2eMembers = await this.props.room.getEncryptionTargetMembers(); - const verified = []; - const unverified = []; - e2eMembers.map(({userId}) => userId) - .filter((userId) => userId !== cli.getUserId()) - .forEach((userId) => { - (cli.checkUserTrust(userId).isCrossSigningVerified() ? - verified : unverified).push(userId); - }); - - /* Check all verified user devices. */ - /* Don't alarm if no other users are verified */ - const isDM = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); - const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified - !isDM && // Don't alarm for self in DMs with other users - (e2eMembers.length != 2) || // Don't alarm for self in 1:1 chats with other users - (e2eMembers.length == 1); // Do alarm for self if we're alone in a room - const targets = includeUser ? [...verified, cli.getUserId()] : verified; - for (const userId of targets) { - const devices = await cli.getStoredDevicesForUser(userId); - const allDevicesVerified = devices.every(({deviceId}) => { - return cli.checkDeviceTrust(userId, deviceId).isVerified(); - }); - if (!allDevicesVerified) { - this.setState({ - e2eStatus: "warning", - }); - return; - } - } - + /* At this point, the user has encryption on and cross-signing on */ this.setState({ - e2eStatus: unverified.length === 0 ? "verified" : "normal", + e2eStatus: await shieldStatusForMembership(cli, this.props.room), }); }, diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts new file mode 100644 index 0000000000..407dae8e97 --- /dev/null +++ b/src/utils/ShieldUtils.ts @@ -0,0 +1,45 @@ +import DMRoomMap from './DMRoomMap'; + +/* For now, a cut-down type spec for the client */ +interface Client { + getUserId: () => string; + checkUserTrust: (userId: string) => { + isCrossSigningVerified: () => boolean + }; + getStoredDevicesForUser: (userId: string) => Promise<[{ deviceId: string }]>; + checkDeviceTrust: (userId: string, deviceId: string) => { + isVerified: () => boolean + } +} + +export async function shieldStatusForMembership(client: Client, room) { + const members = (await room.getEncryptionTargetMembers()).map(({userId}) => userId); + const inDMMap = !!DMRoomMap.shared().getUserIdForRoomId(room.roomId); + + const verified = []; + const unverified = []; + members.filter((userId) => userId !== client.getUserId()) + .forEach((userId) => { + (client.checkUserTrust(userId).isCrossSigningVerified() ? + verified : unverified).push(userId); + }); + + /* Check all verified user devices. */ + /* Don't alarm if no other users are verified */ + const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified + !inDMMap && // Don't alarm for self in DMs with other users + (members.length != 2) || // Don't alarm for self in 1:1 chats with other users + (members.length == 1); // Do alarm for self if we're alone in a room + const targets = includeUser ? [...verified, client.getUserId()] : verified; + for (const userId of targets) { + const devices = await client.getStoredDevicesForUser(userId); + const anyDeviceNotVerified = devices.some(({deviceId}) => { + return !client.checkDeviceTrust(userId, deviceId).isVerified(); + }); + if (anyDeviceNotVerified) { + return "warning"; + } + } + + return unverified.length === 0 ? "verified" : "normal"; +} From 5b7e7f49d1cbc4733769f16a042b0a383d21c6c5 Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 27 Mar 2020 13:50:03 +0000 Subject: [PATCH 078/114] lint --- src/components/structures/RoomView.js | 1 - src/utils/ShieldUtils.ts | 15 ++++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7ce50348c9..74572b962a 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -40,7 +40,6 @@ import rate_limited_func from '../../ratelimitedfunc'; import * as ObjectUtils from '../../ObjectUtils'; import * as Rooms from '../../Rooms'; import eventSearch from '../../Searching'; -import DMRoomMap from '../../utils/DMRoomMap'; import {isOnlyCtrlOrCmdKeyEvent, Key} from '../../Keyboard'; diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts index 407dae8e97..30b1dab7fc 100644 --- a/src/utils/ShieldUtils.ts +++ b/src/utils/ShieldUtils.ts @@ -12,12 +12,17 @@ interface Client { } } -export async function shieldStatusForMembership(client: Client, room) { +interface Room { + getEncryptionTargetMembers: () => Promise<[{userId: string}]>; + roomId: string; +} + +export async function shieldStatusForMembership(client: Client, room: Room) { const members = (await room.getEncryptionTargetMembers()).map(({userId}) => userId); const inDMMap = !!DMRoomMap.shared().getUserIdForRoomId(room.roomId); - const verified = []; - const unverified = []; + const verified: string[] = []; + const unverified: string[] = []; members.filter((userId) => userId !== client.getUserId()) .forEach((userId) => { (client.checkUserTrust(userId).isCrossSigningVerified() ? @@ -28,8 +33,8 @@ export async function shieldStatusForMembership(client: Client, room) { /* Don't alarm if no other users are verified */ const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified !inDMMap && // Don't alarm for self in DMs with other users - (members.length != 2) || // Don't alarm for self in 1:1 chats with other users - (members.length == 1); // Do alarm for self if we're alone in a room + (members.length !== 2) || // Don't alarm for self in 1:1 chats with other users + (members.length === 1); // Do alarm for self if we're alone in a room const targets = includeUser ? [...verified, client.getUserId()] : verified; for (const userId of targets) { const devices = await client.getStoredDevicesForUser(userId); From 1f0d7923d71fa1bc35d5774e5957a332ac427938 Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 27 Mar 2020 13:51:30 +0000 Subject: [PATCH 079/114] with output type --- src/utils/ShieldUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts index 30b1dab7fc..3c7cae8c8d 100644 --- a/src/utils/ShieldUtils.ts +++ b/src/utils/ShieldUtils.ts @@ -17,7 +17,7 @@ interface Room { roomId: string; } -export async function shieldStatusForMembership(client: Client, room: Room) { +export async function shieldStatusForMembership(client: Client, room: Room): Promise { const members = (await room.getEncryptionTargetMembers()).map(({userId}) => userId); const inDMMap = !!DMRoomMap.shared().getUserIdForRoomId(room.roomId); From db6f88c66a76736ce72ec0d22df6e6627b0a59ed Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 14:02:32 +0000 Subject: [PATCH 080/114] Welcome page, support $ssoUrl and $casUrl placeholders Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/EmbeddedPage.js | 9 +++++++++ src/components/structures/MatrixChat.js | 2 +- src/components/views/auth/Welcome.js | 15 ++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/structures/EmbeddedPage.js b/src/components/structures/EmbeddedPage.js index f854dc955f..a0a95ac6f1 100644 --- a/src/components/structures/EmbeddedPage.js +++ b/src/components/structures/EmbeddedPage.js @@ -37,6 +37,8 @@ export default class EmbeddedPage extends React.PureComponent { className: PropTypes.string, // Whether to wrap the page in a scrollbar scrollbar: PropTypes.bool, + // Map of keys to replace with values, e.g {$placeholder: "value"} + replaceMap: PropTypes.object, }; static contextType = MatrixClientContext; @@ -81,6 +83,13 @@ export default class EmbeddedPage extends React.PureComponent { } body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1)); + + if (this.props.replaceMap) { + Object.keys(this.props.replaceMap).forEach(key => { + body = body.split(key).join(this.props.replaceMap[key]); + }); + } + this.setState({ page: body }); }, ); diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 52002f0591..e6702e385e 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -2021,7 +2021,7 @@ export default createReactClass({ } } else if (this.state.view === VIEWS.WELCOME) { const Welcome = sdk.getComponent('auth.Welcome'); - view = ; + view = ; } else if (this.state.view === VIEWS.REGISTER) { const Registration = sdk.getComponent('structures.auth.Registration'); view = ( diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index 58f117ea36..60ceae5343 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -18,6 +18,7 @@ import React from 'react'; import * as sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import AuthPage from "./AuthPage"; +import * as Matrix from "matrix-js-sdk"; export default class Welcome extends React.PureComponent { render() { @@ -33,11 +34,23 @@ export default class Welcome extends React.PureComponent { pageUrl = 'welcome.html'; } + const {hsUrl, isUrl} = this.props.serverConfig; + const tmpClient = Matrix.createClient({ + baseUrl: hsUrl, + idBaseUrl: isUrl, + }); + const callbackUrl = this.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl()); + return (
-
From f4d3cc8ee62671336981f486561be58d4e3e0f6c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 14:05:14 +0000 Subject: [PATCH 081/114] revert stale changes Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/BasePlatform.js | 19 +----------------- src/components/structures/auth/Login.js | 26 +++++++------------------ src/components/views/auth/Welcome.js | 4 ++++ 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 489d9474a1..5d809eb28f 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -22,11 +22,6 @@ limitations under the License. import {MatrixClient} from "matrix-js-sdk"; import dis from './dispatcher'; import BaseEventIndexManager from './indexing/BaseEventIndexManager'; -import Modal from "./Modal"; -import InfoDialog from "./components/views/dialogs/InfoDialog"; -import {_t} from "./languageHandler"; -import Spinner from "./components/views/elements/Spinner"; -import React from "react"; /** * Base class for classes that provide platform-specific functionality @@ -188,21 +183,9 @@ 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 {boolean} showModal whether or not to show the spinner modal. */ - startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas", showModal: boolean) { + startSingleSignOn(mxClient: MatrixClient, loginType: "sso"|"cas") { const callbackUrl = this.getSSOCallbackUrl(mxClient.getHomeserverUrl(), mxClient.getIdentityServerUrl()); - if (showModal) { - Modal.createTrackedDialog('BasePlatform', 'SSO', InfoDialog, { - title: _t("Single sign-on"), - description: , - }); - } window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType); // redirect to SSO } } diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 134f6fa6f3..bfabc34a62 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -53,10 +53,6 @@ _td("Invalid base_url for m.identity_server"); _td("Identity server URL does not appear to be a valid identity server"); _td("General failure"); -const M_LOGIN_CAS = "m.login.cas"; -const M_LOGIN_SSO = "m.login.sso"; -const SSO_FLOWS = [M_LOGIN_SSO, M_LOGIN_CAS]; - /** * A wire component which glues together login UI components and Login logic */ @@ -126,11 +122,11 @@ export default createReactClass({ 'm.login.password': this._renderPasswordStep, // CAS and SSO are the same thing, modulo the url we link to - [M_LOGIN_CAS]: () => this._renderSsoStep("cas"), - [M_LOGIN_SSO]: () => this._renderSsoStep("sso"), + 'm.login.cas': () => this._renderSsoStep("cas"), + 'm.login.sso': () => this._renderSsoStep("sso"), }; - this._initLoginLogic(true); + this._initLoginLogic(); }, componentWillUnmount: function() { @@ -142,7 +138,7 @@ export default createReactClass({ newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return; // Ensure that we end up actually logging in to the right place - this._initLoginLogic(false, newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); + this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl); }, onPasswordLoginError: function(errorText) { @@ -346,12 +342,12 @@ export default createReactClass({ onTryRegisterClick: function(ev) { const step = this._getCurrentFlowStep(); - if (SSO_FLOWS.includes(step)) { + if (step === 'm.login.sso' || step === 'm.login.cas') { // If we're showing SSO it means that registration is also probably disabled, // so intercept the click and instead pretend the user clicked 'Sign in with SSO'. ev.preventDefault(); ev.stopPropagation(); - const ssoKind = step === M_LOGIN_SSO ? 'sso' : 'cas'; + const ssoKind = step === 'm.login.sso' ? 'sso' : 'cas'; PlatformPeg.get().startSingleSignOn(this._loginLogic.createTemporaryClient(), ssoKind); } else { // Don't intercept - just go through to the register page @@ -373,7 +369,7 @@ export default createReactClass({ }); }, - _initLoginLogic: async function(initial, hsUrl, isUrl) { + _initLoginLogic: async function(hsUrl, isUrl) { hsUrl = hsUrl || this.props.serverConfig.hsUrl; isUrl = isUrl || this.props.serverConfig.isUrl; @@ -433,14 +429,6 @@ export default createReactClass({ continue; } - // if this is the initial render and the flow we choose is SSO/CAS then go to it automatically - // we do not do this when the user has changed to the server manually as that may be jarring. - // Only allow it when disable_custom_urls is asserted so that we don't prevent user from changing HS URL - if (initial && SdkConfig.get()['disable_custom_urls'] && SSO_FLOWS.includes(flows[i].type)) { - const tmpCli = this._loginLogic.createTemporaryClient(); - PlatformPeg.get().startSingleSignOn(tmpCli, flows[i].type === M_LOGIN_SSO ? "sso": "cas", true); - } - // we just pick the first flow where we support all the // steps. (we don't have a UI for multiple logins so let's skip // that for now). diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index 60ceae5343..70c5569cd9 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -19,6 +19,10 @@ import * as sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import AuthPage from "./AuthPage"; import * as Matrix from "matrix-js-sdk"; +import {_td} from "../../../languageHandler"; + +// translatable strings for Welcome pages +_td("Sign in with SSO"); export default class Welcome extends React.PureComponent { render() { From 6664535a269da603a88562f1cf943d405d2c425e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 14:05:32 +0000 Subject: [PATCH 082/114] change welcome page placeholders Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/auth/Welcome.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index 70c5569cd9..1893b04d76 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -52,8 +52,8 @@ export default class Welcome extends React.PureComponent { className="mx_WelcomePage" url={pageUrl} replaceMap={{ - "$ssoUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "sso"), - "$casUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "cas"), + "$riot:ssoUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "sso"), + "$riot:casUrl": tmpClient.getSsoLoginUrl(callbackUrl.toString(), "cas"), }} /> From de0895b88193aca8e01c61b4e175a7812317dfa2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 14:06:21 +0000 Subject: [PATCH 083/114] i18n Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/i18n/strings/en_EN.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4677f40ce1..fbb3fd9f6f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -25,8 +25,6 @@ "Error": "Error", "Unable to load! Check your network connectivity and try again.": "Unable to load! Check your network connectivity and try again.", "Dismiss": "Dismiss", - "Single sign-on": "Single sign-on", - "Click here if you're not redirected automatically": "Click here if you're not redirected automatically", "Call Failed": "Call Failed", "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "There are unknown sessions in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.", "Review Sessions": "Review Sessions", @@ -1872,6 +1870,7 @@ "Find other public servers or use a custom server": "Find other public servers or use a custom server", "Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s", "Sign in to your Matrix account on ": "Sign in to your Matrix account on ", + "Sign in with SSO": "Sign in with SSO", "Sorry, your browser is not able to run Riot.": "Sorry, your browser is not able to run Riot.", "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.", "Please install Chrome, Firefox, or Safari for the best experience.": "Please install Chrome, Firefox, or Safari for the best experience.", From 9d0ed6e80040502fb8d8c3db359826133c97ab53 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 27 Mar 2020 14:19:43 +0000 Subject: [PATCH 084/114] fix copy-pasta error Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/auth/Welcome.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/auth/Welcome.js b/src/components/views/auth/Welcome.js index 1893b04d76..7cbcf65d3c 100644 --- a/src/components/views/auth/Welcome.js +++ b/src/components/views/auth/Welcome.js @@ -20,6 +20,7 @@ import SdkConfig from '../../../SdkConfig'; import AuthPage from "./AuthPage"; import * as Matrix from "matrix-js-sdk"; import {_td} from "../../../languageHandler"; +import PlatformPeg from "../../../PlatformPeg"; // translatable strings for Welcome pages _td("Sign in with SSO"); @@ -43,7 +44,8 @@ export default class Welcome extends React.PureComponent { baseUrl: hsUrl, idBaseUrl: isUrl, }); - const callbackUrl = this.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl()); + const plaf = PlatformPeg.get(); + const callbackUrl = plaf.getSSOCallbackUrl(tmpClient.getHomeserverUrl(), tmpClient.getIdentityServerUrl()); return ( From fda533ab48321e09de49ccb3b356171a09918bda Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 27 Mar 2020 15:25:44 +0000 Subject: [PATCH 085/114] test for shield behaviour --- test/utils/ShieldUtils-test.js | 170 +++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 test/utils/ShieldUtils-test.js diff --git a/test/utils/ShieldUtils-test.js b/test/utils/ShieldUtils-test.js new file mode 100644 index 0000000000..5030d9c5c8 --- /dev/null +++ b/test/utils/ShieldUtils-test.js @@ -0,0 +1,170 @@ +import { shieldStatusForMembership } from '../../src/utils/ShieldUtils'; +import DMRoomMap from '../../src/utils/DMRoomMap'; + +function mkClient (selfTrust) { + return { + getUserId: () => "@self:localhost", + checkUserTrust: (userId) => ({ + isCrossSigningVerified: () => userId[1] == "T" + }), + checkDeviceTrust: (userId, deviceId) => ({ + isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T" + }), + getStoredDevicesForUser: async (userId) => ["DEVICE"], + } +} + +describe("mkClient self-test", function () { + test.each([true, false])("behaves well for self-trust=%s", (v) => { + const client = mkClient(v); + expect(client.checkDeviceTrust("@self:localhost", "DEVICE").isVerified()).toBe(v); + }); + + test.each([ + ["@TT:h", true], + ["@TF:h", true], + ["@FT:h", false], + ["@FF:h", false]]) + ("behaves well for user trust %s", (userId, trust) => { + expect(mkClient().checkUserTrust(userId).isCrossSigningVerified()).toBe(trust); + }); + + test.each([ + ["@TT:h", true], + ["@TF:h", false], + ["@FT:h", true], + ["@FF:h", false]]) + ("behaves well for device trust %s", (userId, trust) => { + expect(mkClient().checkDeviceTrust(userId, "device").isVerified()).toBe(trust); + }); +}); + +describe("shieldStatusForMembership self-trust behaviour", function () { + beforeAll(() => { + DMRoomMap._sharedInstance = { + getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, + }; + }); + + it.each( + [[true, true], [true, false], + [false, true], [false, false]] + )("2 unverified: returns 'normal', self-trust = %s, DM = %s", async (trusted, dm) => { + const client = mkClient(trusted); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@FF1:h", "@FF2:h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual("normal"); + }); + + it.each( + [["verified", true, true], ["verified", true, false], + ["verified", false, true], ["warning", false, false]] + )("2 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + const client = mkClient(trusted); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@TT2:h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["normal", true, true], ["normal", true, false], + ["normal", false, true], ["warning", false, false]] + )("2 mixed: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + const client = mkClient(trusted); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@FF2:h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["verified", true, true], ["verified", true, false], + ["warning", false, true], ["warning", false, false]] + )("0 others: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + const client = mkClient(trusted); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["verified", true, true], ["verified", true, false], + ["verified", false, true], ["verified", false, false]] + )("1 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + const client = mkClient(trusted); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@TT:h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["normal", true, true], ["normal", true, false], + ["normal", false, true], ["normal", false, false]] + )("1 unverified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { + const client = mkClient(trusted); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); +}); + +describe("shieldStatusForMembership other-trust behaviour", function () { + beforeAll(() => { + DMRoomMap._sharedInstance = { + getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, + }; + }); + + it.each( + [["warning", true], ["warning", false]] + )("1 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { + const client = mkClient(true); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["warning", true], ["warning", false]] + )("2 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { + const client = mkClient(true); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT: h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["normal", true], ["normal", false]] + )("2 unverified/untrusted: returns '%s', DM = %s", async (result, dm) => { + const client = mkClient(true); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT: h"].map((userId) => ({userId})), + } + const status = await shieldStatusForMembership(client, room); + expect(status).toEqual(result); + }); +}); From 4f3d4426ea05a9d7a8a0846f3422d13039eab27d Mon Sep 17 00:00:00 2001 From: Zoe Date: Fri, 27 Mar 2020 15:37:59 +0000 Subject: [PATCH 086/114] lint --- test/utils/ShieldUtils-test.js | 58 +++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/test/utils/ShieldUtils-test.js b/test/utils/ShieldUtils-test.js index 5030d9c5c8..973798fa4b 100644 --- a/test/utils/ShieldUtils-test.js +++ b/test/utils/ShieldUtils-test.js @@ -1,20 +1,20 @@ import { shieldStatusForMembership } from '../../src/utils/ShieldUtils'; import DMRoomMap from '../../src/utils/DMRoomMap'; -function mkClient (selfTrust) { +function mkClient(selfTrust) { return { getUserId: () => "@self:localhost", checkUserTrust: (userId) => ({ - isCrossSigningVerified: () => userId[1] == "T" + isCrossSigningVerified: () => userId[1] == "T", }), checkDeviceTrust: (userId, deviceId) => ({ - isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T" + isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T", }), getStoredDevicesForUser: async (userId) => ["DEVICE"], - } + }; } -describe("mkClient self-test", function () { +describe("mkClient self-test", function() { test.each([true, false])("behaves well for self-trust=%s", (v) => { const client = mkClient(v); expect(client.checkDeviceTrust("@self:localhost", "DEVICE").isVerified()).toBe(v); @@ -24,8 +24,8 @@ describe("mkClient self-test", function () { ["@TT:h", true], ["@TF:h", true], ["@FT:h", false], - ["@FF:h", false]]) - ("behaves well for user trust %s", (userId, trust) => { + ["@FF:h", false]], + )("behaves well for user trust %s", (userId, trust) => { expect(mkClient().checkUserTrust(userId).isCrossSigningVerified()).toBe(trust); }); @@ -33,13 +33,13 @@ describe("mkClient self-test", function () { ["@TT:h", true], ["@TF:h", false], ["@FT:h", true], - ["@FF:h", false]]) - ("behaves well for device trust %s", (userId, trust) => { + ["@FF:h", false]], + )("behaves well for device trust %s", (userId, trust) => { expect(mkClient().checkDeviceTrust(userId, "device").isVerified()).toBe(trust); }); }); -describe("shieldStatusForMembership self-trust behaviour", function () { +describe("shieldStatusForMembership self-trust behaviour", function() { beforeAll(() => { DMRoomMap._sharedInstance = { getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, @@ -48,84 +48,84 @@ describe("shieldStatusForMembership self-trust behaviour", function () { it.each( [[true, true], [true, false], - [false, true], [false, false]] + [false, true], [false, false]], )("2 unverified: returns 'normal', self-trust = %s, DM = %s", async (trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF1:h", "@FF2:h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual("normal"); }); it.each( [["verified", true, true], ["verified", true, false], - ["verified", false, true], ["warning", false, false]] + ["verified", false, true], ["warning", false, false]], )("2 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@TT2:h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); it.each( [["normal", true, true], ["normal", true, false], - ["normal", false, true], ["warning", false, false]] + ["normal", false, true], ["warning", false, false]], )("2 mixed: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@FF2:h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); it.each( [["verified", true, true], ["verified", true, false], - ["warning", false, true], ["warning", false, false]] + ["warning", false, true], ["warning", false, false]], )("0 others: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); it.each( [["verified", true, true], ["verified", true, false], - ["verified", false, true], ["verified", false, false]] + ["verified", false, true], ["verified", false, false]], )("1 verified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT:h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); it.each( [["normal", true, true], ["normal", true, false], - ["normal", false, true], ["normal", false, false]] + ["normal", false, true], ["normal", false, false]], )("1 unverified: returns '%s', self-trust = %s, DM = %s", async (result, trusted, dm) => { const client = mkClient(trusted); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); }); -describe("shieldStatusForMembership other-trust behaviour", function () { +describe("shieldStatusForMembership other-trust behaviour", function() { beforeAll(() => { DMRoomMap._sharedInstance = { getUserIdForRoomId: (roomId) => roomId === "DM" ? "@any:h" : null, @@ -133,37 +133,37 @@ describe("shieldStatusForMembership other-trust behaviour", function () { }); it.each( - [["warning", true], ["warning", false]] + [["warning", true], ["warning", false]], )("1 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); it.each( - [["warning", true], ["warning", false]] + [["warning", true], ["warning", false]], )("2 verified/untrusted: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT: h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); it.each( - [["normal", true], ["normal", false]] + [["normal", true], ["normal", false]], )("2 unverified/untrusted: returns '%s', DM = %s", async (result, dm) => { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT: h"].map((userId) => ({userId})), - } + }; const status = await shieldStatusForMembership(client, room); expect(status).toEqual(result); }); From 29bb7e38fe0cf3258f986a5ade66fc71578b5618 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 27 Mar 2020 16:45:46 +0100 Subject: [PATCH 087/114] fix undismissable toasts --- src/DeviceListener.js | 138 ++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 71 deletions(-) diff --git a/src/DeviceListener.js b/src/DeviceListener.js index 9161a462eb..f8555c7602 100644 --- a/src/DeviceListener.js +++ b/src/DeviceListener.js @@ -119,89 +119,85 @@ export default class DeviceListener { const crossSigningReady = await cli.isCrossSigningReady(); - if (!crossSigningReady) { - if (this._dismissedThisDeviceToast) { - ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); - return; - } - - // cross signing isn't enabled - nag to enable it - // There are 3 different toasts for: - if (cli.getStoredCrossSigningForUser(cli.getUserId())) { - // Cross-signing on account but this device doesn't trust the master key (verify this session) - ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Verify this session"), - icon: "verification_warning", - props: {kind: 'verify_this_session'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), - }); - } else { - const backupInfo = await this._getKeyBackupInfo(); - if (backupInfo) { - // No cross-signing on account but key backup available (upgrade encryption) + if (this._dismissedThisDeviceToast) { + ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); + } else { + if (!crossSigningReady) { + // cross signing isn't enabled - nag to enable it + // There are 3 different toasts for: + if (cli.getStoredCrossSigningForUser(cli.getUserId())) { + // Cross-signing on account but this device doesn't trust the master key (verify this session) ToastStore.sharedInstance().addOrReplaceToast({ key: THIS_DEVICE_TOAST_KEY, - title: _t("Encryption upgrade available"), + title: _t("Verify this session"), icon: "verification_warning", - props: {kind: 'upgrade_encryption'}, + props: {kind: 'verify_this_session'}, component: sdk.getComponent("toasts.SetupEncryptionToast"), }); } else { - // No cross-signing or key backup on account (set up encryption) + const backupInfo = await this._getKeyBackupInfo(); + if (backupInfo) { + // No cross-signing on account but key backup available (upgrade encryption) + ToastStore.sharedInstance().addOrReplaceToast({ + key: THIS_DEVICE_TOAST_KEY, + title: _t("Encryption upgrade available"), + icon: "verification_warning", + props: {kind: 'upgrade_encryption'}, + component: sdk.getComponent("toasts.SetupEncryptionToast"), + }); + } else { + // No cross-signing or key backup on account (set up encryption) + ToastStore.sharedInstance().addOrReplaceToast({ + key: THIS_DEVICE_TOAST_KEY, + title: _t("Set up encryption"), + icon: "verification_warning", + props: {kind: 'set_up_encryption'}, + component: sdk.getComponent("toasts.SetupEncryptionToast"), + }); + } + } + return; + } else if (await cli.secretStorageKeyNeedsUpgrade()) { + ToastStore.sharedInstance().addOrReplaceToast({ + key: THIS_DEVICE_TOAST_KEY, + title: _t("Encryption upgrade available"), + icon: "verification_warning", + props: {kind: 'upgrade_ssss'}, + component: sdk.getComponent("toasts.SetupEncryptionToast"), + }); + } + } + + // as long as cross-signing isn't ready, + // you can't see or dismiss any device toasts + if (crossSigningReady) { + const newActiveToasts = new Set(); + + const devices = await cli.getStoredDevicesForUser(cli.getUserId()); + for (const device of devices) { + if (device.deviceId == cli.deviceId) continue; + + const deviceTrust = await cli.checkDeviceTrust(cli.getUserId(), device.deviceId); + if (deviceTrust.isCrossSigningVerified() || this._dismissed.has(device.deviceId)) { + ToastStore.sharedInstance().dismissToast(toastKey(device.deviceId)); + } else { + this._activeNagToasts.add(device.deviceId); ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Set up encryption"), + key: toastKey(device.deviceId), + title: _t("Unverified login. Was this you?"), icon: "verification_warning", - props: {kind: 'set_up_encryption'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), + props: { device }, + component: sdk.getComponent("toasts.UnverifiedSessionToast"), }); + newActiveToasts.add(device.deviceId); } } - return; - } else if (await cli.secretStorageKeyNeedsUpgrade()) { - if (this._dismissedThisDeviceToast) { - ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); - return; + + // clear any other outstanding toasts (eg. logged out devices) + for (const deviceId of this._activeNagToasts) { + if (!newActiveToasts.has(deviceId)) ToastStore.sharedInstance().dismissToast(toastKey(deviceId)); } - - ToastStore.sharedInstance().addOrReplaceToast({ - key: THIS_DEVICE_TOAST_KEY, - title: _t("Encryption upgrade available"), - icon: "verification_warning", - props: {kind: 'upgrade_ssss'}, - component: sdk.getComponent("toasts.SetupEncryptionToast"), - }); - } else { - ToastStore.sharedInstance().dismissToast(THIS_DEVICE_TOAST_KEY); + this._activeNagToasts = newActiveToasts; } - - const newActiveToasts = new Set(); - - const devices = await cli.getStoredDevicesForUser(cli.getUserId()); - for (const device of devices) { - if (device.deviceId == cli.deviceId) continue; - - const deviceTrust = await cli.checkDeviceTrust(cli.getUserId(), device.deviceId); - if (deviceTrust.isCrossSigningVerified() || this._dismissed.has(device.deviceId)) { - ToastStore.sharedInstance().dismissToast(toastKey(device.deviceId)); - } else { - this._activeNagToasts.add(device.deviceId); - ToastStore.sharedInstance().addOrReplaceToast({ - key: toastKey(device.deviceId), - title: _t("Unverified login. Was this you?"), - icon: "verification_warning", - props: { device }, - component: sdk.getComponent("toasts.UnverifiedSessionToast"), - }); - newActiveToasts.add(device.deviceId); - } - } - - // clear any other outstanding toasts (eg. logged out devices) - for (const deviceId of this._activeNagToasts) { - if (!newActiveToasts.has(deviceId)) ToastStore.sharedInstance().dismissToast(toastKey(deviceId)); - } - this._activeNagToasts = newActiveToasts; } } From 37619dd127b8d76858452bac016c40f49cfed094 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Sat, 28 Mar 2020 00:21:17 +0000 Subject: [PATCH 088/114] Show red shield for users that become unverified For any users that we previously verified but that are not unverified, we will now mark them and rooms they are in with a red shield. Fixes https://github.com/vector-im/riot-web/issues/12808 --- src/components/views/right_panel/UserInfo.js | 6 ++++-- src/components/views/rooms/MemberTile.js | 6 +++--- src/utils/ShieldUtils.ts | 8 ++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/views/right_panel/UserInfo.js b/src/components/views/right_panel/UserInfo.js index ee47f08aa2..a2081dc9e4 100644 --- a/src/components/views/right_panel/UserInfo.js +++ b/src/components/views/right_panel/UserInfo.js @@ -68,8 +68,10 @@ export const getE2EStatus = (cli, userId, devices) => { return hasUnverifiedDevice ? "warning" : "verified"; } const isMe = userId === cli.getUserId(); - const userVerified = cli.checkUserTrust(userId).isCrossSigningVerified(); - if (!userVerified) return "normal"; + const userTrust = cli.checkUserTrust(userId); + if (!userTrust.isCrossSigningVerified()) { + return userTrust.wasCrossSigningVerified() ? "warning" : "normal"; + } const anyDeviceUnverified = devices.some(device => { const { deviceId } = device; diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index a0e900b5fc..bf2a1bee23 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -121,10 +121,10 @@ export default createReactClass({ const cli = MatrixClientPeg.get(); const { userId } = this.props.member; const isMe = userId === cli.getUserId(); - const userVerified = cli.checkUserTrust(userId).isCrossSigningVerified(); - if (!userVerified) { + const userTrust = cli.checkUserTrust(userId); + if (!userTrust.isCrossSigningVerified()) { this.setState({ - e2eStatus: "normal", + e2eStatus: userTrust.wasCrossSigningVerified() ? "warning" : "normal", }); return; } diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts index 3c7cae8c8d..f427b0b0b6 100644 --- a/src/utils/ShieldUtils.ts +++ b/src/utils/ShieldUtils.ts @@ -5,6 +5,7 @@ interface Client { getUserId: () => string; checkUserTrust: (userId: string) => { isCrossSigningVerified: () => boolean + wasCrossSigningVerified: () => boolean }; getStoredDevicesForUser: (userId: string) => Promise<[{ deviceId: string }]>; checkDeviceTrust: (userId: string, deviceId: string) => { @@ -29,6 +30,13 @@ export async function shieldStatusForMembership(client: Client, room: Room): Pro verified : unverified).push(userId); }); + /* Alarm if any unverified users were verified before. */ + for (const userId of unverified) { + if (client.checkUserTrust(userId).wasCrossSigningVerified()) { + return "warning"; + } + } + /* Check all verified user devices. */ /* Don't alarm if no other users are verified */ const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified From f586aaab358004217ecc7071c0acf77c60605e19 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 28 Mar 2020 00:51:01 +0000 Subject: [PATCH 089/114] Fix ugly scrollbars in TabbedView (settings), emojipicker and widgets Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/TabbedView.tsx | 5 +++-- src/components/views/emojipicker/EmojiPicker.js | 5 +++-- src/components/views/rooms/AuxPanel.js | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index ea485acc1a..c0e0e58db8 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -20,6 +20,7 @@ import * as React from "react"; import {_t} from '../../languageHandler'; import * as PropTypes from "prop-types"; import * as sdk from "../../index"; +import AutoHideScrollbar from './AutoHideScrollbar'; import { ReactNode } from "react"; /** @@ -113,9 +114,9 @@ export default class TabbedView extends React.Component { private _renderTabPanel(tab: Tab): React.ReactNode { return (
-
+ {tab.body} -
+
); } diff --git a/src/components/views/emojipicker/EmojiPicker.js b/src/components/views/emojipicker/EmojiPicker.js index ca8f0c0565..cacc15a5f9 100644 --- a/src/components/views/emojipicker/EmojiPicker.js +++ b/src/components/views/emojipicker/EmojiPicker.js @@ -22,6 +22,7 @@ import { _t } from '../../../languageHandler'; import * as recent from '../../../emojipicker/recent'; import {DATA_BY_CATEGORY, getEmojiFromUnicode} from "../../../emoji"; +import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; export const CATEGORY_HEADER_HEIGHT = 22; export const EMOJI_HEIGHT = 37; @@ -214,7 +215,7 @@ class EmojiPicker extends React.Component {
-
+ this.bodyRef.current = e} onScroll={this.onScroll}> {this.categories.map(category => { const emojis = this.memoizedDataByCategory[category.id]; const categoryElement = ( + {this.state.previewEmoji || !this.props.showQuickReactions ? : } diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index 0b34739e0e..e102b0dba4 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -27,6 +27,7 @@ import { _t } from '../../../languageHandler'; import classNames from 'classnames'; import RateLimitedFunc from '../../../ratelimitedfunc'; import SettingsStore from "../../../settings/SettingsStore"; +import AutoHideScrollbar from "../../structures/AutoHideScrollbar"; export default createReactClass({ @@ -264,14 +265,14 @@ export default createReactClass({ } return ( -
+ { stateViews } { appsDrawer } { fileDropTarget } { callView } { conferenceCallNotification } { this.props.children } -
+ ); }, }); From b2d905ef2c7c53ba4f07e442a6414c1a7c0976be Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 20:07:32 +0100 Subject: [PATCH 090/114] Make FormatButton use AccessibleButtons Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/rooms/MessageComposerFormatBar.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/views/rooms/MessageComposerFormatBar.js b/src/components/views/rooms/MessageComposerFormatBar.js index 79ae9f34e8..42d54f5987 100644 --- a/src/components/views/rooms/MessageComposerFormatBar.js +++ b/src/components/views/rooms/MessageComposerFormatBar.js @@ -19,12 +19,13 @@ import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; import classNames from 'classnames'; +import AccessibleButton from "../elements/AccessibleButton"; export default class MessageComposerFormatBar extends React.PureComponent { static propTypes = { onAction: PropTypes.func.isRequired, shortcuts: PropTypes.object.isRequired, - } + }; constructor(props) { super(props); @@ -64,7 +65,7 @@ class FormatButton extends React.PureComponent { icon: PropTypes.string.isRequired, shortcut: PropTypes.string, visible: PropTypes.bool, - } + }; render() { const InteractiveTooltip = sdk.getComponent('elements.InteractiveTooltip'); @@ -82,11 +83,12 @@ class FormatButton extends React.PureComponent { return ( - - + ); } From 2ff16844e5b1b97f3de5ca391a96b2ad59129fc4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 20:12:10 +0100 Subject: [PATCH 091/114] Make ELS somewhat more accessible Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../views/elements/EventListSummary.js | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/components/views/elements/EventListSummary.js b/src/components/views/elements/EventListSummary.js index 7a69398071..79c84293c2 100644 --- a/src/components/views/elements/EventListSummary.js +++ b/src/components/views/elements/EventListSummary.js @@ -20,6 +20,7 @@ import MemberAvatar from '../avatars/MemberAvatar'; import { _t } from '../../../languageHandler'; import {MatrixEvent, RoomMember} from "matrix-js-sdk"; import {useStateToggle} from "../../../hooks/useStateToggle"; +import AccessibleButton from "./AccessibleButton"; const EventListSummary = ({events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText}) => { const [expanded, toggleExpanded] = useStateToggle(startExpanded); @@ -42,24 +43,15 @@ const EventListSummary = ({events, children, threshold=3, onToggle, startExpande ); } + let body; if (expanded) { - return ( -
-
- { _t('collapse') } -
-
 
- { children } -
- ); - } - - const avatars = summaryMembers.map((m) => ); - return ( -
-
- { _t('expand') } -
+ body = +
 
+ { children } +
; + } else { + const avatars = summaryMembers.map((m) => ); + body = (
@@ -70,6 +62,15 @@ const EventListSummary = ({events, children, threshold=3, onToggle, startExpande
+ ); + } + + return ( +
+ + { expanded ? _t('collapse') : _t('expand') } + + { body }
); }; From 2a54a8aa859a5a20e4693211ca1e0534587a581f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 20:33:01 +0100 Subject: [PATCH 092/114] Escape should cancel a reply Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/rooms/SendMessageComposer.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 90c37780fe..d87d99dc46 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -131,8 +131,13 @@ export default class SendMessageComposer extends React.Component { this.onVerticalArrow(event, false); } else if (this._prepareToEncrypt) { this._prepareToEncrypt(); + } else if (event.key === Key.ESCAPE) { + dis.dispatch({ + action: 'reply_to_event', + event: null, + }); } - } + }; onVerticalArrow(e, up) { // arrows from an initial-caret composer navigates recent messages to edit From acc41585abf3c5608a9661b7fb552ca488d713a4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 20:33:27 +0100 Subject: [PATCH 093/114] Fix CmdOrCtrl+K for collapsed LHS Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 52002f0591..9d17cd8647 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -657,6 +657,7 @@ export default createReactClass({ collapseLhs: true, }); break; + case 'focus_room_filter': // for CtrlOrCmd+K to work by expanding the left panel first case 'show_left_panel': this.setState({ collapseLhs: false, From 167f953ac7653df88ee7378d846147d9ba8b2721 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 20:35:35 +0100 Subject: [PATCH 094/114] Document Composer:Escape to cancel a reply Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/accessibility/KeyboardShortcuts.tsx | 5 +++++ src/i18n/strings/en_EN.json | 1 + 2 files changed, 6 insertions(+) diff --git a/src/accessibility/KeyboardShortcuts.tsx b/src/accessibility/KeyboardShortcuts.tsx index c2739beefa..bcbf3d6810 100644 --- a/src/accessibility/KeyboardShortcuts.tsx +++ b/src/accessibility/KeyboardShortcuts.tsx @@ -118,6 +118,11 @@ const shortcuts: Record = { key: Key.ARROW_DOWN, }], description: _td("Navigate composer history"), + }, { + keybinds: [{ + key: Key.ESCAPE, + }], + description: _td("Cancel replying to a message"), }, ], diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a6e195aa16..2f19fc982c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2216,6 +2216,7 @@ "Navigate recent messages to edit": "Navigate recent messages to edit", "Jump to start/end of the composer": "Jump to start/end of the composer", "Navigate composer history": "Navigate composer history", + "Cancel replying to a message": "Cancel replying to a message", "Toggle microphone mute": "Toggle microphone mute", "Toggle video on/off": "Toggle video on/off", "Jump to room search": "Jump to room search", From 19aae087e032157a7f2787f3ca39685c4315ce7b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 22:59:15 +0100 Subject: [PATCH 095/114] Field: make id optional, generate one if not provided Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../dialogs/eventindex/ManageEventIndexDialog.js | 1 - .../secretstorage/CreateSecretStorageDialog.js | 3 --- src/components/structures/auth/ForgotPassword.js | 3 --- src/components/structures/auth/SoftLogout.js | 1 - .../views/auth/InteractiveAuthEntryComponents.js | 1 - src/components/views/auth/ModularServerConfig.js | 3 ++- src/components/views/auth/PasswordLogin.js | 5 ----- src/components/views/auth/RegistrationForm.js | 2 -- src/components/views/auth/ServerConfig.js | 5 +++-- src/components/views/dialogs/BugReportDialog.js | 2 -- src/components/views/dialogs/CreateRoomDialog.js | 6 +++--- .../views/dialogs/DeactivateAccountDialog.js | 1 - src/components/views/dialogs/DevtoolsDialog.js | 2 +- src/components/views/dialogs/ReportEventDialog.js | 1 - src/components/views/dialogs/TextInputDialog.js | 1 - src/components/views/elements/EditableItemList.js | 2 +- src/components/views/elements/Field.js | 13 +++++++++++-- src/components/views/elements/PowerSelector.js | 4 ++-- src/components/views/elements/RoomAliasField.js | 2 -- src/components/views/room_settings/AliasSettings.js | 1 - .../views/room_settings/RoomProfileSettings.js | 2 +- src/components/views/settings/ChangePassword.js | 4 +--- src/components/views/settings/ProfileSettings.js | 2 +- src/components/views/settings/SetIdServer.js | 4 ++-- .../views/settings/account/EmailAddresses.js | 2 +- .../views/settings/account/PhoneNumbers.js | 4 ++-- .../views/settings/discovery/PhoneNumbers.js | 2 +- .../settings/tabs/user/GeneralUserSettingsTab.js | 3 +-- .../settings/tabs/user/MjolnirUserSettingsTab.js | 2 -- .../tabs/user/PreferencesUserSettingsTab.js | 3 --- .../settings/tabs/user/VoiceUserSettingsTab.js | 6 +++--- 31 files changed, 36 insertions(+), 57 deletions(-) diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js index 371fdcaf64..3d7249b5a1 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.js @@ -168,7 +168,6 @@ export default class ManageEventIndexDialog extends React.Component { totalRooms: formatCountLong(this.state.roomCount), })}
{_t("Enter your account password to confirm the upgrade:")}
{introText}

{error} { _t("Confirm your identity by entering your account password below.") }

- this[FIELD_EMAIL] = field} type="text" label={emailPlaceholder} @@ -524,7 +523,6 @@ export default createReactClass({ onOptionChange={this.onPhoneCountryChange} />; return this[FIELD_PHONE_NUMBER] = field} type="text" label={phoneLabel} diff --git a/src/components/views/auth/ServerConfig.js b/src/components/views/auth/ServerConfig.js index a9e26b8fb7..37517c7484 100644 --- a/src/components/views/auth/ServerConfig.js +++ b/src/components/views/auth/ServerConfig.js @@ -223,7 +223,8 @@ export default class ServerConfig extends React.PureComponent { {sub} , })} - , })} -

- this._aliasFieldRef = ref} onChange={this.onAliasChange} domain={domain} value={this.state.alias} /> + this._aliasFieldRef = ref} onChange={this.onAliasChange} domain={domain} value={this.state.alias} />
); } else { @@ -188,8 +188,8 @@ export default createReactClass({ >
- this._nameFieldRef = ref} label={ _t('Name') } onChange={this.onNameChange} onValidate={this.onNameValidate} value={this.state.name} className="mx_CreateRoomDialog_name" /> - + this._nameFieldRef = ref} label={ _t('Name') } onChange={this.onNameChange} onValidate={this.onNameValidate} value={this.state.name} className="mx_CreateRoomDialog_name" /> + { privateLabel } { publicLabel } diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js index d7468933df..4c14f356e4 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.js +++ b/src/components/views/dialogs/DeactivateAccountDialog.js @@ -174,7 +174,6 @@ export default class DeactivateAccountDialog extends React.Component {

{ _t("To continue, please enter your password:") }

- {adminMessage}
- diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 8583c91a01..862b2cda96 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -23,9 +23,15 @@ import { debounce } from 'lodash'; // Invoke validation from user input (when typing, etc.) at most once every N ms. const VALIDATION_THROTTLE_MS = 200; +const BASE_ID = "mx_Field"; +let count = 1; +function getId() { + return `${BASE_ID}_${count++}`; +} + export default class Field extends React.PureComponent { static propTypes = { - // The field's ID, which binds the input and label together. + // The field's ID, which binds the input and label together. Immutable. id: PropTypes.string.isRequired, // The element to create. Defaults to "input". // To define options for a select, use @@ -70,6 +76,8 @@ export default class Field extends React.PureComponent { feedback: undefined, focused: false, }; + + this.id = this.props.id || getId(); } onFocus = (ev) => { @@ -167,6 +175,7 @@ export default class Field extends React.PureComponent { inputProps.type = inputProps.type || "text"; inputProps.ref = input => this.input = input; inputProps.placeholder = inputProps.placeholder || inputProps.label; + inputProps.id = this.id; // this overwrites the id from props inputProps.onFocus = this.onFocus; inputProps.onChange = this.onChange; @@ -211,7 +220,7 @@ export default class Field extends React.PureComponent { return
{prefixContainer} {fieldInput} - + {postfixContainer} {fieldTooltip}
; diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js index 2f4c08922a..eff14979a9 100644 --- a/src/components/views/elements/PowerSelector.js +++ b/src/components/views/elements/PowerSelector.js @@ -132,7 +132,7 @@ export default createReactClass({ const label = typeof this.props.label === "undefined" ? _t("Power level") : this.props.label; if (this.state.custom) { picker = ( - @@ -151,7 +151,7 @@ export default createReactClass({ }); picker = ( - {options} diff --git a/src/components/views/elements/RoomAliasField.js b/src/components/views/elements/RoomAliasField.js index b38047cd3b..d3de6a5d34 100644 --- a/src/components/views/elements/RoomAliasField.js +++ b/src/components/views/elements/RoomAliasField.js @@ -23,7 +23,6 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg'; // Controlled form component wrapping Field for inputting a room alias scoped to a given domain export default class RoomAliasField extends React.PureComponent { static propTypes = { - id: PropTypes.string.isRequired, domain: PropTypes.string.isRequired, onChange: PropTypes.func, value: PropTypes.string.isRequired, @@ -50,7 +49,6 @@ export default class RoomAliasField extends React.PureComponent { className="mx_RoomAliasField" prefix={poundSign} postfix={domain} - id={this.props.id} ref={ref => this._fieldRef = ref} onValidate={this._onValidate} placeholder={_t("e.g. my-room")} diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index 857a80c34a..3994d78390 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -62,7 +62,6 @@ class EditableAliasesList extends EditableItemList { className="mx_EditableItemList_newItem" >
- -
-
diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.js index 995959dc90..cb37271452 100644 --- a/src/components/views/settings/SetIdServer.js +++ b/src/components/views/settings/SetIdServer.js @@ -403,8 +403,8 @@ export default class SetIdServer extends React.Component { {bodyText} - - -
- - {_t("Theme")} {systemThemeSection} - diff --git a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js index 7f3a2c401d..d22b7ec183 100644 --- a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.js @@ -272,7 +272,6 @@ export default class MjolnirUserSettingsTab extends React.Component {
0) { const defaultDevice = getDefaultDevice(audioOutputs); speakerDropdown = ( - {this._renderDeviceOptions(audioOutputs, 'audioOutput')} @@ -175,7 +175,7 @@ export default class VoiceUserSettingsTab extends React.Component { if (audioInputs.length > 0) { const defaultDevice = getDefaultDevice(audioInputs); microphoneDropdown = ( - {this._renderDeviceOptions(audioInputs, 'audioInput')} @@ -187,7 +187,7 @@ export default class VoiceUserSettingsTab extends React.Component { if (videoInputs.length > 0) { const defaultDevice = getDefaultDevice(videoInputs); webcamDropdown = ( - {this._renderDeviceOptions(videoInputs, 'videoInput')} From 55c2d985715462db03145fb57260a30ff115eef8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 29 Mar 2020 23:16:57 +0100 Subject: [PATCH 096/114] Fix Field ctor Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/Field.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 862b2cda96..797c83bc06 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -69,8 +69,8 @@ export default class Field extends React.PureComponent { // All other props pass through to the . }; - constructor() { - super(); + constructor(props) { + super(props); this.state = { valid: undefined, feedback: undefined, From 098df07c67ed41b7698b9cda0b0beaa2ecf525db Mon Sep 17 00:00:00 2001 From: Zoe Date: Mon, 30 Mar 2020 10:18:47 +0100 Subject: [PATCH 097/114] review feedback --- src/components/structures/RoomView.js | 4 ++-- src/components/views/rooms/RoomTile.js | 4 ++-- src/utils/ShieldUtils.ts | 6 +++--- test/utils/ShieldUtils-test.js | 20 ++++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 74572b962a..1a1f60c7bd 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -55,7 +55,7 @@ import RightPanelStore from "../../stores/RightPanelStore"; import {haveTileForEvent} from "../views/rooms/EventTile"; import RoomContext from "../../contexts/RoomContext"; import MatrixClientContext from "../../contexts/MatrixClientContext"; -import { shieldStatusForMembership } from '../../utils/ShieldUtils'; +import { shieldStatusForRoom } from '../../utils/ShieldUtils'; const DEBUG = false; let debuglog = function() {}; @@ -820,7 +820,7 @@ export default createReactClass({ /* At this point, the user has encryption on and cross-signing on */ this.setState({ - e2eStatus: await shieldStatusForMembership(this.context, room), + e2eStatus: await shieldStatusForRoom(this.context, room), }); }, diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 478d6703b4..0c913b32da 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -37,7 +37,7 @@ import E2EIcon from './E2EIcon'; import InviteOnlyIcon from './InviteOnlyIcon'; // eslint-disable-next-line camelcase import rate_limited_func from '../../../ratelimitedfunc'; -import { shieldStatusForMembership } from '../../../utils/ShieldUtils'; +import { shieldStatusForRoom } from '../../../utils/ShieldUtils'; export default createReactClass({ displayName: 'RoomTile', @@ -157,7 +157,7 @@ export default createReactClass({ /* At this point, the user has encryption on and cross-signing on */ this.setState({ - e2eStatus: await shieldStatusForMembership(cli, this.props.room), + e2eStatus: await shieldStatusForRoom(cli, this.props.room), }); }, diff --git a/src/utils/ShieldUtils.ts b/src/utils/ShieldUtils.ts index 3c7cae8c8d..ef2d475293 100644 --- a/src/utils/ShieldUtils.ts +++ b/src/utils/ShieldUtils.ts @@ -17,7 +17,7 @@ interface Room { roomId: string; } -export async function shieldStatusForMembership(client: Client, room: Room): Promise { +export async function shieldStatusForRoom(client: Client, room: Room): Promise { const members = (await room.getEncryptionTargetMembers()).map(({userId}) => userId); const inDMMap = !!DMRoomMap.shared().getUserIdForRoomId(room.roomId); @@ -33,8 +33,8 @@ export async function shieldStatusForMembership(client: Client, room: Room): Pro /* Don't alarm if no other users are verified */ const includeUser = (verified.length > 0) && // Don't alarm for self in rooms where nobody else is verified !inDMMap && // Don't alarm for self in DMs with other users - (members.length !== 2) || // Don't alarm for self in 1:1 chats with other users - (members.length === 1); // Do alarm for self if we're alone in a room + (members.length !== 2) || // Don't alarm for self in 1:1 chats with other users + (members.length === 1); // Do alarm for self if we're alone in a room const targets = includeUser ? [...verified, client.getUserId()] : verified; for (const userId of targets) { const devices = await client.getStoredDevicesForUser(userId); diff --git a/test/utils/ShieldUtils-test.js b/test/utils/ShieldUtils-test.js index 973798fa4b..949f0ed42b 100644 --- a/test/utils/ShieldUtils-test.js +++ b/test/utils/ShieldUtils-test.js @@ -1,4 +1,4 @@ -import { shieldStatusForMembership } from '../../src/utils/ShieldUtils'; +import { shieldStatusForRoom } from '../../src/utils/ShieldUtils'; import DMRoomMap from '../../src/utils/DMRoomMap'; function mkClient(selfTrust) { @@ -55,7 +55,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF1:h", "@FF2:h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual("normal"); }); @@ -68,7 +68,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@TT2:h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -81,7 +81,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT1:h", "@FF2:h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -94,7 +94,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -107,7 +107,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TT:h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -120,7 +120,7 @@ describe("shieldStatusForMembership self-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); }); @@ -140,7 +140,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -152,7 +152,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT: h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); @@ -164,7 +164,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { roomId: dm ? "DM" : "other", getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT: h"].map((userId) => ({userId})), }; - const status = await shieldStatusForMembership(client, room); + const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); }); }); From eff9db42736fd6b77458dd0cb302f7637f651728 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 30 Mar 2020 12:41:05 +0200 Subject: [PATCH 098/114] bring back global style rules for thin scrollbars (also FF now!) --- res/css/structures/_AutoHideScrollbar.scss | 58 +++++++++++++++------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/res/css/structures/_AutoHideScrollbar.scss b/res/css/structures/_AutoHideScrollbar.scss index 3d91293c98..50842c71bc 100644 --- a/res/css/structures/_AutoHideScrollbar.scss +++ b/res/css/structures/_AutoHideScrollbar.scss @@ -14,28 +14,31 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_AutoHideScrollbar { - overflow-x: hidden; - overflow-y: auto; - overflow-y: overlay; // where supported - -ms-overflow-style: -ms-autohiding-scrollbar; - - &::-webkit-scrollbar { - width: 6px; - height: 6px; - background-color: transparent; - } - - &::-webkit-scrollbar-thumb { - border-radius: 3px; - background-color: transparent; - } - - scrollbar-color: transparent transparent; +// make any scrollbar grey and thin +html { + scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color; +} +// scrollbar-width is not inherited (but -color is, why?!), +// so declare it on every element +* { scrollbar-width: thin; } +::-webkit-scrollbar { + width: 6px; + height: 6px; + background-color: $scrollbar-track-color; +} + +::-webkit-scrollbar-thumb { + border-radius: 3px; + background-color: $scrollbar-thumb-color; +} + +// make auto-hide scrollbars not transparent again on hover .mx_AutoHideScrollbar:hover { + scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color; + &::-webkit-scrollbar { background-color: $scrollbar-track-color; } @@ -43,5 +46,22 @@ limitations under the License. &::-webkit-scrollbar-thumb { background-color: $scrollbar-thumb-color; } - scrollbar-color: $scrollbar-thumb-color $scrollbar-track-color; +} + +// make scrollbars transparent for autohide scrollbars +.mx_AutoHideScrollbar { + overflow-x: hidden; + overflow-y: auto; + overflow-y: overlay; // where supported + -ms-overflow-style: -ms-autohiding-scrollbar; + + &::-webkit-scrollbar { + background-color: transparent; + } + + &::-webkit-scrollbar-thumb { + background-color: transparent; + } + + scrollbar-color: transparent transparent; } From d369695e2229dc270bcaf4ba7a4d991dea5c3cc9 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 30 Mar 2020 13:31:42 +0100 Subject: [PATCH 099/114] Upgrade matrix-js-sdk to 5.2.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7579cab714..ce3ebea03f 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": "5.2.0-rc.1", + "matrix-js-sdk": "5.2.0", "minimist": "^1.2.0", "pako": "^1.0.5", "png-chunks-extract": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 04dcab02ed..bb6c69cb2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5689,10 +5689,10 @@ 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@5.2.0-rc.1: - version "5.2.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-5.2.0-rc.1.tgz#014239eb713ea3b648b7a573785d01edff1efd03" - integrity sha512-Jv/0kyZEtkfrD6mlq3g2YoFI2+eOBFOz5D186jJAvFg1W7UaQ5gn6iualR+2AKa1AIk2AT6lDcgPb1djZ7/3Vw== +matrix-js-sdk@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-5.2.0.tgz#f213fd7671538a57c6d7e0665a2178ab23b950da" + integrity sha512-IscXYNx+k7wq5fuwsvKlXSI6Z1/nQ7TBI8AkStHJOAR8rz1DRoxICLFJtWzpOUSrgQuSaUYpOm4+6hS6IVlmtA== dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From af1446e0ff87e73d4437d22f863e8cc3320d0feb Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 30 Mar 2020 13:41:02 +0100 Subject: [PATCH 100/114] Prepare changelog for v2.3.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 378ba6f1d1..02c085d0b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [2.3.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0) (2020-03-30) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.3.0-rc.1...v2.3.0) + + * Upgrade JS SDK to 5.2.0 + Changes in [2.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.3.0-rc.1) (2020-03-26) ============================================================================================================= [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.2.3...v2.3.0-rc.1) From e32e9cec06ca7f9a91da6aac33604b6e6cdec3e8 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 30 Mar 2020 13:41:02 +0100 Subject: [PATCH 101/114] v2.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce3ebea03f..6e3b4e912f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "2.3.0-rc.1", + "version": "2.3.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 78fd8e4569096043b22210821d20e085802bbcff Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 30 Mar 2020 13:42:52 +0100 Subject: [PATCH 102/114] Reset matrix-js-sdk back to develop branch --- package.json | 2 +- yarn.lock | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6e3b4e912f..7001b1cb21 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": "5.2.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "minimist": "^1.2.0", "pako": "^1.0.5", "png-chunks-extract": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index bb6c69cb2c..c5fc8268a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5689,10 +5689,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@5.2.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "5.2.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-5.2.0.tgz#f213fd7671538a57c6d7e0665a2178ab23b950da" - integrity sha512-IscXYNx+k7wq5fuwsvKlXSI6Z1/nQ7TBI8AkStHJOAR8rz1DRoxICLFJtWzpOUSrgQuSaUYpOm4+6hS6IVlmtA== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/223d37ffce674a23ca73702f04b9ba31cfd84196" dependencies: "@babel/runtime" "^7.8.3" another-json "^0.2.0" From 4371006c58b4565de1ef29d8d591333967fdcf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 30 Mar 2020 14:32:35 +0200 Subject: [PATCH 103/114] EventIndex: Better logging on how many events are added. This adds a bit more info to how many events are added, how many skipped and if they are skipped because they are undecryptable. --- src/indexing/EventIndex.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index 9e27451a78..65e536b4a1 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -469,6 +469,9 @@ export default class EventIndex extends EventEmitter { // decryption keys, do we want to retry this checkpoint at a later // stage? const filteredEvents = matrixEvents.filter(this.isValidEvent); + const undecryptableEvents = matrixEvents.filter((ev) => { + return ev.isDecryptionFailure(); + }); // Collect the redaction events so we can delete the redacted events // from the index. @@ -503,7 +506,10 @@ export default class EventIndex extends EventEmitter { console.log( "EventIndex: Crawled room", client.getRoom(checkpoint.roomId).name, - "and fetched", events.length, "events.", + "and fetched total", matrixEvents.length, "events of which", + events.length, "are being added,", redactionEvents.length, + "are redacted,", matrixEvents.length - events.length, + "are being skipped, undecryptable", undecryptableEvents.length ); try { From 836b348bff796dae1d56f2f19ea08dd258636333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damir=20Jeli=C4=87?= Date: Mon, 30 Mar 2020 14:39:01 +0200 Subject: [PATCH 104/114] EventIndex: Add a trailing comma. --- 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 65e536b4a1..14257af014 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -509,7 +509,7 @@ export default class EventIndex extends EventEmitter { "and fetched total", matrixEvents.length, "events of which", events.length, "are being added,", redactionEvents.length, "are redacted,", matrixEvents.length - events.length, - "are being skipped, undecryptable", undecryptableEvents.length + "are being skipped, undecryptable", undecryptableEvents.length, ); try { From b53b5cc45de7d70b8686246f2c8037845b1ba2a1 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 30 Mar 2020 15:24:43 +0100 Subject: [PATCH 105/114] Add wasCrossSigningVerified in test --- test/utils/ShieldUtils-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/utils/ShieldUtils-test.js b/test/utils/ShieldUtils-test.js index 949f0ed42b..66dfab4234 100644 --- a/test/utils/ShieldUtils-test.js +++ b/test/utils/ShieldUtils-test.js @@ -6,6 +6,7 @@ function mkClient(selfTrust) { getUserId: () => "@self:localhost", checkUserTrust: (userId) => ({ isCrossSigningVerified: () => userId[1] == "T", + wasCrossSigningVerified: () => userId[1] == "T", }), checkDeviceTrust: (userId, deviceId) => ({ isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T", From 520b4c3e65331913647ceb3fec04333478267174 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 30 Mar 2020 16:33:16 +0100 Subject: [PATCH 106/114] Add tests for was verified case --- test/utils/ShieldUtils-test.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/utils/ShieldUtils-test.js b/test/utils/ShieldUtils-test.js index 66dfab4234..5f676579fa 100644 --- a/test/utils/ShieldUtils-test.js +++ b/test/utils/ShieldUtils-test.js @@ -6,7 +6,7 @@ function mkClient(selfTrust) { getUserId: () => "@self:localhost", checkUserTrust: (userId) => ({ isCrossSigningVerified: () => userId[1] == "T", - wasCrossSigningVerified: () => userId[1] == "T", + wasCrossSigningVerified: () => userId[1] == "T" || userId[1] == "W", }), checkDeviceTrust: (userId, deviceId) => ({ isVerified: () => userId === "@self:localhost" ? selfTrust : userId[2] == "T", @@ -151,7 +151,7 @@ describe("shieldStatusForMembership other-trust behaviour", function() { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", - getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT: h"].map((userId) => ({userId})), + getEncryptionTargetMembers: () => ["@self:localhost", "@TF:h", "@TT:h"].map((userId) => ({userId})), }; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); @@ -163,7 +163,19 @@ describe("shieldStatusForMembership other-trust behaviour", function() { const client = mkClient(true); const room = { roomId: dm ? "DM" : "other", - getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT: h"].map((userId) => ({userId})), + getEncryptionTargetMembers: () => ["@self:localhost", "@FF:h", "@FT:h"].map((userId) => ({userId})), + }; + const status = await shieldStatusForRoom(client, room); + expect(status).toEqual(result); + }); + + it.each( + [["warning", true], ["warning", false]], + )("2 was verified: returns '%s', DM = %s", async (result, dm) => { + const client = mkClient(true); + const room = { + roomId: dm ? "DM" : "other", + getEncryptionTargetMembers: () => ["@self:localhost", "@WF:h", "@FT:h"].map((userId) => ({userId})), }; const status = await shieldStatusForRoom(client, room); expect(status).toEqual(result); From ca5d54031162d19379ddec255e8eeeff33d2b750 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 30 Mar 2020 10:40:26 -0600 Subject: [PATCH 107/114] Remove underscore from Jitsi conference names Fixes https://github.com/vector-im/riot-web/issues/12929 Note: we don't need this to fix conferences in our hosted instance (riot.im or modular.im), but it is a common thing for self-hosters, including sometimes ourselves, to accidentally make mistakes on. --- src/CallHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CallHandler.js b/src/CallHandler.js index 362db939a3..8284e788b4 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -430,7 +430,7 @@ async function _startCallApp(roomId, type) { return; } - const confId = `JitsiConference_${generateHumanReadableId()}`; + const confId = `JitsiConference${generateHumanReadableId()}`; const jitsiDomain = SdkConfig.get()['jitsi']['preferredDomain']; let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl(); From 147e7bc57ea5332fdf25129599773c3ed4e974fa Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 30 Mar 2020 18:42:47 +0100 Subject: [PATCH 108/114] Field: mark id as optional in propTypes Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/Field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 797c83bc06..2ebb90da26 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -32,7 +32,7 @@ function getId() { export default class Field extends React.PureComponent { static propTypes = { // The field's ID, which binds the input and label together. Immutable. - id: PropTypes.string.isRequired, + id: PropTypes.string, // The element to create. Defaults to "input". // To define options for a select, use element: PropTypes.oneOf(["input", "select", "textarea"]), From dd4331cd18ee048dd684bd879dd0ddc4a83e1f0a Mon Sep 17 00:00:00 2001 From: waylon531 Date: Sun, 29 Mar 2020 12:45:06 -0700 Subject: [PATCH 109/114] Added the /html command This command lets you send html messages through riot. This is incredibly useful for doing advanced formatting not supported by markdown and lets riot support even more html tags. Signed-off-by: Waylon Cude --- src/SlashCommands.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/SlashCommands.js b/src/SlashCommands.js index d306978f78..72ca4b1566 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -128,6 +128,15 @@ export const CommandMap = { }, category: CommandCategories.messages, }), + html: new Command({ + name: 'html', + args: '', + description: _td('Sends a message as html, without interpreting it as markdown'), + runFn: function(roomId, messages) { + return success(MatrixClientPeg.get().sendHtmlMessage(roomId, messages, messages)); + }, + category: CommandCategories.messages, + }), ddg: new Command({ name: 'ddg', args: '', From 9cf82d8743846efbaf1f40914cf24ffbbcb4f417 Mon Sep 17 00:00:00 2001 From: waylon531 Date: Sun, 29 Mar 2020 13:10:53 -0700 Subject: [PATCH 110/114] Updated strings I added a new string for the /html command and had to regerate strings for i18 Signed-off-by: Waylon Cude --- 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 a6e195aa16..3962899223 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -153,6 +153,7 @@ "Usage": "Usage", "Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", + "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", "/ddg is not a command": "/ddg is not a command", "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", From 690b5945d01134d8fd88672432a93befb03ce710 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 30 Mar 2020 21:40:09 +0100 Subject: [PATCH 111/114] Pass new secret storage key to bootstrap path This passes the newly created secret storage key down to the bootstrap path for temporary caching to avoid prompting the user for it again in the later stages of bootstrapping. Fixes https://github.com/vector-im/riot-web/issues/12867 --- .../CreateSecretStorageDialog.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 78e750b817..1149f230ef 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -69,6 +69,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { this._keyInfo = null; this._encodedRecoveryKey = null; + this._recoveryKey = null; this._recoveryKeyNode = null; this._setZxcvbnResultTimeout = null; @@ -234,14 +235,22 @@ export default class CreateSecretStorageDialog extends React.PureComponent { if (force) { await cli.bootstrapSecretStorage({ authUploadDeviceSigningKeys: this._doBootstrapUIAuth, - createSecretStorageKey: async () => this._keyInfo, + createSecretStorageKey: async () => [ + this._keyInfo, + this._encodedRecoveryKey, + this._recoveryKey, + ], setupNewKeyBackup: true, setupNewSecretStorage: true, }); } else { await cli.bootstrapSecretStorage({ authUploadDeviceSigningKeys: this._doBootstrapUIAuth, - createSecretStorageKey: async () => this._keyInfo, + createSecretStorageKey: async () => [ + this._keyInfo, + this._encodedRecoveryKey, + this._recoveryKey, + ], keyBackupInfo: this.state.backupInfo, setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup, getKeyBackupPassphrase: promptForBackupPassphrase, @@ -299,10 +308,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent { } _onSkipPassPhraseClick = async () => { - const [keyInfo, encodedRecoveryKey] = + const [keyInfo, encodedRecoveryKey, recoveryKey] = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(); this._keyInfo = keyInfo; this._encodedRecoveryKey = encodedRecoveryKey; + this._recoveryKey = recoveryKey; this.setState({ copied: false, downloaded: false, @@ -335,10 +345,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent { if (this.state.passPhrase !== this.state.passPhraseConfirm) return; - const [keyInfo, encodedRecoveryKey] = + const [keyInfo, encodedRecoveryKey, recoveryKey] = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase); this._keyInfo = keyInfo; this._encodedRecoveryKey = encodedRecoveryKey; + this._recoveryKey = recoveryKey; this.setState({ copied: false, downloaded: false, From a2a9dc6cd066da25a0fd609f7ac22215ebd72173 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2020 10:37:56 +0100 Subject: [PATCH 112/114] Fix peeking keeping two timeline update mechanisms in play Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 5 +++++ src/stores/RoomViewStore.js | 12 +++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 1a1f60c7bd..50b66bee93 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -236,6 +236,11 @@ export default createReactClass({ showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), }; + if (!initial && this.state.shouldPeek && !newState.shouldPeek) { + // Stop peeking because we have joined this room now + this.context.stopPeeking(); + } + // Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307 console.log( 'RVS update:', diff --git a/src/stores/RoomViewStore.js b/src/stores/RoomViewStore.js index 64dfd56b2f..b32e088a76 100644 --- a/src/stores/RoomViewStore.js +++ b/src/stores/RoomViewStore.js @@ -123,6 +123,9 @@ class RoomViewStore extends Store { case 'join_room_error': this._joinRoomError(payload); break; + case 'join_room_ready': + this._setState({ shouldPeek: false }); + break; case 'on_client_not_viable': case 'on_logged_out': this.reset(); @@ -259,11 +262,10 @@ class RoomViewStore extends Store { MatrixClientPeg.get().joinRoom( this._state.roomAlias || this._state.roomId, payload.opts, ).then(() => { - // We don't actually need to do anything here: we do *not* - // clear the 'joining' flag because the Room object and/or - // our 'joined' member event may not have come down the sync - // stream yet, and that's the point at which we'd consider - // the user joined to the room. + // We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not + // have come down the sync stream yet, and that's the point at which we'd consider the user joined to the + // room. + dis.dispatch({ action: 'join_room_ready' }); }, (err) => { dis.dispatch({ action: 'join_room_error', From 24c09cc4c8e647db9c6bfe69a681606cce4a3e68 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 31 Mar 2020 10:45:53 +0100 Subject: [PATCH 113/114] Convert secret storage key creation to object --- .../CreateSecretStorageDialog.js | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 1149f230ef..01a2856df0 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -67,8 +67,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent { constructor(props) { super(props); - this._keyInfo = null; - this._encodedRecoveryKey = null; this._recoveryKey = null; this._recoveryKeyNode = null; this._setZxcvbnResultTimeout = null; @@ -181,7 +179,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent { } _onDownloadClick = () => { - const blob = new Blob([this._encodedRecoveryKey], { + const blob = new Blob([this._recoveryKey.encodedPrivateKey], { type: 'text/plain;charset=us-ascii', }); FileSaver.saveAs(blob, 'recovery-key.txt'); @@ -235,22 +233,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent { if (force) { await cli.bootstrapSecretStorage({ authUploadDeviceSigningKeys: this._doBootstrapUIAuth, - createSecretStorageKey: async () => [ - this._keyInfo, - this._encodedRecoveryKey, - this._recoveryKey, - ], + createSecretStorageKey: async () => this._recoveryKey, setupNewKeyBackup: true, setupNewSecretStorage: true, }); } else { await cli.bootstrapSecretStorage({ authUploadDeviceSigningKeys: this._doBootstrapUIAuth, - createSecretStorageKey: async () => [ - this._keyInfo, - this._encodedRecoveryKey, - this._recoveryKey, - ], + createSecretStorageKey: async () => this._recoveryKey, keyBackupInfo: this.state.backupInfo, setupNewKeyBackup: !this.state.backupInfo && this.state.useKeyBackup, getKeyBackupPassphrase: promptForBackupPassphrase, @@ -308,11 +298,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { } _onSkipPassPhraseClick = async () => { - const [keyInfo, encodedRecoveryKey, recoveryKey] = + this._recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(); - this._keyInfo = keyInfo; - this._encodedRecoveryKey = encodedRecoveryKey; - this._recoveryKey = recoveryKey; this.setState({ copied: false, downloaded: false, @@ -345,11 +332,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent { if (this.state.passPhrase !== this.state.passPhraseConfirm) return; - const [keyInfo, encodedRecoveryKey, recoveryKey] = + this._recoveryKey = await MatrixClientPeg.get().createRecoveryKeyFromPassphrase(this.state.passPhrase); - this._keyInfo = keyInfo; - this._encodedRecoveryKey = encodedRecoveryKey; - this._recoveryKey = recoveryKey; this.setState({ copied: false, downloaded: false, @@ -624,7 +608,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
- {this._encodedRecoveryKey} + {this._recoveryKey.encodedPrivateKey}
From 2624017ab9a4c2837a3d04cbad49cd7bae992414 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Mar 2020 12:51:03 +0100 Subject: [PATCH 114/114] Also stop peeking on unmount Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 50b66bee93..2c9e798bd8 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -472,6 +472,10 @@ export default createReactClass({ RoomScrollStateStore.setScrollState(this.state.roomId, this._getScrollState()); } + if (this.state.shouldPeek) { + this.context.stopPeeking(); + } + // stop tracking room changes to format permalinks this._stopAllPermalinkCreators();