diff --git a/app/index.tsx b/app/index.tsx index 88c3a1e3887..0af3fb9825e 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Dimensions, Linking } from 'react-native'; import { KeyCommandsEmitter } from 'react-native-keycommands'; import { initialWindowMetrics, SafeAreaProvider } from 'react-native-safe-area-context'; @@ -24,13 +24,13 @@ import parseQuery from './lib/methods/helpers/parseQuery'; import { initializePushNotifications, onNotification } from './lib/notifications'; import store from './lib/store'; import { initStore } from './lib/store/auxStore'; -import { ThemeContext, TSupportedThemes } from './theme'; +import { ThemeContext } from './theme'; import { debounce, isTablet } from './lib/methods/helpers'; import EventEmitter from './lib/methods/helpers/events'; import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './lib/methods/helpers/log'; import { getTheme, - initialTheme, + initialTheme as getInitialTheme, newThemeState, setNativeTheme, subscribeTheme, @@ -49,16 +49,7 @@ interface IDimensions { fontScale: number; } -interface IState { - theme: TSupportedThemes; - themePreferences: IThemePreference; - width: number; - height: number; - scale: number; - fontScale: number; -} - -const parseDeepLinking = (url: string) => { +const parseDeepLinking = (url: string | null) => { if (url) { url = url.replace(/rocketchat:\/\/|https:\/\/go.rocket.chat\//, ''); const regex = /^(room|auth|invite)\?/; @@ -81,35 +72,54 @@ const parseDeepLinking = (url: string) => { return null; }; -export default class Root extends React.Component<{}, IState> { - private listenerTimeout!: any; +const setMasterDetail = (width: number) => { + const isMasterDetail = getMasterDetail(width); + store.dispatch(setMasterDetailAction(isMasterDetail)); +}; + +const getMasterDetail = (width: number) => { + if (!isTablet) { + return false; + } + return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT; +}; + +const { width: widthWindow, height: heightWindow, scale: scaleWindow, fontScale: fontScaleWindow } = Dimensions.get('window'); +const initialThemePreference = getInitialTheme(); +const initialTheme = getTheme(initialThemePreference); + +const Root = () => { + const [theme, setTheme] = useState(initialTheme); + const [themePreferences, setThemePreferences] = useState(initialThemePreference); + const [dimensions, setDimensions] = useState({ + width: widthWindow, + height: heightWindow, + scale: scaleWindow, + fontScale: fontScaleWindow + }); - private onKeyCommands: any; + const listenerTimeout = useRef>(); + const onKeyCommands = useRef(); - constructor(props: any) { - super(props); - this.init(); + useEffect(() => { + init(); if (!isFDroidBuild) { - this.initCrashReport(); + getAllowCrashReport().then(allowCrashReport => { + toggleCrashErrorsReport(allowCrashReport); + }); + getAllowAnalyticsEvents().then(allowAnalyticsEvents => { + toggleAnalyticsEventsReport(allowAnalyticsEvents); + }); } - const { width, height, scale, fontScale } = Dimensions.get('window'); - const theme = initialTheme(); - this.state = { - theme: getTheme(theme), - themePreferences: theme, - width, - height, - scale, - fontScale - }; if (isTablet) { - this.initTablet(); + setMasterDetail(widthWindow); + onKeyCommands.current = KeyCommandsEmitter.addListener('onKeyCommand', (command: ICommand) => { + EventEmitter.emit(KEY_COMMAND, { event: command }); + }); } - setNativeTheme(theme); - } + setNativeTheme(initialThemePreference); - componentDidMount() { - this.listenerTimeout = setTimeout(() => { + listenerTimeout.current = setTimeout(() => { Linking.addEventListener('url', ({ url }) => { const parsedDeepLinkingURL = parseDeepLinking(url); if (parsedDeepLinkingURL) { @@ -117,21 +127,21 @@ export default class Root extends React.Component<{}, IState> { } }); }, 5000); - Dimensions.addEventListener('change', this.onDimensionsChange); - } + const dimensionSubscription = Dimensions.addEventListener('change', onDimensionsChange); - componentWillUnmount() { - clearTimeout(this.listenerTimeout); - Dimensions.removeEventListener('change', this.onDimensionsChange); + return () => { + clearTimeout(listenerTimeout.current as ReturnType); + dimensionSubscription.remove(); - unsubscribeTheme(); + unsubscribeTheme(); - if (this.onKeyCommands && this.onKeyCommands.remove) { - this.onKeyCommands.remove(); - } - } + if (onKeyCommands?.current?.remove) { + onKeyCommands.current.remove?.(); + } + }; + }, []); - init = async () => { + const init = useCallback(async () => { store.dispatch(appInitLocalSettings()); // Open app from push notification @@ -143,7 +153,7 @@ export default class Root extends React.Component<{}, IState> { // 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; @@ -151,104 +161,54 @@ export default class Root extends React.Component<{}, IState> { // Open app from app icon store.dispatch(appInit()); - }; - - getMasterDetail = (width: number) => { - if (!isTablet) { - return false; - } - return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT; - }; - - setMasterDetail = (width: number) => { - const isMasterDetail = this.getMasterDetail(width); - store.dispatch(setMasterDetailAction(isMasterDetail)); - }; + }, []); // Dimensions update fires twice - onDimensionsChange = debounce(({ window: { width, height, scale, fontScale } }: { window: IDimensions }) => { - this.setDimensions({ - width, - height, - scale, - fontScale - }); - this.setMasterDetail(width); + const onDimensionsChange = debounce(({ window }: { window: IDimensions }) => { + setDimensions(window); + setMasterDetail(window.width); }); - setTheme = (newTheme = {}) => { - // change theme state - this.setState( - prevState => newThemeState(prevState, newTheme as IThemePreference), - () => { - const { themePreferences } = this.state; - // subscribe to Appearance changes - subscribeTheme(themePreferences, this.setTheme); - } - ); + const setNewTheme = (newThemePreference?: IThemePreference) => { + const { theme: newTheme, themePreferences: newThemePreferences } = newThemeState(themePreferences, newThemePreference); + setThemePreferences(newThemePreferences); + setTheme(newTheme); + subscribeTheme(themePreferences, setNewTheme); }; - setDimensions = ({ width, height, scale, fontScale }: IDimensions) => { - this.setState({ width, height, scale, fontScale }); - }; - - initTablet = () => { - const { width } = this.state; - this.setMasterDetail(width); - this.onKeyCommands = KeyCommandsEmitter.addListener('onKeyCommand', (command: ICommand) => { - EventEmitter.emit(KEY_COMMAND, { event: command }); - }); - }; - - initCrashReport = () => { - getAllowCrashReport().then(allowCrashReport => { - toggleCrashErrorsReport(allowCrashReport); - }); - getAllowAnalyticsEvents().then(allowAnalyticsEvents => { - toggleAnalyticsEventsReport(allowAnalyticsEvents); - }); - }; - - render() { - const { themePreferences, theme, width, height, scale, fontScale } = this.state; - return ( - - - + + + - - - - - - - - - - - - - - - - - ); - } -} + + + + + + + + + + + + + + + + ); +}; + +export default Root; diff --git a/app/lib/methods/helpers/theme.ts b/app/lib/methods/helpers/theme.ts index e12dd971786..bfc8fca3d6e 100644 --- a/app/lib/methods/helpers/theme.ts +++ b/app/lib/methods/helpers/theme.ts @@ -36,10 +36,10 @@ export const getTheme = (themePreferences: IThemePreference): TSupportedThemes = return theme === 'dark' ? darkLevel : 'light'; }; -export const newThemeState = (prevState: { themePreferences: IThemePreference }, newTheme: IThemePreference) => { +export const newThemeState = (prevThemePreferences: IThemePreference, newTheme?: IThemePreference) => { // new theme preferences const themePreferences = { - ...prevState.themePreferences, + ...prevThemePreferences, ...newTheme }; // set new state of themePreferences diff --git a/app/theme.tsx b/app/theme.tsx index b11488119df..b35f7661537 100644 --- a/app/theme.tsx +++ b/app/theme.tsx @@ -11,7 +11,7 @@ export type TColors = typeof colors[TSupportedThemes]; export interface IThemeContextProps { theme: TSupportedThemes; themePreferences?: IThemePreference; - setTheme?: (newTheme?: {}) => void; + setTheme?: (newTheme?: IThemePreference) => void; colors: TColors; }