diff --git a/native/navigation/navigation-handler.react.js b/native/navigation/navigation-handler.react.js
index bfcd60fbe..5e33e499a 100644
--- a/native/navigation/navigation-handler.react.js
+++ b/native/navigation/navigation-handler.react.js
@@ -1,87 +1,89 @@
// @flow
import * as React from 'react';
import { cookieSelector } from 'lib/selectors/keyserver-selectors.js';
import { isLoggedIn } from 'lib/selectors/user-selectors.js';
import { logInActionType, logOutActionType } from './action-types.js';
import ModalPruner from './modal-pruner.react.js';
import NavFromReduxHandler from './nav-from-redux-handler.react.js';
import { useIsAppLoggedIn } from './nav-selectors.js';
import { NavContext, type NavAction } from './navigation-context.js';
import PolicyAcknowledgmentHandler from './policy-acknowledgment-handler.react.js';
import ThreadScreenTracker from './thread-screen-tracker.react.js';
+import { MissingRegistrationDataHandler } from '../account/registration/missing-registration-data/missing-registration-data-handler.react.js';
import { authoritativeKeyserverID } from '../authoritative-keyserver.js';
import DevTools from '../redux/dev-tools.react.js';
import { useSelector } from '../redux/redux-utils.js';
import { usePersistedStateLoaded } from '../selectors/app-state-selectors.js';
const NavigationHandler: React.ComponentType<{}> = React.memo<{}>(
function NavigationHandler() {
const navContext = React.useContext(NavContext);
const persistedStateLoaded = usePersistedStateLoaded();
const devTools = __DEV__ ? : null;
if (!navContext || !persistedStateLoaded) {
if (__DEV__) {
return (
<>
{devTools}
>
);
} else {
return null;
}
}
const { dispatch } = navContext;
return (
<>
+
{devTools}
>
);
},
);
NavigationHandler.displayName = 'NavigationHandler';
type LogInHandlerProps = {
+dispatch: (action: NavAction) => void,
};
const LogInHandler = React.memo(function LogInHandler(
props: LogInHandlerProps,
) {
const { dispatch } = props;
const hasCurrentUserInfo = useSelector(isLoggedIn);
const cookie = useSelector(cookieSelector(authoritativeKeyserverID));
const hasUserCookie = !!(cookie && cookie.startsWith('user='));
const loggedIn = hasCurrentUserInfo && hasUserCookie;
const navLoggedIn = useIsAppLoggedIn();
const prevLoggedInRef = React.useRef();
React.useEffect(() => {
if (loggedIn === prevLoggedInRef.current) {
return;
}
prevLoggedInRef.current = loggedIn;
if (loggedIn && !navLoggedIn) {
dispatch({ type: (logInActionType: 'LOG_IN') });
} else if (!loggedIn && navLoggedIn) {
dispatch({ type: (logOutActionType: 'LOG_OUT') });
}
}, [navLoggedIn, loggedIn, dispatch]);
return null;
});
LogInHandler.displayName = 'LogInHandler';
export default NavigationHandler;
diff --git a/native/root.react.js b/native/root.react.js
index 65a1ed6e2..0c6369269 100644
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -1,394 +1,392 @@
// @flow
import { ActionSheetProvider } from '@expo/react-native-action-sheet';
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import AsyncStorage from '@react-native-async-storage/async-storage';
import type {
PossiblyStaleNavigationState,
UnsafeContainerActionEvent,
GenericNavigationAction,
} from '@react-navigation/core';
import { useReduxDevToolsExtension } from '@react-navigation/devtools';
import { NavigationContainer } from '@react-navigation/native';
import * as SplashScreen from 'expo-splash-screen';
import invariant from 'invariant';
import * as React from 'react';
import { Platform, UIManager, StyleSheet } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import Orientation from 'react-native-orientation-locker';
import {
SafeAreaProvider,
initialWindowMetrics,
} from 'react-native-safe-area-context';
import { Provider } from 'react-redux';
import { PersistGate as ReduxPersistGate } from 'redux-persist/es/integration/react.js';
import { ChatMentionContextProvider } from 'lib/components/chat-mention-provider.react.js';
import { EditUserAvatarProvider } from 'lib/components/edit-user-avatar-provider.react.js';
import { ENSCacheProvider } from 'lib/components/ens-cache-provider.react.js';
import { FarcasterDataHandler } from 'lib/components/farcaster-data-handler.react.js';
import IntegrityHandler from 'lib/components/integrity-handler.react.js';
import { MediaCacheProvider } from 'lib/components/media-cache-provider.react.js';
import { NeynarClientProvider } from 'lib/components/neynar-client-provider.react.js';
import PrekeysHandler from 'lib/components/prekeys-handler.react.js';
import { StaffContextProvider } from 'lib/components/staff-provider.react.js';
import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js';
import { IdentitySearchProvider } from 'lib/identity-search/identity-search-context.js';
import { CallKeyserverEndpointProvider } from 'lib/keyserver-conn/call-keyserver-endpoint-provider.react.js';
import KeyserverConnectionsHandler from 'lib/keyserver-conn/keyserver-connections-handler.js';
import { TunnelbrokerProvider } from 'lib/tunnelbroker/tunnelbroker-context.js';
import { actionLogger } from 'lib/utils/action-logger.js';
-import { MissingRegistrationDataHandler } from './account/registration/missing-registration-data/missing-registration-data-handler.react.js';
import { RegistrationContextProvider } from './account/registration/registration-context-provider.react.js';
import NativeEditThreadAvatarProvider from './avatars/native-edit-thread-avatar-provider.react.js';
import BackupHandler from './backup/backup-handler.js';
import { BottomSheetProvider } from './bottom-sheet/bottom-sheet-provider.react.js';
import ChatContextProvider from './chat/chat-context-provider.react.js';
import MessageEditingContextProvider from './chat/message-editing-context-provider.react.js';
import AccessTokenHandler from './components/access-token-handler.react.js';
import ConnectFarcasterAlertHandler from './components/connect-farcaster-alert-handler.react.js';
import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js';
import PersistedStateGate from './components/persisted-state-gate.js';
import ReportHandler from './components/report-handler.react.js';
import VersionSupportedChecker from './components/version-supported.react.js';
import ConnectedStatusBar from './connected-status-bar.react.js';
import { SQLiteDataHandler } from './data/sqlite-data-handler.js';
import ErrorBoundary from './error-boundary.react.js';
import IdentityServiceContextProvider from './identity-service/identity-service-context-provider.react.js';
import InputStateContainer from './input/input-state-container.react.js';
import LifecycleHandler from './lifecycle/lifecycle-handler.react.js';
import MarkdownContextProvider from './markdown/markdown-context-provider.react.js';
import { filesystemMediaCache } from './media/media-cache.js';
import { DeepLinksContextProvider } from './navigation/deep-links-context-provider.react.js';
import { defaultNavigationState } from './navigation/default-state.js';
import { setGlobalNavContext } from './navigation/icky-global.js';
import KeyserverReachabilityHandler from './navigation/keyserver-reachability-handler.js';
import {
NavContext,
type NavContextType,
} from './navigation/navigation-context.js';
import NavigationHandler from './navigation/navigation-handler.react.js';
import { validNavState } from './navigation/navigation-utils.js';
import OrientationHandler from './navigation/orientation-handler.react.js';
import { navStateAsyncStorageKey } from './navigation/persistance.js';
import RootNavigator from './navigation/root-navigator.react.js';
import ConnectivityUpdater from './redux/connectivity-updater.react.js';
import { DimensionsUpdater } from './redux/dimensions-updater.react.js';
import { getPersistor } from './redux/persist.js';
import { store } from './redux/redux-setup.js';
import { processDBStoreOperations, useSelector } from './redux/redux-utils.js';
import { RootContext } from './root-context.js';
import { MessageSearchProvider } from './search/search-provider.react.js';
import Socket from './socket.react.js';
import { useLoadCommFonts } from './themes/fonts.js';
import { DarkTheme, LightTheme } from './themes/navigation.js';
import ThemeHandler from './themes/theme-handler.react.js';
import { provider } from './utils/ethers-utils.js';
import { neynarKey } from './utils/neynar-utils.js';
// Add custom items to expo-dev-menu
import './dev-menu.js';
import './types/message-types-validator.js';
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const navInitAction = Object.freeze({ type: 'NAV/@@INIT' });
const navUnknownAction = Object.freeze({ type: 'NAV/@@UNKNOWN' });
SplashScreen.preventAutoHideAsync().catch(console.log);
function Root() {
const navStateRef = React.useRef();
const navDispatchRef =
React.useRef(
action:
| GenericNavigationAction
| (PossiblyStaleNavigationState => GenericNavigationAction),
) => void>();
const navStateInitializedRef = React.useRef(false);
// We call this here to start the loading process
// We gate the UI on the fonts loading in AppNavigator
useLoadCommFonts();
const [navContext, setNavContext] = React.useState(null);
const updateNavContext = React.useCallback(() => {
if (
!navStateRef.current ||
!navDispatchRef.current ||
!navStateInitializedRef.current
) {
return;
}
const updatedNavContext = {
state: navStateRef.current,
dispatch: navDispatchRef.current,
};
setNavContext(updatedNavContext);
setGlobalNavContext(updatedNavContext);
}, []);
const [initialState, setInitialState] = React.useState(
__DEV__ ? undefined : defaultNavigationState,
);
React.useEffect(() => {
Orientation.lockToPortrait();
void (async () => {
let loadedState = initialState;
if (__DEV__) {
try {
const navStateString = await AsyncStorage.getItem(
navStateAsyncStorageKey,
);
if (navStateString) {
const savedState = JSON.parse(navStateString);
if (validNavState(savedState)) {
loadedState = savedState;
}
}
} catch {}
}
if (!loadedState) {
loadedState = defaultNavigationState;
}
if (loadedState !== initialState) {
setInitialState(loadedState);
}
navStateRef.current = loadedState;
updateNavContext();
actionLogger.addOtherAction('navState', navInitAction, null, loadedState);
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updateNavContext]);
const setNavStateInitialized = React.useCallback(() => {
navStateInitializedRef.current = true;
updateNavContext();
}, [updateNavContext]);
const [rootContext, setRootContext] = React.useState(() => ({
setNavStateInitialized,
}));
const detectUnsupervisedBackgroundRef = React.useCallback(
(detectUnsupervisedBackground: ?(alreadyClosed: boolean) => boolean) => {
setRootContext(prevRootContext => ({
...prevRootContext,
detectUnsupervisedBackground,
}));
},
[],
);
const frozen = useSelector(state => state.frozen);
const queuedActionsRef = React.useRef>([]);
const onNavigationStateChange = React.useCallback(
(state: ?PossiblyStaleNavigationState) => {
invariant(state, 'nav state should be non-null');
const prevState = navStateRef.current;
navStateRef.current = state;
updateNavContext();
const queuedActions = queuedActionsRef.current;
queuedActionsRef.current = [];
if (queuedActions.length === 0) {
queuedActions.push(navUnknownAction);
}
for (const action of queuedActions) {
actionLogger.addOtherAction('navState', action, prevState, state);
}
if (!__DEV__ || frozen) {
return;
}
void (async () => {
try {
await AsyncStorage.setItem(
navStateAsyncStorageKey,
JSON.stringify(state),
);
} catch (e) {
console.log('AsyncStorage threw while trying to persist navState', e);
}
})();
},
[updateNavContext, frozen],
);
const navContainerRef =
React.useRef>();
const containerRef = React.useCallback(
(navContainer: ?React.ElementRef) => {
navContainerRef.current = navContainer;
if (navContainer && !navDispatchRef.current) {
navDispatchRef.current = navContainer.dispatch;
updateNavContext();
}
},
[updateNavContext],
);
useReduxDevToolsExtension(navContainerRef);
const navContainer = navContainerRef.current;
React.useEffect(() => {
if (!navContainer) {
return undefined;
}
return navContainer.addListener(
'__unsafe_action__',
(event: { +data: UnsafeContainerActionEvent, ... }) => {
const { action, noop } = event.data;
const navState = navStateRef.current;
if (noop) {
actionLogger.addOtherAction('navState', action, navState, navState);
return;
}
queuedActionsRef.current.push({
...action,
type: `NAV/${action.type}`,
});
},
);
}, [navContainer]);
const activeTheme = useSelector(state => state.globalThemeInfo.activeTheme);
const theme = (() => {
if (activeTheme === 'light') {
return LightTheme;
} else if (activeTheme === 'dark') {
return DarkTheme;
}
return undefined;
})();
const gated: React.Node = (
<>
>
);
let navigation;
if (initialState) {
navigation = (
-
);
}
return (
{gated}
{navigation}
);
}
const styles = StyleSheet.create({
app: {
flex: 1,
},
});
function AppRoot(): React.Node {
return (
);
}
export default AppRoot;