From 594c2fe2673f14aa38ede16ef16a9ac4bea10441 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 18 Jan 2021 10:56:35 -0300 Subject: [PATCH 01/17] I18n key fix --- app/i18n/locales/en.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index cbc22609867..2ac024273e7 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -223,7 +223,7 @@ export default { Enter_Your_Encryption_Password_desc1: 'This will allow you to access your encrypted private groups and direct messages.', Enter_Your_Encryption_Password_desc2: 'You need to enter the password to encode/decode messages every place you use the chat.', Encryption_error_title: 'Your encryption password seems wrong', - Encryption_error_desc: 'Wasn\'t possible to decode your encryption key to be imported.', + Encryption_error_desc: 'It wasn\'t possible to decode your encryption key to be imported.', Everyone_can_access_this_channel: 'Everyone can access this channel', Error_uploading: 'Error uploading', Expiration_Days: 'Expiration (Days)', From d4fd6b22d940da92f4540566ac4fdfefa5a3e414 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 18 Jan 2021 17:15:02 -0300 Subject: [PATCH 02/17] Add encrypted switch --- app/lib/rocketchat.js | 4 ++-- app/utils/log/events.js | 1 + app/views/CreateDiscussionView/index.js | 31 ++++++++++++++----------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 5020ee4add4..66a3adf6726 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -697,11 +697,11 @@ const RocketChat = { }, createDiscussion({ - prid, pmid, t_name, reply, users + prid, pmid, t_name, reply, users, encrypted }) { // RC 1.0.0 return this.post('rooms.createDiscussion', { - prid, pmid, t_name, reply, users + prid, pmid, t_name, reply, users, encrypted }); }, diff --git a/app/utils/log/events.js b/app/utils/log/events.js index 52ef738380a..4895ad8f273 100644 --- a/app/utils/log/events.js +++ b/app/utils/log/events.js @@ -112,6 +112,7 @@ export default { CREATE_DISCUSSION_CREATE_F: 'create_discussion_create_f', CREATE_DISCUSSION_SELECT_CHANNEL: 'create_discussion_select_channel', CREATE_DISCUSSION_SELECT_USERS: 'create_discussion_select_users', + CREATE_DISCUSSION_TOGGLE_ENCRY: 'create_discussion_toggle_encry', // PROFILE VIEW PROFILE_PICK_AVATAR: 'profile_pick_avatar', diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js index afadb5e6b18..215c9f11c33 100644 --- a/app/views/CreateDiscussionView/index.js +++ b/app/views/CreateDiscussionView/index.js @@ -1,7 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; -import { ScrollView, Text } from 'react-native'; +import { ScrollView, Text, Switch } from 'react-native'; import isEqual from 'lodash/isEqual'; import Loading from '../../containers/Loading'; @@ -10,7 +10,7 @@ import scrollPersistTaps from '../../utils/scrollPersistTaps'; import I18n from '../../i18n'; import * as HeaderButton from '../../containers/HeaderButton'; import StatusBar from '../../containers/StatusBar'; -import { themes } from '../../constants/colors'; +import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors'; import { withTheme } from '../../theme'; import { getUserSelector } from '../../selectors/login'; import TextInput from '../../containers/TextInput'; @@ -54,7 +54,8 @@ class CreateChannelView extends React.Component { message, name: message?.msg || '', users: [], - reply: '' + reply: '', + encrypted: true }; this.setHeader(); } @@ -109,13 +110,13 @@ class CreateChannelView extends React.Component { submit = () => { const { - name: t_name, channel: { prid, rid }, message: { id: pmid }, reply, users + name: t_name, channel: { prid, rid }, message: { id: pmid }, reply, users, encrypted } = this.state; const { create } = this.props; // create discussion create({ - prid: prid || rid, pmid, t_name, reply, users + prid: prid || rid, pmid, t_name, reply, users, encrypted }); }; @@ -142,8 +143,13 @@ class CreateChannelView extends React.Component { this.setState({ users: value }); } + onEncryptedChange = (value) => { + logEvent(events.CREATE_DISCUSSION_TOGGLE_ENCRY); + this.setState({ encrypted: value }); + } + render() { - const { name, users } = this.state; + const { name, users, encrypted } = this.state; const { server, user, loading, blockUnauthenticatedAccess, theme, serverVersion } = this.props; @@ -185,14 +191,11 @@ class CreateChannelView extends React.Component { serverVersion={serverVersion} theme={theme} /> - this.setState({ reply: text })} + {I18n.t('Encrypted')} + From 0eac01b5c58f6d47d5bb164f40a083a0047a0164 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Mon, 18 Jan 2021 17:58:20 -0300 Subject: [PATCH 03/17] Remove unused i18n keys --- app/i18n/locales/ar.js | 2 -- app/i18n/locales/de.js | 2 -- app/i18n/locales/en.js | 2 -- app/i18n/locales/fr.js | 2 -- app/i18n/locales/it.js | 2 -- app/i18n/locales/pt-BR.js | 2 -- app/i18n/locales/ru.js | 2 -- app/i18n/locales/zh-CN.js | 2 -- app/i18n/locales/zh-TW.js | 2 -- 9 files changed, 18 deletions(-) diff --git a/app/i18n/locales/ar.js b/app/i18n/locales/ar.js index 6cb0ff979e4..814210beefd 100644 --- a/app/i18n/locales/ar.js +++ b/app/i18n/locales/ar.js @@ -562,7 +562,6 @@ export default { Username: 'اسم المستخدم', Username_or_email: 'اسم المستخدم أو البريد الالكتروني', Uses_server_configuration: 'يستخدم إعداد الخادم', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'عادةً، النقاش يبدأ بسؤال، على سبيل المثال: كيف أرفع صورة؟', Validating: 'يتم التحقق', Registration_Succeeded: 'تم التسجيل بنجاح', Verify: 'تحقق', @@ -597,7 +596,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'تحتاج إلى الوصول إلى خادم Rocket.Chat واحد على الأقل لمشاركة شيء ما', You_need_to_verifiy_your_email_address_to_get_notications: 'يجب تأكيد البريد الإلكتروني حتى تصلك الإشعارات', Your_certificate: 'شهادتك', - Your_message: 'رسالتك', Your_invite_link_will_expire_after__usesLeft__uses: 'سوف تنتهي صلاحية رابط الدعوة الخاص بك بعد {{usesLeft}} استخدامات', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'ستنتهي صلاحية رابط الدعوة الخاص بك في {{date}} أو بعد {{usesLeft}} استخدامات', Your_invite_link_will_expire_on__date__: 'ستنتهي صلاحية رابط الدعوة الخاص بك في {{date}}', diff --git a/app/i18n/locales/de.js b/app/i18n/locales/de.js index 2413ee66c24..a2afcc60b98 100644 --- a/app/i18n/locales/de.js +++ b/app/i18n/locales/de.js @@ -563,7 +563,6 @@ export default { Username: 'Benutzername', Username_or_email: 'Benutzername oder E-Mail-Adresse', Uses_server_configuration: 'Nutzt Servereinstellungen', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Üblicherweise beginnt eine Diskussion mit einer Frage, beispielsweise: "Wie lade ich ein Bild hoch?"', Validating: 'Validierung', Registration_Succeeded: 'Registrierung erfolgreich!', Verify: 'Überprüfen', @@ -598,7 +597,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Du benötigst Zugang zu mindestens einem Rocket.Chat-Server um etwas zu teilen.', You_need_to_verifiy_your_email_address_to_get_notications: 'Du musst deine Email-Adresse bestätigen um Benachrichtigungen zu erhalten.', Your_certificate: 'Dein Zertifikat', - Your_message: 'Deine Nachricht', Your_invite_link_will_expire_after__usesLeft__uses: 'Dein Einladungs-Link wird nach {{usesLeft}} Benutzungen ablaufen.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Dein Einladungs-Link wird am {{date}} oder nach {{usesLeft}} Benutzungen ablaufen.', Your_invite_link_will_expire_on__date__: 'Dein Einladungs-Link wird am {{date}} ablaufen.', diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js index 2ac024273e7..b34fc69f000 100644 --- a/app/i18n/locales/en.js +++ b/app/i18n/locales/en.js @@ -565,7 +565,6 @@ export default { Username: 'Username', Username_or_email: 'Username or email', Uses_server_configuration: 'Uses server configuration', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Usually, a discussion starts with a question, like "How do I upload a picture?"', Validating: 'Validating', Registration_Succeeded: 'Registration Succeeded!', Verify: 'Verify', @@ -600,7 +599,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'You need to access at least one Rocket.Chat server to share something.', You_need_to_verifiy_your_email_address_to_get_notications: 'You need to verify your email address to get notifications', Your_certificate: 'Your Certificate', - Your_message: 'Your message', Your_invite_link_will_expire_after__usesLeft__uses: 'Your invite link will expire after {{usesLeft}} uses.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Your invite link will expire on {{date}} or after {{usesLeft}} uses.', Your_invite_link_will_expire_on__date__: 'Your invite link will expire on {{date}}.', diff --git a/app/i18n/locales/fr.js b/app/i18n/locales/fr.js index 31a0e280d8b..71051fb90f6 100644 --- a/app/i18n/locales/fr.js +++ b/app/i18n/locales/fr.js @@ -531,7 +531,6 @@ export default { Username: 'Nom d\'utilisateur', Username_or_email: 'Nom d\'utilisateur ou address e-mail', Uses_server_configuration: 'Utilise la configuration du serveur', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Habituellement, une discussion commence par une question, comme "Comment télécharger une image?"', Validating: 'Validation', Registration_Succeeded: 'Inscription réussie!', Verify: 'Vérifier', @@ -565,7 +564,6 @@ export default { Logged_out_by_server: 'Vous avez été déconnecté par le serveur. Veuillez vous reconnecter.', You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Vous devez accéder à au moins un serveur Rocket.Chat pour partager quelque chose.', Your_certificate: 'Votre Certificat', - Your_message: 'Votre message', Your_invite_link_will_expire_after__usesLeft__uses: 'Votre lien d\'invitation expirera après {{usesLeft}} utilisations.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Votre lien d\'invitation expirera le {{date}} ou après {{usesLeft}} utilisations.', Your_invite_link_will_expire_on__date__: 'Votre lien d\'invitation expirera le {{date}}.', diff --git a/app/i18n/locales/it.js b/app/i18n/locales/it.js index f5fdcdb5454..35bd581c5d6 100644 --- a/app/i18n/locales/it.js +++ b/app/i18n/locales/it.js @@ -560,7 +560,6 @@ export default { Username: 'Username', Username_or_email: 'Username o email', Uses_server_configuration: 'Usa la configurazione del server', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Una discussione inizia solitamente con una domanda, ad esempio: "Come posso caricare una foto?"', Validating: 'Validazione', Registration_Succeeded: 'Registrazione completata!', Verify: 'Verifica', @@ -595,7 +594,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Devi accedere ad almeno un server Rocket.Chat prima di condividere qualcosa.', You_need_to_verifiy_your_email_address_to_get_notications: 'Devi verificare il tuo indirizzo e-mail per ricevere le notifiche', Your_certificate: 'Il tuo certificato', - Your_message: 'Il tuo messaggio', Your_invite_link_will_expire_after__usesLeft__uses: 'Il tuo link di invito scadrà dopo {{usesLeft}} utilizzi.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Il tuo link di invito scadrà il {{date}} oppure dopo {{usesLeft}} utilizzi.', Your_invite_link_will_expire_on__date__: 'Il tuo link di invito scadrà il {{date}}.', diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js index e9625e9643c..dfc416dd577 100644 --- a/app/i18n/locales/pt-BR.js +++ b/app/i18n/locales/pt-BR.js @@ -522,7 +522,6 @@ export default { Username: 'Usuário', Username_or_email: 'Usuário ou email', Uses_server_configuration: 'Usar configuração do servidor', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Normalmente, uma discussão começa com uma pergunta como: Como faço para enviar uma foto?', Verify: 'Verificar', Verify_email_title: 'Registrado com sucesso!', Verify_email_desc: 'Nós lhe enviamos um e-mail para confirmar o seu registro. Se você não receber um e-mail em breve, por favor retorne e tente novamente.', @@ -541,7 +540,6 @@ export default { You_are_in_preview_mode: 'Está é uma prévia do canal', You_are_offline: 'Você está offline', You_can_search_using_RegExp_eg: 'Você pode usar expressões regulares, por exemplo `/^text$/i`', - Your_message: 'Sua mensagem', You_need_to_verifiy_your_email_address_to_get_notications: 'Você precisa confirmar seu endereço de e-mail para obter notificações', You_colon: 'Você: ', you_were_mentioned: 'você foi mencionado', diff --git a/app/i18n/locales/ru.js b/app/i18n/locales/ru.js index 6b82d196876..4d37232bf42 100644 --- a/app/i18n/locales/ru.js +++ b/app/i18n/locales/ru.js @@ -563,7 +563,6 @@ export default { Username: 'Имя пользователя', Username_or_email: 'Имя пользователя или email', Uses_server_configuration: 'Используется конфигурация сервера', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: 'Обычно Обсуждение начинается с какого-либо вопроса, например, "Как мне загрузить эту картинку?"', Validating: 'Проверка', Registration_Succeeded: 'Регистрация Успешна!', Verify: 'Проверить', @@ -598,7 +597,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: 'Вам нужно получить доступ как минимум к одному серверу Rocket.Chat, чтобы поделиться чем-то.', You_need_to_verifiy_your_email_address_to_get_notications: 'Вам необходимо проверить ваш email адрес, чтобы получать уведомления', Your_certificate: 'Ваш сертификат', - Your_message: 'Ваше сообщение', Your_invite_link_will_expire_after__usesLeft__uses: 'Ваша ссылка-приглашение станет не действительной после {{usesLeft}} ее использований.', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: 'Ваша ссылка-приглашение станет не действительной {{date}} или после {{usesLeft}} ее использований.', Your_invite_link_will_expire_on__date__: 'Срок действия вашей ссылки-приглашения будет окончен {{date}}.', diff --git a/app/i18n/locales/zh-CN.js b/app/i18n/locales/zh-CN.js index 11f555ee70f..62ee3da5359 100644 --- a/app/i18n/locales/zh-CN.js +++ b/app/i18n/locales/zh-CN.js @@ -563,7 +563,6 @@ export default { Username: '用户名', Username_or_email: '用户名或邮箱', Uses_server_configuration: '使用服务器设置', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: '通常, 一个讨论会由一个问题开始, 例如 \\"如何上传图片?\\"', Validating: '正在验证', Registration_Succeeded: '注册成功', Verify: '验证', @@ -598,7 +597,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: '您需要访问至少一台Rocket.Chat服务器才能共享某些内容。', You_need_to_verifiy_your_email_address_to_get_notications: '您需要先验证您的邮箱以启用通知', Your_certificate: '你的证书', - Your_message: '你的信息', Your_invite_link_will_expire_after__usesLeft__uses: '您的邀请链接将在{{usesLeft}}使用后到期。', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: '您的邀请链接将于{{date}}或{{usesLeft}}使用后到期。', Your_invite_link_will_expire_on__date__: '您的邀请链接将于{{date}}到期。', diff --git a/app/i18n/locales/zh-TW.js b/app/i18n/locales/zh-TW.js index 366e7c7f1be..7bfcc3b9db4 100644 --- a/app/i18n/locales/zh-TW.js +++ b/app/i18n/locales/zh-TW.js @@ -563,7 +563,6 @@ export default { Username: '使用者名稱', Username_or_email: '使用者名稱或電子郵件', Uses_server_configuration: '使用伺服器設定', - Usually_a_discussion_starts_with_a_question_like_How_do_I_upload_a_picture: '通常,討論會由一個問題開始,像是 \\\'如何上傳一個圖片?\\\'', Validating: '正在驗證', Registration_Succeeded: '註冊成功', Verify: '驗證', @@ -598,7 +597,6 @@ export default { You_need_to_access_at_least_one_RocketChat_server_to_share_something: '您需要至少連接一個 Rocket.Chat 伺服器才能共享某些内容。', You_need_to_verifiy_your_email_address_to_get_notications: '您需要先驗證您的電子郵件以啟用通知', Your_certificate: '你的證書', - Your_message: '你的訊息', Your_invite_link_will_expire_after__usesLeft__uses: '您的邀請連結將在{{usesLeft}}使用後到期。', Your_invite_link_will_expire_on__date__or_after__usesLeft__uses: '您的邀請連結將於{{date}}或{{usesLeft}}使用後到期。', Your_invite_link_will_expire_on__date__: '您的邀請連結將於{{date}}到期。', From 9fd57496225e5d7c83dedcd9a00422f04ea8533a Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 10:31:32 -0300 Subject: [PATCH 04/17] Add enabled to encryption reducer --- app/actions/actionsTypes.js | 2 +- app/actions/encryption.js | 8 ++++++++ app/reducers/encryption.js | 7 +++++++ app/sagas/encryption.js | 13 +++++++------ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/actions/actionsTypes.js b/app/actions/actionsTypes.js index e27a3d11ef4..0d18c9445a1 100644 --- a/app/actions/actionsTypes.js +++ b/app/actions/actionsTypes.js @@ -69,4 +69,4 @@ export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [ export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD']); export const APP_STATE = createRequestTypes('APP_STATE', ['FOREGROUND', 'BACKGROUND']); export const ENTERPRISE_MODULES = createRequestTypes('ENTERPRISE_MODULES', ['CLEAR', 'SET']); -export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET_BANNER']); +export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DECODE_KEY', 'SET', 'SET_BANNER']); diff --git a/app/actions/encryption.js b/app/actions/encryption.js index 15eb7df20c2..390dfe9034b 100644 --- a/app/actions/encryption.js +++ b/app/actions/encryption.js @@ -12,6 +12,14 @@ export function encryptionStop() { }; } +export function encryptionSet(enabled = false, banner = null) { + return { + type: types.ENCRYPTION.SET, + enabled, + banner + }; +} + export function encryptionSetBanner(banner) { return { type: types.ENCRYPTION.SET_BANNER, diff --git a/app/reducers/encryption.js b/app/reducers/encryption.js index 729c8be4156..0145ae2d136 100644 --- a/app/reducers/encryption.js +++ b/app/reducers/encryption.js @@ -1,11 +1,18 @@ import { ENCRYPTION } from '../actions/actionsTypes'; const initialState = { + enabled: false, banner: null }; export default function encryption(state = initialState, action) { switch (action.type) { + case ENCRYPTION.SET: + return { + ...state, + enabled: action.enabled, + banner: action.banner + }; case ENCRYPTION.SET_BANNER: return { ...state, diff --git a/app/sagas/encryption.js b/app/sagas/encryption.js index 3aaed531c65..f28c7783419 100644 --- a/app/sagas/encryption.js +++ b/app/sagas/encryption.js @@ -2,7 +2,7 @@ import EJSON from 'ejson'; import { takeLatest, select, put } from 'redux-saga/effects'; import { ENCRYPTION } from '../actions/actionsTypes'; -import { encryptionSetBanner } from '../actions/encryption'; +import { encryptionSet } from '../actions/encryption'; import { Encryption } from '../lib/encryption'; import Navigation from '../lib/Navigation'; import { @@ -52,14 +52,14 @@ const handleEncryptionInit = function* handleEncryptionInit() { // A private key was received from the server, but it's not saved locally yet // Show the banner asking for the password if (!storedPrivateKey && keys?.privateKey) { - yield put(encryptionSetBanner(E2E_BANNER_TYPE.REQUEST_PASSWORD)); + yield put(encryptionSet(false, E2E_BANNER_TYPE.REQUEST_PASSWORD)); return; } // If the user has a private key stored, but never entered the password const storedRandomPassword = yield UserPreferences.getStringAsync(`${ server }-${ E2E_RANDOM_PASSWORD_KEY }`); if (storedRandomPassword) { - yield put(encryptionSetBanner(E2E_BANNER_TYPE.SAVE_PASSWORD)); + yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD)); } // Fetch stored public e2e key for this server @@ -72,10 +72,11 @@ const handleEncryptionInit = function* handleEncryptionInit() { if (storedPublicKey && storedPrivateKey) { // Persist these keys yield Encryption.persistKeys(server, storedPublicKey, storedPrivateKey); + yield put(encryptionSet(true)); } else { // Create new keys since the user doesn't have any yield Encryption.createKeys(user.id, server); - yield put(encryptionSetBanner(E2E_BANNER_TYPE.SAVE_PASSWORD)); + yield put(encryptionSet(true, E2E_BANNER_TYPE.SAVE_PASSWORD)); } // Decrypt all pending messages/subscriptions @@ -87,7 +88,7 @@ const handleEncryptionInit = function* handleEncryptionInit() { const handleEncryptionStop = function* handleEncryptionStop() { // Hide encryption banner - yield put(encryptionSetBanner()); + yield put(encryptionSet()); // Stop Encryption client Encryption.stop(); }; @@ -112,7 +113,7 @@ const handleEncryptionDecodeKey = function* handleEncryptionDecodeKey({ password Encryption.initialize(user.id); // Hide encryption banner - yield put(encryptionSetBanner()); + yield put(encryptionSet(true)); Navigation.back(); } catch { From 6ab09288ccddc7910dbdfc919f55ceb365851ba5 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 13:35:22 -0300 Subject: [PATCH 05/17] Show encrypted option on CreateDiscussionView only when e2e encryption is properly set --- app/views/CreateDiscussionView/index.js | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js index 215c9f11c33..7d35ba166a5 100644 --- a/app/views/CreateDiscussionView/index.js +++ b/app/views/CreateDiscussionView/index.js @@ -41,7 +41,8 @@ class CreateChannelView extends React.Component { theme: PropTypes.string, isMasterDetail: PropTypes.bool, blockUnauthenticatedAccess: PropTypes.bool, - serverVersion: PropTypes.string + serverVersion: PropTypes.string, + encryptionEnabled: PropTypes.bool } constructor(props) { @@ -55,7 +56,7 @@ class CreateChannelView extends React.Component { name: message?.msg || '', users: [], reply: '', - encrypted: true + encrypted: props.encryptionEnabled }; this.setHeader(); } @@ -151,7 +152,7 @@ class CreateChannelView extends React.Component { render() { const { name, users, encrypted } = this.state; const { - server, user, loading, blockUnauthenticatedAccess, theme, serverVersion + server, user, loading, blockUnauthenticatedAccess, theme, serverVersion, encryptionEnabled } = this.props; return ( - {I18n.t('Encrypted')} - + {encryptionEnabled + ? ( + <> + {I18n.t('Encrypted')} + + + ) : null} @@ -214,7 +220,8 @@ const mapStateToProps = state => ({ result: state.createDiscussion.result, blockUnauthenticatedAccess: state.settings.Accounts_AvatarBlockUnauthenticatedAccess ?? true, serverVersion: state.share.server.version || state.server.version, - isMasterDetail: state.app.isMasterDetail + isMasterDetail: state.app.isMasterDetail, + encryptionEnabled: state.encryption.enabled }); const mapDispatchToProps = dispatch => ({ From c4df4a7a913e3fe7d0245e032ccf15e7d5956c9f Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 14:25:07 -0300 Subject: [PATCH 06/17] Add localSearch and use it on search --- app/lib/rocketchat.js | 50 ++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 66a3adf6726..4712037e3eb 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -596,25 +596,19 @@ const RocketChat = { readMessages, resendMessage, - async search({ text, filterUsers = true, filterRooms = true }) { + async localSearch({ text, filterUsers = true, filterRooms = true }) { const searchText = text.trim(); - - if (this.oldPromise) { - this.oldPromise('cancel'); - } - if (searchText === '') { - delete this.oldPromise; return []; } - const db = database.active; const likeString = sanitizeLikeString(searchText); let data = await db.collections.get('subscriptions').query( Q.or( Q.where('name', Q.like(`%${ likeString }%`)), Q.where('fname', Q.like(`%${ likeString }%`)) - ) + ), + Q.experimentalSortBy('room_updated_at', Q.desc) ).fetch(); if (filterUsers && !filterRooms) { @@ -625,19 +619,31 @@ const RocketChat = { data = data.slice(0, 7); - data = data.map((sub) => { - if (sub.t !== 'd') { - return ({ - rid: sub.rid, - name: sub.name, - fname: sub.fname, - avatarETag: sub.avatarETag, - t: sub.t, - search: true - }); - } - return sub; - }); + data = data.map(sub => ({ + rid: sub.rid, + name: sub.name, + fname: sub.fname, + avatarETag: sub.avatarETag, + t: sub.t, + encrypted: sub.encrypted, + search: true + })); + + return data; + }, + + async search({ text, filterUsers = true, filterRooms = true }) { + const searchText = text.trim(); + + if (this.oldPromise) { + this.oldPromise('cancel'); + } + + if (searchText === '') { + return []; + } + + let data = await this.localSearch({ text, filterUsers, filterRooms }); const usernames = data.map(sub => sub.name); try { From 6265eed151f63dd386b41b9794039029187e1fcf Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 14:25:44 -0300 Subject: [PATCH 07/17] Use encrypted from parent channel --- app/views/CreateDiscussionView/SelectChannel.js | 4 ++-- app/views/CreateDiscussionView/index.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/CreateDiscussionView/SelectChannel.js b/app/views/CreateDiscussionView/SelectChannel.js index fff60982d33..c7b6acbfb76 100644 --- a/app/views/CreateDiscussionView/SelectChannel.js +++ b/app/views/CreateDiscussionView/SelectChannel.js @@ -18,7 +18,7 @@ const SelectChannel = ({ const getChannels = debounce(async(keyword = '') => { try { - const res = await RocketChat.search({ text: keyword, filterUsers: false }); + const res = await RocketChat.localSearch({ text: keyword }); setChannels(res); } catch { // do nothing @@ -47,7 +47,7 @@ const SelectChannel = ({ value={initial && [initial]} disabled={initial} options={channels.map(channel => ({ - value: channel.rid, + value: channel, text: { text: RocketChat.getRoomTitle(channel) }, imageUrl: getAvatar(channel) }))} diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js index 7d35ba166a5..31deb8b5554 100644 --- a/app/views/CreateDiscussionView/index.js +++ b/app/views/CreateDiscussionView/index.js @@ -136,7 +136,7 @@ class CreateChannelView extends React.Component { selectChannel = ({ value }) => { logEvent(events.CREATE_DISCUSSION_SELECT_CHANNEL); - this.setState({ channel: { rid: value } }); + this.setState({ channel: value, encrypted: value?.encrypted }); } selectUsers = ({ value }) => { From d559d409e45790a636ee1fc1e0a666f80b2287d6 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 16:02:54 -0300 Subject: [PATCH 08/17] Fix method calls as rest api with 2fa enabled --- app/lib/rocketchat.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 4712037e3eb..97fed2026c9 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -871,14 +871,7 @@ const RocketChat = { methodCallWrapper(method, ...params) { const { API_Use_REST_For_DDP_Calls } = reduxStore.getState().settings; if (API_Use_REST_For_DDP_Calls) { - return new Promise(async(resolve, reject) => { - const data = await this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) }); - const response = JSON.parse(data.message); - if (response?.error) { - return reject(response.error); - } - return resolve(response.result); - }); + return this.post(`method.call/${ method }`, { message: JSON.stringify({ method, params }) }); } return this.methodCall(method, ...params); }, @@ -1073,14 +1066,30 @@ const RocketChat = { }, post(...args) { return new Promise(async(resolve, reject) => { + const isMethodCall = args[0]?.startsWith('method.call/'); try { const result = await this.sdk.post(...args); + + /** + * if API_Use_REST_For_DDP_Calls is enabled and it's a method call, + * responses have a different object structure + */ + if (isMethodCall) { + const response = JSON.parse(result.message); + if (response?.error) { + throw response.error; + } + return resolve(response.result); + } return resolve(result); } catch (e) { - if (e.data && (e.data.errorType === 'totp-required' || e.data.errorType === 'totp-invalid')) { - const { details } = e.data; + const errorType = isMethodCall ? e?.error : e?.data?.errorType; + const totpInvalid = 'totp-invalid'; + const totpRequired = 'totp-required'; + if ([totpInvalid, totpRequired].includes(errorType)) { + const { details } = isMethodCall ? e : e?.data; try { - await twoFactor({ method: details?.method, invalid: e.data.errorType === 'totp-invalid' }); + await twoFactor({ method: details?.method, invalid: errorType === totpInvalid }); return resolve(this.post(...args)); } catch { // twoFactor was canceled From acbaef30665e4421a15b99523fd72317cdef4fac Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 16:19:48 -0300 Subject: [PATCH 09/17] Fix logout after reset keys --- app/views/E2EEncryptionSecurityView.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/views/E2EEncryptionSecurityView.js b/app/views/E2EEncryptionSecurityView.js index 8d246b5a088..a9e597ae155 100644 --- a/app/views/E2EEncryptionSecurityView.js +++ b/app/views/E2EEncryptionSecurityView.js @@ -80,12 +80,18 @@ class E2EEncryptionSecurityView extends React.Component { title: I18n.t('Are_you_sure_question_mark'), message: I18n.t('E2E_encryption_reset_message'), confirmationText: I18n.t('E2E_encryption_reset_confirmation'), - onPress: () => { + onPress: async() => { logEvent(events.E2E_SEC_RESET_OWN_KEY); try { - RocketChat.e2eResetOwnKey(); - const { logout } = this.props; - logout(); + const res = await RocketChat.e2eResetOwnKey(); + /** + * It might return an empty object when TOTP is enabled, + * that's why we're using strict equality to boolean + */ + if (res === true) { + const { logout } = this.props; + logout(); + } } catch (e) { log(e); showErrorAlert(I18n.t('E2E_encryption_reset_error')); From 5fd26ec82d4df2130ac8b709794d8552710fd599 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 16:30:48 -0300 Subject: [PATCH 10/17] Use encryption reducer instead of lib directly to check render --- app/lib/encryption/encryption.js | 4 ---- app/views/E2EEncryptionSecurityView.js | 9 +++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/lib/encryption/encryption.js b/app/lib/encryption/encryption.js index 90cbe5310e5..972f2c5889c 100644 --- a/app/lib/encryption/encryption.js +++ b/app/lib/encryption/encryption.js @@ -68,10 +68,6 @@ class Encryption { return this.readyPromise; } - get hasPrivateKey() { - return !!this.privateKey; - } - // Stop Encryption client stop = () => { this.userId = null; diff --git a/app/views/E2EEncryptionSecurityView.js b/app/views/E2EEncryptionSecurityView.js index a9e597ae155..661d6e84da6 100644 --- a/app/views/E2EEncryptionSecurityView.js +++ b/app/views/E2EEncryptionSecurityView.js @@ -102,9 +102,8 @@ class E2EEncryptionSecurityView extends React.Component { renderChangePassword = () => { const { newPassword } = this.state; - const { theme } = this.props; - const { hasPrivateKey } = Encryption; - if (!hasPrivateKey) { + const { theme, encryptionEnabled } = this.props; + if (!encryptionEnabled) { return null; } return ( @@ -167,7 +166,8 @@ class E2EEncryptionSecurityView extends React.Component { const mapStateToProps = state => ({ server: state.server.server, - user: getUserSelector(state) + user: getUserSelector(state), + encryptionEnabled: state.encryption.enabled }); const mapDispatchToProps = dispatch => ({ @@ -185,6 +185,7 @@ E2EEncryptionSecurityView.propTypes = { id: PropTypes.string }), server: PropTypes.string, + encryptionEnabled: PropTypes.bool, logout: PropTypes.func }; From 589f9055a4497b4d13de84470f67e441dc25721c Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 16:46:17 -0300 Subject: [PATCH 11/17] Check for room type logic to display encryption option on create discussion --- app/views/CreateDiscussionView/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.js index 31deb8b5554..43329f1020f 100644 --- a/app/views/CreateDiscussionView/index.js +++ b/app/views/CreateDiscussionView/index.js @@ -26,6 +26,7 @@ import styles from './styles'; import SafeAreaView from '../../containers/SafeAreaView'; import { goRoom } from '../../utils/goRoom'; import { logEvent, events } from '../../utils/log'; +import { E2E_ROOM_TYPES } from '../../lib/encryption/constants'; class CreateChannelView extends React.Component { propTypes = { @@ -150,7 +151,9 @@ class CreateChannelView extends React.Component { } render() { - const { name, users, encrypted } = this.state; + const { + name, users, encrypted, channel + } = this.state; const { server, user, loading, blockUnauthenticatedAccess, theme, serverVersion, encryptionEnabled } = this.props; @@ -192,7 +195,7 @@ class CreateChannelView extends React.Component { serverVersion={serverVersion} theme={theme} /> - {encryptionEnabled + {encryptionEnabled && E2E_ROOM_TYPES[channel?.t] ? ( <> {I18n.t('Encrypted')} From b0291d5d8498e3999f78d041c7b9157edbabebff Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 17:32:41 -0300 Subject: [PATCH 12/17] Check toggle-room-e2e-encryption permission on RoomActionsView --- app/views/RoomActionsView/index.js | 44 ++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 9d8278bc5f6..7b2616eb544 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -5,6 +5,7 @@ import { } from 'react-native'; import { connect } from 'react-redux'; import _ from 'lodash'; +import semver from 'semver'; import Touch from '../../utils/touch'; import { setLoading as setLoadingAction } from '../../actions/selectedUsers'; @@ -30,7 +31,7 @@ import protectedFunction from '../../lib/methods/helpers/protectedFunction'; import database from '../../lib/database'; import { withDimensions } from '../../dimensions'; -class RoomActionsView extends React.Component { +class RoomActionsView extends React.PureComponent { static navigationOptions = ({ navigation, isMasterDetail }) => { const options = { title: I18n.t('Actions') @@ -46,11 +47,12 @@ class RoomActionsView extends React.Component { route: PropTypes.object, leaveRoom: PropTypes.func, jitsiEnabled: PropTypes.bool, - e2eEnabled: PropTypes.bool, + encryptionEnabled: PropTypes.bool, setLoadingInvite: PropTypes.func, closeRoom: PropTypes.func, theme: PropTypes.string, - fontScale: PropTypes.number + fontScale: PropTypes.number, + serverVersion: PropTypes.string } constructor(props) { @@ -71,7 +73,8 @@ class RoomActionsView extends React.Component { canInviteUser: false, canForwardGuest: false, canReturnQueue: false, - canEdit: false + canEdit: false, + canToggleEncryption: false }; if (room && room.observe && room.rid) { this.roomObservable = room.observe(); @@ -120,6 +123,7 @@ class RoomActionsView extends React.Component { this.canAddUser(); this.canInviteUser(); this.canEdit(); + this.canToggleEncryption(); // livechat permissions if (room.t === 'l') { @@ -152,7 +156,6 @@ class RoomActionsView extends React.Component { } } - // eslint-disable-next-line react/sort-comp canAddUser = async() => { const { room, joined } = this.state; const { rid, t } = room; @@ -193,6 +196,15 @@ class RoomActionsView extends React.Component { this.setState({ canEdit }); } + canToggleEncryption = async() => { + const { room } = this.state; + const { rid } = room; + const permissions = await RocketChat.hasPermission(['toggle-room-e2e-encryption'], rid); + + const canToggleEncryption = permissions && permissions['toggle-room-e2e-encryption']; + this.setState({ canToggleEncryption }); + } + canViewMembers = async() => { const { room } = this.state; const { rid, t, broadcast } = room; @@ -481,14 +493,21 @@ class RoomActionsView extends React.Component { renderE2EEncryption = () => { const { - room, canEdit + room, canEdit, canToggleEncryption } = this.state; - const { e2eEnabled } = this.props; + const { encryptionEnabled, serverVersion } = this.props; + + let hasPermission = false; + if (serverVersion && semver.lt(serverVersion, '3.11.0')) { + hasPermission = canEdit; + } else { + hasPermission = canToggleEncryption; + } - // If can edit this room - // If this room type can be Encrypted - // If e2e is enabled for this server - if (canEdit && E2E_ROOM_TYPES[room?.t] && e2eEnabled) { + // If user has permission to toggle encryption + // If this room type can be encrypted + // If e2e is enabled + if (hasPermission && E2E_ROOM_TYPES[room?.t] && encryptionEnabled) { return ( @@ -847,7 +866,8 @@ class RoomActionsView extends React.Component { const mapStateToProps = state => ({ jitsiEnabled: state.settings.Jitsi_Enabled || false, - e2eEnabled: state.settings.E2E_Enable || false + encryptionEnabled: state.encryption.enabled, + serverVersion: state.server.version }); const mapDispatchToProps = dispatch => ({ From bd66db28a66c9eb9d7125e61b208daefe58fecbf Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 17:35:31 -0300 Subject: [PATCH 13/17] Check for encryption status instead of setting on server --- app/views/CreateChannelView.js | 12 ++++++------ app/views/RoomInfoEditView/index.js | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 9afbc4e59b1..70982094de8 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -85,7 +85,7 @@ class CreateChannelView extends React.Component { error: PropTypes.object, failure: PropTypes.bool, isFetching: PropTypes.bool, - e2eEnabled: PropTypes.bool, + encryptionEnabled: PropTypes.bool, users: PropTypes.array.isRequired, user: PropTypes.shape({ id: PropTypes.string, @@ -107,7 +107,7 @@ class CreateChannelView extends React.Component { channelName, type, readOnly, broadcast, encrypted } = this.state; const { - users, isFetching, e2eEnabled, theme + users, isFetching, encryptionEnabled, theme } = this.props; if (nextProps.theme !== theme) { return true; @@ -130,7 +130,7 @@ class CreateChannelView extends React.Component { if (nextProps.isFetching !== isFetching) { return true; } - if (nextProps.e2eEnabled !== e2eEnabled) { + if (nextProps.encryptionEnabled !== encryptionEnabled) { return true; } if (!equal(nextProps.users, users)) { @@ -230,9 +230,9 @@ class CreateChannelView extends React.Component { renderEncrypted() { const { type, encrypted } = this.state; - const { e2eEnabled } = this.props; + const { encryptionEnabled } = this.props; - if (!e2eEnabled) { + if (!encryptionEnabled) { return null; } @@ -366,7 +366,7 @@ class CreateChannelView extends React.Component { const mapStateToProps = state => ({ baseUrl: state.server.server, isFetching: state.createChannel.isFetching, - e2eEnabled: state.settings.E2E_Enable, + encryptionEnabled: state.encryption.enabled, users: state.selectedUsers.users, user: getUserSelector(state) }); diff --git a/app/views/RoomInfoEditView/index.js b/app/views/RoomInfoEditView/index.js index b42c83ecedf..ff56a139990 100644 --- a/app/views/RoomInfoEditView/index.js +++ b/app/views/RoomInfoEditView/index.js @@ -60,7 +60,7 @@ class RoomInfoEditView extends React.Component { route: PropTypes.object, deleteRoom: PropTypes.func, serverVersion: PropTypes.string, - e2eEnabled: PropTypes.bool, + encryptionEnabled: PropTypes.bool, theme: PropTypes.string }; @@ -414,7 +414,7 @@ class RoomInfoEditView extends React.Component { const { name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived, enableSysMes, encrypted, avatar } = this.state; - const { serverVersion, e2eEnabled, theme } = this.props; + const { serverVersion, encryptionEnabled, theme } = this.props; const { dangerColor } = themes[theme]; return ( @@ -561,7 +561,7 @@ class RoomInfoEditView extends React.Component { {this.renderSystemMessages()} ) : null} - {e2eEnabled ? ( + {encryptionEnabled ? ( ({ serverVersion: state.share.server.version || state.server.version, - e2eEnabled: state.settings.E2E_Enable || false + encryptionEnabled: state.encryption.enabled }); const mapDispatchToProps = dispatch => ({ From 4fd6d25028a266b5c85df3237071a44c703abca6 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 17:40:12 -0300 Subject: [PATCH 14/17] Fix --- app/views/RoomActionsView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 7b2616eb544..4d15ef2e004 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -31,7 +31,7 @@ import protectedFunction from '../../lib/methods/helpers/protectedFunction'; import database from '../../lib/database'; import { withDimensions } from '../../dimensions'; -class RoomActionsView extends React.PureComponent { +class RoomActionsView extends React.Component { static navigationOptions = ({ navigation, isMasterDetail }) => { const options = { title: I18n.t('Actions') From a2eaabdf858e28bdcbf4703dcf10cd1c7074a199 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 19 Jan 2021 18:46:15 -0300 Subject: [PATCH 15/17] Disable switch instead of hide it --- app/views/RoomActionsView/index.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 4d15ef2e004..37dc0a52c73 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -247,13 +247,21 @@ class RoomActionsView extends React.Component { } renderEncryptedSwitch = () => { - const { room } = this.state; + const { room, canToggleEncryption, canEdit } = this.state; const { encrypted } = room; + const { serverVersion } = this.props; + let hasPermission = false; + if (serverVersion && semver.lt(semver.coerce(serverVersion, '3.11.0'))) { + hasPermission = canEdit; + } else { + hasPermission = canToggleEncryption; + } return ( ); } @@ -492,22 +500,12 @@ class RoomActionsView extends React.Component { } renderE2EEncryption = () => { - const { - room, canEdit, canToggleEncryption - } = this.state; - const { encryptionEnabled, serverVersion } = this.props; - - let hasPermission = false; - if (serverVersion && semver.lt(serverVersion, '3.11.0')) { - hasPermission = canEdit; - } else { - hasPermission = canToggleEncryption; - } + const { room } = this.state; + const { encryptionEnabled } = this.props; - // If user has permission to toggle encryption // If this room type can be encrypted // If e2e is enabled - if (hasPermission && E2E_ROOM_TYPES[room?.t] && encryptionEnabled) { + if (E2E_ROOM_TYPES[room?.t] && encryptionEnabled) { return ( From 6ceb77fafed5aaa26159f49b645518708fb31529 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 20 Jan 2021 10:55:37 -0300 Subject: [PATCH 16/17] Fix spotlight for DMs --- app/lib/rocketchat.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 97fed2026c9..58aa40ed455 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -619,15 +619,20 @@ const RocketChat = { data = data.slice(0, 7); - data = data.map(sub => ({ - rid: sub.rid, - name: sub.name, - fname: sub.fname, - avatarETag: sub.avatarETag, - t: sub.t, - encrypted: sub.encrypted, - search: true - })); + data = data.map((sub) => { + if (sub.t !== 'd') { + return { + rid: sub.rid, + name: sub.name, + fname: sub.fname, + avatarETag: sub.avatarETag, + t: sub.t, + encrypted: sub.encrypted, + search: true + }; + } + return sub; + }); return data; }, From e53fcebce322ed3ab9e91fd341d61ee1f4464a88 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Wed, 20 Jan 2021 14:10:06 -0300 Subject: [PATCH 17/17] Fix server test --- app/views/RoomActionsView/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 37dc0a52c73..a3e2233361c 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -251,7 +251,7 @@ class RoomActionsView extends React.Component { const { encrypted } = room; const { serverVersion } = this.props; let hasPermission = false; - if (serverVersion && semver.lt(semver.coerce(serverVersion, '3.11.0'))) { + if (serverVersion && semver.lt(semver.coerce(serverVersion), '3.11.0')) { hasPermission = canEdit; } else { hasPermission = canToggleEncryption;