Chore: Migrate redux module createChannel to typescript (#3602)

* chore: migrate createChannel to ts and add tests

* chore: fix naming

* chore: add more types and remove mapDispatchToProps from components

* remove todo

* update tests

* chore: migrate interface to reducer and fix errors on return

* chore: insert IApplicationState to mapStateToProps state type

* Remove spread

* fix type

* fix import and state type

Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Gleidson Daniel Silva 2022-02-02 15:02:02 -03:00 committed by GitHub
parent c442a8473d
commit 7ad900a515
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 189 additions and 125 deletions

View File

@ -1,23 +0,0 @@
import * as types from './actionsTypes';
export function createChannelRequest(data) {
return {
type: types.CREATE_CHANNEL.REQUEST,
data
};
}
export function createChannelSuccess(data) {
return {
type: types.CREATE_CHANNEL.SUCCESS,
data
};
}
export function createChannelFailure(err, isTeam) {
return {
type: types.CREATE_CHANNEL.FAILURE,
err,
isTeam
};
}

View File

@ -0,0 +1,41 @@
import { Action } from 'redux';
import { TCreateChannelResult } from '../reducers/createChannel';
import { CREATE_CHANNEL } from './actionsTypes';
interface ICreateChannelRequest extends Action {
data: TCreateChannelResult;
}
interface ICreateChannelSuccess extends Action {
data: TCreateChannelResult;
}
interface ICreateChannelFailure extends Action {
err: any;
isTeam: boolean;
}
export type TActionCreateChannel = ICreateChannelRequest & ICreateChannelSuccess & ICreateChannelFailure;
export function createChannelRequest(data: TCreateChannelResult): ICreateChannelRequest {
return {
type: CREATE_CHANNEL.REQUEST,
data
};
}
export function createChannelSuccess(data: TCreateChannelResult): ICreateChannelSuccess {
return {
type: CREATE_CHANNEL.SUCCESS,
data
};
}
export function createChannelFailure(err: any, isTeam: boolean): ICreateChannelFailure {
return {
type: CREATE_CHANNEL.FAILURE,
err,
isTeam
};
}

View File

@ -1,5 +1,6 @@
// ACTIONS
import { TActionActiveUsers } from '../../actions/activeUsers';
import { TActionCreateChannel } from '../../actions/createChannel';
import { TActionCustomEmojis } from '../../actions/customEmojis';
import { TActionEncryption } from '../../actions/encryption';
import { TActionInviteLinks } from '../../actions/inviteLinks';
@ -13,6 +14,7 @@ import { TActionUserTyping } from '../../actions/usersTyping';
// REDUCERS
import { IActiveUsers } from '../../reducers/activeUsers';
import { IConnect } from '../../reducers/connect';
import { ICreateChannel } from '../../reducers/createChannel';
import { IEncryption } from '../../reducers/encryption';
import { IInviteLinks } from '../../reducers/inviteLinks';
import { IRoles } from '../../reducers/roles';
@ -27,7 +29,7 @@ export interface IApplicationState {
meteor: IConnect;
server: IServer;
selectedUsers: ISelectedUsers;
createChannel: any;
createChannel: ICreateChannel;
app: any;
room: any;
rooms: any;
@ -54,5 +56,6 @@ export type TApplicationActions = TActionActiveUsers &
TActionEncryption &
TActionSortPreferences &
TActionUserTyping &
TActionCreateChannel &
TActionsShare &
TActionServer;

View File

@ -1,36 +0,0 @@
import { CREATE_CHANNEL } from '../actions/actionsTypes';
const initialState = {
isFetching: false,
failure: false,
result: {},
error: {}
};
export default function (state = initialState, action) {
switch (action.type) {
case CREATE_CHANNEL.REQUEST:
return {
...state,
isFetching: true,
failure: false,
error: {}
};
case CREATE_CHANNEL.SUCCESS:
return {
...state,
isFetching: false,
failure: false,
result: action.data
};
case CREATE_CHANNEL.FAILURE:
return {
...state,
isFetching: false,
failure: true,
error: action.err
};
default:
return state;
}
}

View File

@ -0,0 +1,44 @@
import { createChannelRequest, createChannelSuccess, createChannelFailure } from '../actions/createChannel';
import { initialState } from './createChannel';
import { mockedStore } from './mockedStore';
describe('test reducer', () => {
const data = {
name: 'test',
users: ['diego', 'karla'],
type: true,
readOnly: true,
broadcast: true,
encrypted: true,
isTeam: true,
teamId: 'xxx'
};
it('should return initial state', () => {
const { createChannel } = mockedStore.getState();
expect(createChannel).toEqual(initialState);
});
it('should return correct createChannel state after dispatch createChannelRequest action', () => {
mockedStore.dispatch(createChannelRequest(data));
const { createChannel } = mockedStore.getState();
expect(createChannel).toEqual({ isFetching: true, failure: false, error: {}, result: {} });
});
it('should return correct createChannel state after dispatch createChannelSuccess action', () => {
mockedStore.dispatch(createChannelSuccess(data));
const { createChannel } = mockedStore.getState();
expect(createChannel).toEqual({ isFetching: false, failure: false, result: { ...data }, error: {} });
});
it('should return correct createChannel state after dispatch createChannelFailure action', () => {
mockedStore.dispatch(createChannelFailure({ err: true }, true));
const { createChannel } = mockedStore.getState();
expect(createChannel).toEqual({
isFetching: false,
failure: true,
result: { ...data },
error: { err: true }
});
});
});

View File

@ -0,0 +1,61 @@
import { TApplicationActions } from '../definitions';
import { CREATE_CHANNEL } from '../actions/actionsTypes';
interface ICreateChannelResult {
name: string;
users: string[];
teamId: string;
type: boolean;
readOnly: boolean;
encrypted: boolean;
broadcast: boolean;
isTeam: boolean;
}
interface ICreateChannelResultOnlyGroup {
group: boolean;
}
export type TCreateChannelResult = ICreateChannelResult | ICreateChannelResultOnlyGroup;
export interface ICreateChannel {
isFetching: boolean;
failure: boolean;
result: TCreateChannelResult | {};
error: any;
}
export const initialState: ICreateChannel = {
isFetching: false,
failure: false,
result: {},
error: {}
};
export default function (state = initialState, action: TApplicationActions): ICreateChannel {
switch (action.type) {
case CREATE_CHANNEL.REQUEST:
return {
...state,
isFetching: true,
failure: false,
error: {}
};
case CREATE_CHANNEL.SUCCESS:
return {
...state,
isFetching: false,
failure: false,
result: action.data
};
case CREATE_CHANNEL.FAILURE:
return {
...state,
isFetching: false,
failure: true,
error: action.err
};
default:
return state;
}
}

View File

@ -1,16 +1,13 @@
import React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/native';
import { FlatList, ScrollView, StyleSheet, Switch, Text, View, SwitchProps } from 'react-native';
import { dequal } from 'dequal';
import * as List from '../containers/List';
import TextInput from '../presentation/TextInput';
import Loading from '../containers/Loading';
import { createChannelRequest as createChannelRequestAction } from '../actions/createChannel';
import { removeUser as removeUserAction } from '../actions/selectedUsers';
import { createChannelRequest } from '../actions/createChannel';
import { removeUser } from '../actions/selectedUsers';
import KeyboardView from '../presentation/KeyboardView';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n';
@ -26,6 +23,7 @@ import SafeAreaView from '../containers/SafeAreaView';
import RocketChat from '../lib/rocketchat';
import sharedStyles from './Styles';
import { ChatsStackParamList } from '../stacks/types';
import { IApplicationState, IBaseScreen } from '../definitions';
const styles = StyleSheet.create({
container: {
@ -75,12 +73,6 @@ interface IOtherUser {
fname: string;
}
interface ICreateFunction extends Omit<ICreateChannelViewState, 'channelName' | 'permissions'> {
name: string;
users: string[];
teamId: string;
}
interface ICreateChannelViewState {
channelName: string;
type: boolean;
@ -91,12 +83,8 @@ interface ICreateChannelViewState {
permissions: boolean[];
}
interface ICreateChannelViewProps {
navigation: StackNavigationProp<ChatsStackParamList, 'CreateChannelView'>;
route: RouteProp<ChatsStackParamList, 'CreateChannelView'>;
interface ICreateChannelViewProps extends IBaseScreen<ChatsStackParamList, 'CreateChannelView'> {
baseUrl: string;
create: (data: ICreateFunction) => void;
removeUser: (user: IOtherUser) => void;
error: object;
failure: boolean;
isFetching: boolean;
@ -107,7 +95,6 @@ interface ICreateChannelViewProps {
token: string;
roles: string[];
};
theme: string;
teamId: string;
createPublicChannelPermission: string[];
createPrivateChannelPermission: string[];
@ -223,7 +210,7 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
submit = () => {
const { channelName, type, readOnly, broadcast, encrypted, isTeam } = this.state;
const { users: usersProps, isFetching, create } = this.props;
const { users: usersProps, isFetching, dispatch } = this.props;
if (!channelName.trim() || isFetching) {
return;
@ -233,7 +220,7 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
const users = usersProps.map(user => user.name);
// create channel or team
create({
const data = {
name: channelName,
users,
type,
@ -242,15 +229,15 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
encrypted,
isTeam,
teamId: this.teamId!
});
};
dispatch(createChannelRequest(data));
Review.pushPositiveEvent();
};
removeUser = (user: IOtherUser) => {
logEvent(events.CR_REMOVE_USER);
const { removeUser } = this.props;
removeUser(user);
const { dispatch } = this.props;
dispatch(removeUser(user));
};
renderSwitch = ({ id, value, label, onValueChange, disabled = false }: ISwitch) => {
@ -434,7 +421,7 @@ class CreateChannelView extends React.Component<ICreateChannelViewProps, ICreate
}
}
const mapStateToProps = (state: any) => ({
const mapStateToProps = (state: IApplicationState) => ({
baseUrl: state.server.server,
isFetching: state.createChannel.isFetching,
encryptionEnabled: state.encryption.enabled,
@ -444,9 +431,4 @@ const mapStateToProps = (state: any) => ({
createPrivateChannelPermission: state.permissions['create-p']
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
create: (data: ICreateFunction) => dispatch(createChannelRequestAction(data)),
removeUser: (user: IOtherUser) => dispatch(removeUserAction(user))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(CreateChannelView));
export default connect(mapStateToProps)(withTheme(CreateChannelView));

View File

@ -1,30 +1,29 @@
import React from 'react';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { FlatList, StyleSheet, Text, View } from 'react-native';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
import { Q } from '@nozbe/watermelondb';
import { StackNavigationOptions } from '@react-navigation/stack';
import { dequal } from 'dequal';
import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';
import { connect } from 'react-redux';
import * as List from '../containers/List';
import Touch from '../utils/touch';
import database from '../lib/database';
import RocketChat from '../lib/rocketchat';
import UserItem from '../presentation/UserItem';
import I18n from '../i18n';
import log, { events, logEvent } from '../utils/log';
import SearchBox from '../containers/SearchBox';
import { CustomIcon } from '../lib/Icons';
import * as HeaderButton from '../containers/HeaderButton';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors';
import { withTheme } from '../theme';
import Navigation from '../lib/Navigation';
import { createChannelRequest } from '../actions/createChannel';
import { goRoom } from '../utils/goRoom';
import { themes } from '../constants/colors';
import * as HeaderButton from '../containers/HeaderButton';
import * as List from '../containers/List';
import SafeAreaView from '../containers/SafeAreaView';
import SearchBox from '../containers/SearchBox';
import StatusBar from '../containers/StatusBar';
import { IApplicationState, IBaseScreen, TSubscriptionModel } from '../definitions';
import I18n from '../i18n';
import database from '../lib/database';
import { CustomIcon } from '../lib/Icons';
import Navigation from '../lib/Navigation';
import RocketChat from '../lib/rocketchat';
import { compareServerVersion, methods } from '../lib/utils';
import { TSubscriptionModel } from '../definitions';
import UserItem from '../presentation/UserItem';
import { withTheme } from '../theme';
import { goRoom } from '../utils/goRoom';
import log, { events, logEvent } from '../utils/log';
import Touch from '../utils/touch';
import sharedStyles from './Styles';
const QUERY_SIZE = 50;
@ -74,11 +73,8 @@ interface INewMessageViewState {
permissions: boolean[];
}
interface INewMessageViewProps {
navigation: StackNavigationProp<any, 'NewMessageView'>;
create: (params: { group: boolean }) => void;
interface INewMessageViewProps extends IBaseScreen<any, 'NewMessageView'> {
maxUsers: number;
theme: string;
isMasterDetail: boolean;
serverVersion: string;
createTeamPermission: string[];
@ -175,9 +171,9 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
createGroupChat = () => {
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
const { create, maxUsers, navigation } = this.props;
const { dispatch, maxUsers, navigation } = this.props;
navigation.navigate('SelectedUsersViewCreateChannel', {
nextAction: () => create({ group: true }),
nextAction: () => dispatch(createChannelRequest({ group: true })),
buttonText: I18n.t('Create'),
maxUsers
});
@ -337,10 +333,10 @@ class NewMessageView extends React.Component<INewMessageViewProps, INewMessageVi
}
}
const mapStateToProps = (state: any) => ({
serverVersion: state.server.version,
const mapStateToProps = (state: IApplicationState) => ({
serverVersion: state.server.version as string,
isMasterDetail: state.app.isMasterDetail,
maxUsers: state.settings.DirectMesssage_maxUsers || 1,
maxUsers: (state.settings.DirectMesssage_maxUsers as number) || 1,
createTeamPermission: state.permissions['create-team'],
createDirectMessagePermission: state.permissions['create-d'],
createPublicChannelPermission: state.permissions['create-c'],
@ -348,8 +344,4 @@ const mapStateToProps = (state: any) => ({
createDiscussionPermission: state.permissions['start-discussion']
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
create: (params: { group: boolean }) => dispatch(createChannelRequest(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));
export default connect(mapStateToProps)(withTheme(NewMessageView));