Merge branch 'develop' into chore/migration-ts-redux-invite-links

This commit is contained in:
Gleidson Daniel Silva 2022-01-26 10:42:05 -03:00 committed by GitHub
commit cbca46ab55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 370 additions and 192 deletions

View File

@ -1,20 +0,0 @@
import * as types from './actionsTypes';
export function setRoles(roles) {
return {
type: types.ROLES.SET,
roles
};
}
export function updateRoles(id, desc) {
return {
type: types.ROLES.UPDATE,
payload: { id, desc }
};
}
export function removeRoles(id) {
return {
type: types.ROLES.REMOVE,
payload: { id }
};
}

39
app/actions/roles.ts Normal file
View File

@ -0,0 +1,39 @@
import { Action } from 'redux';
import { IRoles } from '../reducers/roles';
import { ROLES } from './actionsTypes';
export interface ISetRoles extends Action {
roles: IRoles;
}
export interface IUpdateRoles extends Action {
payload: { id: string; desc: string };
}
export interface IRemoveRoles extends Action {
payload: { id: string };
}
export type IActionRoles = ISetRoles & IUpdateRoles & IRemoveRoles;
export function setRoles(roles: IRoles): ISetRoles {
return {
type: ROLES.SET,
roles
};
}
export function updateRoles(id: string, desc: string): IUpdateRoles {
return {
type: ROLES.UPDATE,
payload: { id, desc }
};
}
export function removeRoles(id: string): IRemoveRoles {
return {
type: ROLES.REMOVE,
payload: { id }
};
}

View File

@ -1,58 +0,0 @@
import * as types from './actionsTypes';
export function roomsRequest(params = { allData: false }) {
return {
type: types.ROOMS.REQUEST,
params
};
}
export function roomsSuccess() {
return {
type: types.ROOMS.SUCCESS
};
}
export function roomsFailure(err) {
return {
type: types.ROOMS.FAILURE,
err
};
}
export function roomsRefresh() {
return {
type: types.ROOMS.REFRESH
};
}
export function setSearch(searchText) {
return {
type: types.ROOMS.SET_SEARCH,
searchText
};
}
export function closeServerDropdown() {
return {
type: types.ROOMS.CLOSE_SERVER_DROPDOWN
};
}
export function toggleServerDropdown() {
return {
type: types.ROOMS.TOGGLE_SERVER_DROPDOWN
};
}
export function openSearchHeader() {
return {
type: types.ROOMS.OPEN_SEARCH_HEADER
};
}
export function closeSearchHeader() {
return {
type: types.ROOMS.CLOSE_SEARCH_HEADER
};
}

78
app/actions/rooms.ts Normal file
View File

@ -0,0 +1,78 @@
import { Action } from 'redux';
import { ROOMS } from './actionsTypes';
export interface IRoomsRequest extends Action {
params: any;
}
export interface ISetSearch extends Action {
searchText: string;
}
export interface IRoomsFailure extends Action {
err: Record<string, any> | string;
}
export type IRoomsAction = IRoomsRequest & ISetSearch & IRoomsFailure;
export function roomsRequest(
params: {
allData: boolean;
} = { allData: false }
): IRoomsRequest {
return {
type: ROOMS.REQUEST,
params
};
}
export function roomsSuccess(): Action {
return {
type: ROOMS.SUCCESS
};
}
export function roomsFailure(err: string): IRoomsFailure {
return {
type: ROOMS.FAILURE,
err
};
}
export function roomsRefresh(): Action {
return {
type: ROOMS.REFRESH
};
}
export function setSearch(searchText: string): ISetSearch {
return {
type: ROOMS.SET_SEARCH,
searchText
};
}
export function closeServerDropdown(): Action {
return {
type: ROOMS.CLOSE_SERVER_DROPDOWN
};
}
export function toggleServerDropdown(): Action {
return {
type: ROOMS.TOGGLE_SERVER_DROPDOWN
};
}
export function openSearchHeader(): Action {
return {
type: ROOMS.OPEN_SEARCH_HEADER
};
}
export function closeSearchHeader(): Action {
return {
type: ROOMS.CLOSE_SEARCH_HEADER
};
}

View File

@ -1,21 +0,0 @@
import { SETTINGS } from './actionsTypes';
export function addSettings(settings) {
return {
type: SETTINGS.ADD,
payload: settings
};
}
export function updateSettings(id, value) {
return {
type: SETTINGS.UPDATE,
payload: { id, value }
};
}
export function clearSettings() {
return {
type: SETTINGS.CLEAR
};
}

34
app/actions/settings.ts Normal file
View File

@ -0,0 +1,34 @@
import { Action } from 'redux';
import { ISettings, TSettings } from '../reducers/settings';
import { SETTINGS } from './actionsTypes';
interface IAddSettings extends Action {
payload: ISettings;
}
interface IUpdateSettings extends Action {
payload: { id: string; value: TSettings };
}
export type IActionSettings = IAddSettings & IUpdateSettings;
export function addSettings(settings: ISettings): IAddSettings {
return {
type: SETTINGS.ADD,
payload: settings
};
}
export function updateSettings(id: string, value: TSettings): IUpdateSettings {
return {
type: SETTINGS.UPDATE,
payload: { id, value }
};
}
export function clearSettings(): Action {
return {
type: SETTINGS.CLEAR
};
}

View File

@ -1,13 +1,17 @@
import { TActionActiveUsers } from '../../actions/activeUsers';
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 { IInviteLinks } from '../../reducers/inviteLinks';
import { IRoles } from '../../reducers/roles';
import { ISelectedUsers } from '../../reducers/selectedUsers';
import { ISettings } from '../../reducers/settings';
export interface IApplicationState {
settings: any;
settings: ISettings;
login: any;
meteor: any;
server: any;
@ -27,7 +31,7 @@ export interface IApplicationState {
enterpriseModules: any;
encryption: any;
permissions: any;
roles: any;
roles: IRoles;
}
export type TApplicationActions = TActionActiveUsers & TActionSelectedUsers & TActionInviteLinks;
export type TApplicationActions = TActionActiveUsers & TActionSelectedUsers & TActionInviteLinks & IActionRoles & IActionSettings;

View File

@ -0,0 +1,35 @@
import { setRoles, updateRoles, removeRoles } from '../actions/roles';
import { mockedStore } from './mockedStore';
import { initialState } from './roles';
describe('test roles reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().roles;
expect(state).toEqual(initialState);
});
it('should return modified store after call setRoles action', () => {
const roles = { admin: 'enabled', user: 'enabled', dog: 'carlitos' };
mockedStore.dispatch(setRoles(roles));
const state = mockedStore.getState().roles;
expect(state.admin).toEqual('enabled');
expect(state.user).toEqual('enabled');
expect(state.dog).toEqual('carlitos');
});
it('should return modified store after call updateRoles action', () => {
mockedStore.dispatch(updateRoles('admin', 'disabled'));
const state = mockedStore.getState().roles;
expect(state.admin).toEqual('disabled');
expect(state.user).toEqual('enabled');
expect(state.dog).toEqual('carlitos');
});
it('should return modified store after call removeRoles action', () => {
mockedStore.dispatch(removeRoles('dog'));
const state = mockedStore.getState().roles;
expect(state.admin).toEqual('disabled');
expect(state.user).toEqual('enabled');
expect(state.dog).toEqual(undefined);
});
});

View File

@ -1,8 +1,11 @@
import { ROLES } from '../actions/actionsTypes';
import { IActionRoles } from '../actions/roles';
const initialState = {};
export type IRoles = Record<string, string>;
export default function permissions(state = initialState, action) {
export const initialState: IRoles = {};
export default function roles(state = initialState, action: IActionRoles): IRoles {
switch (action.type) {
case ROLES.SET:
return action.roles;

View File

@ -0,0 +1,77 @@
import {
closeSearchHeader,
closeServerDropdown,
openSearchHeader,
roomsFailure,
roomsRefresh,
roomsRequest,
roomsSuccess,
setSearch,
toggleServerDropdown
} from '../actions/rooms';
import { mockedStore } from './mockedStore';
import { initialState } from './rooms';
describe('test selectedUsers reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().rooms;
expect(state).toEqual(initialState);
});
it('should return modified store after call roomsRequest', () => {
mockedStore.dispatch(roomsRequest());
const state = mockedStore.getState().rooms;
const manipulated = { ...initialState, isFetching: true, failure: false, errorMessage: {} };
expect(state).toEqual(manipulated);
});
it('should return modified store after call roomsSuccess', () => {
mockedStore.dispatch(roomsSuccess());
const state = mockedStore.getState().rooms;
const manipulated = { ...initialState, isFetching: false, refreshing: false };
expect(state).toEqual(manipulated);
});
it('should return modified store after call roomsRefresh', () => {
mockedStore.dispatch(roomsRefresh());
const state = mockedStore.getState().rooms;
const manipulated = { ...initialState, isFetching: true, refreshing: true };
expect(state).toEqual(manipulated);
});
it('should return modified store after call setSearch', () => {
mockedStore.dispatch(setSearch('dog'));
const state = mockedStore.getState().rooms;
expect(state.searchText).toEqual('dog');
});
it('should return modified store after call closeServerDropdown', () => {
mockedStore.dispatch(closeServerDropdown());
const state = mockedStore.getState().rooms;
expect(state.closeServerDropdown).toEqual(!initialState.closeServerDropdown);
});
it('should return modified store after call toggleServerDropdown', () => {
mockedStore.dispatch(toggleServerDropdown());
const state = mockedStore.getState().rooms;
expect(state.showServerDropdown).toEqual(!initialState.showServerDropdown);
});
it('should return modified store after call openSearchHeader', () => {
mockedStore.dispatch(openSearchHeader());
const state = mockedStore.getState().rooms;
expect(state.showSearchHeader).toEqual(true);
});
it('should return modified store after call closeSearchHeader', () => {
mockedStore.dispatch(closeSearchHeader());
const state = mockedStore.getState().rooms;
expect(state.showSearchHeader).toEqual(false);
});
it('should return modified store after call roomsFailure', () => {
mockedStore.dispatch(roomsFailure('error'));
const state = mockedStore.getState().rooms;
expect(state.errorMessage).toEqual('error');
});
});

View File

@ -1,6 +1,18 @@
import * as types from '../actions/actionsTypes';
import { IRoomsAction } from '../actions/rooms';
import { ROOMS } from '../actions/actionsTypes';
const initialState = {
export interface IRooms {
isFetching: boolean;
refreshing: boolean;
failure: boolean;
errorMessage: Record<string, any> | string;
searchText: string;
showServerDropdown: boolean;
closeServerDropdown: boolean;
showSearchHeader: boolean;
}
export const initialState: IRooms = {
isFetching: false,
refreshing: false,
failure: false,
@ -11,22 +23,22 @@ const initialState = {
showSearchHeader: false
};
export default function login(state = initialState, action) {
export default function rooms(state = initialState, action: IRoomsAction): IRooms {
switch (action.type) {
case types.ROOMS.REQUEST:
case ROOMS.REQUEST:
return {
...state,
isFetching: true,
failure: false,
errorMessage: {}
};
case types.ROOMS.SUCCESS:
case ROOMS.SUCCESS:
return {
...state,
isFetching: false,
refreshing: false
};
case types.ROOMS.FAILURE:
case ROOMS.FAILURE:
return {
...state,
isFetching: false,
@ -34,33 +46,33 @@ export default function login(state = initialState, action) {
failure: true,
errorMessage: action.err
};
case types.ROOMS.REFRESH:
case ROOMS.REFRESH:
return {
...state,
isFetching: true,
refreshing: true
};
case types.ROOMS.SET_SEARCH:
case ROOMS.SET_SEARCH:
return {
...state,
searchText: action.searchText
};
case types.ROOMS.CLOSE_SERVER_DROPDOWN:
case ROOMS.CLOSE_SERVER_DROPDOWN:
return {
...state,
closeServerDropdown: !state.closeServerDropdown
};
case types.ROOMS.TOGGLE_SERVER_DROPDOWN:
case ROOMS.TOGGLE_SERVER_DROPDOWN:
return {
...state,
showServerDropdown: !state.showServerDropdown
};
case types.ROOMS.OPEN_SEARCH_HEADER:
case ROOMS.OPEN_SEARCH_HEADER:
return {
...state,
showSearchHeader: true
};
case types.ROOMS.CLOSE_SEARCH_HEADER:
case ROOMS.CLOSE_SEARCH_HEADER:
return {
...state,
showSearchHeader: false

View File

@ -0,0 +1,31 @@
import { addSettings, clearSettings, updateSettings } from '../actions/settings';
import { mockedStore } from './mockedStore';
import { initialState } from './settings';
describe('test settings reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().settings;
expect(state).toEqual(initialState);
});
const settings = { API_Use_REST_For_DDP_Calls: true, FileUpload_MaxFileSize: 600857600, Jitsi_URL_Room_Prefix: 'RocketChat' };
it('should return modified store after call addSettings action', () => {
mockedStore.dispatch(addSettings(settings));
const state = mockedStore.getState().settings;
expect(state).toEqual(settings);
});
it('should return correctly settings after call updateSettings action', () => {
const id = 'Jitsi_URL_Room_Prefix';
mockedStore.dispatch(updateSettings(id, 'ChatRocket'));
const state = mockedStore.getState().settings;
expect(state[id]).toEqual('ChatRocket');
});
it('should return initial state after clearSettings', () => {
mockedStore.dispatch(clearSettings());
const state = mockedStore.getState().settings;
expect(state).toEqual({});
});
});

View File

@ -1,8 +1,13 @@
import { IActionSettings } from '../actions/settings';
import { SETTINGS } from '../actions/actionsTypes';
const initialState = {};
export type TSettings = string | number | boolean;
export default (state = initialState, action) => {
export type ISettings = Record<string, TSettings>;
export const initialState: ISettings = {};
export default (state = initialState, action: IActionSettings): ISettings => {
switch (action.type) {
case SETTINGS.ADD:
return {

View File

@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { toggleServerDropdown, closeServerDropdown, setSearch as setSearchAction } from '../../../actions/rooms';
import { toggleServerDropdown, closeServerDropdown, setSearch } from '../../../actions/rooms';
import { withTheme } from '../../../theme';
import EventEmitter from '../../../utils/events';
import { KEY_COMMAND, handleCommandOpenServerDropdown } from '../../../commands';
@ -19,10 +19,7 @@ class RoomsListHeaderView extends PureComponent {
connected: PropTypes.bool,
isFetching: PropTypes.bool,
theme: PropTypes.string,
server: PropTypes.string,
open: PropTypes.func,
close: PropTypes.func,
setSearch: PropTypes.func
server: PropTypes.string
};
componentDidMount() {
@ -45,17 +42,17 @@ class RoomsListHeaderView extends PureComponent {
};
onSearchChangeText = text => {
const { setSearch } = this.props;
setSearch(text.trim());
const { dispatch } = this.props;
dispatch(setSearch(text.trim()));
};
onPress = () => {
logEvent(events.RL_TOGGLE_SERVER_DROPDOWN);
const { showServerDropdown, close, open } = this.props;
const { showServerDropdown, dispatch } = this.props;
if (showServerDropdown) {
close();
dispatch(closeServerDropdown());
} else {
open();
dispatch(toggleServerDropdown());
}
};
@ -89,10 +86,4 @@ const mapStateToProps = state => ({
server: state.server.server
});
const mapDispatchtoProps = dispatch => ({
close: () => dispatch(closeServerDropdown()),
open: () => dispatch(toggleServerDropdown()),
setSearch: searchText => dispatch(setSearchAction(searchText))
});
export default connect(mapStateToProps, mapDispatchtoProps)(withTheme(RoomsListHeaderView));
export default connect(mapStateToProps)(withTheme(RoomsListHeaderView));

View File

@ -6,9 +6,9 @@ import { withSafeAreaInsets } from 'react-native-safe-area-context';
import * as List from '../../containers/List';
import Button from '../../containers/Button';
import { toggleServerDropdown as toggleServerDropdownAction } from '../../actions/rooms';
import { selectServerRequest as selectServerRequestAction, serverInitAdd as serverInitAddAction } from '../../actions/server';
import { appStart as appStartAction, ROOT_OUTSIDE } from '../../actions/app';
import { toggleServerDropdown } from '../../actions/rooms';
import { selectServerRequest, serverInitAdd } from '../../actions/server';
import { appStart, ROOT_OUTSIDE } from '../../actions/app';
import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n';
import EventEmitter from '../../utils/events';
@ -36,11 +36,7 @@ class ServerDropdown extends Component {
closeServerDropdown: PropTypes.bool,
server: PropTypes.string,
theme: PropTypes.string,
isMasterDetail: PropTypes.bool,
appStart: PropTypes.func,
toggleServerDropdown: PropTypes.func,
selectServerRequest: PropTypes.func,
initAdd: PropTypes.func
isMasterDetail: PropTypes.bool
};
constructor(props) {
@ -89,13 +85,13 @@ class ServerDropdown extends Component {
}
close = () => {
const { toggleServerDropdown } = this.props;
const { dispatch } = this.props;
Animated.timing(this.animatedValue, {
toValue: 0,
duration: ANIMATION_DURATION,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true
}).start(() => toggleServerDropdown());
}).start(() => dispatch(toggleServerDropdown()));
};
createWorkspace = async () => {
@ -108,10 +104,10 @@ class ServerDropdown extends Component {
};
navToNewServer = previousServer => {
const { appStart, initAdd } = this.props;
const { dispatch } = this.props;
batch(() => {
appStart({ root: ROOT_OUTSIDE });
initAdd(previousServer);
dispatch(appStart({ root: ROOT_OUTSIDE }));
dispatch(serverInitAdd(previousServer));
});
};
@ -125,7 +121,7 @@ class ServerDropdown extends Component {
};
select = async (server, version) => {
const { server: currentServer, selectServerRequest, isMasterDetail } = this.props;
const { server: currentServer, dispatch, isMasterDetail } = this.props;
this.close();
if (currentServer !== server) {
logEvent(events.RL_CHANGE_SERVER);
@ -142,7 +138,7 @@ class ServerDropdown extends Component {
}, ANIMATION_DURATION);
} else {
await localAuthenticate(server);
selectServerRequest(server, version);
dispatch(selectServerRequest(server, version, true, true));
}
}
};
@ -263,11 +259,4 @@ const mapStateToProps = state => ({
isMasterDetail: state.app.isMasterDetail
});
const mapDispatchToProps = dispatch => ({
toggleServerDropdown: () => dispatch(toggleServerDropdownAction()),
selectServerRequest: (server, version) => dispatch(selectServerRequestAction(server, version, true, true)),
appStart: params => dispatch(appStartAction(params)),
initAdd: previousServer => dispatch(serverInitAddAction(previousServer))
});
export default connect(mapStateToProps, mapDispatchToProps)(withSafeAreaInsets(withTheme(ServerDropdown)));
export default connect(mapStateToProps)(withSafeAreaInsets(withTheme(ServerDropdown)));

View File

@ -12,19 +12,13 @@ import RocketChat from '../../lib/rocketchat';
import RoomItem, { ROW_HEIGHT, ROW_HEIGHT_CONDENSED } from '../../presentation/RoomItem';
import log, { logEvent, events } from '../../utils/log';
import I18n from '../../i18n';
import {
closeSearchHeader as closeSearchHeaderAction,
closeServerDropdown as closeServerDropdownAction,
openSearchHeader as openSearchHeaderAction,
roomsRequest as roomsRequestAction
} from '../../actions/rooms';
import { appStart as appStartAction, ROOT_OUTSIDE } from '../../actions/app';
import { closeSearchHeader, closeServerDropdown, openSearchHeader, roomsRequest } from '../../actions/rooms';
import { appStart, ROOT_OUTSIDE } from '../../actions/app';
import debounce from '../../utils/debounce';
import { isIOS, isTablet } from '../../utils/deviceInfo';
import * as HeaderButton from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
import ActivityIndicator from '../../containers/ActivityIndicator';
import { selectServerRequest as selectServerRequestAction, serverInitAdd as serverInitAddAction } from '../../actions/server';
import { animateNextTransition } from '../../utils/layoutAnimation';
import { withTheme } from '../../theme';
import { themes } from '../../constants/colors';
@ -124,11 +118,6 @@ class RoomsListView extends React.Component {
refreshing: PropTypes.bool,
StoreLastMessage: PropTypes.bool,
theme: PropTypes.string,
openSearchHeader: PropTypes.func,
closeSearchHeader: PropTypes.func,
appStart: PropTypes.func,
roomsRequest: PropTypes.func,
closeServerDropdown: PropTypes.func,
useRealName: PropTypes.bool,
isMasterDetail: PropTypes.bool,
rooms: PropTypes.array,
@ -169,7 +158,7 @@ class RoomsListView extends React.Component {
}
componentDidMount() {
const { navigation, closeServerDropdown } = this.props;
const { navigation, dispatch } = this.props;
this.handleHasPermission();
this.mounted = true;
@ -193,7 +182,7 @@ class RoomsListView extends React.Component {
});
this.unsubscribeBlur = navigation.addListener('blur', () => {
this.animated = false;
closeServerDropdown();
dispatch(closeServerDropdown());
this.cancelSearch();
if (this.backHandler && this.backHandler.remove) {
this.backHandler.remove();
@ -553,9 +542,9 @@ class RoomsListView extends React.Component {
initSearching = () => {
logEvent(events.RL_SEARCH);
const { openSearchHeader } = this.props;
const { dispatch } = this.props;
this.internalSetState({ searching: true }, () => {
openSearchHeader();
dispatch(openSearchHeader());
this.search('');
this.setHeader();
});
@ -563,7 +552,7 @@ class RoomsListView extends React.Component {
cancelSearch = () => {
const { searching } = this.state;
const { closeSearchHeader } = this.props;
const { dispatch } = this.props;
if (!searching) {
return;
@ -573,7 +562,7 @@ class RoomsListView extends React.Component {
this.setState({ searching: false, search: [] }, () => {
this.setHeader();
closeSearchHeader();
dispatch(closeSearchHeader());
setTimeout(() => {
this.scrollToTop();
}, 200);
@ -842,7 +831,7 @@ class RoomsListView extends React.Component {
};
handleCommands = ({ event }) => {
const { navigation, server, isMasterDetail, appStart, initAdd } = this.props;
const { navigation, server, isMasterDetail, dispatch, initAdd } = this.props;
const { input } = event;
if (handleCommandShowPreferences(event)) {
navigation.navigate('SettingsView');
@ -862,7 +851,7 @@ class RoomsListView extends React.Component {
}
} else if (handleCommandAddNewServer(event)) {
batch(() => {
appStart({ root: ROOT_OUTSIDE });
dispatch(appStart({ root: ROOT_OUTSIDE }));
initAdd(server);
});
}
@ -870,11 +859,11 @@ class RoomsListView extends React.Component {
onRefresh = () => {
const { searching } = this.state;
const { roomsRequest } = this.props;
const { dispatch } = this.props;
if (searching) {
return;
}
roomsRequest({ allData: true });
dispatch(roomsRequest({ allData: true }));
};
onEndReached = () => {
@ -1045,14 +1034,4 @@ const mapStateToProps = state => ({
createDiscussionPermission: state.permissions['start-discussion']
});
const mapDispatchToProps = dispatch => ({
openSearchHeader: () => dispatch(openSearchHeaderAction()),
closeSearchHeader: () => dispatch(closeSearchHeaderAction()),
roomsRequest: params => dispatch(roomsRequestAction(params)),
selectServerRequest: server => dispatch(selectServerRequestAction(server)),
closeServerDropdown: () => dispatch(closeServerDropdownAction()),
appStart: params => dispatch(appStartAction(params)),
initAdd: previousServer => dispatch(serverInitAddAction(previousServer))
});
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));
export default connect(mapStateToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));