Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix : migrate root component to react functional component #6033

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
4 changes: 2 additions & 2 deletions .storybook/storybook.requires.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ const normalizedStories = [
directory: "./app",
files: "**/*.stories.?(ts|tsx|js|jsx)",
importPathMatcher:
/^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/,
/^\.(?:(?:^|[\\/]|(?:(?:(?!(?:^|[\\/])\.).)*?)[\\/])(?!\.)(?=.)[^\\/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/,
// @ts-ignore
req: require.context(
"../app",
true,
/^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/
/^\.(?:(?:^|[\\/]|(?:(?:(?!(?:^|[\\/])\.).)*?)[\\/])(?!\.)(?=.)[^\\/]*?\.stories\.(?:ts|tsx|js|jsx)?)$/
),
},
];
Expand Down
253 changes: 125 additions & 128 deletions app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { Dimensions, EmitterSubscription, Linking } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { SafeAreaProvider, initialWindowMetrics } from 'react-native-safe-area-context';
Expand All @@ -17,7 +17,7 @@ import { IThemePreference } from './definitions/ITheme';
import { DimensionsContext } from './dimensions';
import { MIN_WIDTH_MASTER_DETAIL_LAYOUT, colors, isFDroidBuild, themes } from './lib/constants';
import { getAllowAnalyticsEvents, getAllowCrashReport } from './lib/methods';
import { debounce, isTablet } from './lib/methods/helpers';
import { isTablet, useDebounce } from './lib/methods/helpers';
import { toggleAnalyticsEventsReport, toggleCrashErrorsReport } from './lib/methods/helpers/log';
import parseQuery from './lib/methods/helpers/parseQuery';
import {
Expand Down Expand Up @@ -78,163 +78,160 @@ const parseDeepLinking = (url: string) => {
return null;
};

export default class Root extends React.Component<{}, IState> {
private listenerTimeout!: any;
private dimensionsListener?: EmitterSubscription;
const Root: React.FC = () => {
const [dimensions, setDimensions] = useState<IDimensions>(Dimensions.get('window'));
const [state, setState] = useState<{ themePreferences: IThemePreference; theme: TSupportedThemes }>({
themePreferences: initialTheme(),
theme: getTheme(initialTheme())
});
const initTheme = initialTheme();
const [themeState, setThemeState] = useState<IState>({
...state,
width: dimensions.width,
height: dimensions.height,
scale: dimensions.scale,
fontScale: dimensions.fontScale
});

useEffect(() => {
const init = async () => {
store.dispatch(appInitLocalSettings());

// Open app from push notification
const notification = await initializePushNotifications();
if (notification) {
onNotification(notification);
return;
}

await getInitialNotification();

// Open app from deep linking
const deepLinking = await Linking.getInitialURL();
const parsedDeepLinkingURL = parseDeepLinking(deepLinking!);
if (parsedDeepLinkingURL) {
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
return;
}

// Open app from app icon
store.dispatch(appInit());
};

const initTablet = () => {
const { width } = themeState;
setMasterDetail(width);
};

init();

constructor(props: any) {
super(props);
this.init();
if (!isFDroidBuild) {
this.initCrashReport();
initCrashReport();
}
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();
initTablet();
}
setNativeTheme(theme);
}

componentDidMount() {
this.listenerTimeout = setTimeout(() => {
setNativeTheme(initTheme);

const timeout = setTimeout(() => {
Linking.addEventListener('url', ({ url }) => {
const parsedDeepLinkingURL = parseDeepLinking(url);
if (parsedDeepLinkingURL) {
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
}
});
}, 5000);
this.dimensionsListener = Dimensions.addEventListener('change', this.onDimensionsChange);
}

componentWillUnmount() {
clearTimeout(this.listenerTimeout);
this.dimensionsListener?.remove?.();

unsubscribeTheme();
}

init = async () => {
store.dispatch(appInitLocalSettings());
const dimensionsListener: EmitterSubscription = Dimensions.addEventListener('change', onDimensionsChange);

// Open app from push notification
const notification = await initializePushNotifications();
if (notification) {
onNotification(notification);
return;
}

await getInitialNotification();

// Open app from deep linking
const deepLinking = await Linking.getInitialURL();
const parsedDeepLinkingURL = parseDeepLinking(deepLinking!);
if (parsedDeepLinkingURL) {
store.dispatch(deepLinkingOpen(parsedDeepLinkingURL));
return;
}

// Open app from app icon
store.dispatch(appInit());
};
return () => {
clearTimeout(timeout);
dimensionsListener.remove();
unsubscribeTheme();
};
}, []);

getMasterDetail = (width: number) => {
if (!isTablet) {
return false;
}
return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT;
};
useEffect(() => {
subscribeTheme(themePreferences, () => setTheme(themePreferences));
}, [themeState.themePreferences]);

setMasterDetail = (width: number) => {
const isMasterDetail = this.getMasterDetail(width);
store.dispatch(setMasterDetailAction(isMasterDetail));
const initCrashReport = () => {
getAllowCrashReport().then(allowCrashReport => {
toggleCrashErrorsReport(allowCrashReport);
});
getAllowAnalyticsEvents().then(allowAnalyticsEvents => {
toggleAnalyticsEventsReport(allowAnalyticsEvents);
});
};

// Dimensions update fires twice
onDimensionsChange = debounce(({ window: { width, height, scale, fontScale } }: { window: IDimensions }) => {
this.setDimensions({
const onDimensionsChange = useDebounce(({ window: { width, height, scale, fontScale } }: { window: IDimensions }) => {
setDimensions({
width,
height,
scale,
fontScale
});
this.setMasterDetail(width);
});
setMasterDetail(width);
}, 300); // TODO : review the wait value

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 setTheme = (newTheme?: {} | undefined) => {
if (!newTheme) {
return;
}

setDimensions = ({ width, height, scale, fontScale }: IDimensions) => {
this.setState({ width, height, scale, fontScale });
// Typecast newTheme to IThemePreference
setState(prevState => newThemeState(prevState, newTheme as IThemePreference));
setThemeState(prevstate => ({ ...prevstate, ...state }));
};

initTablet = () => {
const { width } = this.state;
this.setMasterDetail(width);
const getMasterDetail = (width: number) => {
if (!isTablet) {
return false;
}
return width > MIN_WIDTH_MASTER_DETAIL_LAYOUT;
};

initCrashReport = () => {
getAllowCrashReport().then(allowCrashReport => {
toggleCrashErrorsReport(allowCrashReport);
});
getAllowAnalyticsEvents().then(allowAnalyticsEvents => {
toggleAnalyticsEventsReport(allowAnalyticsEvents);
});
const setMasterDetail = (width: number) => {
const isMasterDetail = getMasterDetail(width);
store.dispatch(setMasterDetailAction(isMasterDetail));
};

render() {
const { themePreferences, theme, width, height, scale, fontScale } = this.state;
return (
<SafeAreaProvider initialMetrics={initialWindowMetrics} style={{ backgroundColor: themes[this.state.theme].surfaceRoom }}>
<Provider store={store}>
<ThemeContext.Provider
const { themePreferences, theme, width, height, scale, fontScale } = themeState;
return (
<SafeAreaProvider initialMetrics={initialWindowMetrics} style={{ backgroundColor: themes[themeState.theme].surfaceRoom }}>
<Provider store={store}>
<ThemeContext.Provider
value={{
theme,
themePreferences,
setTheme,
colors: colors[theme]
}}>
<DimensionsContext.Provider
value={{
theme,
themePreferences,
setTheme: this.setTheme,
colors: colors[theme]
width,
height,
scale,
fontScale,
setDimensions
}}>
<DimensionsContext.Provider
value={{
width,
height,
scale,
fontScale,
setDimensions: this.setDimensions
}}>
<GestureHandlerRootView>
<ActionSheetProvider>
<AppContainer />
<TwoFactor />
<ScreenLockedView />
<ChangePasscodeView />
<InAppNotification />
<Toast />
<Loading />
</ActionSheetProvider>
</GestureHandlerRootView>
</DimensionsContext.Provider>
</ThemeContext.Provider>
</Provider>
</SafeAreaProvider>
);
}
}
<GestureHandlerRootView>
<ActionSheetProvider>
<AppContainer />
<TwoFactor />
<ScreenLockedView />
<ChangePasscodeView />
<InAppNotification />
<Toast />
<Loading />
</ActionSheetProvider>
</GestureHandlerRootView>
</DimensionsContext.Provider>
</ThemeContext.Provider>
</Provider>
</SafeAreaProvider>
);
};
export default Root;