From 58067af86048949bedb08abf31f2e3b416f9df14 Mon Sep 17 00:00:00 2001 From: Diego Mello Date: Tue, 5 Mar 2024 15:35:57 -0300 Subject: [PATCH] Enter e2ee password toasts --- app/actions/actionsTypes.ts | 9 +++++- app/actions/encryption.ts | 6 ++++ app/i18n/locales/en.json | 4 ++- app/reducers/encryption.ts | 15 +++++++++- app/sagas/encryption.js | 7 ++--- app/views/E2EEnterYourPasswordView.tsx | 38 +++++++++++++++++++++++++- 6 files changed, 71 insertions(+), 8 deletions(-) diff --git a/app/actions/actionsTypes.ts b/app/actions/actionsTypes.ts index ae8485ba0..d5399d457 100644 --- a/app/actions/actionsTypes.ts +++ b/app/actions/actionsTypes.ts @@ -81,7 +81,14 @@ export const INVITE_LINKS = createRequestTypes('INVITE_LINKS', [ export const SETTINGS = createRequestTypes('SETTINGS', ['CLEAR', 'ADD', 'UPDATE']); 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', 'SET_BANNER']); +export const ENCRYPTION = createRequestTypes('ENCRYPTION', [ + 'INIT', + 'STOP', + 'DECODE_KEY', + 'DECODE_KEY_FAILURE', + 'SET', + 'SET_BANNER' +]); export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']); export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']); diff --git a/app/actions/encryption.ts b/app/actions/encryption.ts index be3fb43fa..673f71404 100644 --- a/app/actions/encryption.ts +++ b/app/actions/encryption.ts @@ -50,3 +50,9 @@ export function encryptionDecodeKey(password: string): IEncryptionDecodeKey { password }; } + +export function encryptionDecodeKeyFailed(): Action { + return { + type: ENCRYPTION.DECODE_KEY_FAILURE + }; +} diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json index e18d030d2..ab7766392 100644 --- a/app/i18n/locales/en.json +++ b/app/i18n/locales/en.json @@ -839,5 +839,7 @@ "you": "you", "you_were_mentioned": "you were mentioned", "encrypted_room_title": "This room is encrypted", - "encrypted_room_description": "Enter your end-to-end encryption password to access." + "encrypted_room_description": "Enter your end-to-end encryption password to access.", + "e2ee_enabled": "End-to-end encryption is enabled", + "e2ee_disabled": "End-to-end encryption is disabled" } \ No newline at end of file diff --git a/app/reducers/encryption.ts b/app/reducers/encryption.ts index 061dc7c94..10339be39 100644 --- a/app/reducers/encryption.ts +++ b/app/reducers/encryption.ts @@ -5,11 +5,13 @@ export type IBanner = string; export interface IEncryption { enabled: boolean; banner: IBanner; + failure: boolean; } export const initialState: IEncryption = { enabled: false, - banner: '' + banner: '', + failure: true }; export default function encryption(state = initialState, action: TApplicationActions): IEncryption { @@ -25,6 +27,17 @@ export default function encryption(state = initialState, action: TApplicationAct ...state, banner: action.banner }; + case ENCRYPTION.DECODE_KEY: + return { + ...state, + failure: false + }; + case ENCRYPTION.DECODE_KEY_FAILURE: + return { + ...state, + enabled: false, + failure: true + }; case ENCRYPTION.INIT: return initialState; default: diff --git a/app/sagas/encryption.js b/app/sagas/encryption.js index 9f1482f16..f06a4d497 100644 --- a/app/sagas/encryption.js +++ b/app/sagas/encryption.js @@ -2,13 +2,14 @@ import EJSON from 'ejson'; import { put, select, takeLatest } from 'redux-saga/effects'; import { ENCRYPTION } from '../actions/actionsTypes'; -import { encryptionSet } from '../actions/encryption'; +import { encryptionDecodeKeyFailed, encryptionSet } from '../actions/encryption'; import { Encryption } from '../lib/encryption'; import Navigation from '../lib/navigation/appNavigation'; import database from '../lib/database'; import UserPreferences from '../lib/methods/userPreferences'; import { getUserSelector } from '../selectors/login'; import { showErrorAlert } from '../lib/methods/helpers/info'; +import { showToast } from '../lib/methods/helpers/showToast'; import I18n from '../i18n'; import log from '../lib/methods/helpers/log'; import { E2E_BANNER_TYPE, E2E_PRIVATE_KEY, E2E_PUBLIC_KEY, E2E_RANDOM_PASSWORD_KEY } from '../lib/constants'; @@ -111,11 +112,9 @@ const handleEncryptionDecodeKey = function* handleEncryptionDecodeKey({ password // Hide encryption banner yield put(encryptionSet(true)); - - Navigation.back(); } catch { // Can't decrypt user private key - showErrorAlert(I18n.t('Encryption_error_desc'), I18n.t('Encryption_error_title')); + yield put(encryptionDecodeKeyFailed()); } }; diff --git a/app/views/E2EEnterYourPasswordView.tsx b/app/views/E2EEnterYourPasswordView.tsx index 18245da63..161ee1e8c 100644 --- a/app/views/E2EEnterYourPasswordView.tsx +++ b/app/views/E2EEnterYourPasswordView.tsx @@ -1,4 +1,4 @@ -import { useNavigation } from '@react-navigation/native'; +import { useIsFocused, useNavigation } from '@react-navigation/native'; import React, { useLayoutEffect, useState } from 'react'; import { ScrollView, StyleSheet, Text } from 'react-native'; import { useDispatch } from 'react-redux'; @@ -11,10 +11,13 @@ import SafeAreaView from '../containers/SafeAreaView'; import StatusBar from '../containers/StatusBar'; import { FormTextInput } from '../containers/TextInput'; import I18n from '../i18n'; +import { useAppSelector, usePrevious } from '../lib/hooks'; import { events, logEvent } from '../lib/methods/helpers/log'; import scrollPersistTaps from '../lib/methods/helpers/scrollPersistTaps'; import { useTheme } from '../theme'; import sharedStyles from './Styles'; +import { showToast } from '../lib/methods/helpers/showToast'; +import { showErrorAlert, useDebounce } from '../lib/methods/helpers'; const styles = StyleSheet.create({ info: { @@ -28,7 +31,40 @@ const E2EEnterYourPasswordView = (): React.ReactElement => { const [password, setPassword] = useState(''); const { colors } = useTheme(); const navigation = useNavigation(); + const isFocused = useIsFocused(); const dispatch = useDispatch(); + const { enabled: encryptionEnabled, failure: encryptionFailure } = useAppSelector(state => state.encryption); + const prevEncryptionFailure = usePrevious(encryptionFailure); + + /** + * If e2ee is enabled, close screen and display success toast. + * Note: Debounce prevents `isFocused` from running another re-render + * and triggering another toast + */ + const displayEncryptionEnabled = useDebounce( + () => { + navigation.goBack(); + showToast(I18n.t('e2ee_enabled')); + }, + 1000, + { leading: true } + ); + + if (encryptionEnabled) { + displayEncryptionEnabled(); + } + + // Wrong password + if (encryptionFailure !== prevEncryptionFailure) { + if (encryptionFailure && password) { + showErrorAlert(I18n.t('Encryption_error_desc'), I18n.t('Encryption_error_title')); + } + } + + // If screen is closed and e2ee is still disabled, warns the user via toast + if (!isFocused && encryptionFailure && !encryptionEnabled) { + showToast(I18n.t('e2ee_disabled')); + } useLayoutEffect(() => { navigation.setOptions({