From 65f5b03bdf8b313dc43e6547ae5e10189feb9883 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto <47038980+reinaldonetof@users.noreply.github.com> Date: Thu, 2 Dec 2021 10:39:48 -0300 Subject: [PATCH] Chore: Migrate ShareListView to Typescript (#3459) Co-authored-by: Gerzon Z --- app/presentation/DirectoryItem/index.tsx | 12 +- .../Header/{Header.ios.js => Header.ios.tsx} | 14 +- .../Header/{Header.android.js => Header.tsx} | 10 +- .../Header/{index.js => index.tsx} | 16 +- app/views/ShareListView/Header/interface.ts | 13 ++ .../ShareListView/{index.js => index.tsx} | 139 ++++++++++++------ .../ShareListView/{styles.js => styles.ts} | 0 7 files changed, 120 insertions(+), 84 deletions(-) rename app/views/ShareListView/Header/{Header.ios.js => Header.ios.tsx} (83%) rename app/views/ShareListView/Header/{Header.android.js => Header.tsx} (86%) rename app/views/ShareListView/Header/{index.js => index.tsx} (52%) create mode 100644 app/views/ShareListView/Header/interface.ts rename app/views/ShareListView/{index.js => index.tsx} (79%) rename app/views/ShareListView/{styles.js => styles.ts} (100%) diff --git a/app/presentation/DirectoryItem/index.tsx b/app/presentation/DirectoryItem/index.tsx index b8d9811a..234c1e31 100644 --- a/app/presentation/DirectoryItem/index.tsx +++ b/app/presentation/DirectoryItem/index.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Text, View } from 'react-native'; +import { Text, View, ViewStyle } from 'react-native'; import Touch from '../../utils/touch'; import Avatar from '../../containers/Avatar'; @@ -10,7 +10,7 @@ import { themes } from '../../constants/colors'; export { ROW_HEIGHT }; interface IDirectoryItemLabel { - text: string; + text?: string; theme: string; } @@ -21,9 +21,9 @@ interface IDirectoryItem { type: string; onPress(): void; testID: string; - style: any; - rightLabel: string; - rid: string; + style?: ViewStyle; + rightLabel?: string; + rid?: string; theme: string; teamMain?: boolean; } @@ -32,7 +32,7 @@ const DirectoryItemLabel = React.memo(({ text, theme }: IDirectoryItemLabel) => if (!text) { return null; } - return {text}; + return {text}; }); const DirectoryItem = ({ diff --git a/app/views/ShareListView/Header/Header.ios.js b/app/views/ShareListView/Header/Header.ios.tsx similarity index 83% rename from app/views/ShareListView/Header/Header.ios.js rename to app/views/ShareListView/Header/Header.ios.tsx index d68bacff..c1f5e116 100644 --- a/app/views/ShareListView/Header/Header.ios.js +++ b/app/views/ShareListView/Header/Header.ios.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import PropTypes from 'prop-types'; import { Keyboard, StyleSheet, View } from 'react-native'; import ShareExtension from 'rn-extensions-share'; @@ -8,6 +7,7 @@ import * as HeaderButton from '../../../containers/HeaderButton'; import { themes } from '../../../constants/colors'; import sharedStyles from '../../Styles'; import { animateNextTransition } from '../../../utils/layoutAnimation'; +import { IShareListHeaderIos } from './interface'; const styles = StyleSheet.create({ container: { @@ -16,10 +16,10 @@ const styles = StyleSheet.create({ } }); -const Header = React.memo(({ searching, onChangeSearchText, initSearch, cancelSearch, theme }) => { +const Header = React.memo(({ searching, onChangeSearchText, initSearch, cancelSearch, theme }: IShareListHeaderIos) => { const [text, setText] = useState(''); - const onChangeText = searchText => { + const onChangeText = (searchText: string) => { onChangeSearchText(searchText); setText(searchText); }; @@ -59,12 +59,4 @@ const Header = React.memo(({ searching, onChangeSearchText, initSearch, cancelSe ); }); -Header.propTypes = { - searching: PropTypes.bool, - onChangeSearchText: PropTypes.func, - initSearch: PropTypes.func, - cancelSearch: PropTypes.func, - theme: PropTypes.string -}; - export default Header; diff --git a/app/views/ShareListView/Header/Header.android.js b/app/views/ShareListView/Header/Header.tsx similarity index 86% rename from app/views/ShareListView/Header/Header.android.js rename to app/views/ShareListView/Header/Header.tsx index 727fa536..616f3f48 100644 --- a/app/views/ShareListView/Header/Header.android.js +++ b/app/views/ShareListView/Header/Header.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import PropTypes from 'prop-types'; import TextInput from '../../../presentation/TextInput'; import I18n from '../../../i18n'; import { themes } from '../../../constants/colors'; import sharedStyles from '../../Styles'; +import { IShareListHeader } from './interface'; const styles = StyleSheet.create({ container: { @@ -24,7 +24,7 @@ const styles = StyleSheet.create({ } }); -const Header = React.memo(({ searching, onChangeSearchText, theme }) => { +const Header = React.memo(({ searching, onChangeSearchText, theme }: IShareListHeader) => { const titleColorStyle = { color: themes[theme].headerTintColor }; const isLight = theme === 'light'; if (searching) { @@ -43,10 +43,4 @@ const Header = React.memo(({ searching, onChangeSearchText, theme }) => { return {I18n.t('Send_to')}; }); -Header.propTypes = { - searching: PropTypes.bool, - onChangeSearchText: PropTypes.func, - theme: PropTypes.string -}; - export default Header; diff --git a/app/views/ShareListView/Header/index.js b/app/views/ShareListView/Header/index.tsx similarity index 52% rename from app/views/ShareListView/Header/index.js rename to app/views/ShareListView/Header/index.tsx index d66c9a80..e1feab43 100644 --- a/app/views/ShareListView/Header/index.js +++ b/app/views/ShareListView/Header/index.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import PropTypes from 'prop-types'; import Header from './Header'; +import { IShareListHeader } from './interface'; -const ShareListHeader = React.memo(({ searching, initSearch, cancelSearch, search, theme }) => { - const onSearchChangeText = text => { - search(text.trim()); +const ShareListHeader = React.memo(({ searching, initSearch, cancelSearch, onChangeSearchText, theme }: IShareListHeader) => { + const onSearchChangeText = (text: string) => { + onChangeSearchText(text.trim()); }; return ( @@ -19,12 +19,4 @@ const ShareListHeader = React.memo(({ searching, initSearch, cancelSearch, searc ); }); -ShareListHeader.propTypes = { - searching: PropTypes.bool, - initSearch: PropTypes.func, - cancelSearch: PropTypes.func, - search: PropTypes.func, - theme: PropTypes.string -}; - export default ShareListHeader; diff --git a/app/views/ShareListView/Header/interface.ts b/app/views/ShareListView/Header/interface.ts new file mode 100644 index 00000000..25266fb5 --- /dev/null +++ b/app/views/ShareListView/Header/interface.ts @@ -0,0 +1,13 @@ +import { TextInputProps } from 'react-native'; + +type RequiredOnChangeText = Required>; + +export interface IShareListHeader { + searching: boolean; + onChangeSearchText: RequiredOnChangeText['onChangeText']; + theme: string; + initSearch?: () => void; + cancelSearch?: () => void; +} + +export type IShareListHeaderIos = Required; diff --git a/app/views/ShareListView/index.js b/app/views/ShareListView/index.tsx similarity index 79% rename from app/views/ShareListView/index.js rename to app/views/ShareListView/index.tsx index e0a82a50..1c9f0e41 100644 --- a/app/views/ShareListView/index.js +++ b/app/views/ShareListView/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import PropTypes from 'prop-types'; -import { BackHandler, FlatList, Keyboard, PermissionsAndroid, ScrollView, Text, View } from 'react-native'; +import { StackNavigationProp } from '@react-navigation/stack'; +import { BackHandler, FlatList, Keyboard, PermissionsAndroid, ScrollView, Text, View, Rationale } from 'react-native'; import ShareExtension from 'rn-extensions-share'; import * as FileSystem from 'expo-file-system'; import { connect } from 'react-redux'; @@ -25,24 +25,75 @@ import { sanitizeLikeString } from '../../lib/database/utils'; import styles from './styles'; import ShareListHeader from './Header'; -const permission = { +interface IFile { + value: string; + type: string; +} + +interface IAttachment { + filename: string; + description: string; + size: number; + mime: any; + path: string; +} + +interface IChat { + rid: string; + t: string; + name: string; + fname: string; + blocked: boolean; + blocker: boolean; + prid: string; + uids: string[]; + usernames: string[]; + topic: string; + description: string; +} + +interface IServerInfo { + useRealName: boolean; +} +interface IState { + searching: boolean; + searchText: string; + searchResults: IChat[]; + chats: IChat[]; + serversCount: number; + attachments: IAttachment[]; + text: string; + loading: boolean; + serverInfo: IServerInfo; + needsPermission: boolean; +} + +interface INavigationOption { + navigation: StackNavigationProp; +} + +interface IShareListViewProps extends INavigationOption { + server: string; + token: string; + userId: string; + theme: string; +} + +const permission: Rationale = { title: I18n.t('Read_External_Permission'), - message: I18n.t('Read_External_Permission_Message') + message: I18n.t('Read_External_Permission_Message'), + buttonPositive: 'Ok' }; -const getItemLayout = (data, index) => ({ length: data.length, offset: ROW_HEIGHT * index, index }); -const keyExtractor = item => item.rid; +const getItemLayout = (data: any, index: number) => ({ length: data.length, offset: ROW_HEIGHT * index, index }); +const keyExtractor = (item: IChat) => item.rid; -class ShareListView extends React.Component { - static propTypes = { - navigation: PropTypes.object, - server: PropTypes.string, - token: PropTypes.string, - userId: PropTypes.string, - theme: PropTypes.string - }; +class ShareListView extends React.Component { + private unsubscribeFocus: (() => void) | undefined; - constructor(props) { + private unsubscribeBlur: (() => void) | undefined; + + constructor(props: IShareListViewProps) { super(props); this.state = { searching: false, @@ -53,7 +104,7 @@ class ShareListView extends React.Component { attachments: [], text: '', loading: true, - serverInfo: null, + serverInfo: {} as IServerInfo, needsPermission: isAndroid || false }; this.setHeader(); @@ -70,7 +121,7 @@ class ShareListView extends React.Component { async componentDidMount() { const { server } = this.props; try { - const data = await ShareExtension.data(); + const data = (await ShareExtension.data()) as IFile[]; if (isAndroid) { await this.askForPermission(data); } @@ -85,7 +136,7 @@ class ShareListView extends React.Component { size: file.size, mime: mime.lookup(file.uri), path: file.uri - })); + })) as IAttachment[]; const text = data.filter(item => item.type === 'text').reduce((acc, item) => `${item.value}\n${acc}`, ''); this.setState({ text, @@ -98,14 +149,14 @@ class ShareListView extends React.Component { this.getSubscriptions(server); } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: IShareListViewProps) { const { server } = this.props; if (nextProps.server !== server) { this.getSubscriptions(nextProps.server); } } - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: IShareListViewProps, nextState: IState) { const { searching, needsPermission } = this.state; if (nextState.searching !== searching) { return true; @@ -151,7 +202,7 @@ class ShareListView extends React.Component { searching={searching} initSearch={this.initSearch} cancelSearch={this.cancelSearch} - search={this.search} + onChangeSearchText={this.search} theme={theme} /> ) @@ -168,7 +219,7 @@ class ShareListView extends React.Component { ) : ( ), - headerTitle: () => , + headerTitle: () => , headerRight: () => searching ? null : ( @@ -178,16 +229,16 @@ class ShareListView extends React.Component { }); }; - // eslint-disable-next-line react/sort-comp - internalSetState = (...args) => { + internalSetState = (...args: object[]) => { const { navigation } = this.props; if (navigation.isFocused()) { animateNextTransition(); } + // @ts-ignore this.setState(...args); }; - query = async text => { + query = async (text?: string) => { const db = database.active; const defaultWhereClause = [ Q.where('archived', false), @@ -195,15 +246,16 @@ class ShareListView extends React.Component { Q.experimentalSkip(0), Q.experimentalTake(20), Q.experimentalSortBy('room_updated_at', Q.desc) - ]; + ] as (Q.WhereDescription | Q.Skip | Q.Take | Q.SortBy | Q.Or)[]; if (text) { const likeString = sanitizeLikeString(text); defaultWhereClause.push(Q.or(Q.where('name', Q.like(`%${likeString}%`)), Q.where('fname', Q.like(`%${likeString}%`)))); } - const data = await db + const data = (await db .get('subscriptions') .query(...defaultWhereClause) - .fetch(); + .fetch()) as IChat[]; + return data.map(item => ({ rid: item.rid, t: item.t, @@ -218,7 +270,7 @@ class ShareListView extends React.Component { })); }; - getSubscriptions = async server => { + getSubscriptions = async (server: string) => { const serversDB = database.servers; if (server) { @@ -242,7 +294,7 @@ class ShareListView extends React.Component { } }; - askForPermission = async data => { + askForPermission = async (data: IFile[]) => { const mediaIndex = data.findIndex(item => item.type === 'media'); if (mediaIndex !== -1) { const result = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE, permission); @@ -255,15 +307,14 @@ class ShareListView extends React.Component { return Promise.resolve(); }; - uriToPath = uri => decodeURIComponent(isIOS ? uri.replace(/^file:\/\//, '') : uri); + uriToPath = (uri: string) => decodeURIComponent(isIOS ? uri.replace(/^file:\/\//, '') : uri); - getRoomTitle = item => { + getRoomTitle = (item: IChat) => { const { serverInfo } = this.state; - const { useRealName } = serverInfo; - return ((item.prid || useRealName) && item.fname) || item.name; + return ((item.prid || serverInfo?.useRealName) && item.fname) || item.name; }; - shareMessage = room => { + shareMessage = (room: IChat) => { const { attachments, text, serverInfo } = this.state; const { navigation } = this.props; @@ -276,7 +327,7 @@ class ShareListView extends React.Component { }); }; - search = async text => { + search = async (text: string) => { const result = await this.query(text); this.internalSetState({ searchResults: result, @@ -303,7 +354,7 @@ class ShareListView extends React.Component { return false; }; - renderSectionHeader = header => { + renderSectionHeader = (header: string) => { const { searching } = this.state; const { theme } = this.props; if (searching) { @@ -320,10 +371,9 @@ class ShareListView extends React.Component { ); }; - renderItem = ({ item }) => { + renderItem = ({ item }: { item: IChat }) => { const { serverInfo } = this.state; - const { useRealName } = serverInfo; - const { userId, token, server, theme } = this.props; + const { theme } = this.props; let description; switch (item.t) { case 'c': @@ -333,7 +383,7 @@ class ShareListView extends React.Component { description = item.topic || item.description; break; case 'd': - description = useRealName ? item.name : item.fname; + description = serverInfo?.useRealName ? item.name : item.fname; break; default: description = item.fname; @@ -341,12 +391,7 @@ class ShareListView extends React.Component { } return ( ({ +const mapStateToProps = ({ share }: any) => ({ userId: share.user && share.user.id, token: share.user && share.user.token, server: share.server.server diff --git a/app/views/ShareListView/styles.js b/app/views/ShareListView/styles.ts similarity index 100% rename from app/views/ShareListView/styles.js rename to app/views/ShareListView/styles.ts