diff --git a/app/actions/room.ts b/app/actions/room.ts index 9c817f56..f0d5ed81 100644 --- a/app/actions/room.ts +++ b/app/actions/room.ts @@ -4,7 +4,7 @@ import { ERoomType } from '../definitions/ERoomType'; import { ROOM } from './actionsTypes'; // TYPE RETURN RELATED -type ISelected = Record; +type ISelected = string[]; export interface ITransferData { roomId: string; diff --git a/app/actions/settings.ts b/app/actions/settings.ts index 77b8dcc7..8db86888 100644 --- a/app/actions/settings.ts +++ b/app/actions/settings.ts @@ -1,26 +1,26 @@ import { Action } from 'redux'; -import { ISettings, TSettings } from '../reducers/settings'; +import { TSettingsState, TSupportedSettings, TSettingsValues } from '../reducers/settings'; import { SETTINGS } from './actionsTypes'; interface IAddSettings extends Action { - payload: ISettings; + payload: TSettingsState; } interface IUpdateSettings extends Action { - payload: { id: string; value: TSettings }; + payload: { id: TSupportedSettings; value: TSettingsValues }; } export type IActionSettings = IAddSettings & IUpdateSettings; -export function addSettings(settings: ISettings): IAddSettings { +export function addSettings(settings: TSettingsState): IAddSettings { return { type: SETTINGS.ADD, payload: settings }; } -export function updateSettings(id: string, value: TSettings): IUpdateSettings { +export function updateSettings(id: TSupportedSettings, value: TSettingsValues): IUpdateSettings { return { type: SETTINGS.UPDATE, payload: { id, value } diff --git a/app/constants/settings.ts b/app/constants/settings.ts index fb9c7e6b..b8477942 100644 --- a/app/constants/settings.ts +++ b/app/constants/settings.ts @@ -206,4 +206,4 @@ export default { Canned_Responses_Enable: { type: 'valueAsBoolean' } -}; +} as const; diff --git a/app/definitions/ISubscription.ts b/app/definitions/ISubscription.ts index 22caf04c..a0dd4c5a 100644 --- a/app/definitions/ISubscription.ts +++ b/app/definitions/ISubscription.ts @@ -12,7 +12,7 @@ export enum SubscriptionType { DIRECT = 'd', CHANNEL = 'c', OMNICHANNEL = 'l', - E2E = 'e2e', + E2E = 'e2e', // FIXME: this is not a type of subscription THREAD = 'thread' // FIXME: this is not a type of subscription } @@ -36,7 +36,7 @@ export interface ISubscription { _updatedAt?: string; // from server v?: IVisitor; f: boolean; - t: SubscriptionType; + t: string; // TODO: we need to review this type later ts: string | Date; ls: Date; name: string; @@ -81,6 +81,7 @@ export interface ISubscription { usernames?: string[]; visitor?: IVisitor; departmentId?: string; + status?: string; servedBy?: IServedBy; livechatData?: any; tags?: string[]; diff --git a/app/definitions/index.ts b/app/definitions/index.ts index c11e91b1..fca277b2 100644 --- a/app/definitions/index.ts +++ b/app/definitions/index.ts @@ -30,6 +30,7 @@ export interface IBaseScreen, S ext route: RouteProp; dispatch: Dispatch; theme: string; + isMasterDetail: boolean; } export * from './redux'; diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index 7b4e52db..d4fede41 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -28,15 +28,15 @@ 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'; +import { TSettingsState } from '../../reducers/settings'; import { IShare } from '../../reducers/share'; import { IPermissionsState } from '../../reducers/permissions'; import { IEnterpriseModules } from '../../reducers/enterpriseModules'; export interface IApplicationState { - settings: ISettings; - meteor: IConnect; + settings: TSettingsState; login: ILogin; + meteor: IConnect; server: IServer; selectedUsers: ISelectedUsers; app: IApp; diff --git a/app/lib/encryption/constants.ts b/app/lib/encryption/constants.ts index d10539ed..566fa1b6 100644 --- a/app/lib/encryption/constants.ts +++ b/app/lib/encryption/constants.ts @@ -10,7 +10,7 @@ export const E2E_BANNER_TYPE = { REQUEST_PASSWORD: 'REQUEST_PASSWORD', SAVE_PASSWORD: 'SAVE_PASSWORD' }; -export const E2E_ROOM_TYPES = { +export const E2E_ROOM_TYPES: Record = { d: 'd', p: 'p' }; diff --git a/app/lib/methods/getRoomInfo.ts b/app/lib/methods/getRoomInfo.ts index b74dbaca..ae2a87a4 100644 --- a/app/lib/methods/getRoomInfo.ts +++ b/app/lib/methods/getRoomInfo.ts @@ -1,4 +1,4 @@ -import { IRoom } from '../../definitions'; +import { IRoom, SubscriptionType } from '../../definitions'; import { getSubscriptionByRoomId } from '../database/services/Subscription'; import RocketChat from '../rocketchat'; @@ -10,7 +10,7 @@ const getRoomInfo = async (rid: string): Promise { it('should return initial state', () => { @@ -8,7 +8,11 @@ describe('test settings reducer', () => { expect(state).toEqual(initialState); }); - const settings = { API_Use_REST_For_DDP_Calls: true, FileUpload_MaxFileSize: 600857600, Jitsi_URL_Room_Prefix: 'RocketChat' }; + const settings: TSettingsState = { + API_Use_REST_For_DDP_Calls: true, + FileUpload_MaxFileSize: 600857600, + Jitsi_URL_Room_Prefix: 'RocketChat' + }; it('should return modified store after call addSettings action', () => { mockedStore.dispatch(addSettings(settings)); diff --git a/app/reducers/settings.ts b/app/reducers/settings.ts index 498027c0..090cf73a 100644 --- a/app/reducers/settings.ts +++ b/app/reducers/settings.ts @@ -1,13 +1,17 @@ import { IActionSettings } from '../actions/settings'; import { SETTINGS } from '../actions/actionsTypes'; +import settings from '../constants/settings'; -export type TSettings = string | number | boolean | string[]; +export type TSupportedSettings = keyof typeof settings; +export type TSettingsValues = string | number | boolean | string[]; -export type ISettings = Record; +export type TSettingsState = { + [K in TSupportedSettings]?: TSettingsValues; +}; -export const initialState: ISettings = {}; +export const initialState: TSettingsState = {}; -export default (state = initialState, action: IActionSettings): ISettings => { +export default (state = initialState, action: IActionSettings): TSettingsState => { switch (action.type) { case SETTINGS.ADD: return { diff --git a/app/stacks/types.ts b/app/stacks/types.ts index 95e30aa2..a1d8dcda 100644 --- a/app/stacks/types.ts +++ b/app/stacks/types.ts @@ -19,35 +19,37 @@ export type ChatsStackParamList = { NewMessageStackNavigator: any; NewMessageStack: undefined; RoomsListView: undefined; - RoomView?: { - rid: string; - t: SubscriptionType; - tmid?: string; - message?: object; // TODO: TMessageModel? - name?: string; - fname?: string; - prid?: string; - room?: TSubscriptionModel | { rid: string; t: string; name?: string; fname?: string; prid?: string }; - jumpToMessageId?: string; - jumpToThreadId?: string; - roomUserId?: string | null; - usedCannedResponse?: string; - }; + RoomView: + | { + rid: string; + t: SubscriptionType; + tmid?: string; + message?: object; // TODO: TMessageModel? + name?: string; + fname?: string; + prid?: string; + room?: TSubscriptionModel | { rid: string; t: string; name?: string; fname?: string; prid?: string }; + jumpToMessageId?: string; + jumpToThreadId?: string; + roomUserId?: string | null; + usedCannedResponse?: string; + } + | undefined; // Navigates back to RoomView already on stack RoomActionsView: { - room?: ISubscription; + room: TSubscriptionModel; member: any; rid: string; t: SubscriptionType; joined: boolean; }; SelectListView: { - data: IRoom[]; + data?: IRoom[]; title: string; - infoText: string; + infoText?: string; nextAction: (selected: string[]) => void; - showAlert: () => void; - isSearch: boolean; - onSearch: (text: string) => Partial; + showAlert?: () => void; + isSearch?: boolean; + onSearch?: (text: string) => Promise | any>; isRadio?: boolean; }; RoomInfoView: { diff --git a/app/utils/goRoom.ts b/app/utils/goRoom.ts index 5a25ee56..f295dfaf 100644 --- a/app/utils/goRoom.ts +++ b/app/utils/goRoom.ts @@ -1,9 +1,9 @@ import { ChatsStackParamList } from '../stacks/types'; import Navigation from '../lib/Navigation'; import RocketChat from '../lib/rocketchat'; -import { IVisitor, SubscriptionType } from '../definitions/ISubscription'; +import { ISubscription, IVisitor, SubscriptionType, TSubscriptionModel } from '../definitions/ISubscription'; -export interface IGoRoomItem { +interface IGoRoomItem { search?: boolean; // comes from spotlight username?: string; t?: SubscriptionType; @@ -13,12 +13,14 @@ export interface IGoRoomItem { visitor?: IVisitor; } +export type TGoRoomItem = IGoRoomItem | TSubscriptionModel | ISubscription; + const navigate = ({ item, isMasterDetail, ...props }: { - item: IGoRoomItem; + item: TGoRoomItem; isMasterDetail: boolean; navigationMethod?: () => ChatsStackParamList; }) => { @@ -45,13 +47,13 @@ export const goRoom = async ({ isMasterDetail = false, ...props }: { - item: IGoRoomItem; + item: TGoRoomItem; isMasterDetail: boolean; navigationMethod?: any; jumpToMessageId?: string; usedCannedResponse?: string; }): Promise => { - if (item.t === SubscriptionType.DIRECT && item?.search) { + if (!('id' in item) && item.t === SubscriptionType.DIRECT && item?.search) { // if user is using the search we need first to join/create room try { const { username } = item; diff --git a/app/views/CannedResponseDetail.tsx b/app/views/CannedResponseDetail.tsx index 8c42b7af..1bb0ae64 100644 --- a/app/views/CannedResponseDetail.tsx +++ b/app/views/CannedResponseDetail.tsx @@ -115,7 +115,7 @@ const CannedResponseDetail = ({ navigation, route }: ICannedResponseDetailProps) t: room.t, fname: name }), - t: room.t, + t: room.t as any, roomUserId: RocketChat.getUidDirectMessage(room), usedCannedResponse: item.text }; diff --git a/app/views/CannedResponsesListView/index.tsx b/app/views/CannedResponsesListView/index.tsx index 47ec73c6..ef3d0d21 100644 --- a/app/views/CannedResponsesListView/index.tsx +++ b/app/views/CannedResponsesListView/index.tsx @@ -118,7 +118,7 @@ const CannedResponsesListView = ({ navigation, route }: ICannedResponsesListView t: room.t, fname: name }), - t: room.t, + t: room.t as any, roomUserId: RocketChat.getUidDirectMessage(room), usedCannedResponse: item.text }; diff --git a/app/views/RoomActionsView/index.js b/app/views/RoomActionsView/index.tsx similarity index 87% rename from app/views/RoomActionsView/index.js rename to app/views/RoomActionsView/index.tsx index a9ede0ca..eda47860 100644 --- a/app/views/RoomActionsView/index.js +++ b/app/views/RoomActionsView/index.tsx @@ -1,38 +1,94 @@ +import { Q } from '@nozbe/watermelondb'; +import { StackNavigationOptions } from '@react-navigation/stack'; +import isEmpty from 'lodash/isEmpty'; import React from 'react'; -import PropTypes from 'prop-types'; import { Share, Switch, Text, View } from 'react-native'; import { connect } from 'react-redux'; -import isEmpty from 'lodash/isEmpty'; -import { Q } from '@nozbe/watermelondb'; +import { Observable, Subscription } from 'rxjs'; -import { compareServerVersion } from '../../lib/utils'; -import Touch from '../../utils/touch'; -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'; -import * as List from '../../containers/List'; -import RocketChat from '../../lib/rocketchat'; -import log, { events, logEvent } from '../../utils/log'; -import RoomTypeIcon from '../../containers/RoomTypeIcon'; -import I18n from '../../i18n'; -import StatusBar from '../../containers/StatusBar'; +import { setLoading } from '../../actions/selectedUsers'; import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors'; -import { withTheme } from '../../theme'; +import Avatar from '../../containers/Avatar'; import * as HeaderButton from '../../containers/HeaderButton'; +import * as List from '../../containers/List'; import { MarkdownPreview } from '../../containers/markdown'; -import { showConfirmationAlert, showErrorAlert } from '../../utils/info'; +import RoomTypeIcon from '../../containers/RoomTypeIcon'; import SafeAreaView from '../../containers/SafeAreaView'; +import Status from '../../containers/Status'; +import StatusBar from '../../containers/StatusBar'; +import { IApplicationState, IBaseScreen, IRoom, ISubscription, IUser, TSubscriptionModel } from '../../definitions'; +import { withDimensions } from '../../dimensions'; +import I18n from '../../i18n'; +import database from '../../lib/database'; import { E2E_ROOM_TYPES } from '../../lib/encryption/constants'; import protectedFunction from '../../lib/methods/helpers/protectedFunction'; -import database from '../../lib/database'; -import { withDimensions } from '../../dimensions'; +import RocketChat from '../../lib/rocketchat'; +import { compareServerVersion } from '../../lib/utils'; +import { getUserSelector } from '../../selectors/login'; +import { ChatsStackParamList } from '../../stacks/types'; +import { withTheme } from '../../theme'; +import { showConfirmationAlert, showErrorAlert } from '../../utils/info'; +import log, { events, logEvent } from '../../utils/log'; +import Touch from '../../utils/touch'; +import sharedStyles from '../Styles'; import styles from './styles'; +import { ERoomType } from '../../definitions/ERoomType'; -class RoomActionsView extends React.Component { - static navigationOptions = ({ navigation, isMasterDetail }) => { - const options = { +interface IRoomActionsViewProps extends IBaseScreen { + userId: string; + jitsiEnabled: boolean; + jitsiEnableTeams: boolean; + jitsiEnableChannels: boolean; + encryptionEnabled: boolean; + fontScale: number; + serverVersion: string | null; + addUserToJoinedRoomPermission?: string[]; + addUserToAnyCRoomPermission?: string[]; + addUserToAnyPRoomPermission?: string[]; + createInviteLinksPermission?: string[]; + editRoomPermission?: string[]; + toggleRoomE2EEncryptionPermission?: string[]; + viewBroadcastMemberListPermission?: string[]; + transferLivechatGuestPermission?: string[]; + createTeamPermission?: string[]; + addTeamChannelPermission?: string[]; + convertTeamPermission?: string[]; + viewCannedResponsesPermission?: string[]; +} + +interface IRoomActionsViewState { + room: TSubscriptionModel; + membersCount: number; + member: Partial; + joined: boolean; + canViewMembers: boolean; + canAutoTranslate: boolean; + canAddUser: boolean; + canInviteUser: boolean; + canForwardGuest: boolean; + canReturnQueue: boolean; + canEdit: boolean; + canToggleEncryption: boolean; + canCreateTeam: boolean; + canAddChannelToTeam: boolean; + canConvertTeam: boolean; + canViewCannedResponse: boolean; +} + +class RoomActionsView extends React.Component { + private mounted: boolean; + private rid: string; + private t: string; + private joined: boolean; + private roomObservable?: Observable; + private subscription?: Subscription; + + static navigationOptions = ({ + navigation, + isMasterDetail + }: Pick): StackNavigationOptions => { + const options: StackNavigationOptions = { title: I18n.t('Actions') }; if (isMasterDetail) { @@ -41,34 +97,7 @@ class RoomActionsView extends React.Component { return options; }; - static propTypes = { - navigation: PropTypes.object, - route: PropTypes.object, - leaveRoom: PropTypes.func, - jitsiEnabled: PropTypes.bool, - jitsiEnableTeams: PropTypes.bool, - jitsiEnableChannels: PropTypes.bool, - encryptionEnabled: PropTypes.bool, - setLoadingInvite: PropTypes.func, - closeRoom: PropTypes.func, - theme: PropTypes.string, - fontScale: PropTypes.number, - serverVersion: PropTypes.string, - addUserToJoinedRoomPermission: PropTypes.array, - addUserToAnyCRoomPermission: PropTypes.array, - addUserToAnyPRoomPermission: PropTypes.array, - createInviteLinksPermission: PropTypes.array, - editRoomPermission: PropTypes.array, - toggleRoomE2EEncryptionPermission: PropTypes.array, - viewBroadcastMemberListPermission: PropTypes.array, - transferLivechatGuestPermission: PropTypes.array, - createTeamPermission: PropTypes.array, - addTeamChannelPermission: PropTypes.array, - convertTeamPermission: PropTypes.array, - viewCannedResponsesPermission: PropTypes.array - }; - - constructor(props) { + constructor(props: IRoomActionsViewProps) { super(props); this.mounted = false; const room = props.route.params?.room; @@ -77,7 +106,7 @@ class RoomActionsView extends React.Component { this.t = props.route.params?.t; this.joined = props.route.params?.joined; this.state = { - room: room || { rid: this.rid, t: this.t }, + room: room || ({ rid: this.rid, t: this.t } as any), membersCount: 0, member: member || {}, joined: !!room, @@ -100,6 +129,7 @@ class RoomActionsView extends React.Component { if (this.mounted) { this.setState({ room: changes }); } else { + // @ts-ignore this.state.room = changes; } }); @@ -123,7 +153,7 @@ class RoomActionsView extends React.Component { if (room && room.t !== 'd' && this.canViewMembers()) { try { - const counters = await RocketChat.getRoomCounters(room.rid, room.t); + const counters = await RocketChat.getRoomCounters(room.rid, room.t as any); if (counters.success) { this.setState({ membersCount: counters.members, joined: counters.joined }); } @@ -177,9 +207,15 @@ class RoomActionsView extends React.Component { return room.t === 'l' && room.status === 'queued' && !this.joined; } - onPressTouchable = item => { + // TODO: assert params required for navigation + onPressTouchable = (item: { route?: keyof ChatsStackParamList; params?: object; event?: Function }) => { const { route, event, params } = item; if (route) { + /** + * TODO: params can vary too much and ts is going to be happy + * Instead of playing with this, we should think on a better `logEvent` function + */ + // @ts-ignore logEvent(events[`RA_GO_${route.replace('View', '').toUpperCase()}${params.name ? params.name.toUpperCase() : ''}`]); const { navigation } = this.props; navigation.navigate(route, params); @@ -349,7 +385,7 @@ class RoomActionsView extends React.Component { onPress: async () => { try { await RocketChat.returnLivechat(rid); - } catch (e) { + } catch (e: any) { showErrorAlert(e.reason, I18n.t('Oops')); } } @@ -364,7 +400,7 @@ class RoomActionsView extends React.Component { const roomUserId = RocketChat.getUidDirectMessage(room); const result = await RocketChat.getUserInfo(roomUserId); if (result.success) { - this.setState({ member: result.user }); + this.setState({ member: result.user as any }); } } } catch (e) { @@ -394,7 +430,7 @@ class RoomActionsView extends React.Component { const { rid, blocker } = room; const { member } = this.state; try { - await RocketChat.toggleBlockUser(rid, member._id, !blocker); + await RocketChat.toggleBlockUser(rid, member._id as string, !blocker); } catch (e) { logEvent(events.RA_TOGGLE_BLOCK_USER_F); log(e); @@ -411,9 +447,9 @@ class RoomActionsView extends React.Component { const encrypted = !room.encrypted; try { // Instantly feedback to the user - await db.action(async () => { + await db.write(async () => { await room.update( - protectedFunction(r => { + protectedFunction((r: TSubscriptionModel) => { r.encrypted = encrypted; }) ); @@ -431,9 +467,9 @@ class RoomActionsView extends React.Component { } // If something goes wrong we go back to the previous value - await db.action(async () => { + await db.write(async () => { await room.update( - protectedFunction(r => { + protectedFunction((r: TSubscriptionModel) => { r.encrypted = room.encrypted; }) ); @@ -463,28 +499,31 @@ class RoomActionsView extends React.Component { 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: () => dispatch(leaveRoom('channel', room)) + onPress: () => dispatch(leaveRoom(ERoomType.c, room)) }); }; convertTeamToChannel = async () => { const { room } = this.state; - const { navigation } = this.props; + const { navigation, userId } = this.props; try { - const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id }); + if (!room.teamId) { + return; + } + const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId }); if (result.rooms?.length) { - const teamChannels = result.rooms.map(r => ({ + const teamChannels = result.rooms.map((r: any) => ({ rid: r._id, name: r.name, teamId: r.teamId })); navigation.navigate('SelectListView', { title: 'Converting_Team_To_Channel', - data: teamChannels, + data: teamChannels as any, infoText: 'Select_Team_Channels_To_Delete', - nextAction: data => this.convertTeamToChannelConfirmation(data) + nextAction: (data: string[]) => this.convertTeamToChannelConfirmation(data) }); } else { this.convertTeamToChannelConfirmation(); @@ -494,12 +533,15 @@ class RoomActionsView extends React.Component { } }; - handleConvertTeamToChannel = async selected => { + handleConvertTeamToChannel = async (selected: string[]) => { logEvent(events.RA_CONVERT_TEAM_TO_CHANNEL); try { const { room } = this.state; const { navigation } = this.props; + if (!room.teamId) { + return; + } const result = await RocketChat.convertTeamToChannel({ teamId: room.teamId, selected }); if (result.success) { @@ -511,7 +553,7 @@ class RoomActionsView extends React.Component { } }; - convertTeamToChannelConfirmation = (selected = []) => { + convertTeamToChannelConfirmation = (selected: string[] = []) => { showConfirmationAlert({ title: I18n.t('Confirmation'), message: I18n.t('You_are_converting_the_team'), @@ -522,13 +564,16 @@ class RoomActionsView extends React.Component { leaveTeam = async () => { const { room } = this.state; - const { navigation, dispatch } = this.props; + const { navigation, dispatch, userId } = this.props; try { - const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId: room.u._id }); + if (!room.teamId) { + return; + } + const result = await RocketChat.teamListRoomsOfUser({ teamId: room.teamId, userId }); if (result.rooms?.length) { - const teamChannels = result.rooms.map(r => ({ + const teamChannels = result.rooms.map((r: any) => ({ rid: r._id, name: r.name, teamId: r.teamId, @@ -538,21 +583,21 @@ class RoomActionsView extends React.Component { title: 'Leave_Team', data: teamChannels, infoText: 'Select_Team_Channels', - nextAction: data => dispatch(leaveRoom('team', room, data)), + nextAction: data => dispatch(leaveRoom(ERoomType.t, 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: () => dispatch(leaveRoom('team', room)) + onPress: () => dispatch(leaveRoom(ERoomType.t, 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: () => dispatch(leaveRoom('team', room)) + onPress: () => dispatch(leaveRoom(ERoomType.t, room)) }); } }; @@ -562,7 +607,7 @@ class RoomActionsView extends React.Component { try { const { room } = this.state; const { navigation } = this.props; - const result = await RocketChat.convertChannelToTeam({ rid: room.rid, name: room.name, type: room.t }); + const result = await RocketChat.convertChannelToTeam({ rid: room.rid, name: room.name, type: room.t as any }); if (result.success) { navigation.navigate('RoomView'); @@ -582,7 +627,7 @@ class RoomActionsView extends React.Component { }); }; - handleMoveToTeam = async selected => { + handleMoveToTeam = async (selected: string[]) => { logEvent(events.RA_MOVE_TO_TEAM); try { const { room } = this.state; @@ -603,14 +648,14 @@ class RoomActionsView extends React.Component { const { navigation } = this.props; const db = database.active; const subCollection = db.get('subscriptions'); - const teamRooms = await subCollection.query(Q.where('team_main', true)); + const teamRooms = await subCollection.query(Q.where('team_main', true)).fetch(); if (teamRooms.length) { const data = teamRooms.map(team => ({ rid: team.teamId, t: team.t, name: team.name - })); + })) as IRoom[]; // TODO: review this usage later navigation.navigate('SelectListView', { title: 'Move_to_Team', infoText: 'Move_Channel_Paragraph', @@ -637,7 +682,7 @@ class RoomActionsView extends React.Component { } }; - searchTeam = async onChangeText => { + searchTeam = async (onChangeText: string) => { logEvent(events.RA_SEARCH_TEAM); try { const { addTeamChannelPermission, createTeamPermission } = this.props; @@ -650,9 +695,10 @@ class RoomActionsView extends React.Component { Q.where('name', Q.like(`%${onChangeText}%`)), Q.experimentalTake(QUERY_SIZE), Q.experimentalSortBy('room_updated_at', Q.desc) - ); + ) + .fetch(); - const asyncFilter = async teamArray => { + const asyncFilter = async (teamArray: TSubscriptionModel[]) => { const results = await Promise.all( teamArray.map(async team => { const permissions = await RocketChat.hasPermission([addTeamChannelPermission, createTeamPermission], team.rid); @@ -698,7 +744,6 @@ class RoomActionsView extends React.Component { } style={{ backgroundColor: themes[theme].backgroundColor }} accessibilityLabel={I18n.t('Room_Info')} - accessibilityTraits='button' enabled={!isGroupChat} testID='room-actions-info' theme={theme}> @@ -708,7 +753,7 @@ class RoomActionsView extends React.Component { - ) : null} + ) : undefined} {room.t === 'd' ? ( @@ -782,7 +827,7 @@ class RoomActionsView extends React.Component { // If this room type can be encrypted // If e2e is enabled - if (E2E_ROOM_TYPES[room?.t] && encryptionEnabled) { + if (E2E_ROOM_TYPES[room.t] && encryptionEnabled) { return ( @@ -853,7 +898,7 @@ class RoomActionsView extends React.Component { return null; }; - teamChannelActions = (t, room) => { + teamChannelActions = (t: string, room: ISubscription) => { const { canEdit, canCreateTeam, canAddChannelToTeam } = this.state; const canConvertToTeam = canEdit && canCreateTeam && !room.teamMain; const canMoveToTeam = canEdit && canAddChannelToTeam && !room.teamId; @@ -897,7 +942,7 @@ class RoomActionsView extends React.Component { ); }; - teamToChannelActions = (t, room) => { + teamToChannelActions = (t: string, room: ISubscription) => { const { canEdit, canConvertTeam } = this.state; const canConvertTeamToChannel = canEdit && canConvertTeam && !!room?.teamMain; @@ -952,7 +997,7 @@ class RoomActionsView extends React.Component { <> 0 ? `${membersCount} ${I18n.t('members')}` : null} + subtitle={membersCount > 0 ? `${membersCount} ${I18n.t('members')}` : undefined} onPress={() => this.onPressTouchable({ route: 'RoomMembersView', params: { rid, room } })} testID='room-actions-members' left={() => } @@ -1221,10 +1266,11 @@ class RoomActionsView extends React.Component { } } -const mapStateToProps = state => ({ - jitsiEnabled: state.settings.Jitsi_Enabled || false, - jitsiEnableTeams: state.settings.Jitsi_Enable_Teams || false, - jitsiEnableChannels: state.settings.Jitsi_Enable_Channels || false, +const mapStateToProps = (state: IApplicationState) => ({ + userId: getUserSelector(state).id, + jitsiEnabled: (state.settings.Jitsi_Enabled || false) as boolean, + jitsiEnableTeams: (state.settings.Jitsi_Enable_Teams || false) as boolean, + jitsiEnableChannels: (state.settings.Jitsi_Enable_Channels || false) as boolean, encryptionEnabled: state.encryption.enabled, serverVersion: state.server.version, isMasterDetail: state.app.isMasterDetail, diff --git a/app/views/RoomActionsView/styles.js b/app/views/RoomActionsView/styles.ts similarity index 100% rename from app/views/RoomActionsView/styles.js rename to app/views/RoomActionsView/styles.ts diff --git a/app/views/RoomInfoEditView/index.tsx b/app/views/RoomInfoEditView/index.tsx index 4c77b901..d79af6d4 100644 --- a/app/views/RoomInfoEditView/index.tsx +++ b/app/views/RoomInfoEditView/index.tsx @@ -16,7 +16,7 @@ import StatusBar from '../../containers/StatusBar'; import RCTextInput from '../../containers/TextInput'; import { LISTENER } from '../../containers/Toast'; import { MultiSelect } from '../../containers/UIKit/MultiSelect'; -import { IApplicationState, IBaseScreen, ISubscription, TSubscriptionModel } from '../../definitions'; +import { IApplicationState, IBaseScreen, ISubscription, SubscriptionType, TSubscriptionModel } from '../../definitions'; import { ERoomType } from '../../definitions/ERoomType'; import I18n from '../../i18n'; import database from '../../lib/database'; @@ -372,7 +372,7 @@ class RoomInfoEditView extends React.Component) => { + nextAction: (selected: string[]) => { showConfirmationAlert({ message: I18n.t('You_are_deleting_the_team', { team: RocketChat.getRoomTitle(room) }), confirmationText: I18n.t('Yes_action_it', { action: I18n.t('delete') }), @@ -437,7 +437,7 @@ class RoomInfoEditView extends React.Component { try { logEvent(events.RI_EDIT_TOGGLE_ARCHIVE); - await RocketChat.toggleArchiveRoom(rid, t, !archived); + await RocketChat.toggleArchiveRoom(rid, t as SubscriptionType, !archived); } catch (e) { logEvent(events.RI_EDIT_TOGGLE_ARCHIVE_F); log(e); diff --git a/app/views/RoomMembersView/index.tsx b/app/views/RoomMembersView/index.tsx index bed9cccb..ad98dc5d 100644 --- a/app/views/RoomMembersView/index.tsx +++ b/app/views/RoomMembersView/index.tsx @@ -24,7 +24,7 @@ import { getUserSelector } from '../../selectors/login'; import { ModalStackParamList } from '../../stacks/MasterDetailStack/types'; import { withTheme } from '../../theme'; import EventEmitter from '../../utils/events'; -import { goRoom, IGoRoomItem } from '../../utils/goRoom'; +import { goRoom, TGoRoomItem } from '../../utils/goRoom'; import { showConfirmationAlert, showErrorAlert } from '../../utils/info'; import log from '../../utils/log'; import scrollPersistTaps from '../../utils/scrollPersistTaps'; @@ -469,7 +469,7 @@ class RoomMembersView extends React.Component { + goRoom = (item: TGoRoomItem) => { const { navigation, isMasterDetail } = this.props; if (isMasterDetail) { // @ts-ignore diff --git a/app/views/RoomView/index.tsx b/app/views/RoomView/index.tsx index da60d62e..f5aa59e1 100644 --- a/app/views/RoomView/index.tsx +++ b/app/views/RoomView/index.tsx @@ -52,7 +52,7 @@ import { getHeaderTitlePosition } from '../../containers/Header'; import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../../lib/encryption/constants'; import { takeInquiry } from '../../ee/omnichannel/lib'; import Loading from '../../containers/Loading'; -import { goRoom } from '../../utils/goRoom'; +import { goRoom, TGoRoomItem } from '../../utils/goRoom'; import getThreadName from '../../lib/methods/getThreadName'; import getRoomInfo from '../../lib/methods/getRoomInfo'; import RoomServices from './services'; @@ -545,7 +545,7 @@ class RoomView extends React.Component { navigation.push('RoomActionsView', { rid: this.rid, t: this.t as SubscriptionType, - room: room as ISubscription, + room: room as TSubscriptionModel, member, joined }); @@ -1040,7 +1040,7 @@ class RoomView extends React.Component { const { navigation, isMasterDetail } = this.props; const roomInfo = await getRoomInfo(message.rid); return goRoom({ - item: roomInfo as Partial, + item: roomInfo as TGoRoomItem, isMasterDetail, navigationMethod: navigation.push, jumpToMessageId: message.id diff --git a/app/views/SelectListView.tsx b/app/views/SelectListView.tsx index 6265983b..5a3a2d70 100644 --- a/app/views/SelectListView.tsx +++ b/app/views/SelectListView.tsx @@ -29,7 +29,7 @@ const styles = StyleSheet.create({ }); interface ISelectListViewState { - data: IRoom[]; + data?: IRoom[]; dataFiltered?: IRoom[]; isSearching: boolean; selected: string[]; @@ -53,7 +53,7 @@ class SelectListView extends React.Component Partial; + private onSearch?: (text: string) => Promise | any>; private isRadio?: boolean; @@ -61,10 +61,10 @@ class SelectListView extends React.Component {}); + this.isSearch = props.route?.params?.isSearch ?? false; this.onSearch = props.route?.params?.onSearch; this.isRadio = props.route?.params?.isRadio; this.state = { @@ -122,7 +122,7 @@ class SelectListView extends React.Component { try { this.setState({ isSearching: true }); - const result = (await this.onSearch(text)) as IRoom[]; + const result = (await this.onSearch?.(text)) as IRoom[]; this.setState({ dataFiltered: result }); } catch (e) { log(e);