diff --git a/.eslintrc.js b/.eslintrc.js index 8edbeaf5d..f5b6d39c8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -235,7 +235,8 @@ module.exports = { ignoreRestSiblings: true } ], - 'new-cap': 'off' + 'new-cap': 'off', + 'lines-between-class-members': 'off' }, globals: { JSX: true diff --git a/app/containers/HeaderButton/Common.tsx b/app/containers/HeaderButton/Common.tsx index f1e00628c..b40543ddb 100644 --- a/app/containers/HeaderButton/Common.tsx +++ b/app/containers/HeaderButton/Common.tsx @@ -7,8 +7,8 @@ import Item from './HeaderButtonItem'; interface IHeaderButtonCommon { navigation: any; - onPress(): void; - testID: string; + onPress?(): void; + testID?: string; } // Left diff --git a/app/containers/UIKit/MultiSelect/index.tsx b/app/containers/UIKit/MultiSelect/index.tsx index cd8d21a38..95bedbebd 100644 --- a/app/containers/UIKit/MultiSelect/index.tsx +++ b/app/containers/UIKit/MultiSelect/index.tsx @@ -14,19 +14,19 @@ import Input from './Input'; import styles from './styles'; interface IMultiSelect { - options: []; + options: any[]; onChange: Function; placeholder: { text: string; }; - context: number; - loading: boolean; - multiselect: boolean; + context?: number; + loading?: boolean; + multiselect?: boolean; onSearch: Function; onClose: Function; inputStyle: object; - value: { text: any }[]; - disabled: boolean; + value?: any[]; + disabled?: boolean | object; theme: string; } diff --git a/app/presentation/DirectoryItem/index.tsx b/app/presentation/DirectoryItem/index.tsx index d818b3edb..b8d9811a8 100644 --- a/app/presentation/DirectoryItem/index.tsx +++ b/app/presentation/DirectoryItem/index.tsx @@ -25,14 +25,14 @@ interface IDirectoryItem { rightLabel: string; rid: string; theme: string; - teamMain: boolean; + teamMain?: boolean; } const DirectoryItemLabel = React.memo(({ text, theme }: IDirectoryItemLabel) => { if (!text) { return null; } - return {text}; + return {text}; }); const DirectoryItem = ({ @@ -47,7 +47,7 @@ const DirectoryItem = ({ rid, theme, teamMain -}: IDirectoryItem) => ( +}: IDirectoryItem): JSX.Element => ( diff --git a/app/utils/log/events.js b/app/utils/log/events.js index 2ab3f539b..c39ccc424 100644 --- a/app/utils/log/events.js +++ b/app/utils/log/events.js @@ -85,6 +85,7 @@ export default { // DIRECTORY VIEW DIRECTORY_SEARCH_USERS: 'directory_search_users', DIRECTORY_SEARCH_CHANNELS: 'directory_search_channels', + DIRECTORY_SEARCH_TEAMS: 'directory_search_teams', // NEW MESSAGE VIEW NEW_MSG_CREATE_CHANNEL: 'new_msg_create_channel', diff --git a/app/views/AutoTranslateView/index.js b/app/views/AutoTranslateView/index.tsx similarity index 81% rename from app/views/AutoTranslateView/index.js rename to app/views/AutoTranslateView/index.tsx index 0c3e9505a..92a77543a 100644 --- a/app/views/AutoTranslateView/index.js +++ b/app/views/AutoTranslateView/index.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { FlatList, StyleSheet, Switch } from 'react-native'; import RocketChat from '../../lib/rocketchat'; @@ -17,17 +16,33 @@ const styles = StyleSheet.create({ } }); -class AutoTranslateView extends React.Component { +interface IRoom { + observe: Function; + autoTranslateLanguage: boolean; + autoTranslate: boolean; +} + +interface IAutoTranslateViewProps { + route: { + params: { + rid?: string; + room?: IRoom; + }; + }; + theme: string; +} + +class AutoTranslateView extends React.Component { static navigationOptions = () => ({ title: I18n.t('Auto_Translate') }); - static propTypes = { - route: PropTypes.object, - theme: PropTypes.string - }; + private mounted: boolean; + private rid: string | undefined; + private roomObservable: any; + private subscription: any; - constructor(props) { + constructor(props: IAutoTranslateViewProps) { super(props); this.mounted = false; this.rid = props.route.params?.rid; @@ -35,7 +50,7 @@ class AutoTranslateView extends React.Component { if (room && room.observe) { this.roomObservable = room.observe(); - this.subscription = this.roomObservable.subscribe(changes => { + this.subscription = this.roomObservable.subscribe((changes: IRoom) => { if (this.mounted) { const { selectedLanguage, enableAutoTranslate } = this.state; if (selectedLanguage !== changes.autoTranslateLanguage) { @@ -49,8 +64,8 @@ class AutoTranslateView extends React.Component { } this.state = { languages: [], - selectedLanguage: room.autoTranslateLanguage, - enableAutoTranslate: room.autoTranslate + selectedLanguage: room?.autoTranslateLanguage, + enableAutoTranslate: room?.autoTranslate }; } @@ -87,13 +102,15 @@ class AutoTranslateView extends React.Component { } }; - saveAutoTranslateLanguage = async language => { + saveAutoTranslateLanguage = async (language: string) => { logEvent(events.AT_SET_LANG); try { + // TODO: remove the parameter options, after migrate the RocketChat await RocketChat.saveAutoTranslate({ rid: this.rid, field: 'autoTranslateLanguage', - value: language + value: language, + options: null }); this.setState({ selectedLanguage: language }); } catch (error) { @@ -112,7 +129,7 @@ class AutoTranslateView extends React.Component { return ; }; - renderItem = ({ item }) => { + renderItem = ({ item }: { item: { language: string; name: string } }) => { const { selectedLanguage } = this.state; const { language, name } = item; const isSelected = selectedLanguage === language; diff --git a/app/views/CreateDiscussionView/SelectChannel.js b/app/views/CreateDiscussionView/SelectChannel.tsx similarity index 75% rename from app/views/CreateDiscussionView/SelectChannel.js rename to app/views/CreateDiscussionView/SelectChannel.tsx index 5dfecfc37..e7653973f 100644 --- a/app/views/CreateDiscussionView/SelectChannel.js +++ b/app/views/CreateDiscussionView/SelectChannel.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { Text } from 'react-native'; -import PropTypes from 'prop-types'; import debounce from '../../utils/debounce'; import { avatarURL } from '../../utils/avatar'; @@ -9,8 +8,18 @@ import I18n from '../../i18n'; import { MultiSelect } from '../../containers/UIKit/MultiSelect'; import { themes } from '../../constants/colors'; import styles from './styles'; +import { ICreateDiscussionViewSelectChannel } from './interfaces'; -const SelectChannel = ({ server, token, userId, onChannelSelect, initial, blockUnauthenticatedAccess, serverVersion, theme }) => { +const SelectChannel = ({ + server, + token, + userId, + onChannelSelect, + initial, + blockUnauthenticatedAccess, + serverVersion, + theme +}: ICreateDiscussionViewSelectChannel): JSX.Element => { const [channels, setChannels] = useState([]); const getChannels = debounce(async (keyword = '') => { @@ -22,7 +31,9 @@ const SelectChannel = ({ server, token, userId, onChannelSelect, initial, blockU } }, 300); - const getAvatar = item => + const getAvatar = (item: any) => + // TODO: remove this ts-ignore when migrate the file: app/utils/avatar.js + // @ts-ignore avatarURL({ text: RocketChat.getRoomAvatar(item), type: item.t, @@ -55,15 +66,5 @@ const SelectChannel = ({ server, token, userId, onChannelSelect, initial, blockU ); }; -SelectChannel.propTypes = { - server: PropTypes.string, - token: PropTypes.string, - userId: PropTypes.string, - initial: PropTypes.object, - onChannelSelect: PropTypes.func, - blockUnauthenticatedAccess: PropTypes.bool, - serverVersion: PropTypes.string, - theme: PropTypes.string -}; export default SelectChannel; diff --git a/app/views/CreateDiscussionView/SelectUsers.js b/app/views/CreateDiscussionView/SelectUsers.tsx similarity index 69% rename from app/views/CreateDiscussionView/SelectUsers.js rename to app/views/CreateDiscussionView/SelectUsers.tsx index 323fc0fd7..65a4e0a4a 100644 --- a/app/views/CreateDiscussionView/SelectUsers.js +++ b/app/views/CreateDiscussionView/SelectUsers.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { Text } from 'react-native'; -import PropTypes from 'prop-types'; import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit'; import { Q } from '@nozbe/watermelondb'; @@ -12,19 +11,37 @@ import I18n from '../../i18n'; import { MultiSelect } from '../../containers/UIKit/MultiSelect'; import { themes } from '../../constants/colors'; import styles from './styles'; +import { ICreateDiscussionViewSelectUsers } from './interfaces'; -const SelectUsers = ({ server, token, userId, selected, onUserSelect, blockUnauthenticatedAccess, serverVersion, theme }) => { - const [users, setUsers] = useState([]); +interface IUser { + name: string; + username: string; +} + +const SelectUsers = ({ + server, + token, + userId, + selected, + onUserSelect, + blockUnauthenticatedAccess, + serverVersion, + theme +}: ICreateDiscussionViewSelectUsers): JSX.Element => { + const [users, setUsers] = useState([]); const getUsers = debounce(async (keyword = '') => { try { const db = database.active; const usersCollection = db.get('users'); const res = await RocketChat.search({ text: keyword, filterRooms: false }); - let items = [...users.filter(u => selected.includes(u.name)), ...res.filter(r => !users.find(u => u.name === r.name))]; + let items = [ + ...users.filter((u: IUser) => selected.includes(u.name)), + ...res.filter((r: IUser) => !users.find((u: IUser) => u.name === r.name)) + ]; const records = await usersCollection.query(Q.where('username', Q.oneOf(items.map(u => u.name)))).fetch(); items = items.map(item => { - const index = records.findIndex(r => r.username === item.name); + const index = records.findIndex((r: IUser) => r.username === item.name); if (index > -1) { const record = records[index]; return { @@ -44,7 +61,9 @@ const SelectUsers = ({ server, token, userId, selected, onUserSelect, blockUnaut } }, 300); - const getAvatar = item => + const getAvatar = (item: any) => + // TODO: remove this ts-ignore when migrate the file: app/utils/avatar.js + // @ts-ignore avatarURL({ text: RocketChat.getRoomAvatar(item), type: 'd', @@ -63,12 +82,12 @@ const SelectUsers = ({ server, token, userId, selected, onUserSelect, blockUnaut inputStyle={styles.inputStyle} onSearch={getUsers} onChange={onUserSelect} - options={users.map(user => ({ + options={users.map((user: IUser) => ({ value: user.name, text: { text: RocketChat.getRoomTitle(user) }, imageUrl: getAvatar(user) }))} - onClose={() => setUsers(users.filter(u => selected.includes(u.name)))} + onClose={() => setUsers(users.filter((u: IUser) => selected.includes(u.name)))} placeholder={{ text: `${I18n.t('Select_Users')}...` }} context={BLOCK_CONTEXT.FORM} multiselect @@ -76,15 +95,5 @@ const SelectUsers = ({ server, token, userId, selected, onUserSelect, blockUnaut ); }; -SelectUsers.propTypes = { - server: PropTypes.string, - token: PropTypes.string, - userId: PropTypes.string, - selected: PropTypes.array, - onUserSelect: PropTypes.func, - blockUnauthenticatedAccess: PropTypes.bool, - serverVersion: PropTypes.string, - theme: PropTypes.string -}; export default SelectUsers; diff --git a/app/views/CreateDiscussionView/index.js b/app/views/CreateDiscussionView/index.tsx similarity index 86% rename from app/views/CreateDiscussionView/index.js rename to app/views/CreateDiscussionView/index.tsx index 0765d8261..98b461f0a 100644 --- a/app/views/CreateDiscussionView/index.js +++ b/app/views/CreateDiscussionView/index.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; import { ScrollView, Switch, Text } from 'react-native'; import Loading from '../../containers/Loading'; @@ -24,30 +23,16 @@ import { E2E_ROOM_TYPES } from '../../lib/encryption/constants'; import styles from './styles'; import SelectUsers from './SelectUsers'; import SelectChannel from './SelectChannel'; +import { ICreateChannelViewProps } from './interfaces'; -class CreateChannelView extends React.Component { - propTypes = { - navigation: PropTypes.object, - route: PropTypes.object, - server: PropTypes.string, - user: PropTypes.object, - create: PropTypes.func, - loading: PropTypes.bool, - result: PropTypes.object, - failure: PropTypes.bool, - error: PropTypes.object, - theme: PropTypes.string, - isMasterDetail: PropTypes.bool, - blockUnauthenticatedAccess: PropTypes.bool, - serverVersion: PropTypes.string, - encryptionEnabled: PropTypes.bool - }; +class CreateChannelView extends React.Component { + private channel: any; - constructor(props) { + constructor(props: ICreateChannelViewProps) { super(props); const { route } = props; this.channel = route.params?.channel; - const message = route.params?.message ?? {}; + const message: any = route.params?.message ?? {}; this.state = { channel: this.channel, message, @@ -59,7 +44,7 @@ class CreateChannelView extends React.Component { this.setHeader(); } - componentDidUpdate(prevProps, prevState) { + componentDidUpdate(prevProps: any, prevState: any) { const { channel, name } = this.state; const { loading, failure, error, result, isMasterDetail } = this.props; @@ -118,7 +103,7 @@ class CreateChannelView extends React.Component { } = this.state; const { create } = this.props; - const params = { + const params: any = { prid: prid || rid, pmid, t_name, @@ -138,12 +123,12 @@ class CreateChannelView extends React.Component { return channel && channel.rid && channel.rid.trim().length && name.trim().length; }; - selectChannel = ({ value }) => { + selectChannel = ({ value }: any) => { logEvent(events.CD_SELECT_CHANNEL); this.setState({ channel: value, encrypted: value?.encrypted }); }; - selectUsers = ({ value }) => { + selectUsers = ({ value }: any) => { logEvent(events.CD_SELECT_USERS); this.setState({ users: value }); }; @@ -151,10 +136,12 @@ class CreateChannelView extends React.Component { get isEncryptionEnabled() { const { channel } = this.state; const { encryptionEnabled } = this.props; + // TODO: remove this ts-ignore when migrate the file: app/lib/encryption/constants.js + // @ts-ignore return encryptionEnabled && E2E_ROOM_TYPES[channel?.t]; } - onEncryptedChange = value => { + onEncryptedChange = (value: any) => { logEvent(events.CD_TOGGLE_ENCRY); this.setState({ encrypted: value }); }; @@ -163,12 +150,14 @@ class CreateChannelView extends React.Component { const { name, users, encrypted } = this.state; const { server, user, loading, blockUnauthenticatedAccess, theme, serverVersion } = this.props; return ( + // @ts-ignore + {/* @ts-ignore*/} {I18n.t('Discussion_Desc')} this.setState({ name: text })} + onChangeText={(text: string) => this.setState({ name: text })} theme={theme} /> ({ +const mapStateToProps = (state: any) => ({ user: getUserSelector(state), server: state.server.server, error: state.createDiscussion.error, @@ -227,8 +217,8 @@ const mapStateToProps = state => ({ encryptionEnabled: state.encryption.enabled }); -const mapDispatchToProps = dispatch => ({ - create: data => dispatch(createDiscussionRequest(data)) +const mapDispatchToProps = (dispatch: any) => ({ + create: (data: any) => dispatch(createDiscussionRequest(data)) }); export default connect(mapStateToProps, mapDispatchToProps)(withTheme(CreateChannelView)); diff --git a/app/views/CreateDiscussionView/interfaces.ts b/app/views/CreateDiscussionView/interfaces.ts new file mode 100644 index 000000000..468833119 --- /dev/null +++ b/app/views/CreateDiscussionView/interfaces.ts @@ -0,0 +1,55 @@ +export interface ICreateChannelViewProps { + navigation: any; + route: { + params?: { + channel: string; + message: { + msg: string; + }; + showCloseModal: boolean; + }; + }; + server: string; + user: { + id: string; + token: string; + }; + create: Function; + loading: boolean; + result: { + rid: string; + t: string; + prid: string; + }; + failure: boolean; + error: { + reason: string; + }; + theme: string; + isMasterDetail: boolean; + blockUnauthenticatedAccess: boolean; + serverVersion: string; + encryptionEnabled: boolean; +} + +export interface ICreateDiscussionViewSelectChannel { + server: string; + token: string; + userId: string; + initial: object; + onChannelSelect: Function; + blockUnauthenticatedAccess: boolean; + serverVersion: string; + theme: string; +} + +export interface ICreateDiscussionViewSelectUsers { + server: string; + token: string; + userId: string; + selected: any[]; + onUserSelect: Function; + blockUnauthenticatedAccess: boolean; + serverVersion: string; + theme: string; +} diff --git a/app/views/CreateDiscussionView/styles.js b/app/views/CreateDiscussionView/styles.ts similarity index 100% rename from app/views/CreateDiscussionView/styles.js rename to app/views/CreateDiscussionView/styles.ts diff --git a/app/views/DirectoryView/Options.js b/app/views/DirectoryView/Options.tsx similarity index 90% rename from app/views/DirectoryView/Options.js rename to app/views/DirectoryView/Options.tsx index 7d6b1e2df..fcc0f7bf6 100644 --- a/app/views/DirectoryView/Options.js +++ b/app/views/DirectoryView/Options.tsx @@ -1,6 +1,5 @@ import React, { PureComponent } from 'react'; import { Animated, Easing, Switch, Text, TouchableWithoutFeedback, View } from 'react-native'; -import PropTypes from 'prop-types'; import Touch from '../../utils/touch'; import { CustomIcon } from '../../lib/Icons'; @@ -16,18 +15,20 @@ const ANIMATION_PROPS = { useNativeDriver: true }; -export default class DirectoryOptions extends PureComponent { - static propTypes = { - type: PropTypes.string, - globalUsers: PropTypes.bool, - isFederationEnabled: PropTypes.bool, - close: PropTypes.func, - changeType: PropTypes.func, - toggleWorkspace: PropTypes.func, - theme: PropTypes.string - }; +interface IDirectoryOptionsProps { + type: string; + globalUsers: boolean; + isFederationEnabled: boolean; + close: Function; + changeType: Function; + toggleWorkspace(): void; + theme: string; +} - constructor(props) { +export default class DirectoryOptions extends PureComponent { + private animatedValue: Animated.Value; + + constructor(props: IDirectoryOptionsProps) { super(props); this.animatedValue = new Animated.Value(0); } @@ -47,7 +48,7 @@ export default class DirectoryOptions extends PureComponent { }).start(() => close()); }; - renderItem = itemType => { + renderItem = (itemType: string) => { const { changeType, type: propType, theme } = this.props; let text = 'Users'; let icon = 'user'; diff --git a/app/views/DirectoryView/index.js b/app/views/DirectoryView/index.tsx similarity index 89% rename from app/views/DirectoryView/index.js rename to app/views/DirectoryView/index.tsx index e05a806fc..25d53b831 100644 --- a/app/views/DirectoryView/index.js +++ b/app/views/DirectoryView/index.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { FlatList, Text, View } from 'react-native'; import { connect } from 'react-redux'; @@ -24,9 +23,22 @@ import { goRoom } from '../../utils/goRoom'; import styles from './styles'; import Options from './Options'; -class DirectoryView extends React.Component { - static navigationOptions = ({ navigation, isMasterDetail }) => { - const options = { +interface IDirectoryViewProps { + navigation: object; + baseUrl: string; + isFederationEnabled: boolean; + user: { + id: string; + token: string; + }; + theme: string; + directoryDefaultView: string; + isMasterDetail: boolean; +} + +class DirectoryView extends React.Component { + static navigationOptions = ({ navigation, isMasterDetail }: any) => { + const options: any = { title: I18n.t('Directory') }; if (isMasterDetail) { @@ -35,20 +47,7 @@ class DirectoryView extends React.Component { return options; }; - static propTypes = { - navigation: PropTypes.object, - baseUrl: PropTypes.string, - isFederationEnabled: PropTypes.bool, - user: PropTypes.shape({ - id: PropTypes.string, - token: PropTypes.string - }), - theme: PropTypes.string, - directoryDefaultView: PropTypes.string, - isMasterDetail: PropTypes.bool - }; - - constructor(props) { + constructor(props: IDirectoryViewProps) { super(props); this.state = { data: [], @@ -65,7 +64,7 @@ class DirectoryView extends React.Component { this.load({}); } - onSearchChangeText = text => { + onSearchChangeText = (text: string) => { this.setState({ text }, this.search); }; @@ -115,7 +114,7 @@ class DirectoryView extends React.Component { this.load({ newSearch: true }); }; - changeType = type => { + changeType = (type: string) => { this.setState({ type, data: [] }, () => this.search()); if (type === 'users') { @@ -129,17 +128,17 @@ class DirectoryView extends React.Component { toggleWorkspace = () => { this.setState( - ({ globalUsers }) => ({ globalUsers: !globalUsers, data: [] }), + ({ globalUsers }: any) => ({ globalUsers: !globalUsers, data: [] }), () => this.search() ); }; toggleDropdown = () => { - this.setState(({ showOptionsDropdown }) => ({ showOptionsDropdown: !showOptionsDropdown })); + this.setState(({ showOptionsDropdown }: any) => ({ showOptionsDropdown: !showOptionsDropdown })); }; - goRoom = item => { - const { navigation, isMasterDetail } = this.props; + goRoom = (item: any) => { + const { navigation, isMasterDetail }: any = this.props; if (isMasterDetail) { navigation.navigate('DrawerNavigator'); } else { @@ -148,7 +147,7 @@ class DirectoryView extends React.Component { goRoom({ item, isMasterDetail }); }; - onPressItem = async item => { + onPressItem = async (item: any) => { const { type } = this.state; if (type === 'users') { const result = await RocketChat.createDirectMessage(item.username); @@ -215,7 +214,7 @@ class DirectoryView extends React.Component { ); }; - renderItem = ({ item, index }) => { + renderItem = ({ item, index }: any) => { const { data, type } = this.state; const { baseUrl, user, theme } = this.props; @@ -308,7 +307,7 @@ class DirectoryView extends React.Component { }; } -const mapStateToProps = state => ({ +const mapStateToProps = (state: any) => ({ baseUrl: state.server.server, user: getUserSelector(state), isFederationEnabled: state.settings.FEDERATION_Enabled, diff --git a/app/views/DirectoryView/styles.js b/app/views/DirectoryView/styles.ts similarity index 98% rename from app/views/DirectoryView/styles.js rename to app/views/DirectoryView/styles.ts index b026041c3..543e6babb 100644 --- a/app/views/DirectoryView/styles.js +++ b/app/views/DirectoryView/styles.ts @@ -35,6 +35,7 @@ export default StyleSheet.create({ top: 0 }, backdrop: { + // @ts-ignore ...StyleSheet.absoluteFill }, dropdownContainerHeader: { diff --git a/yarn.lock b/yarn.lock index b0d99a98b..ef839c91b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3209,8 +3209,8 @@ integrity sha512-NF5KlFt642ZucP/KHnYGBNYLD6O7bcrZMKfRQlH5Y3/1xpnPX1g4wuygtiV7XArMU1FopQT+qmCUPPj8IMDTcw== "@rocket.chat/sdk@RocketChat/Rocket.Chat.js.SDK#mobile": - version "1.0.0-mobile" - resolved "https://codeload.github.com/RocketChat/Rocket.Chat.js.SDK/tar.gz/e0e42466073d1444d74cf8ec6581f4122b4387cc" + version "1.1.0-mobile" + resolved "https://codeload.github.com/RocketChat/Rocket.Chat.js.SDK/tar.gz/0ee2ded22b08b34ce7ab62b26e42a713dca0d1ac" dependencies: js-sha256 "^0.9.0" lru-cache "^4.1.1"