diff --git a/src/App.tsx b/src/App.tsx
index cbe5948f8d4e..0e247d5faa53 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -83,7 +83,6 @@ function App({url}: AppProps) {
- {/* @ts-expect-error TODO: Remove this once Expensify (https://github.com/Expensify/App/issues/25231) is migrated to TypeScript. */}
diff --git a/src/Expensify.js b/src/Expensify.tsx
similarity index 64%
rename from src/Expensify.js
rename to src/Expensify.tsx
index dfb59a0f8848..f822862ec434 100644
--- a/src/Expensify.js
+++ b/src/Expensify.tsx
@@ -1,9 +1,8 @@
-import lodashGet from 'lodash/get';
-import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react';
+import type {NativeEventSubscription} from 'react-native';
import {AppState, Linking} from 'react-native';
+import type {OnyxEntry} from 'react-native-onyx';
import Onyx, {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
import ConfirmModal from './components/ConfirmModal';
import DeeplinkWrapper from './components/DeeplinkWrapper';
import EmojiPicker from './components/EmojiPicker/EmojiPicker';
@@ -12,14 +11,13 @@ import GrowlNotification from './components/GrowlNotification';
import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper';
import SplashScreenHider from './components/SplashScreenHider';
import UpdateAppModal from './components/UpdateAppModal';
-import withLocalize, {withLocalizePropTypes} from './components/withLocalize';
import CONST from './CONST';
+import useLocalize from './hooks/useLocalize';
import * as EmojiPickerAction from './libs/actions/EmojiPickerAction';
import * as Report from './libs/actions/Report';
import * as User from './libs/actions/User';
import * as ActiveClientManager from './libs/ActiveClientManager';
import BootSplash from './libs/BootSplash';
-import compose from './libs/compose';
import * as Growl from './libs/Growl';
import Log from './libs/Log';
import migrateOnyx from './libs/migrateOnyx';
@@ -27,16 +25,18 @@ import Navigation from './libs/Navigation/Navigation';
import NavigationRoot from './libs/Navigation/NavigationRoot';
import NetworkConnection from './libs/NetworkConnection';
import PushNotification from './libs/Notification/PushNotification';
-// eslint-disable-next-line no-unused-vars
-import subscribePushNotification from './libs/Notification/PushNotification/subscribePushNotification';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import './libs/Notification/PushNotification/subscribePushNotification';
import StartupTimer from './libs/StartupTimer';
// This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection
-// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
import UnreadIndicatorUpdater from './libs/UnreadIndicatorUpdater';
import Visibility from './libs/Visibility';
import ONYXKEYS from './ONYXKEYS';
import PopoverReportActionContextMenu from './pages/home/report/ContextMenu/PopoverReportActionContextMenu';
import * as ReportActionContextMenu from './pages/home/report/ContextMenu/ReportActionContextMenu';
+import type {Route} from './ROUTES';
+import type {ScreenShareRequest, Session} from './types/onyx';
Onyx.registerLogger(({level, message}) => {
if (level === 'alert') {
@@ -47,82 +47,63 @@ Onyx.registerLogger(({level, message}) => {
}
});
-const propTypes = {
- /* Onyx Props */
+type ExpensifyOnyxProps = {
+ /** Whether the app is waiting for the server's response to determine if a room is public */
+ isCheckingPublicRoom: OnyxEntry;
/** Session info for the currently logged in user. */
- session: PropTypes.shape({
- /** Currently logged in user authToken */
- authToken: PropTypes.string,
-
- /** Currently logged in user accountID */
- accountID: PropTypes.number,
- }),
+ session: OnyxEntry;
/** Whether a new update is available and ready to install. */
- updateAvailable: PropTypes.bool,
+ updateAvailable: OnyxEntry;
- /** Tells us if the sidebar has rendered - TODO: We don't use it as temporary solution to fix not hidding splashscreen */
- // eslint-disable-next-line react/no-unused-prop-types
- isSidebarLoaded: PropTypes.bool,
+ /** Tells us if the sidebar has rendered */
+ isSidebarLoaded: OnyxEntry;
/** Information about a screen share call requested by a GuidesPlus agent */
- screenShareRequest: PropTypes.shape({
- /** Access token required to join a screen share room, generated by the backend */
- accessToken: PropTypes.string,
-
- /** Name of the screen share room to join */
- roomName: PropTypes.string,
- }),
-
- /** Whether the app is waiting for the server's response to determine if a room is public */
- isCheckingPublicRoom: PropTypes.bool,
+ screenShareRequest: OnyxEntry;
/** True when the user must update to the latest minimum version of the app */
- updateRequired: PropTypes.bool,
+ updateRequired: OnyxEntry;
/** Whether we should display the notification alerting the user that focus mode has been auto-enabled */
- focusModeNotification: PropTypes.bool,
+ focusModeNotification: OnyxEntry;
/** Last visited path in the app */
- lastVisitedPath: PropTypes.string,
-
- ...withLocalizePropTypes,
+ lastVisitedPath: OnyxEntry;
};
-const defaultProps = {
- session: {
- authToken: null,
- accountID: null,
- },
- updateAvailable: false,
- isSidebarLoaded: false,
- screenShareRequest: null,
- isCheckingPublicRoom: true,
- updateRequired: false,
- focusModeNotification: false,
- lastVisitedPath: undefined,
-};
+type ExpensifyProps = ExpensifyOnyxProps;
const SplashScreenHiddenContext = React.createContext({});
-function Expensify(props) {
- const appStateChangeListener = useRef(null);
+function Expensify({
+ isCheckingPublicRoom = true,
+ session,
+ updateAvailable,
+ isSidebarLoaded = false,
+ screenShareRequest,
+ updateRequired = false,
+ focusModeNotification = false,
+ lastVisitedPath,
+}: ExpensifyProps) {
+ const appStateChangeListener = useRef(null);
const [isNavigationReady, setIsNavigationReady] = useState(false);
const [isOnyxMigrated, setIsOnyxMigrated] = useState(false);
const [isSplashHidden, setIsSplashHidden] = useState(false);
const [hasAttemptedToOpenPublicRoom, setAttemptedToOpenPublicRoom] = useState(false);
- const [initialUrl, setInitialUrl] = useState(null);
+ const {translate} = useLocalize();
+ const [initialUrl, setInitialUrl] = useState(null);
useEffect(() => {
- if (props.isCheckingPublicRoom) {
+ if (isCheckingPublicRoom) {
return;
}
setAttemptedToOpenPublicRoom(true);
- }, [props.isCheckingPublicRoom]);
+ }, [isCheckingPublicRoom]);
- const isAuthenticated = useMemo(() => Boolean(lodashGet(props.session, 'authToken', null)), [props.session]);
- const autoAuthState = useMemo(() => lodashGet(props.session, 'autoAuthState', ''), [props.session]);
+ const isAuthenticated = useMemo(() => !!(session?.authToken ?? null), [session]);
+ const autoAuthState = useMemo(() => session?.autoAuthState ?? '', [session]);
const contextValue = useMemo(
() => ({
@@ -168,8 +149,16 @@ function Expensify(props) {
Log.info('[BootSplash] splash screen status', false, {appState, status});
if (status === 'visible') {
- const propsToLog = _.omit(props, ['children', 'session']);
- propsToLog.isAuthenticated = isAuthenticated;
+ const propsToLog: Omit = {
+ isCheckingPublicRoom,
+ updateRequired,
+ updateAvailable,
+ isSidebarLoaded,
+ screenShareRequest,
+ focusModeNotification,
+ isAuthenticated,
+ lastVisitedPath,
+ };
Log.alert('[BootSplash] splash screen is still visible', {propsToLog}, false);
}
});
@@ -194,7 +183,7 @@ function Expensify(props) {
// If the app is opened from a deep link, get the reportID (if exists) from the deep link and navigate to the chat report
Linking.getInitialURL().then((url) => {
setInitialUrl(url);
- Report.openReportFromDeepLink(url, isAuthenticated);
+ Report.openReportFromDeepLink(url ?? '', isAuthenticated);
});
// Open chat report from a deep link (only mobile native)
@@ -216,7 +205,7 @@ function Expensify(props) {
return null;
}
- if (props.updateRequired) {
+ if (updateRequired) {
throw new Error(CONST.ERROR.UPDATE_REQUIRED);
}
@@ -231,20 +220,19 @@ function Expensify(props) {
{/* We include the modal for showing a new update at the top level so the option is always present. */}
- {/* If the update is required we won't show this option since a full screen update view will be displayed instead. */}
- {props.updateAvailable && !props.updateRequired ? : null}
- {props.screenShareRequest ? (
+ {updateAvailable && !updateRequired ? : null}
+ {screenShareRequest ? (
User.joinScreenShare(props.screenShareRequest.accessToken, props.screenShareRequest.roomName)}
+ title={translate('guides.screenShare')}
+ onConfirm={() => User.joinScreenShare(screenShareRequest.accessToken, screenShareRequest.roomName)}
onCancel={User.clearScreenShareRequest}
- prompt={props.translate('guides.screenShareRequest')}
- confirmText={props.translate('common.join')}
- cancelText={props.translate('common.decline')}
+ prompt={translate('guides.screenShareRequest')}
+ confirmText={translate('common.join')}
+ cancelText={translate('common.decline')}
isVisible
/>
) : null}
- {props.focusModeNotification ? : null}
+ {focusModeNotification ? : null}
>
)}
@@ -254,7 +242,7 @@ function Expensify(props) {
@@ -265,40 +253,35 @@ function Expensify(props) {
);
}
-Expensify.propTypes = propTypes;
-Expensify.defaultProps = defaultProps;
-export default compose(
- withLocalize,
- withOnyx({
- isCheckingPublicRoom: {
- key: ONYXKEYS.IS_CHECKING_PUBLIC_ROOM,
- initWithStoredValues: false,
- },
- session: {
- key: ONYXKEYS.SESSION,
- },
- updateAvailable: {
- key: ONYXKEYS.UPDATE_AVAILABLE,
- initWithStoredValues: false,
- },
- isSidebarLoaded: {
- key: ONYXKEYS.IS_SIDEBAR_LOADED,
- },
- screenShareRequest: {
- key: ONYXKEYS.SCREEN_SHARE_REQUEST,
- },
- updateRequired: {
- key: ONYXKEYS.UPDATE_REQUIRED,
- initWithStoredValues: false,
- },
- focusModeNotification: {
- key: ONYXKEYS.FOCUS_MODE_NOTIFICATION,
- initWithStoredValues: false,
- },
- lastVisitedPath: {
- key: ONYXKEYS.LAST_VISITED_PATH,
- },
- }),
-)(Expensify);
+export default withOnyx({
+ isCheckingPublicRoom: {
+ key: ONYXKEYS.IS_CHECKING_PUBLIC_ROOM,
+ initWithStoredValues: false,
+ },
+ session: {
+ key: ONYXKEYS.SESSION,
+ },
+ updateAvailable: {
+ key: ONYXKEYS.UPDATE_AVAILABLE,
+ initWithStoredValues: false,
+ },
+ updateRequired: {
+ key: ONYXKEYS.UPDATE_REQUIRED,
+ initWithStoredValues: false,
+ },
+ isSidebarLoaded: {
+ key: ONYXKEYS.IS_SIDEBAR_LOADED,
+ },
+ screenShareRequest: {
+ key: ONYXKEYS.SCREEN_SHARE_REQUEST,
+ },
+ focusModeNotification: {
+ key: ONYXKEYS.FOCUS_MODE_NOTIFICATION,
+ initWithStoredValues: false,
+ },
+ lastVisitedPath: {
+ key: ONYXKEYS.LAST_VISITED_PATH,
+ },
+})(Expensify);
export {SplashScreenHiddenContext};
diff --git a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx
index 0b4154a15e80..47cbc559a67b 100644
--- a/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx
+++ b/src/pages/home/report/ContextMenu/PopoverReportActionContextMenu.tsx
@@ -31,7 +31,7 @@ function extractPointerEvent(event: GestureResponderEvent | MouseEvent): MouseEv
}
// eslint-disable-next-line @typescript-eslint/naming-convention
-function PopoverReportActionContextMenu(_props: never, ref: ForwardedRef) {
+function PopoverReportActionContextMenu(_props: unknown, ref: ForwardedRef) {
const {translate} = useLocalize();
const reportIDRef = useRef('0');
const typeRef = useRef();