diff --git a/app/AppContainer.js b/app/AppContainer.tsx similarity index 93% rename from app/AppContainer.js rename to app/AppContainer.tsx index 9ec17ad43..51ccba5f7 100644 --- a/app/AppContainer.js +++ b/app/AppContainer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import PropTypes from 'prop-types'; import { NavigationContainer } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { connect } from 'react-redux'; @@ -35,7 +34,7 @@ const SetUsernameStack = () => ( // App const Stack = createStackNavigator(); -const App = React.memo(({ root, isMasterDetail }) => { +const App = React.memo(({ root, isMasterDetail }: {root: string, isMasterDetail: boolean}) => { if (!root) { return null; } @@ -100,15 +99,10 @@ const App = React.memo(({ root, isMasterDetail }) => { ); }); -const mapStateToProps = state => ({ +const mapStateToProps = (state: any) => ({ root: state.app.root, isMasterDetail: state.app.isMasterDetail }); -App.propTypes = { - root: PropTypes.string, - isMasterDetail: PropTypes.bool -}; - const AppContainer = connect(mapStateToProps)(App); export default AppContainer; diff --git a/app/dimensions.js b/app/dimensions.tsx similarity index 53% rename from app/dimensions.js rename to app/dimensions.tsx index 50f5c7b9a..32ccb93c3 100644 --- a/app/dimensions.js +++ b/app/dimensions.tsx @@ -2,10 +2,18 @@ import React from 'react'; import { Dimensions } from 'react-native'; import hoistNonReactStatics from 'hoist-non-react-statics'; -export const DimensionsContext = React.createContext(Dimensions.get('window')); +interface IDimensionsContextProps { + width: number; + height: number; + scale: number; + fontScale: number; + setDimensions: ({ width, height, scale, fontScale }: { width: number; height: number; scale: number; fontScale: number; }) => void; +} -export function withDimensions(Component) { - const DimensionsComponent = props => ( +export const DimensionsContext = React.createContext>(Dimensions.get('window')); + +export function withDimensions(Component: any) { + const DimensionsComponent = (props: any) => ( {contexts => } @@ -18,7 +26,7 @@ export const useDimensions = () => React.useContext(DimensionsContext); export const useOrientation = () => { const { width, height } = React.useContext(DimensionsContext); - const isPortrait = height > width; + const isPortrait = (height)! > (width)!; return { isPortrait, isLandscape: !isPortrait diff --git a/app/emojis.js b/app/emojis.ts similarity index 100% rename from app/emojis.js rename to app/emojis.ts diff --git a/app/index.js b/app/index.tsx similarity index 81% rename from app/index.js rename to app/index.tsx index efcf1557e..c9aca4711 100644 --- a/app/index.js +++ b/app/index.tsx @@ -6,12 +6,7 @@ import { KeyCommandsEmitter } from 'react-native-keycommands'; import RNScreens from 'react-native-screens'; import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context'; -import { - defaultTheme, - newThemeState, - subscribeTheme, - unsubscribeTheme -} from './utils/theme'; +import { defaultTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme'; import UserPreferences from './lib/userPreferences'; import EventEmitter from './utils/events'; import { appInit, appInitLocalSettings, setMasterDetail as setMasterDetailAction } from './actions/app'; @@ -24,9 +19,7 @@ import { ThemeContext } from './theme'; import { DimensionsContext } from './dimensions'; import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat'; import { MIN_WIDTH_MASTER_DETAIL_LAYOUT } from './constants/tablet'; -import { - isTablet, supportSystemTheme -} from './utils/deviceInfo'; +import { isTablet, supportSystemTheme } from './utils/deviceInfo'; import { KEY_COMMAND } from './commands'; import AppContainer from './AppContainer'; import TwoFactor from './containers/TwoFactor'; @@ -40,7 +33,26 @@ import { isFDroidBuild } from './constants/environment'; RNScreens.enableScreens(); -const parseDeepLinking = (url) => { +type TDimensions = { + width: number, + height: number, + scale: number, + fontScale: number +} +interface IProps {} +interface IState { + theme: string, + themePreferences: { + currentTheme: 'automatic' | 'light', + darkLevel: string + }, + width: number; + height: number; + scale: number; + fontScale: number; +} + +const parseDeepLinking = (url: string) => { if (url) { url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, ''); const regex = /^(room|auth|invite)\?/; @@ -61,16 +73,17 @@ const parseDeepLinking = (url) => { return null; }; -export default class Root extends React.Component { - constructor(props) { +export default class Root extends React.Component { + private listenerTimeout!: NodeJS.Timeout; + private onKeyCommands: any; + + constructor(props: IProps) { super(props); this.init(); if (!isFDroidBuild) { this.initCrashReport(); } - const { - width, height, scale, fontScale - } = Dimensions.get('window'); + const { width, height, scale, fontScale } = Dimensions.get('window'); this.state = { theme: defaultTheme(), themePreferences: { @@ -111,7 +124,7 @@ export default class Root extends React.Component { } init = async() => { - UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme); + UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(() => this.setTheme()); store.dispatch(appInitLocalSettings()); // Open app from push notification @@ -123,7 +136,7 @@ export default class Root extends React.Component { // Open app from deep linking const deepLinking = await Linking.getInitialURL(); - const parsedDeepLinkingURL = parseDeepLinking(deepLinking); + const parsedDeepLinkingURL = parseDeepLinking(deepLinking!); if (parsedDeepLinkingURL) { store.dispatch(deepLinkingOpen(parsedDeepLinkingURL)); return; @@ -133,14 +146,14 @@ export default class Root extends React.Component { store.dispatch(appInit()); } - getMasterDetail = (width) => { + getMasterDetail = (width: number) => { if (!isTablet) { return false; } return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT; } - setMasterDetail = (width) => { + setMasterDetail = (width: number) => { const isMasterDetail = this.getMasterDetail(width); store.dispatch(setMasterDetailAction(isMasterDetail)); }; @@ -150,7 +163,7 @@ export default class Root extends React.Component { window: { width, height, scale, fontScale } - }) => { + }: {window: TDimensions}) => { this.setDimensions({ width, height, scale, fontScale }); @@ -166,12 +179,8 @@ export default class Root extends React.Component { }); } - setDimensions = ({ - width, height, scale, fontScale - }) => { - this.setState({ - width, height, scale, fontScale - }); + setDimensions = ({width, height, scale, fontScale}: TDimensions) => { + this.setState({width, height, scale, fontScale}); } initTablet = () => { @@ -179,7 +188,7 @@ export default class Root extends React.Component { this.setMasterDetail(width); this.onKeyCommands = KeyCommandsEmitter.addListener( 'onKeyCommand', - (command) => { + (command: unknown) => { EventEmitter.emit(KEY_COMMAND, { event: command }); } ); @@ -197,9 +206,7 @@ export default class Root extends React.Component { } render() { - const { - themePreferences, theme, width, height, scale, fontScale - } = this.state; + const { themePreferences, theme, width, height, scale, fontScale } = this.state; return ( diff --git a/app/lib/Navigation.js b/app/lib/Navigation.js deleted file mode 100644 index 34aba1769..000000000 --- a/app/lib/Navigation.js +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import { CommonActions, StackActions } from '@react-navigation/native'; - -const navigationRef = React.createRef(); -const routeNameRef = React.createRef(); - -function navigate(name, params) { - navigationRef.current?.navigate(name, params); -} - -function back() { - navigationRef.current?.dispatch(CommonActions.goBack()); -} - -function replace(name, params) { - navigationRef.current?.dispatch(StackActions.replace(name, params)); -} - -export default { - navigationRef, - routeNameRef, - navigate, - back, - replace -}; diff --git a/app/lib/Navigation.ts b/app/lib/Navigation.ts new file mode 100644 index 000000000..7b7cb1a21 --- /dev/null +++ b/app/lib/Navigation.ts @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { CommonActions, StackActions, NavigationContainerRef } from '@react-navigation/native'; + +const navigationRef = React.createRef(); +const routeNameRef: React.MutableRefObject = React.createRef(); + +function navigate(name: string, params: any) { + navigationRef.current?.navigate(name, params); +} + +function back() { + navigationRef.current?.dispatch(CommonActions.goBack()); +} + +function replace(name: string, params: any) { + navigationRef.current?.dispatch(StackActions.replace(name, params)); +} + +export default { + navigationRef, + routeNameRef, + navigate, + back, + replace +}; diff --git a/app/lib/ShareNavigation.js b/app/lib/ShareNavigation.js deleted file mode 100644 index 6cc77eac2..000000000 --- a/app/lib/ShareNavigation.js +++ /dev/null @@ -1,9 +0,0 @@ -import { createRef } from 'react'; - -const navigationRef = createRef(); -const routeNameRef = createRef(); - -export default { - navigationRef, - routeNameRef -}; diff --git a/app/lib/ShareNavigation.ts b/app/lib/ShareNavigation.ts new file mode 100644 index 000000000..c0f5a9ad5 --- /dev/null +++ b/app/lib/ShareNavigation.ts @@ -0,0 +1,11 @@ +import * as React from 'react'; +import { NavigationContainerRef } from '@react-navigation/native'; + + +const navigationRef = React.createRef(); +const routeNameRef: React.MutableRefObject = React.createRef(); + +export default { + navigationRef, + routeNameRef +}; diff --git a/app/share.js b/app/share.tsx similarity index 80% rename from app/share.js rename to app/share.tsx index dbf0bfbff..e3448563d 100644 --- a/app/share.js +++ b/app/share.tsx @@ -1,24 +1,16 @@ import React, { useContext } from 'react'; import { Dimensions } from 'react-native'; -import PropTypes from 'prop-types'; import { NavigationContainer } from '@react-navigation/native'; import { AppearanceProvider } from 'react-native-appearance'; import { createStackNavigator } from '@react-navigation/stack'; import { Provider } from 'react-redux'; -import { - defaultTheme, - newThemeState, - subscribeTheme, - unsubscribeTheme -} from './utils/theme'; +import { defaultTheme, newThemeState, subscribeTheme, unsubscribeTheme } from './utils/theme'; import UserPreferences from './lib/userPreferences'; import Navigation from './lib/ShareNavigation'; import store from './lib/createStore'; import { supportSystemTheme } from './utils/deviceInfo'; -import { - defaultHeader, themedHeader, getActiveRouteName, navigationTheme -} from './utils/navigation'; +import { defaultHeader, themedHeader, getActiveRouteName, navigationTheme } from './utils/navigation'; import RocketChat, { THEME_PREFERENCES_KEY } from './lib/rocketchat'; import { ThemeContext } from './theme'; import { localAuthenticate } from './utils/localAuthentication'; @@ -36,6 +28,26 @@ import AuthLoadingView from './views/AuthLoadingView'; import { DimensionsContext } from './dimensions'; import debounce from './utils/debounce'; +type TDimensions = { + width: number, + height: number, + scale: number, + fontScale: number +} +interface IProps {} +interface IState { + theme: string, + themePreferences: { + currentTheme: 'automatic' | 'light', + darkLevel: string + }, + root: any; + width: number; + height: number; + scale: number; + fontScale: number; +} + const Inside = createStackNavigator(); const InsideStack = () => { const { theme } = useContext(ThemeContext); @@ -44,10 +56,7 @@ const InsideStack = () => { ...defaultHeader, ...themedHeader(theme) }; - screenOptions.headerStyle = { - ...screenOptions.headerStyle, - height: 57 - }; + screenOptions.headerStyle = {...screenOptions.headerStyle, height: 57}; return ( @@ -85,7 +94,7 @@ const OutsideStack = () => { // App const Stack = createStackNavigator(); -export const App = ({ root }) => ( +export const App = ({ root }: any) => ( <> {!root ? ( @@ -110,16 +119,10 @@ export const App = ({ root }) => ( ); -App.propTypes = { - root: PropTypes.string -}; - -class Root extends React.Component { - constructor(props) { +class Root extends React.Component { + constructor(props: any) { super(props); - const { - width, height, scale, fontScale - } = Dimensions.get('screen'); + const { width, height, scale, fontScale } = Dimensions.get('screen'); this.state = { theme: defaultTheme(), themePreferences: { @@ -141,7 +144,7 @@ class Root extends React.Component { } init = async() => { - UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(this.setTheme); + UserPreferences.getMapAsync(THEME_PREFERENCES_KEY).then(() => this.setTheme()); const currentServer = await UserPreferences.getStringAsync(RocketChat.CURRENT_SERVER); @@ -169,29 +172,18 @@ class Root extends React.Component { } // Dimensions update fires twice - onDimensionsChange = debounce(({ - window: { - width, height, scale, fontScale - } - }) => { - this.setDimensions({ - width, height, scale, fontScale - }); - this.setMasterDetail(width); + onDimensionsChange = debounce(({window: { width, height, scale, fontScale}}: {window: TDimensions}) => { + this.setDimensions({ width, height, scale, fontScale }); }) - setDimensions = ({ - width, height, scale, fontScale - }) => { + setDimensions = ({ width, height, scale, fontScale }: TDimensions) => { this.setState({ width, height, scale, fontScale }); } render() { - const { - theme, root, width, height, scale, fontScale - } = this.state; + const { theme, root, width, height, scale, fontScale } = this.state; const navTheme = navigationTheme(theme); return ( diff --git a/app/theme.js b/app/theme.js deleted file mode 100644 index c34f56de0..000000000 --- a/app/theme.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import hoistNonReactStatics from 'hoist-non-react-statics'; - -export const ThemeContext = React.createContext({ theme: 'light' }); - -export function withTheme(Component) { - const ThemedComponent = props => ( - - {contexts => } - - ); - hoistNonReactStatics(ThemedComponent, Component); - return ThemedComponent; -} - -export const useTheme = () => React.useContext(ThemeContext); diff --git a/app/theme.tsx b/app/theme.tsx new file mode 100644 index 000000000..641ee3fc1 --- /dev/null +++ b/app/theme.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import hoistNonReactStatics from 'hoist-non-react-statics'; + +interface IThemeContextProps { + theme: string, + themePreferences: { + currentTheme: 'automatic' | 'light', + darkLevel: string + }, + setTheme: (newTheme?: {}) => void; +} + +export const ThemeContext = React.createContext>({ theme: 'light' }); + +export function withTheme(Component: any) { + const ThemedComponent = (props: any) => ( + + {contexts => } + + ); + hoistNonReactStatics(ThemedComponent, Component); + return ThemedComponent; +} + +export const useTheme = () => React.useContext(ThemeContext); diff --git a/app/utils/navigation/index.js b/app/utils/navigation/index.ts similarity index 80% rename from app/utils/navigation/index.js rename to app/utils/navigation/index.ts index 6f4faf6c1..8d269e566 100644 --- a/app/utils/navigation/index.js +++ b/app/utils/navigation/index.ts @@ -16,13 +16,13 @@ export const cardStyle = { backgroundColor: 'rgba(0,0,0,0)' }; -export const borderBottom = theme => ({ +export const borderBottom: any = (theme: any) => ({ borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: themes[theme].headerBorder, elevation: 0 }); -export const themedHeader = theme => ({ +export const themedHeader = (theme: any) => ({ headerStyle: { ...borderBottom(theme), backgroundColor: themes[theme].headerBackground @@ -31,7 +31,7 @@ export const themedHeader = theme => ({ headerTitleStyle: { color: themes[theme].headerTitleColor } }); -export const navigationTheme = (theme) => { +export const navigationTheme = (theme: any) => { const defaultNavTheme = theme === 'light' ? DefaultTheme : DarkTheme; return { @@ -45,7 +45,7 @@ export const navigationTheme = (theme) => { }; // Gets the current screen from navigation state -export const getActiveRoute = (state) => { +export const getActiveRoute: any = (state: any) => { const route = state?.routes[state?.index]; if (route?.state) { @@ -56,4 +56,4 @@ export const getActiveRoute = (state) => { return route; }; -export const getActiveRouteName = state => getActiveRoute(state)?.name; +export const getActiveRouteName = (state: any) => getActiveRoute(state)?.name; diff --git a/app/views/WithoutServersView.js b/app/views/WithoutServersView.tsx similarity index 86% rename from app/views/WithoutServersView.js rename to app/views/WithoutServersView.tsx index b4f101a69..b0c8e6128 100644 --- a/app/views/WithoutServersView.js +++ b/app/views/WithoutServersView.tsx @@ -1,8 +1,5 @@ import React from 'react'; -import { - StyleSheet, View, Text -} from 'react-native'; -import PropTypes from 'prop-types'; +import { StyleSheet, View, Text } from 'react-native'; import ShareExtension from 'rn-extensions-share'; import * as HeaderButton from '../containers/HeaderButton'; @@ -29,7 +26,7 @@ const styles = StyleSheet.create({ } }); -class WithoutServerView extends React.Component { +class WithoutServerView extends React.Component { static navigationOptions = () => ({ title: 'Rocket.Chat', headerLeft: () => ( @@ -40,10 +37,6 @@ class WithoutServerView extends React.Component { ) }) - static propTypes = { - theme: PropTypes.string - } - render() { const { theme } = this.props; return (