From d73886bd60c0142e9058487b88ce6d95dc63da56 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Fri, 18 Feb 2022 10:46:19 -0300 Subject: [PATCH] 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 --- app/actions/room.js | 62 ----------- app/actions/room.ts | 109 ++++++++++++++++++++ app/definitions/ERoomType.ts | 7 ++ app/definitions/redux/index.ts | 3 +- app/reducers/room.test.ts | 58 +++++++++++ app/reducers/{room.js => room.ts} | 15 ++- app/views/CannedResponsesListView/index.tsx | 2 +- app/views/ForwardLivechatView.tsx | 50 ++++----- app/views/RoomActionsView/index.js | 34 +++--- app/views/TeamChannelsView.tsx | 8 +- 10 files changed, 227 insertions(+), 121 deletions(-) delete mode 100644 app/actions/room.js create mode 100644 app/actions/room.ts create mode 100644 app/definitions/ERoomType.ts create mode 100644 app/reducers/room.test.ts rename app/reducers/{room.js => room.ts} (73%) diff --git a/app/actions/room.js b/app/actions/room.js deleted file mode 100644 index 2753d7b9a..000000000 --- a/app/actions/room.js +++ /dev/null @@ -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 - }; -} diff --git a/app/actions/room.ts b/app/actions/room.ts new file mode 100644 index 000000000..9c817f565 --- /dev/null +++ b/app/actions/room.ts @@ -0,0 +1,109 @@ +import { Action } from 'redux'; + +import { ERoomType } from '../definitions/ERoomType'; +import { ROOM } from './actionsTypes'; + +// TYPE RETURN RELATED +type ISelected = Record; + +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; + +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 + }; +} diff --git a/app/definitions/ERoomType.ts b/app/definitions/ERoomType.ts new file mode 100644 index 000000000..010fa78f0 --- /dev/null +++ b/app/definitions/ERoomType.ts @@ -0,0 +1,7 @@ +export enum ERoomType { + p = 'group', + c = 'channel', + d = 'direct', + t = 'team', + l = 'omnichannel' +} diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index e23f5e321..b7927a927 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -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; diff --git a/app/reducers/room.test.ts b/app/reducers/room.test.ts new file mode 100644 index 000000000..7ffba8e2a --- /dev/null +++ b/app/reducers/room.test.ts @@ -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); + }); +}); diff --git a/app/reducers/room.js b/app/reducers/room.ts similarity index 73% rename from app/reducers/room.js rename to app/reducers/room.ts index 086e43f5c..e5b9815b4 100644 --- a/app/reducers/room.js +++ b/app/reducers/room.ts @@ -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 { diff --git a/app/views/CannedResponsesListView/index.tsx b/app/views/CannedResponsesListView/index.tsx index 1c0f4ed18..47ec73c65 100644 --- a/app/views/CannedResponsesListView/index.tsx +++ b/app/views/CannedResponsesListView/index.tsx @@ -260,7 +260,7 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView /> ), - headerTitle: () => , + headerTitle: () => , headerTitleContainerStyle: { left: headerTitlePosition.left, right: headerTitlePosition.right diff --git a/app/views/ForwardLivechatView.tsx b/app/views/ForwardLivechatView.tsx index ff0840750..21c99a0f6 100644 --- a/app/views/ForwardLivechatView.tsx +++ b/app/views/ForwardLivechatView.tsx @@ -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; - route: RouteProp; - 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([]); +const ForwardLivechatView = ({ navigation, route, theme }: IBaseScreen) => { + const [departments, setDepartments] = useState([]); const [departmentId, setDepartment] = useState(''); const [departmentTotal, setDepartmentTotal] = useState(0); const [users, setUsers] = useState([]); const [userId, setUser] = useState(); const [room, setRoom] = useState({} 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); diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.js index 44075868b..a9ede0ca3 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.js @@ -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))); diff --git a/app/views/TeamChannelsView.tsx b/app/views/TeamChannelsView.tsx index d82ae7053..a76c18ba8 100644 --- a/app/views/TeamChannelsView.tsx +++ b/app/views/TeamChannelsView.tsx @@ -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 { private teamId: string; @@ -438,7 +441,8 @@ class TeamChannelsView extends React.Component dispatch(deleteRoom(item._id, item.t)) + // VERIFY ON PR + onPress: () => dispatch(deleteRoom(item._id, item)) } ], { cancelable: false }