diff --git a/src/hooks/useAutoUpdateTimezone.ts b/src/hooks/useAutoUpdateTimezone.ts deleted file mode 100644 index 9c453a8d89cf..000000000000 --- a/src/hooks/useAutoUpdateTimezone.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {useEffect} from 'react'; -import {updateAutomaticTimezone} from '@userActions/PersonalDetails'; -import type {SelectedTimezone} from '@src/types/onyx/PersonalDetails'; -import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; - -const useAutoUpdateTimezone = () => { - const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const timezone = currentUserPersonalDetails?.timezone ?? {}; - useEffect(() => { - const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone as SelectedTimezone; - const hasValidCurrentTimezone = typeof currentTimezone === 'string' && currentTimezone.trim().length > 0; - - if (hasValidCurrentTimezone && timezone?.automatic && timezone?.selected !== currentTimezone) { - updateAutomaticTimezone({ - automatic: true, - selected: currentTimezone, - }); - } - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [timezone?.automatic, timezone?.selected]); -}; - -export default useAutoUpdateTimezone; diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 256e933ac0ee..a970bee28857 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -2,6 +2,8 @@ import type {RouteProp} from '@react-navigation/native'; import {useNavigation} from '@react-navigation/native'; import type {StackCardInterpolationProps} from '@react-navigation/stack'; import React, {memo, useContext, useEffect, useMemo, useRef, useState} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; import ComposeProviders from '@components/ComposeProviders'; import DelegateNoAccessModalProvider from '@components/DelegateNoAccessModalProvider'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; @@ -12,7 +14,6 @@ import PriorityModeController from '@components/PriorityModeController'; import {SearchContextProvider} from '@components/Search/SearchContext'; import {useSearchRouterContext} from '@components/Search/SearchRouter/SearchRouterContext'; import SearchRouterModal from '@components/Search/SearchRouter/SearchRouterModal'; -import useAutoUpdateTimezone from '@hooks/useAutoUpdateTimezone'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useOnboardingFlowRouter from '@hooks/useOnboardingFlow'; import useOnyx from '@hooks/useOnyx'; @@ -50,6 +51,7 @@ import WorkspacesListPage from '@pages/workspace/WorkspacesListPage'; import * as App from '@userActions/App'; import * as Download from '@userActions/Download'; import * as Modal from '@userActions/Modal'; +import * as PersonalDetails from '@userActions/PersonalDetails'; import * as Report from '@userActions/Report'; import * as Session from '@userActions/Session'; import * as User from '@userActions/User'; @@ -61,6 +63,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; +import type {SelectedTimezone, Timezone} from '@src/types/onyx/PersonalDetails'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type ReactComponentModule from '@src/types/utils/ReactComponentModule'; import attachmentModalScreenOptions from './attachmentModalScreenOptions'; import createRootStackNavigator from './createRootStackNavigator'; @@ -105,8 +109,58 @@ function initializePusher() { User.subscribeToUserEvents(); }); } +let timezone: Timezone | null; +let currentAccountID = -1; +let lastUpdateIDAppliedToClient: OnyxEntry; + +Onyx.connect({ + key: ONYXKEYS.SESSION, + callback: (value) => { + // When signed out, val hasn't accountID + if (!(value && 'accountID' in value)) { + currentAccountID = -1; + timezone = null; + return; + } + + currentAccountID = value.accountID ?? CONST.DEFAULT_NUMBER_ID; + + if (Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) { + // This means sign in in RHP was successful, so we can subscribe to user events + initializePusher(); + } + }, +}); + +Onyx.connect({ + key: ONYXKEYS.PERSONAL_DETAILS_LIST, + callback: (value) => { + if (!value || !isEmptyObject(timezone)) { + return; + } + + timezone = value?.[currentAccountID]?.timezone ?? {}; + const currentTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone as SelectedTimezone; + + // If the current timezone is different than the user's timezone, and their timezone is set to automatic + // then update their timezone. + if (!isEmptyObject(currentTimezone) && timezone?.automatic && timezone?.selected !== currentTimezone) { + PersonalDetails.updateAutomaticTimezone({ + automatic: true, + selected: currentTimezone, + }); + } + }, +}); + +Onyx.connect({ + key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, + callback: (value) => { + lastUpdateIDAppliedToClient = value; + }, +}); -function handleNetworkReconnect(lastUpdateIDAppliedToClient: number | undefined, isLoadingApp: boolean) { +function handleNetworkReconnect(isLoadingApp: boolean) { if (isLoadingApp) { App.openApp(); } else { @@ -158,7 +212,6 @@ function AuthScreens() { const {shouldUseNarrowLayout} = useResponsiveLayout(); const rootNavigatorScreenOptions = useRootNavigatorScreenOptions(); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [lastUpdateIDAppliedToClient] = useOnyx(ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, {canBeMissing: true}); const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const {toggleSearch} = useSearchRouterContext(); const currentUrl = getCurrentUrl(); @@ -200,17 +253,6 @@ function AuthScreens() { ); }, [onboardingCompanySize, isOnboardingCompleted, isOnboardingLoading, prevIsOnboardingLoading]); - useEffect(() => { - if (!Navigation.isActiveRoute(ROUTES.SIGN_IN_MODAL)) { - return; - } - // This means sign in in RHP was successful, so we can subscribe to user events - initializePusher(); - // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps - }, [session]); - - useAutoUpdateTimezone(); - useEffect(() => { NavBarManager.setButtonStyle(theme.navigationBarButtonsStyle); @@ -248,7 +290,7 @@ function AuthScreens() { } NetworkConnection.listenForReconnect(); - NetworkConnection.onReconnect(() => handleNetworkReconnect(lastUpdateIDAppliedToClient, !!isLoadingApp)); + NetworkConnection.onReconnect(() => handleNetworkReconnect(!!isLoadingApp)); PusherConnectionManager.init(); initializePusher(); // Sometimes when we transition from old dot to new dot, the client is not the leader diff --git a/tests/unit/useAutoUpdateTimezoneTest.tsx b/tests/unit/useAutoUpdateTimezoneTest.tsx deleted file mode 100644 index 00034636f7f5..000000000000 --- a/tests/unit/useAutoUpdateTimezoneTest.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import {render} from '@testing-library/react-native'; -import React from 'react'; -import useAutoUpdateTimezone from '@hooks/useAutoUpdateTimezone'; -import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; -// eslint-disable-next-line no-restricted-syntax -import * as PersonalDetails from '@userActions/PersonalDetails'; - -jest.mock('@hooks/useCurrentUserPersonalDetails'); - -describe('useAutoUpdateTimezone', () => { - const mockUseCurrentUserPersonalDetails = useCurrentUserPersonalDetails as jest.MockedFunction; - const updateAutomaticTimezoneSpy = jest.spyOn(PersonalDetails, 'updateAutomaticTimezone'); - - const originalDateTimeFormat = Intl.DateTimeFormat; - - function setSystemTimezone(tz: string | null) { - // @ts-expect-error overriding for tests - Intl.DateTimeFormat = jest.fn().mockReturnValue({ - resolvedOptions: () => ({timeZone: tz}), - }); - } - - function TestComponent() { - useAutoUpdateTimezone(); - return null; - } - - beforeEach(() => { - jest.clearAllMocks(); - setSystemTimezone('America/New_York'); - }); - - afterAll(() => { - Intl.DateTimeFormat = originalDateTimeFormat; - }); - - it('updates timezone when automatic and mismatch', () => { - setSystemTimezone('America/Los_Angeles'); - mockUseCurrentUserPersonalDetails.mockReturnValue({ - accountID: 1, - timezone: { - automatic: true, - selected: 'Europe/Warsaw', - }, - } as unknown as ReturnType); - - render(); - - expect(updateAutomaticTimezoneSpy).toHaveBeenCalledWith({ - automatic: true, - selected: 'America/Los_Angeles', - }); - }); - - it('does not call update when selected matches system timezone', () => { - mockUseCurrentUserPersonalDetails.mockReturnValue({ - accountID: 1, - timezone: { - automatic: true, - selected: 'America/New_York', - }, - } as unknown as ReturnType); - - render(); - - expect(updateAutomaticTimezoneSpy).not.toHaveBeenCalled(); - }); - - it('does not call update when automatic is false', () => { - mockUseCurrentUserPersonalDetails.mockReturnValue({ - accountID: 1, - timezone: { - automatic: false, - selected: 'America/Los_Angeles', - }, - } as unknown as ReturnType); - - render(); - - expect(updateAutomaticTimezoneSpy).not.toHaveBeenCalled(); - }); - - it('does not call update when system timezone is invalid', () => { - setSystemTimezone(''); - mockUseCurrentUserPersonalDetails.mockReturnValue({ - accountID: 1, - timezone: { - automatic: true, - selected: 'America/Los_Angeles', - }, - } as unknown as ReturnType); - - render(); - - expect(updateAutomaticTimezoneSpy).not.toHaveBeenCalled(); - }); -});