diff --git a/app/containers/FormContainer.tsx b/app/containers/FormContainer.tsx index 46522508..3a784b38 100644 --- a/app/containers/FormContainer.tsx +++ b/app/containers/FormContainer.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ScrollView, StyleSheet, View } from 'react-native'; +import { ScrollView, ScrollViewProps, StyleSheet, View } from 'react-native'; import { themes } from '../constants/colors'; import sharedStyles from '../views/Styles'; @@ -10,10 +10,10 @@ import AppVersion from './AppVersion'; import { isTablet } from '../utils/deviceInfo'; import SafeAreaView from './SafeAreaView'; -interface IFormContainer { +interface IFormContainer extends ScrollViewProps { theme: string; testID: string; - children: JSX.Element; + children: React.ReactNode; } const styles = StyleSheet.create({ diff --git a/app/utils/scaling.js b/app/utils/scaling.js deleted file mode 100644 index 7cf33f1f..00000000 --- a/app/utils/scaling.js +++ /dev/null @@ -1,11 +0,0 @@ -import { isTablet } from './deviceInfo'; - -const guidelineBaseWidth = isTablet ? 600 : 375; -const guidelineBaseHeight = isTablet ? 800 : 667; - -// TODO: we need to refactor this -const scale = (size, width) => (width / guidelineBaseWidth) * size; -const verticalScale = (size, height) => (height / guidelineBaseHeight) * size; -const moderateScale = (size, factor = 0.5, width) => size + (scale(size, width) - size) * factor; - -export { scale, verticalScale, moderateScale }; diff --git a/app/utils/scaling.ts b/app/utils/scaling.ts new file mode 100644 index 00000000..45d248d3 --- /dev/null +++ b/app/utils/scaling.ts @@ -0,0 +1,16 @@ +import { isTablet } from './deviceInfo'; + +const guidelineBaseWidth = isTablet ? 600 : 375; +const guidelineBaseHeight = isTablet ? 800 : 667; + +function scale({ size, width }: { size: number; width: number }): number { + return (width / guidelineBaseWidth) * size; +} +function verticalScale({ size, height }: { size: number; height: number }): number { + return (height / guidelineBaseHeight) * size; +} +function moderateScale({ size, factor = 0.5, width }: { size: number; factor?: number; width: number }): number { + return size + (scale({ size, width }) - size) * factor; +} + +export { scale, verticalScale, moderateScale }; diff --git a/app/views/NewServerView/ServerInput/Item.js b/app/views/NewServerView/ServerInput/Item.tsx similarity index 79% rename from app/views/NewServerView/ServerInput/Item.js rename to app/views/NewServerView/ServerInput/Item.tsx index 59ae77f0..9fb44719 100644 --- a/app/views/NewServerView/ServerInput/Item.js +++ b/app/views/NewServerView/ServerInput/Item.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; -import PropTypes from 'prop-types'; import { BorderlessButton } from 'react-native-gesture-handler'; import { themes } from '../../../constants/colors'; import { CustomIcon } from '../../../lib/Icons'; import sharedStyles from '../../Styles'; import Touch from '../../../utils/touch'; +import { IServer } from '../index'; const styles = StyleSheet.create({ container: { @@ -27,13 +27,20 @@ const styles = StyleSheet.create({ } }); -const Item = ({ item, theme, onPress, onDelete }) => ( +interface IItem { + item: IServer; + theme: string; + onPress(url: string): void; + onDelete(item: IServer): void; +} + +const Item = ({ item, theme, onPress, onDelete }: IItem): JSX.Element => ( onPress(item.url)} theme={theme} testID={`server-history-${item.url}`}> {item.url} - + {item.username} @@ -43,11 +50,4 @@ const Item = ({ item, theme, onPress, onDelete }) => ( ); -Item.propTypes = { - item: PropTypes.object, - theme: PropTypes.string, - onPress: PropTypes.func, - onDelete: PropTypes.func -}; - export default Item; diff --git a/app/views/NewServerView/ServerInput/index.js b/app/views/NewServerView/ServerInput/index.tsx similarity index 81% rename from app/views/NewServerView/ServerInput/index.js rename to app/views/NewServerView/ServerInput/index.tsx index ef80c468..c3399b73 100644 --- a/app/views/NewServerView/ServerInput/index.js +++ b/app/views/NewServerView/ServerInput/index.tsx @@ -1,12 +1,12 @@ import React, { useState } from 'react'; import { FlatList, StyleSheet, View } from 'react-native'; -import PropTypes from 'prop-types'; import TextInput from '../../../containers/TextInput'; import * as List from '../../../containers/List'; import { themes } from '../../../constants/colors'; import I18n from '../../../i18n'; import Item from './Item'; +import { IServer } from '../index'; const styles = StyleSheet.create({ container: { @@ -28,7 +28,25 @@ const styles = StyleSheet.create({ } }); -const ServerInput = ({ text, theme, serversHistory, onChangeText, onSubmit, onDelete, onPressServerHistory }) => { +interface IServerInput { + text: string; + theme: string; + serversHistory: any[]; + onChangeText(text: string): void; + onSubmit(): void; + onDelete(item: IServer): void; + onPressServerHistory(serverHistory: IServer): void; +} + +const ServerInput = ({ + text, + theme, + serversHistory, + onChangeText, + onSubmit, + onDelete, + onPressServerHistory +}: IServerInput): JSX.Element => { const [focused, setFocused] = useState(false); return ( @@ -68,14 +86,4 @@ const ServerInput = ({ text, theme, serversHistory, onChangeText, onSubmit, onDe ); }; -ServerInput.propTypes = { - text: PropTypes.string, - theme: PropTypes.string, - serversHistory: PropTypes.array, - onChangeText: PropTypes.func, - onSubmit: PropTypes.func, - onDelete: PropTypes.func, - onPressServerHistory: PropTypes.func -}; - export default ServerInput; diff --git a/app/views/NewServerView/index.js b/app/views/NewServerView/index.tsx similarity index 75% rename from app/views/NewServerView/index.js rename to app/views/NewServerView/index.tsx index 8eaba349..815ec134 100644 --- a/app/views/NewServerView/index.js +++ b/app/views/NewServerView/index.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { Text, Keyboard, StyleSheet, View, BackHandler, Image } from 'react-native'; import { connect } from 'react-redux'; import { Base64 } from 'js-base64'; @@ -7,6 +6,9 @@ import parse from 'url-parse'; import { Q } from '@nozbe/watermelondb'; import { TouchableOpacity } from 'react-native-gesture-handler'; import Orientation from 'react-native-orientation-locker'; +import { StackNavigationProp } from '@react-navigation/stack'; +import { Dispatch } from 'redux'; +import Model from '@nozbe/watermelondb/Model'; import UserPreferences from '../../lib/userPreferences'; import EventEmitter from '../../utils/events'; @@ -19,7 +21,6 @@ import FormContainer, { FormContainerInner } from '../../containers/FormContaine import I18n from '../../i18n'; import { themes } from '../../constants/colors'; import { events, logEvent } from '../../utils/log'; -import { animateNextTransition } from '../../utils/layoutAnimation'; import { withTheme } from '../../theme'; import { BASIC_AUTH_KEY, setBasicAuth } from '../../utils/fetch'; import * as HeaderButton from '../../containers/HeaderButton'; @@ -66,19 +67,32 @@ const styles = StyleSheet.create({ } }); -class NewServerView extends React.Component { - static propTypes = { - navigation: PropTypes.object, - theme: PropTypes.string, - connecting: PropTypes.bool.isRequired, - connectServer: PropTypes.func.isRequired, - selectServer: PropTypes.func.isRequired, - previousServer: PropTypes.string, - inviteLinksClear: PropTypes.func, - serverFinishAdd: PropTypes.func - }; +export interface IServer extends Model { + url: string; + username: string; +} +interface INewServerView { + navigation: StackNavigationProp; + theme: string; + connecting: boolean; + connectServer(server: string, username?: string, fromServerHistory?: boolean): void; + selectServer(server: string): void; + previousServer: string; + inviteLinksClear(): void; + serverFinishAdd(): void; + width: number; + height: number; +} - constructor(props) { +interface IState { + text: string; + connectingOpen: boolean; + certificate: any; + serversHistory: IServer[]; +} + +class NewServerView extends React.Component { + constructor(props: INewServerView) { super(props); if (!isTablet) { Orientation.lockToPortrait(); @@ -131,21 +145,21 @@ class NewServerView extends React.Component { return false; }; - onChangeText = text => { + onChangeText = (text: string) => { this.setState({ text }); this.queryServerHistory(text); }; - queryServerHistory = async text => { + queryServerHistory = async (text?: string) => { const db = database.servers; try { const serversHistoryCollection = db.get('servers_history'); let whereClause = [Q.where('username', Q.notEq(null)), Q.experimentalSortBy('updated_at', Q.desc), Q.experimentalTake(3)]; - const likeString = sanitizeLikeString(text); if (text) { + const likeString = sanitizeLikeString(text); whereClause = [...whereClause, Q.where('url', Q.like(`%${likeString}%`))]; } - const serversHistory = await serversHistoryCollection.query(...whereClause).fetch(); + const serversHistory = (await serversHistoryCollection.query(...whereClause).fetch()) as IServer[]; this.setState({ serversHistory }); } catch { // Do nothing @@ -158,7 +172,7 @@ class NewServerView extends React.Component { selectServer(previousServer); }; - handleNewServerEvent = event => { + handleNewServerEvent = (event: { server: string }) => { let { server } = event; if (!server) { return; @@ -169,13 +183,11 @@ class NewServerView extends React.Component { connectServer(server); }; - onPressServerHistory = serverHistory => { - this.setState({ text: serverHistory?.url }, () => - this.submit({ fromServerHistory: true, username: serverHistory?.username }) - ); + onPressServerHistory = (serverHistory: IServer) => { + this.setState({ text: serverHistory.url }, () => this.submit(true, serverHistory?.username)); }; - submit = async ({ fromServerHistory = false, username }) => { + submit = async (fromServerHistory?: boolean, username?: string) => { logEvent(events.NS_CONNECT_TO_WORKSPACE); const { text, certificate } = this.state; const { connectServer } = this.props; @@ -207,7 +219,7 @@ class NewServerView extends React.Component { connectServer('https://open.rocket.chat'); }; - basicAuth = async (server, text) => { + basicAuth = async (server: string, text: string) => { try { const parsedUrl = parse(text, true); if (parsedUrl.auth.length) { @@ -222,14 +234,14 @@ class NewServerView extends React.Component { chooseCertificate = async () => { try { - const certificate = await SSLPinning.pickCertificate(); + const certificate = await SSLPinning?.pickCertificate(); this.setState({ certificate }); } catch { // Do nothing } }; - completeUrl = url => { + completeUrl = (url: string) => { const parsedUrl = parse(url, true); if (parsedUrl.auth.length) { url = parsedUrl.origin; @@ -252,14 +264,11 @@ class NewServerView extends React.Component { return url.replace(/\/+$/, '').replace(/\\/g, '/'); }; - uriToPath = uri => uri.replace('file://', ''); - - saveCertificate = certificate => { - animateNextTransition(); - this.setState({ certificate }); - }; + uriToPath = (uri: string) => uri.replace('file://', ''); handleRemove = () => { + // TODO: Remove ts-ignore when migrate the showConfirmationAlert + // @ts-ignore showConfirmationAlert({ message: I18n.t('You_will_unset_a_certificate_for_this_server'), confirmationText: I18n.t('Remove'), @@ -267,14 +276,15 @@ class NewServerView extends React.Component { }); }; - deleteServerHistory = async item => { - const { serversHistory } = this.state; + deleteServerHistory = async (item: IServer) => { const db = database.servers; try { - await db.action(async () => { + await db.write(async () => { await item.destroyPermanently(); }); - this.setState({ serversHistory: serversHistory.filter(server => server.id !== item.id) }); + this.setState((prevstate: IState) => ({ + serversHistory: prevstate.serversHistory.filter((server: IServer) => server.id !== item.id) + })); } catch { // Nothing } @@ -288,20 +298,21 @@ class NewServerView extends React.Component { style={[ styles.certificatePicker, { - marginBottom: verticalScale(previousServer && !isTablet ? 10 : 30, height) + marginBottom: verticalScale({ size: previousServer && !isTablet ? 10 : 30, height }) } ]}> {certificate ? I18n.t('Your_certificate') : I18n.t('Do_you_have_a_certificate')} - + {certificate ?? I18n.t('Apply_Your_Certificate')} @@ -321,10 +332,10 @@ class NewServerView extends React.Component { style={[ styles.onboardingImage, { - marginBottom: verticalScale(10, height), - marginTop: isTablet ? 0 : verticalScale(marginTop, height), - width: verticalScale(100, height), - height: verticalScale(100, height) + marginBottom: verticalScale({ size: 10, height }), + marginTop: isTablet ? 0 : verticalScale({ size: marginTop, height }), + width: verticalScale({ size: 100, height }), + height: verticalScale({ size: 100, height }) } ]} source={require('../../static/images/logo.png')} @@ -335,8 +346,8 @@ class NewServerView extends React.Component { styles.title, { color: themes[theme].titleText, - fontSize: moderateScale(22, null, width), - marginBottom: verticalScale(8, height) + fontSize: moderateScale({ size: 22, width }), + marginBottom: verticalScale({ size: 8, height }) } ]}> Rocket.Chat @@ -346,8 +357,8 @@ class NewServerView extends React.Component { styles.subtitle, { color: themes[theme].controlText, - fontSize: moderateScale(16, null, width), - marginBottom: verticalScale(30, height) + fontSize: moderateScale({ size: 16, width }), + marginBottom: verticalScale({ size: 30, height }) } ]}> {I18n.t('Onboarding_subtitle')} @@ -367,7 +378,7 @@ class NewServerView extends React.Component { onPress={this.submit} disabled={!text || connecting} loading={!connectingOpen && connecting} - style={[styles.connectButton, { marginTop: verticalScale(16, height) }]} + style={[styles.connectButton, { marginTop: verticalScale({ size: 16, height }) }]} theme={theme} testID='new-server-view-button' /> @@ -377,8 +388,8 @@ class NewServerView extends React.Component { styles.description, { color: themes[theme].auxiliaryText, - fontSize: moderateScale(14, null, width), - marginBottom: verticalScale(16, height) + fontSize: moderateScale({ size: 14, width }), + marginBottom: verticalScale({ size: 16, height }) } ]}> {I18n.t('Onboarding_join_open_description')} @@ -400,14 +411,15 @@ class NewServerView extends React.Component { } } -const mapStateToProps = state => ({ +const mapStateToProps = (state: any) => ({ connecting: state.server.connecting, previousServer: state.server.previousServer }); -const mapDispatchToProps = dispatch => ({ - connectServer: (...params) => dispatch(serverRequest(...params)), - selectServer: server => dispatch(selectServerRequest(server)), +const mapDispatchToProps = (dispatch: Dispatch) => ({ + connectServer: (server: string, username: string & null, fromServerHistory?: boolean) => + dispatch(serverRequest(server, username, fromServerHistory)), + selectServer: (server: string) => dispatch(selectServerRequest(server)), inviteLinksClear: () => dispatch(inviteLinksClearAction()), serverFinishAdd: () => dispatch(serverFinishAddAction()) });