diff --git a/app/actions/createChannel.js b/app/actions/createChannel.js deleted file mode 100644 index c93b47ef..00000000 --- a/app/actions/createChannel.js +++ /dev/null @@ -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 - }; -} diff --git a/app/actions/createChannel.ts b/app/actions/createChannel.ts new file mode 100644 index 00000000..32df47f6 --- /dev/null +++ b/app/actions/createChannel.ts @@ -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 + }; +} diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index 723889d9..c3d28158 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -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; diff --git a/app/reducers/createChannel.js b/app/reducers/createChannel.js deleted file mode 100644 index 60e13f00..00000000 --- a/app/reducers/createChannel.js +++ /dev/null @@ -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; - } -} diff --git a/app/reducers/createChannel.test.ts b/app/reducers/createChannel.test.ts new file mode 100644 index 00000000..ed51eb45 --- /dev/null +++ b/app/reducers/createChannel.test.ts @@ -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 } + }); + }); +}); diff --git a/app/reducers/createChannel.ts b/app/reducers/createChannel.ts new file mode 100644 index 00000000..713a59f4 --- /dev/null +++ b/app/reducers/createChannel.ts @@ -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; + } +} diff --git a/app/views/CreateChannelView.tsx b/app/views/CreateChannelView.tsx index e8d719ab..6a11639b 100644 --- a/app/views/CreateChannelView.tsx +++ b/app/views/CreateChannelView.tsx @@ -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 { - name: string; - users: string[]; - teamId: string; -} - interface ICreateChannelViewState { channelName: string; type: boolean; @@ -91,12 +83,8 @@ interface ICreateChannelViewState { permissions: boolean[]; } -interface ICreateChannelViewProps { - navigation: StackNavigationProp; - route: RouteProp; +interface ICreateChannelViewProps extends IBaseScreen { 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 { 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 user.name); // create channel or team - create({ + const data = { name: channelName, users, type, @@ -242,15 +229,15 @@ class CreateChannelView extends React.Component { 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 ({ +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)); diff --git a/app/views/NewMessageView.tsx b/app/views/NewMessageView.tsx index 25df2668..66468abf 100644 --- a/app/views/NewMessageView.tsx +++ b/app/views/NewMessageView.tsx @@ -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; - create: (params: { group: boolean }) => void; +interface INewMessageViewProps extends IBaseScreen { maxUsers: number; - theme: string; isMasterDetail: boolean; serverVersion: string; createTeamPermission: string[]; @@ -175,9 +171,9 @@ class NewMessageView extends React.Component { 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 ({ - 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));