Chore: Migrate redux module room to typescript (#3636)

* chore: migrate redux module room to typescript and remove dispatch on dependencies

* chore: add tests to redux module room

* chore: create ERoomType and use on implemention

* chore: update enum name

* fix test id
This commit is contained in:
Gleidson Daniel Silva 2022-02-18 10:46:19 -03:00 committed by GitHub
parent eb38761a37
commit d73886bd60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 227 additions and 121 deletions

View File

@ -1,62 +0,0 @@
import * as types from './actionsTypes';
export function subscribeRoom(rid) {
return {
type: types.ROOM.SUBSCRIBE,
rid
};
}
export function unsubscribeRoom(rid) {
return {
type: types.ROOM.UNSUBSCRIBE,
rid
};
}
export function leaveRoom(roomType, room, selected) {
return {
type: types.ROOM.LEAVE,
room,
roomType,
selected
};
}
export function deleteRoom(roomType, room, selected) {
return {
type: types.ROOM.DELETE,
room,
roomType,
selected
};
}
export function closeRoom(rid) {
return {
type: types.ROOM.CLOSE,
rid
};
}
export function forwardRoom(rid, transferData) {
return {
type: types.ROOM.FORWARD,
transferData,
rid
};
}
export function removedRoom() {
return {
type: types.ROOM.REMOVED
};
}
export function userTyping(rid, status = true) {
return {
type: types.ROOM.USER_TYPING,
rid,
status
};
}

109
app/actions/room.ts Normal file
View File

@ -0,0 +1,109 @@
import { Action } from 'redux';
import { ERoomType } from '../definitions/ERoomType';
import { ROOM } from './actionsTypes';
// TYPE RETURN RELATED
type ISelected = Record<string, string>;
export interface ITransferData {
roomId: string;
userId?: string;
departmentId?: string;
}
// ACTION RETURN RELATED
interface IBaseReturn extends Action {
rid: string;
}
type TSubscribeRoom = IBaseReturn;
type TUnsubscribeRoom = IBaseReturn;
type TCloseRoom = IBaseReturn;
type TRoom = Record<string, any>;
interface ILeaveRoom extends Action {
roomType: ERoomType;
room: TRoom;
selected?: ISelected;
}
interface IDeleteRoom extends Action {
roomType: ERoomType;
room: TRoom;
selected?: ISelected;
}
interface IForwardRoom extends Action {
transferData: ITransferData;
rid: string;
}
interface IUserTyping extends Action {
rid: string;
status: boolean;
}
export type TActionsRoom = TSubscribeRoom & TUnsubscribeRoom & TCloseRoom & ILeaveRoom & IDeleteRoom & IForwardRoom & IUserTyping;
export function subscribeRoom(rid: string): TSubscribeRoom {
return {
type: ROOM.SUBSCRIBE,
rid
};
}
export function unsubscribeRoom(rid: string): TUnsubscribeRoom {
return {
type: ROOM.UNSUBSCRIBE,
rid
};
}
export function leaveRoom(roomType: ERoomType, room: TRoom, selected?: ISelected): ILeaveRoom {
return {
type: ROOM.LEAVE,
room,
roomType,
selected
};
}
export function deleteRoom(roomType: ERoomType, room: TRoom, selected?: ISelected): IDeleteRoom {
return {
type: ROOM.DELETE,
room,
roomType,
selected
};
}
export function closeRoom(rid: string): TCloseRoom {
return {
type: ROOM.CLOSE,
rid
};
}
export function forwardRoom(rid: string, transferData: ITransferData): IForwardRoom {
return {
type: ROOM.FORWARD,
transferData,
rid
};
}
export function removedRoom(): Action {
return {
type: ROOM.REMOVED
};
}
export function userTyping(rid: string, status = true): IUserTyping {
return {
type: ROOM.USER_TYPING,
rid,
status
};
}

View File

@ -0,0 +1,7 @@
export enum ERoomType {
p = 'group',
c = 'channel',
d = 'direct',
t = 'team',
l = 'omnichannel'
}

View File

@ -24,6 +24,7 @@ import { ICreateDiscussion } from '../../reducers/createDiscussion';
import { IEncryption } from '../../reducers/encryption';
import { IInviteLinks } from '../../reducers/inviteLinks';
import { IRoles } from '../../reducers/roles';
import { IRoom } from '../../reducers/room';
import { ISelectedUsers } from '../../reducers/selectedUsers';
import { IServer } from '../../reducers/server';
import { ISettings } from '../../reducers/settings';
@ -39,7 +40,7 @@ export interface IApplicationState {
selectedUsers: ISelectedUsers;
app: IApp;
createChannel: ICreateChannel;
room: any;
room: IRoom;
rooms: any;
sortPreferences: any;
share: IShare;

58
app/reducers/room.test.ts Normal file
View File

@ -0,0 +1,58 @@
import { closeRoom, deleteRoom, forwardRoom, leaveRoom, removedRoom, subscribeRoom, unsubscribeRoom } from '../actions/room';
import { ERoomType } from '../definitions/ERoomType';
import { mockedStore } from './mockedStore';
import { initialState } from './room';
describe('test room reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().room;
expect(state).toEqual(initialState);
});
it('should return modified store after subscribeRoom', () => {
mockedStore.dispatch(subscribeRoom('GENERAL'));
const state = mockedStore.getState().room;
expect(state.rooms).toEqual(['GENERAL']);
});
it('should return empty store after remove unsubscribeRoom', () => {
mockedStore.dispatch(unsubscribeRoom('GENERAL'));
const state = mockedStore.getState().room;
expect(state.rooms).toEqual([]);
});
it('should return initial state after leaveRoom', () => {
mockedStore.dispatch(leaveRoom(ERoomType.c, { rid: ERoomType.c }));
const { rid, isDeleting } = mockedStore.getState().room;
expect(rid).toEqual(ERoomType.c);
expect(isDeleting).toEqual(true);
});
it('should return initial state after deleteRoom', () => {
mockedStore.dispatch(deleteRoom(ERoomType.l, { rid: ERoomType.l }));
const { rid, isDeleting } = mockedStore.getState().room;
expect(rid).toEqual(ERoomType.l);
expect(isDeleting).toEqual(true);
});
it('should return initial state after closeRoom', () => {
mockedStore.dispatch(closeRoom('CLOSING'));
const { rid, isDeleting } = mockedStore.getState().room;
expect(rid).toEqual('CLOSING');
expect(isDeleting).toEqual(true);
});
it('should return initial state after forwardRoom', () => {
const transferData = { roomId: 'FORWARDING' };
mockedStore.dispatch(forwardRoom('FORWARDING', transferData));
const { rid, isDeleting } = mockedStore.getState().room;
expect(rid).toEqual('FORWARDING');
expect(isDeleting).toEqual(true);
});
it('should return loading after call removedRoom', () => {
mockedStore.dispatch(removedRoom());
const { isDeleting } = mockedStore.getState().room;
expect(isDeleting).toEqual(false);
});
});

View File

@ -1,12 +1,21 @@
import { TActionsRoom } from '../actions/room';
import { ROOM } from '../actions/actionsTypes';
const initialState = {
rid: null,
export type IRoomRecord = string[];
export interface IRoom {
rid: string;
isDeleting: boolean;
rooms: IRoomRecord;
}
export const initialState: IRoom = {
rid: '',
isDeleting: false,
rooms: []
};
export default function (state = initialState, action) {
export default function (state = initialState, action: TActionsRoom): IRoom {
switch (action.type) {
case ROOM.SUBSCRIBE:
return {

View File

@ -260,7 +260,7 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView
/>
</HeaderButton.Container>
),
headerTitle: () => <SearchHeader onSearchChangeText={onChangeText} />,
headerTitle: () => <SearchHeader onSearchChangeText={onChangeText} testID='team-channels-view-search-header' />,
headerTitleContainerStyle: {
left: headerTitlePosition.left,
right: headerTitlePosition.right

View File

@ -1,20 +1,17 @@
import React, { useEffect, useState } from 'react';
import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native';
import { StyleSheet, View } from 'react-native';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import isEmpty from 'lodash/isEmpty';
import React, { useEffect, useState } from 'react';
import { StyleSheet, View } from 'react-native';
import { useDispatch } from 'react-redux';
import I18n from '../i18n';
import { withTheme } from '../theme';
import { forwardRoom, ITransferData } from '../actions/room';
import { themes } from '../constants/colors';
import RocketChat from '../lib/rocketchat';
import OrSeparator from '../containers/OrSeparator';
import Input from '../containers/UIKit/MultiSelect/Input';
import { forwardRoom as forwardRoomAction } from '../actions/room';
import { IRoom } from '../definitions';
import { IBaseScreen, IRoom } from '../definitions';
import I18n from '../i18n';
import RocketChat from '../lib/rocketchat';
import { ChatsStackParamList } from '../stacks/types';
import { withTheme } from '../theme';
import { IOptionsField } from './NotificationPreferencesView/options';
const styles = StyleSheet.create({
@ -23,33 +20,26 @@ const styles = StyleSheet.create({
padding: 16
}
});
interface ITransferData {
roomId: string;
userId?: string;
departmentId?: string;
}
interface IUser {
username: string;
_id: string;
}
interface IForwardLivechatViewProps {
navigation: StackNavigationProp<ChatsStackParamList, 'ForwardLivechatView'>;
route: RouteProp<ChatsStackParamList, 'ForwardLivechatView'>;
theme: string;
forwardRoom: (rid: string, transferData: ITransferData) => void;
interface IParsedData {
label: string;
value: string;
}
const COUNT_DEPARTMENT = 50;
const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForwardLivechatViewProps) => {
const [departments, setDepartments] = useState<IOptionsField[]>([]);
const ForwardLivechatView = ({ navigation, route, theme }: IBaseScreen<ChatsStackParamList, 'ForwardLivechatView'>) => {
const [departments, setDepartments] = useState<IParsedData[]>([]);
const [departmentId, setDepartment] = useState('');
const [departmentTotal, setDepartmentTotal] = useState(0);
const [users, setUsers] = useState<IOptionsField[]>([]);
const [userId, setUser] = useState();
const [room, setRoom] = useState<IRoom>({} as IRoom);
const dispatch = useDispatch();
const rid = route.params?.rid;
@ -57,7 +47,7 @@ const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForward
try {
const result = await RocketChat.getDepartments({ count: COUNT_DEPARTMENT, text, offset });
if (result.success) {
const parsedDepartments: IOptionsField[] = result.departments.map(department => ({
const parsedDepartments: IParsedData[] = result.departments.map(department => ({
label: department.name,
value: department._id
}));
@ -116,7 +106,7 @@ const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForward
transferData.departmentId = departmentId;
}
forwardRoom(rid, transferData);
dispatch(forwardRoom(rid, transferData));
};
useEffect(() => {
@ -171,8 +161,4 @@ const ForwardLivechatView = ({ forwardRoom, navigation, route, theme }: IForward
);
};
const mapDispatchToProps = (dispatch: Dispatch) => ({
forwardRoom: (rid: string, transferData: ITransferData) => dispatch(forwardRoomAction(rid, transferData))
});
export default connect(null, mapDispatchToProps)(withTheme(ForwardLivechatView));
export default withTheme(ForwardLivechatView);

View File

@ -7,8 +7,8 @@ import { Q } from '@nozbe/watermelondb';
import { compareServerVersion } from '../../lib/utils';
import Touch from '../../utils/touch';
import { setLoading as setLoadingAction } from '../../actions/selectedUsers';
import { closeRoom as closeRoomAction, leaveRoom as leaveRoomAction } from '../../actions/room';
import { setLoading } from '../../actions/selectedUsers';
import { closeRoom, leaveRoom } from '../../actions/room';
import sharedStyles from '../Styles';
import Avatar from '../../containers/Avatar';
import Status from '../../containers/Status';
@ -334,9 +334,9 @@ class RoomActionsView extends React.Component {
const {
room: { rid }
} = this.state;
const { closeRoom } = this.props;
const { dispatch } = this.props;
closeRoom(rid);
dispatch(closeRoom(rid));
};
returnLivechat = () => {
@ -375,16 +375,16 @@ class RoomActionsView extends React.Component {
addUser = async () => {
const { room } = this.state;
const { setLoadingInvite, navigation } = this.props;
const { dispatch, navigation } = this.props;
const { rid } = room;
try {
setLoadingInvite(true);
dispatch(setLoading(true));
await RocketChat.addUsersToRoom(rid);
navigation.pop();
} catch (e) {
log(e);
} finally {
setLoadingInvite(false);
dispatch(setLoading(false));
}
};
@ -458,12 +458,12 @@ class RoomActionsView extends React.Component {
leaveChannel = () => {
const { room } = this.state;
const { leaveRoom } = this.props;
const { dispatch } = this.props;
showConfirmationAlert({
message: I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => leaveRoom('channel', room)
onPress: () => dispatch(leaveRoom('channel', room))
});
};
@ -522,7 +522,7 @@ class RoomActionsView extends React.Component {
leaveTeam = async () => {
const { room } = this.state;
const { navigation, leaveRoom } = this.props;
const { navigation, dispatch } = this.props;
try {
const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id });
@ -538,21 +538,21 @@ class RoomActionsView extends React.Component {
title: 'Leave_Team',
data: teamChannels,
infoText: 'Select_Team_Channels',
nextAction: data => leaveRoom('team', room, data),
nextAction: data => dispatch(leaveRoom('team', room, data)),
showAlert: () => showErrorAlert(I18n.t('Last_owner_team_room'), I18n.t('Cannot_leave'))
});
} else {
showConfirmationAlert({
message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => leaveRoom('team', room)
onPress: () => dispatch(leaveRoom('team', room))
});
}
} catch (e) {
showConfirmationAlert({
message: I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }),
confirmationText: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
onPress: () => leaveRoom('team', room)
onPress: () => dispatch(leaveRoom('team', room))
});
}
};
@ -1242,10 +1242,4 @@ const mapStateToProps = state => ({
viewCannedResponsesPermission: state.permissions['view-canned-responses']
});
const mapDispatchToProps = dispatch => ({
leaveRoom: (roomType, room, selected) => dispatch(leaveRoomAction(roomType, room, selected)),
closeRoom: rid => dispatch(closeRoomAction(rid)),
setLoadingInvite: loading => dispatch(setLoadingAction(loading))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(withDimensions(RoomActionsView)));
export default connect(mapStateToProps)(withTheme(withDimensions(RoomActionsView)));

View File

@ -4,6 +4,7 @@ import React from 'react';
import { Alert, FlatList, Keyboard } from 'react-native';
import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { deleteRoom } from '../actions/room';
import { themes } from '../constants/colors';
@ -17,6 +18,7 @@ import SafeAreaView from '../containers/SafeAreaView';
import SearchHeader from '../containers/SearchHeader';
import StatusBar from '../containers/StatusBar';
import { IApplicationState, IBaseScreen } from '../definitions';
import { ERoomType } from '../definitions/ERoomType';
import { withDimensions } from '../dimensions';
import I18n from '../i18n';
import database from '../lib/database';
@ -48,7 +50,7 @@ const keyExtractor = (item: IItem) => item._id;
// This interface comes from request RocketChat.getTeamListRoom
interface IItem {
_id: string;
_id: ERoomType;
fname: string;
customFields: object;
broadcast: boolean;
@ -97,6 +99,7 @@ interface ITeamChannelsViewProps extends IProps {
showActionSheet: (options: any) => void;
showAvatar: boolean;
displayMode: string;
dispatch: Dispatch;
}
class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChannelsViewState> {
private teamId: string;
@ -438,7 +441,8 @@ class TeamChannelsView extends React.Component<ITeamChannelsViewProps, ITeamChan
{
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
style: 'destructive',
onPress: () => dispatch(deleteRoom(item._id, item.t))
// VERIFY ON PR
onPress: () => dispatch(deleteRoom(item._id, item))
}
],
{ cancelable: false }