diff --git a/app/actions/deepLinking.js b/app/actions/deepLinking.js deleted file mode 100644 index dfd540b6f..000000000 --- a/app/actions/deepLinking.js +++ /dev/null @@ -1,8 +0,0 @@ -import * as types from './actionsTypes'; - -export function deepLinkingOpen(params) { - return { - type: types.DEEP_LINKING.OPEN, - params - }; -} diff --git a/app/actions/deepLinking.ts b/app/actions/deepLinking.ts new file mode 100644 index 000000000..78ea929bb --- /dev/null +++ b/app/actions/deepLinking.ts @@ -0,0 +1,25 @@ +import { Action } from 'redux'; + +import { DEEP_LINKING } from './actionsTypes'; + +interface IParams { + path: string; + rid: string; + messageId: string; + host: string; + isCall: boolean; + fullURL: string; + type: string; + token: string; +} + +interface IDeepLinkingOpen extends Action { + params: Partial; +} + +export function deepLinkingOpen(params: Partial): IDeepLinkingOpen { + return { + type: DEEP_LINKING.OPEN, + params + }; +} diff --git a/app/actions/sortPreferences.js b/app/actions/sortPreferences.js deleted file mode 100644 index e452e74c0..000000000 --- a/app/actions/sortPreferences.js +++ /dev/null @@ -1,15 +0,0 @@ -import * as types from './actionsTypes'; - -export function setAllPreferences(preferences) { - return { - type: types.SORT_PREFERENCES.SET_ALL, - preferences - }; -} - -export function setPreference(preference) { - return { - type: types.SORT_PREFERENCES.SET, - preference - }; -} diff --git a/app/actions/sortPreferences.ts b/app/actions/sortPreferences.ts new file mode 100644 index 000000000..6a3328616 --- /dev/null +++ b/app/actions/sortPreferences.ts @@ -0,0 +1,28 @@ +import { Action } from 'redux'; + +import { IPreferences } from '../definitions'; +import { SORT_PREFERENCES } from './actionsTypes'; + +interface ISetAllPreferences extends Action { + preferences: IPreferences; +} + +interface ISetPreference extends Action { + preference: Partial; +} + +export type TActionSortPreferences = ISetAllPreferences & ISetPreference; + +export function setAllPreferences(preferences: IPreferences): ISetAllPreferences { + return { + type: SORT_PREFERENCES.SET_ALL, + preferences + }; +} + +export function setPreference(preference: Partial): ISetPreference { + return { + type: SORT_PREFERENCES.SET, + preference + }; +} diff --git a/app/actions/usersTyping.js b/app/actions/usersTyping.js deleted file mode 100644 index fb8f5980b..000000000 --- a/app/actions/usersTyping.js +++ /dev/null @@ -1,21 +0,0 @@ -import { USERS_TYPING } from './actionsTypes'; - -export function addUserTyping(username) { - return { - type: USERS_TYPING.ADD, - username - }; -} - -export function removeUserTyping(username) { - return { - type: USERS_TYPING.REMOVE, - username - }; -} - -export function clearUserTyping() { - return { - type: USERS_TYPING.CLEAR - }; -} diff --git a/app/actions/usersTyping.ts b/app/actions/usersTyping.ts new file mode 100644 index 000000000..19077ce65 --- /dev/null +++ b/app/actions/usersTyping.ts @@ -0,0 +1,29 @@ +import { Action } from 'redux'; + +import { USERS_TYPING } from './actionsTypes'; + +export interface IUsersTypingGenericAction extends Action { + username: string; +} + +export type TActionUserTyping = IUsersTypingGenericAction & Action; + +export function addUserTyping(username: string): IUsersTypingGenericAction { + return { + type: USERS_TYPING.ADD, + username + }; +} + +export function removeUserTyping(username: string): IUsersTypingGenericAction { + return { + type: USERS_TYPING.REMOVE, + username + }; +} + +export function clearUserTyping(): Action { + return { + type: USERS_TYPING.CLEAR + }; +} diff --git a/app/containers/RoomHeader/index.tsx b/app/containers/RoomHeader/index.tsx index fb00bc10c..4c21ecba2 100644 --- a/app/containers/RoomHeader/index.tsx +++ b/app/containers/RoomHeader/index.tsx @@ -1,10 +1,11 @@ +import { dequal } from 'dequal'; import React, { Component } from 'react'; import { connect } from 'react-redux'; -import { dequal } from 'dequal'; -import RoomHeader from './RoomHeader'; +import { IApplicationState } from '../../definitions'; import { withDimensions } from '../../dimensions'; import I18n from '../../i18n'; +import RoomHeader from './RoomHeader'; interface IRoomHeaderContainerProps { title: string; @@ -122,8 +123,8 @@ class RoomHeaderContainer extends Component { } } -const mapStateToProps = (state: any, ownProps: any) => { - let statusText; +const mapStateToProps = (state: IApplicationState, ownProps: any) => { + let statusText = ''; let status = 'offline'; const { roomUserId, type, visitor = {}, tmid } = ownProps; diff --git a/app/definitions/IPreferences.ts b/app/definitions/IPreferences.ts new file mode 100644 index 000000000..d444dd7e4 --- /dev/null +++ b/app/definitions/IPreferences.ts @@ -0,0 +1,10 @@ +import { SortBy, DisplayMode } from '../constants/constantDisplayMode'; + +export interface IPreferences { + sortBy: SortBy; + groupByType: boolean; + showFavorites: boolean; + showUnread: boolean; + showAvatar: boolean; + displayMode: DisplayMode; +} diff --git a/app/definitions/index.ts b/app/definitions/index.ts index 8db731d92..5abdd95c3 100644 --- a/app/definitions/index.ts +++ b/app/definitions/index.ts @@ -8,6 +8,7 @@ export * from './INotification'; export * from './IRoom'; export * from './IServer'; export * from './ISubscription'; +export * from './IPreferences'; export * from './IServerHistory'; export interface IBaseScreen, S extends string> { diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index 87753bb12..0489eeaf5 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -7,6 +7,8 @@ import { TActionInviteLinks } from '../../actions/inviteLinks'; import { IActionRoles } from '../../actions/roles'; import { TActionSelectedUsers } from '../../actions/selectedUsers'; import { IActionSettings } from '../../actions/settings'; +import { TActionSortPreferences } from '../../actions/sortPreferences'; +import { TActionUserTyping } from '../../actions/usersTyping'; // REDUCERS import { IActiveUsers } from '../../reducers/activeUsers'; import { IEncryption } from '../../reducers/encryption'; @@ -47,4 +49,6 @@ export type TApplicationActions = TActionActiveUsers & IActionRoles & IActionSettings & TActionEncryption & + TActionSortPreferences & + TActionUserTyping & TActionServer; diff --git a/app/reducers/activeUsers.ts b/app/reducers/activeUsers.ts index 9877a5ceb..1c32a13fb 100644 --- a/app/reducers/activeUsers.ts +++ b/app/reducers/activeUsers.ts @@ -4,7 +4,7 @@ import { SET_ACTIVE_USERS } from '../actions/actionsTypes'; type TUserStatus = 'online' | 'offline'; export interface IActiveUser { status: TUserStatus; - statusText?: string; + statusText: string; } export interface IActiveUsers { diff --git a/app/reducers/sortPreferences.test.ts b/app/reducers/sortPreferences.test.ts new file mode 100644 index 000000000..5de29933d --- /dev/null +++ b/app/reducers/sortPreferences.test.ts @@ -0,0 +1,35 @@ +import { IPreferences } from '../definitions'; +import { setAllPreferences, setPreference } from '../actions/sortPreferences'; +import { mockedStore } from './mockedStore'; +import { initialState } from './sortPreferences'; +import { DisplayMode, SortBy } from '../constants/constantDisplayMode'; + +describe('test sortPreferences reducer', () => { + it('should return initial state', () => { + const state = mockedStore.getState().sortPreferences; + expect(state).toEqual(initialState); + }); + + it('should return correctly value after call setPreference action', () => { + const preferences: IPreferences = { + displayMode: DisplayMode.Condensed, + groupByType: true, + showAvatar: true, + showFavorites: true, + showUnread: true, + sortBy: SortBy.Activity + }; + mockedStore.dispatch(setAllPreferences(preferences)); + const state = mockedStore.getState().sortPreferences; + expect(state).toEqual(preferences); + }); + + it('should return correctly value after call setPreference action', () => { + const preference: Partial = { + displayMode: DisplayMode.Expanded + }; + mockedStore.dispatch(setPreference(preference)); + const { displayMode } = mockedStore.getState().sortPreferences; + expect(displayMode).toEqual(DisplayMode.Expanded); + }); +}); diff --git a/app/reducers/sortPreferences.js b/app/reducers/sortPreferences.ts similarity index 72% rename from app/reducers/sortPreferences.js rename to app/reducers/sortPreferences.ts index 4ad9e797d..2083e8f7a 100644 --- a/app/reducers/sortPreferences.js +++ b/app/reducers/sortPreferences.ts @@ -1,7 +1,8 @@ import { SORT_PREFERENCES } from '../actions/actionsTypes'; import { DisplayMode, SortBy } from '../constants/constantDisplayMode'; +import { IPreferences, TApplicationActions } from '../definitions'; -const initialState = { +export const initialState: IPreferences = { sortBy: SortBy.Activity, groupByType: false, showFavorites: false, @@ -10,7 +11,7 @@ const initialState = { displayMode: DisplayMode.Expanded }; -export default (state = initialState, action) => { +export default (state = initialState, action: TApplicationActions): IPreferences => { switch (action.type) { case SORT_PREFERENCES.SET_ALL: return { diff --git a/app/reducers/usersTyping.test.ts b/app/reducers/usersTyping.test.ts new file mode 100644 index 000000000..26e527882 --- /dev/null +++ b/app/reducers/usersTyping.test.ts @@ -0,0 +1,30 @@ +import { addUserTyping, removeUserTyping, clearUserTyping } from '../actions/usersTyping'; +import { mockedStore } from './mockedStore'; +import { initialState } from './usersTyping'; + +describe('test usersTyping reducer', () => { + it('should return initial state', () => { + const state = mockedStore.getState().usersTyping; + expect(state).toEqual(initialState); + }); + + it('should return modified store after addUserTyping', () => { + mockedStore.dispatch(addUserTyping('diego')); + mockedStore.dispatch(addUserTyping('carlos')); + mockedStore.dispatch(addUserTyping('maria')); + const state = mockedStore.getState().usersTyping; + expect(state).toEqual(['diego', 'carlos', 'maria']); + }); + + it('should return modified store after removeUserTyping', () => { + mockedStore.dispatch(removeUserTyping('diego')); + const state = mockedStore.getState().usersTyping; + expect(state).toEqual(['carlos', 'maria']); + }); + + it('should return initial state after reset', () => { + mockedStore.dispatch(clearUserTyping()); + const state = mockedStore.getState().usersTyping; + expect(state).toEqual(initialState); + }); +}); diff --git a/app/reducers/usersTyping.js b/app/reducers/usersTyping.ts similarity index 72% rename from app/reducers/usersTyping.js rename to app/reducers/usersTyping.ts index acecab632..ecfbbb77d 100644 --- a/app/reducers/usersTyping.js +++ b/app/reducers/usersTyping.ts @@ -1,8 +1,11 @@ import { USERS_TYPING } from '../actions/actionsTypes'; +import { TApplicationActions } from '../definitions'; -const initialState = []; +export type IUsersTyping = string[]; -export default function usersTyping(state = initialState, action) { +export const initialState: IUsersTyping = []; + +export default function usersTyping(state = initialState, action: TApplicationActions): IUsersTyping { switch (action.type) { case USERS_TYPING.ADD: if (state.findIndex(item => item === action.username) === -1) { diff --git a/app/views/DisplayPrefsView.tsx b/app/views/DisplayPrefsView.tsx index 959682c4c..f793eb920 100644 --- a/app/views/DisplayPrefsView.tsx +++ b/app/views/DisplayPrefsView.tsx @@ -1,31 +1,23 @@ +import { StackNavigationProp } from '@react-navigation/stack'; import React, { useEffect } from 'react'; import { Switch } from 'react-native'; import { RadioButton } from 'react-native-ui-lib'; -import { StackNavigationProp } from '@react-navigation/stack'; import { useDispatch, useSelector } from 'react-redux'; import { setPreference } from '../actions/sortPreferences'; -import RocketChat from '../lib/rocketchat'; -import StatusBar from '../containers/StatusBar'; -import I18n from '../i18n'; -import * as List from '../containers/List'; -import { useTheme } from '../theme'; import { themes } from '../constants/colors'; -import * as HeaderButton from '../containers/HeaderButton'; -import SafeAreaView from '../containers/SafeAreaView'; -import { ICON_SIZE } from '../containers/List/constants'; -import log, { events, logEvent } from '../utils/log'; import { DisplayMode, SortBy } from '../constants/constantDisplayMode'; +import * as HeaderButton from '../containers/HeaderButton'; +import * as List from '../containers/List'; +import { ICON_SIZE } from '../containers/List/constants'; +import SafeAreaView from '../containers/SafeAreaView'; +import StatusBar from '../containers/StatusBar'; +import { IApplicationState, IPreferences } from '../definitions'; +import I18n from '../i18n'; +import RocketChat from '../lib/rocketchat'; import { SettingsStackParamList } from '../stacks/types'; - -interface IParam { - sortBy: SortBy; - groupByType: boolean; - showFavorites: boolean; - showUnread: boolean; - showAvatar: boolean; - displayMode: DisplayMode; -} +import { useTheme } from '../theme'; +import log, { events, logEvent } from '../utils/log'; interface IDisplayPrefsView { navigation: StackNavigationProp; @@ -36,7 +28,7 @@ const DisplayPrefsView = (props: IDisplayPrefsView): JSX.Element => { const { theme } = useTheme(); const { sortBy, groupByType, showFavorites, showUnread, showAvatar, displayMode } = useSelector( - (state: any) => state.sortPreferences + (state: IApplicationState) => state.sortPreferences ); const { isMasterDetail } = useSelector((state: any) => state.app); const dispatch = useDispatch(); @@ -53,7 +45,7 @@ const DisplayPrefsView = (props: IDisplayPrefsView): JSX.Element => { } }, []); - const setSortPreference = async (param: Partial) => { + const setSortPreference = async (param: Partial) => { try { dispatch(setPreference(param)); await RocketChat.saveSortPreference(param); diff --git a/app/views/TeamChannelsView.tsx b/app/views/TeamChannelsView.tsx index bf2df4ff1..160c28ff5 100644 --- a/app/views/TeamChannelsView.tsx +++ b/app/views/TeamChannelsView.tsx @@ -1,36 +1,36 @@ +import { Q } from '@nozbe/watermelondb'; +import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; import React from 'react'; import { Alert, FlatList, Keyboard } from 'react-native'; -import { RouteProp } from '@react-navigation/native'; -import { Dispatch } from 'redux'; -import { Q } from '@nozbe/watermelondb'; import { EdgeInsets, withSafeAreaInsets } from 'react-native-safe-area-context'; import { connect } from 'react-redux'; -import { HeaderBackButton, StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; -import StatusBar from '../containers/StatusBar'; -import RoomHeader from '../containers/RoomHeader'; -import { withTheme } from '../theme'; -import log, { events, logEvent } from '../utils/log'; -import database from '../lib/database'; -import { getUserSelector } from '../selectors/login'; +import { deleteRoom } from '../actions/room'; +import { themes } from '../constants/colors'; +import { withActionSheet } from '../containers/ActionSheet'; +import ActivityIndicator from '../containers/ActivityIndicator'; +import BackgroundContainer from '../containers/BackgroundContainer'; import { getHeaderTitlePosition } from '../containers/Header'; import * as HeaderButton from '../containers/HeaderButton'; -import BackgroundContainer from '../containers/BackgroundContainer'; +import RoomHeader from '../containers/RoomHeader'; import SafeAreaView from '../containers/SafeAreaView'; -import ActivityIndicator from '../containers/ActivityIndicator'; import SearchHeader from '../containers/SearchHeader'; -import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem'; -import RocketChat from '../lib/rocketchat'; +import StatusBar from '../containers/StatusBar'; +import { IApplicationState, IBaseScreen } from '../definitions'; import { withDimensions } from '../dimensions'; -import { isIOS } from '../utils/deviceInfo'; -import debounce from '../utils/debounce'; -import { showErrorAlert } from '../utils/info'; -import { goRoom } from '../utils/goRoom'; import I18n from '../i18n'; -import { withActionSheet } from '../containers/ActionSheet'; -import { deleteRoom as deleteRoomAction } from '../actions/room'; +import database from '../lib/database'; import { CustomIcon } from '../lib/Icons'; -import { themes } from '../constants/colors'; +import RocketChat from '../lib/rocketchat'; +import RoomItem, { ROW_HEIGHT } from '../presentation/RoomItem'; +import { getUserSelector } from '../selectors/login'; +import { ChatsStackParamList } from '../stacks/types'; +import { withTheme } from '../theme'; +import debounce from '../utils/debounce'; +import { isIOS } from '../utils/deviceInfo'; +import { goRoom } from '../utils/goRoom'; +import { showErrorAlert } from '../utils/info'; +import log, { events, logEvent } from '../utils/log'; const API_FETCH_COUNT = 25; const PERMISSION_DELETE_C = 'delete-c'; @@ -78,9 +78,11 @@ interface ITeamChannelsViewState { showCreate: boolean; } -interface ITeamChannelsViewProps { - route: RouteProp<{ TeamChannelsView: { teamId: string } }, 'TeamChannelsView'>; +type IProps = Omit, 'navigation'> & { navigation: StackNavigationProp; +}; + +interface ITeamChannelsViewProps extends IProps { isMasterDetail: boolean; insets: EdgeInsets; theme: string; @@ -93,7 +95,6 @@ interface ITeamChannelsViewProps { deleteCPermission: string[]; deletePPermission: string[]; showActionSheet: (options: any) => void; - deleteRoom: (rid: string, t: string) => void; showAvatar: boolean; displayMode: string; } @@ -422,7 +423,7 @@ class TeamChannelsView extends React.Component { logEvent(events.TC_DELETE_ROOM); - const { deleteRoom } = this.props; + const { dispatch } = this.props; Alert.alert( I18n.t('Are_you_sure_question_mark'), @@ -435,7 +436,7 @@ class TeamChannelsView extends React.Component deleteRoom(item._id, item.t) + onPress: () => dispatch(deleteRoom(item._id, item.t)) } ], { cancelable: false } @@ -574,7 +575,7 @@ class TeamChannelsView extends React.Component ({ +const mapStateToProps = (state: IApplicationState) => ({ baseUrl: state.server.server, user: getUserSelector(state), useRealName: state.settings.UI_Use_Real_Name, @@ -589,11 +590,4 @@ const mapStateToProps = (state: any) => ({ displayMode: state.sortPreferences.displayMode }); -const mapDispatchToProps = (dispatch: Dispatch) => ({ - deleteRoom: (rid: string, t: string) => dispatch(deleteRoomAction(rid, t)) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(withDimensions(withSafeAreaInsets(withTheme(withActionSheet(TeamChannelsView))))); +export default connect(mapStateToProps)(withDimensions(withSafeAreaInsets(withTheme(withActionSheet(TeamChannelsView)))));