diff --git a/lib/selectors/keyserver-selectors.js b/lib/selectors/keyserver-selectors.js index 195dd4122..bda27f7da 100644 --- a/lib/selectors/keyserver-selectors.js +++ b/lib/selectors/keyserver-selectors.js @@ -1,53 +1,61 @@ // @flow import { createSelector } from 'reselect'; +import type { PlatformDetails } from '../types/device-types'; import type { KeyserverInfo } from '../types/keyserver-types'; import type { AppState } from '../types/redux-types.js'; import type { ConnectionInfo } from '../types/socket-types.js'; import { ashoatKeyserverID } from '../utils/validation-utils.js'; const cookieSelector: (state: AppState) => ?string = (state: AppState) => state.keyserverStore.keyserverInfos[ashoatKeyserverID]?.cookie; const cookiesSelector: (state: AppState) => { +[keyserverID: string]: string, } = createSelector( (state: AppState) => state.keyserverStore.keyserverInfos, (infos: { +[key: string]: KeyserverInfo }) => { const cookies = {}; for (const keyserverID in infos) { cookies[keyserverID] = infos[keyserverID].cookie; } return cookies; }, ); const sessionIDSelector: (state: AppState) => ?string = (state: AppState) => state.keyserverStore.keyserverInfos[ashoatKeyserverID]?.sessionID; const updatesCurrentAsOfSelector: (state: AppState) => number = ( state: AppState, ) => state.keyserverStore.keyserverInfos[ashoatKeyserverID]?.updatesCurrentAsOf ?? 0; const currentAsOfSelector: (state: AppState) => number = (state: AppState) => state.messageStore.currentAsOf[ashoatKeyserverID] ?? 0; const urlPrefixSelector: (state: AppState) => ?string = (state: AppState) => state.keyserverStore.keyserverInfos[ashoatKeyserverID]?.urlPrefix; const connectionSelector: (state: AppState) => ?ConnectionInfo = ( state: AppState, ) => state.keyserverStore.keyserverInfos[ashoatKeyserverID]?.connection; +const lastCommunicatedPlatformDetailsSelector: ( + state: AppState, +) => ?PlatformDetails = (state: AppState) => + state.keyserverStore.keyserverInfos[ashoatKeyserverID] + ?.lastCommunicatedPlatformDetails; + export { cookieSelector, cookiesSelector, sessionIDSelector, updatesCurrentAsOfSelector, currentAsOfSelector, urlPrefixSelector, connectionSelector, + lastCommunicatedPlatformDetailsSelector, }; diff --git a/lib/selectors/server-calls.js b/lib/selectors/server-calls.js index 16a18452b..2e55a50b8 100644 --- a/lib/selectors/server-calls.js +++ b/lib/selectors/server-calls.js @@ -1,53 +1,54 @@ // @flow import { createSelector } from 'reselect'; import { cookieSelector, sessionIDSelector, urlPrefixSelector, connectionSelector, + lastCommunicatedPlatformDetailsSelector, } from './keyserver-selectors.js'; import type { PlatformDetails } from '../types/device-types.js'; import type { AppState } from '../types/redux-types.js'; import type { ConnectionInfo, ConnectionStatus, } from '../types/socket-types.js'; import { type CurrentUserInfo } from '../types/user-types.js'; export type ServerCallState = { +cookie: ?string, +urlPrefix: ?string, +sessionID: ?string, +currentUserInfo: ?CurrentUserInfo, +connectionStatus: ?ConnectionStatus, +lastCommunicatedPlatformDetails: ?PlatformDetails, }; const serverCallStateSelector: (state: AppState) => ServerCallState = createSelector( cookieSelector, urlPrefixSelector, sessionIDSelector, (state: AppState) => state.currentUserInfo, connectionSelector, - (state: AppState) => state.lastCommunicatedPlatformDetails, + lastCommunicatedPlatformDetailsSelector, ( cookie: ?string, urlPrefix: ?string, sessionID: ?string, currentUserInfo: ?CurrentUserInfo, connectionInfo: ?ConnectionInfo, lastCommunicatedPlatformDetails: ?PlatformDetails, ) => ({ cookie, urlPrefix, sessionID, currentUserInfo, connectionStatus: connectionInfo?.status, lastCommunicatedPlatformDetails, }), ); export { serverCallStateSelector }; diff --git a/native/socket.react.js b/native/socket.react.js index bfbf7b758..927725bb8 100644 --- a/native/socket.react.js +++ b/native/socket.react.js @@ -1,171 +1,172 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { useDispatch } from 'react-redux'; import { logOut, logOutActionTypes } from 'lib/actions/user-actions.js'; import { preRequestUserStateSelector } from 'lib/selectors/account-selectors.js'; import { cookieSelector, urlPrefixSelector, connectionSelector, + lastCommunicatedPlatformDetailsSelector, } from 'lib/selectors/keyserver-selectors.js'; import { isLoggedIn } from 'lib/selectors/user-selectors.js'; import { accountHasPassword } from 'lib/shared/account-utils.js'; import Socket, { type BaseSocketProps } from 'lib/socket/socket.react.js'; import { logInActionSources } from 'lib/types/account-types.js'; import { useServerCall, useDispatchActionPromise, fetchNewCookieFromNativeCredentials, } from 'lib/utils/action-utils.js'; import { InputStateContext } from './input/input-state.js'; import { activeMessageListSelector, nativeCalendarQuery, } from './navigation/nav-selectors.js'; import { NavContext } from './navigation/navigation-context.js'; import { useSelector } from './redux/redux-utils.js'; import { noDataAfterPolicyAcknowledgmentSelector } from './selectors/account-selectors.js'; import { openSocketSelector, sessionIdentificationSelector, nativeGetClientResponsesSelector, nativeSessionStateFuncSelector, } from './selectors/socket-selectors.js'; import Alert from './utils/alert.js'; import { useInitialNotificationsEncryptedMessage } from './utils/crypto-utils.js'; const NativeSocket: React.ComponentType = React.memo(function NativeSocket(props: BaseSocketProps) { const inputState = React.useContext(InputStateContext); const navContext = React.useContext(NavContext); const cookie = useSelector(cookieSelector); const urlPrefix = useSelector(urlPrefixSelector); invariant(urlPrefix, 'missing urlPrefix for given keyserver id'); const connection = useSelector(connectionSelector); invariant(connection, 'keyserver missing from keyserverStore'); const frozen = useSelector(state => state.frozen); const active = useSelector( state => isLoggedIn(state) && state.lifecycleState !== 'background', ); const noDataAfterPolicyAcknowledgment = useSelector( noDataAfterPolicyAcknowledgmentSelector, ); const currentUserInfo = useSelector(state => state.currentUserInfo); const openSocket = useSelector(openSocketSelector); invariant(openSocket, 'openSocket failed to be created'); const sessionIdentification = useSelector(sessionIdentificationSelector); const preRequestUserState = useSelector(preRequestUserStateSelector); const getInitialNotificationsEncryptedMessage = useInitialNotificationsEncryptedMessage(); const getClientResponses = useSelector(state => nativeGetClientResponsesSelector({ redux: state, navContext, getInitialNotificationsEncryptedMessage, }), ); const sessionStateFunc = useSelector(state => nativeSessionStateFuncSelector({ redux: state, navContext, }), ); const currentCalendarQuery = useSelector(state => nativeCalendarQuery({ redux: state, navContext, }), ); const canSendReports = useSelector( state => !state.frozen && state.connectivity.hasWiFi && (!inputState || !inputState.uploadInProgress()), ); const activeThread = React.useMemo(() => { if (!active) { return null; } return activeMessageListSelector(navContext); }, [active, navContext]); const lastCommunicatedPlatformDetails = useSelector( - state => state.lastCommunicatedPlatformDetails, + lastCommunicatedPlatformDetailsSelector, ); const dispatch = useDispatch(); const dispatchActionPromise = useDispatchActionPromise(); const callLogOut = useServerCall(logOut); const socketCrashLoopRecovery = React.useCallback(async () => { if (!accountHasPassword(currentUserInfo)) { dispatchActionPromise( logOutActionTypes, callLogOut(preRequestUserState), ); Alert.alert( 'Log in needed', 'After acknowledging the policies, we need you to log in to your account again', [{ text: 'OK' }], ); return; } await fetchNewCookieFromNativeCredentials( dispatch, cookie, urlPrefix, logInActionSources.refetchUserDataAfterAcknowledgment, getInitialNotificationsEncryptedMessage, ); }, [ callLogOut, cookie, currentUserInfo, dispatch, dispatchActionPromise, preRequestUserState, urlPrefix, getInitialNotificationsEncryptedMessage, ]); return ( ); }); export default NativeSocket; diff --git a/web/socket.react.js b/web/socket.react.js index 9c4db8990..2f7edbd9d 100644 --- a/web/socket.react.js +++ b/web/socket.react.js @@ -1,95 +1,96 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { useDispatch } from 'react-redux'; import { logOut } from 'lib/actions/user-actions.js'; import { preRequestUserStateSelector } from 'lib/selectors/account-selectors.js'; import { cookieSelector, urlPrefixSelector, connectionSelector, + lastCommunicatedPlatformDetailsSelector, } from 'lib/selectors/keyserver-selectors.js'; import Socket, { type BaseSocketProps } from 'lib/socket/socket.react.js'; import { useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils.js'; import { useSelector } from './redux/redux-utils.js'; import { activeThreadSelector, webCalendarQuery, } from './selectors/nav-selectors.js'; import { openSocketSelector, sessionIdentificationSelector, webGetClientResponsesSelector, webSessionStateFuncSelector, } from './selectors/socket-selectors.js'; const WebSocket: React.ComponentType = React.memo(function WebSocket(props) { const cookie = useSelector(cookieSelector); const urlPrefix = useSelector(urlPrefixSelector); invariant(urlPrefix, 'missing urlPrefix for given keyserver id'); const connection = useSelector(connectionSelector); invariant(connection, 'keyserver missing from keyserverStore'); const active = useSelector( state => !!state.currentUserInfo && !state.currentUserInfo.anonymous && state.lifecycleState !== 'background', ); const openSocket = useSelector(openSocketSelector); invariant(openSocket, 'openSocket failed to be created'); const sessionIdentification = useSelector(sessionIdentificationSelector); const preRequestUserState = useSelector(preRequestUserStateSelector); const getClientResponses = useSelector(webGetClientResponsesSelector); const sessionStateFunc = useSelector(webSessionStateFuncSelector); const currentCalendarQuery = useSelector(webCalendarQuery); const reduxActiveThread = useSelector(activeThreadSelector); const windowActive = useSelector(state => state.windowActive); const activeThread = React.useMemo(() => { if (!active || !windowActive) { return null; } return reduxActiveThread; }, [active, windowActive, reduxActiveThread]); const dispatch = useDispatch(); const dispatchActionPromise = useDispatchActionPromise(); const callLogOut = useServerCall(logOut); const lastCommunicatedPlatformDetails = useSelector( - state => state.lastCommunicatedPlatformDetails, + lastCommunicatedPlatformDetailsSelector, ); return ( ); }); export default WebSocket;