Merge branch 'develop' into chore.migrate-redux-server-to-ts

This commit is contained in:
Gerzon Z 2022-01-26 11:53:43 -04:00
commit 2889a1cc0f
12 changed files with 189 additions and 111 deletions

View File

@ -1,8 +0,0 @@
import * as types from './actionsTypes';
export function setCustomEmojis(emojis) {
return {
type: types.SET_CUSTOM_EMOJIS,
emojis
};
}

View File

@ -0,0 +1,17 @@
import { Action } from 'redux';
import { ICustomEmojis } from '../reducers/customEmojis';
import { SET_CUSTOM_EMOJIS } from './actionsTypes';
export interface ISetCustomEmojis extends Action {
emojis: ICustomEmojis;
}
export type TActionCustomEmojis = ISetCustomEmojis;
export function setCustomEmojis(emojis: ICustomEmojis): ISetCustomEmojis {
return {
type: SET_CUSTOM_EMOJIS,
emojis
};
}

View File

@ -1,35 +0,0 @@
import * as types from './actionsTypes';
export function encryptionInit() {
return {
type: types.ENCRYPTION.INIT
};
}
export function encryptionStop() {
return {
type: types.ENCRYPTION.STOP
};
}
export function encryptionSet(enabled = false, banner = null) {
return {
type: types.ENCRYPTION.SET,
enabled,
banner
};
}
export function encryptionSetBanner(banner) {
return {
type: types.ENCRYPTION.SET_BANNER,
banner
};
}
export function encryptionDecodeKey(password) {
return {
type: types.ENCRYPTION.DECODE_KEY,
password
};
}

52
app/actions/encryption.ts Normal file
View File

@ -0,0 +1,52 @@
import { Action } from 'redux';
import { IBanner } from '../reducers/encryption';
import { ENCRYPTION } from './actionsTypes';
export interface IEncryptionSet extends Action {
enabled: boolean;
banner: IBanner;
}
export interface IEncryptionSetBanner extends Action {
banner: IBanner;
}
export interface IEncryptionDecodeKey extends Action {
password: string;
}
export type TActionEncryption = IEncryptionSet & IEncryptionSetBanner & IEncryptionDecodeKey;
export function encryptionInit(): Action {
return {
type: ENCRYPTION.INIT
};
}
export function encryptionStop(): Action {
return {
type: ENCRYPTION.STOP
};
}
export function encryptionSet(enabled = false, banner: IBanner = ''): IEncryptionSet {
return {
type: ENCRYPTION.SET,
enabled,
banner
};
}
export function encryptionSetBanner(banner: IBanner = ''): IEncryptionSetBanner {
return {
type: ENCRYPTION.SET_BANNER,
banner
};
}
export function encryptionDecodeKey(password: string): IEncryptionDecodeKey {
return {
type: ENCRYPTION.DECODE_KEY,
password
};
}

View File

@ -1,12 +1,15 @@
// ACTIONS
import { TActionServer } from '../../actions/server';
import { TActionActiveUsers } from '../../actions/activeUsers';
import { TActionCustomEmojis } from '../../actions/customEmojis';
import { TActionEncryption } from '../../actions/encryption';
import { TActionInviteLinks } from '../../actions/inviteLinks';
import { IActionRoles } from '../../actions/roles';
import { TActionSelectedUsers } from '../../actions/selectedUsers';
import { IActionSettings } from '../../actions/settings';
// REDUCERS
import { IActiveUsers } from '../../reducers/activeUsers';
import { IEncryption } from '../../reducers/encryption';
import { IInviteLinks } from '../../reducers/inviteLinks';
import { IRoles } from '../../reducers/roles';
import { ISelectedUsers } from '../../reducers/selectedUsers';
@ -32,14 +35,16 @@ export interface IApplicationState {
createDiscussion: any;
inquiry: any;
enterpriseModules: any;
encryption: any;
encryption: IEncryption;
permissions: any;
roles: IRoles;
}
export type TApplicationActions = TActionActiveUsers &
TActionSelectedUsers &
TActionCustomEmojis &
TActionInviteLinks &
IActionRoles &
IActionSettings &
TActionEncryption &
TActionServer;

View File

@ -1,14 +0,0 @@
import { SET_CUSTOM_EMOJIS } from '../actions/actionsTypes';
const initialState = {
customEmojis: {}
};
export default function customEmojis(state = initialState, action) {
switch (action.type) {
case SET_CUSTOM_EMOJIS:
return action.emojis;
default:
return state;
}
}

View File

@ -0,0 +1,16 @@
import { setCustomEmojis } from '../actions/customEmojis';
import { ICustomEmojis, initialState } from './customEmojis';
import { mockedStore } from './mockedStore';
describe('test reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().customEmojis;
expect(state).toEqual(initialState);
});
it('should return modified store after action', () => {
const emojis: ICustomEmojis = { dog: { name: 'dog', extension: 'jpg' }, cat: { name: 'cat', extension: 'jpg' } };
mockedStore.dispatch(setCustomEmojis(emojis));
const state = mockedStore.getState().customEmojis;
expect(state).toEqual(emojis);
});
});

View File

@ -0,0 +1,23 @@
import { SET_CUSTOM_EMOJIS } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions';
// There are at least three interfaces for emoji, but none of them includes only this data.
interface IEmoji {
name: string;
extension: string;
}
export interface ICustomEmojis {
[key: string]: IEmoji;
}
export const initialState: ICustomEmojis = {};
export default function customEmojis(state = initialState, action: TApplicationActions): ICustomEmojis {
switch (action.type) {
case SET_CUSTOM_EMOJIS:
return action.emojis;
default:
return state;
}
}

View File

@ -0,0 +1,28 @@
import { encryptionSet, encryptionInit, encryptionSetBanner } from '../actions/encryption';
import { mockedStore } from './mockedStore';
import { initialState } from './encryption';
describe('test encryption reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().encryption;
expect(state).toEqual(initialState);
});
it('should return modified store after encryptionSet', () => {
mockedStore.dispatch(encryptionSet(true, 'BANNER'));
const state = mockedStore.getState().encryption;
expect(state).toEqual({ banner: 'BANNER', enabled: true });
});
it('should return empty store after encryptionInit', () => {
mockedStore.dispatch(encryptionInit());
const state = mockedStore.getState().encryption;
expect(state).toEqual({ banner: '', enabled: false });
});
it('should return initial state after encryptionSetBanner', () => {
mockedStore.dispatch(encryptionSetBanner('BANNER_NEW'));
const state = mockedStore.getState().encryption;
expect(state).toEqual({ banner: 'BANNER_NEW', enabled: false });
});
});

View File

@ -1,11 +1,18 @@
import { ENCRYPTION } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions';
const initialState = {
export type IBanner = string;
export interface IEncryption {
enabled: boolean;
banner: IBanner;
}
export const initialState: IEncryption = {
enabled: false,
banner: null
banner: ''
};
export default function encryption(state = initialState, action) {
export default function encryption(state = initialState, action: TApplicationActions): IEncryption {
switch (action.type) {
case ENCRYPTION.SET:
return {

View File

@ -1,23 +1,23 @@
import { StackNavigationOptions } from '@react-navigation/stack';
import React from 'react';
import { ScrollView, StyleSheet, Text } from 'react-native';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import I18n from '../i18n';
import { withTheme } from '../theme';
import Button from '../containers/Button';
import { encryptionDecodeKey } from '../actions/encryption';
import { themes } from '../constants/colors';
import TextInput from '../containers/TextInput';
import SafeAreaView from '../containers/SafeAreaView';
import Button from '../containers/Button';
import * as HeaderButton from '../containers/HeaderButton';
import { encryptionDecodeKey as encryptionDecodeKeyAction } from '../actions/encryption';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import KeyboardView from '../presentation/KeyboardView';
import SafeAreaView from '../containers/SafeAreaView';
import StatusBar from '../containers/StatusBar';
import { events, logEvent } from '../utils/log';
import sharedStyles from './Styles';
import TextInput from '../containers/TextInput';
import { IBaseScreen } from '../definitions';
import I18n from '../i18n';
import KeyboardView from '../presentation/KeyboardView';
import { E2EEnterYourPasswordStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
import { events, logEvent } from '../utils/log';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import sharedStyles from './Styles';
const styles = StyleSheet.create({
container: {
@ -34,21 +34,17 @@ interface IE2EEnterYourPasswordViewState {
password: string;
}
interface IE2EEnterYourPasswordViewProps {
encryptionDecodeKey: (password: string) => void;
theme: string;
navigation: StackNavigationProp<E2EEnterYourPasswordStackParamList, 'E2EEnterYourPasswordView'>;
}
type TE2EEnterYourPasswordViewProps = IBaseScreen<E2EEnterYourPasswordStackParamList, 'E2EEnterYourPasswordView'>;
class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> {
class E2EEnterYourPasswordView extends React.Component<TE2EEnterYourPasswordViewProps, IE2EEnterYourPasswordViewState> {
private passwordInput?: TextInput;
static navigationOptions = ({ navigation }: Pick<IE2EEnterYourPasswordViewProps, 'navigation'>): StackNavigationOptions => ({
static navigationOptions = ({ navigation }: Pick<TE2EEnterYourPasswordViewProps, 'navigation'>): StackNavigationOptions => ({
headerLeft: () => <HeaderButton.CloseModal navigation={navigation} testID='e2e-enter-your-password-view-close' />,
title: I18n.t('Enter_Your_E2E_Password')
});
constructor(props: IE2EEnterYourPasswordViewProps) {
constructor(props: TE2EEnterYourPasswordViewProps) {
super(props);
this.state = {
password: ''
@ -58,8 +54,8 @@ class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordView
submit = () => {
logEvent(events.E2E_ENTER_PW_SUBMIT);
const { password } = this.state;
const { encryptionDecodeKey } = this.props;
encryptionDecodeKey(password);
const { dispatch } = this.props;
dispatch(encryptionDecodeKey(password));
};
render() {
@ -109,7 +105,4 @@ class E2EEnterYourPasswordView extends React.Component<IE2EEnterYourPasswordView
}
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
encryptionDecodeKey: (password: string) => dispatch(encryptionDecodeKeyAction(password))
});
export default connect(null, mapDispatchToProps)(withTheme(E2EEnterYourPasswordView));
export default connect(null)(withTheme(E2EEnterYourPasswordView));

View File

@ -1,25 +1,24 @@
import React from 'react';
import { StackNavigationProp } from '@react-navigation/stack';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { Clipboard, ScrollView, StyleSheet, Text, View } from 'react-native';
import { connect } from 'react-redux';
import { encryptionSetBanner as encryptionSetBannerAction } from '../actions/encryption';
import { E2E_RANDOM_PASSWORD_KEY } from '../lib/encryption/constants';
import { encryptionSetBanner } from '../actions/encryption';
import { themes } from '../constants/colors';
import Button from '../containers/Button';
import * as HeaderButton from '../containers/HeaderButton';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import SafeAreaView from '../containers/SafeAreaView';
import UserPreferences from '../lib/userPreferences';
import { events, logEvent } from '../utils/log';
import StatusBar from '../containers/StatusBar';
import { LISTENER } from '../containers/Toast';
import { themes } from '../constants/colors';
import EventEmitter from '../utils/events';
import Button from '../containers/Button';
import { withTheme } from '../theme';
import { IApplicationState, IBaseScreen } from '../definitions';
import I18n from '../i18n';
import sharedStyles from './Styles';
import { E2E_RANDOM_PASSWORD_KEY } from '../lib/encryption/constants';
import UserPreferences from '../lib/userPreferences';
import { E2ESaveYourPasswordStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
import EventEmitter from '../utils/events';
import { events, logEvent } from '../utils/log';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import sharedStyles from './Styles';
const styles = StyleSheet.create({
container: {
@ -59,11 +58,8 @@ interface IE2ESaveYourPasswordViewState {
password: string;
}
interface IE2ESaveYourPasswordViewProps {
interface IE2ESaveYourPasswordViewProps extends IBaseScreen<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'> {
server: string;
navigation: StackNavigationProp<E2ESaveYourPasswordStackParamList, 'E2ESaveYourPasswordView'>;
encryptionSetBanner(): void;
theme: string;
}
class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewProps, IE2ESaveYourPasswordViewState> {
@ -103,11 +99,11 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
onSaved = async () => {
logEvent(events.E2E_SAVE_PW_SAVED);
const { navigation, server, encryptionSetBanner } = this.props;
const { navigation, server, dispatch } = this.props;
// Remove stored password
await UserPreferences.removeItem(`${server}-${E2E_RANDOM_PASSWORD_KEY}`);
// Hide encryption banner
encryptionSetBanner();
dispatch(encryptionSetBanner());
navigation.pop();
};
@ -173,10 +169,8 @@ class E2ESaveYourPasswordView extends React.Component<IE2ESaveYourPasswordViewPr
}
}
const mapStateToProps = (state: any) => ({
const mapStateToProps = (state: IApplicationState) => ({
server: state.server.server
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
encryptionSetBanner: () => dispatch(encryptionSetBannerAction())
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(E2ESaveYourPasswordView));
export default connect(mapStateToProps)(withTheme(E2ESaveYourPasswordView));