diff --git a/lib/utils/keyserver-call.js b/lib/utils/keyserver-call.js index 7ab378877..cac58aa7a 100644 --- a/lib/utils/keyserver-call.js +++ b/lib/utils/keyserver-call.js @@ -1,232 +1,228 @@ // @flow import _memoize from 'lodash/memoize.js'; import * as React from 'react'; import { createSelector } from 'reselect'; import { bindCookieAndUtilsIntoCallServerEndpoint } from './action-utils.js'; import type { BindServerCallsParams } from './action-utils.js'; import type { CallServerEndpoint, CallServerEndpointOptions, } from './call-server-endpoint.js'; import { promiseAll } from './promises.js'; import { useSelector, useDispatch } from './redux-utils.js'; import { useDerivedObject } from '../hooks/objects.js'; import type { PlatformDetails } from '../types/device-types.js'; import type { Endpoint } from '../types/endpoints.js'; import type { KeyserverInfo } from '../types/keyserver-types.js'; import type { Dispatch } from '../types/redux-types.js'; import type { CurrentUserInfo } from '../types/user-types.js'; export type CallKeyserverEndpoint = ( endpoint: Endpoint, requests: { +[keyserverID: string]: ?{ +[string]: mixed } }, options?: ?CallServerEndpointOptions, ) => Promise<{ +[keyserverID: string]: any }>; export type ActionFunc = ( callServerEndpoint: CallKeyserverEndpoint, // The second argument is only used in actions that call all keyservers, // and the request to all keyservers are exactly the same. // An example of such action is fetchEntries. allKeyserverIDs: $ReadOnlyArray, ) => Args => Promise; // _memoize memoizes the function by caching the result. // The first argument of the memoized function is used as the map cache key. const baseCreateBoundServerCallsSelector = ( - // eslint-disable-next-line no-unused-vars keyserverID: string, ): (BindServerCallsParams => CallServerEndpoint) => createSelector( (state: BindServerCallsParams) => state.dispatch, (state: BindServerCallsParams) => state.cookie, (state: BindServerCallsParams) => state.urlPrefix, (state: BindServerCallsParams) => state.sessionID, (state: BindServerCallsParams) => state.currentUserInfo, (state: BindServerCallsParams) => state.isSocketConnected, (state: BindServerCallsParams) => state.lastCommunicatedPlatformDetails, ( dispatch: Dispatch, cookie: ?string, urlPrefix: string, sessionID: ?string, currentUserInfo: ?CurrentUserInfo, isSocketConnected: boolean, lastCommunicatedPlatformDetails: ?PlatformDetails, ) => bindCookieAndUtilsIntoCallServerEndpoint({ dispatch, cookie, urlPrefix, sessionID, currentUserInfo, isSocketConnected, lastCommunicatedPlatformDetails, keyserverID, }), ); type CreateBoundServerCallsSelectorType = ( keyserverID: string, ) => BindServerCallsParams => CallServerEndpoint; const createBoundServerCallsSelector: CreateBoundServerCallsSelectorType = _memoize(baseCreateBoundServerCallsSelector); type KeyserverInfoPartial = $ReadOnly<{ ...Partial, +urlPrefix: $PropertyType, }>; type KeyserverCallInfo = { +cookie: ?string, +urlPrefix: string, +sessionID: ?string, +isSocketConnected: boolean, +lastCommunicatedPlatformDetails: ?PlatformDetails, }; const createKeyserverCallSelector: () => KeyserverInfoPartial => KeyserverCallInfo = () => createSelector( (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.cookie, (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.urlPrefix, (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.sessionID, (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.connection?.status === 'connected', (keyserverInfo: KeyserverInfoPartial) => keyserverInfo.lastCommunicatedPlatformDetails, ( cookie: ?string, urlPrefix: string, sessionID: ?string, isSocketConnected: boolean, lastCommunicatedPlatformDetails: ?PlatformDetails, ) => ({ cookie, urlPrefix, sessionID, isSocketConnected, lastCommunicatedPlatformDetails, }), ); function useKeyserverCallInfos(keyserverInfos: { +[keyserverID: string]: KeyserverInfoPartial, }): { +[keyserverID: string]: KeyserverCallInfo } { return useDerivedObject( keyserverInfos, createKeyserverCallSelector, ); } -type BindKeyserverCallParams = { - +dispatch: Dispatch, - +currentUserInfo: ?CurrentUserInfo, - +keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo }, -}; - -const bindCallKeyserverEndpointSelector: BindKeyserverCallParams => < - Args: mixed, - Return, ->( +type BindCallKeyserverSelector = ( keyserverCall: ActionFunc, -) => Args => Promise = createSelector( - (state: BindKeyserverCallParams) => state.dispatch, - (state: BindKeyserverCallParams) => state.currentUserInfo, - (state: BindKeyserverCallParams) => state.keyserverCallInfos, - ( - dispatch: Dispatch, - currentUserInfo: ?CurrentUserInfo, - keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo }, - ) => { - return _memoize( - ( - keyserverCall: ActionFunc, - ): (Args => Promise) => { - const callKeyserverEndpoint = ( - endpoint: Endpoint, - requests: { +[keyserverID: string]: ?{ +[string]: mixed } }, - options?: ?CallServerEndpointOptions, - ) => { - const bindCallKeyserverEndpoint = (keyserverID: string) => { - const { - cookie, - urlPrefix, - sessionID, - isSocketConnected, - lastCommunicatedPlatformDetails, - } = keyserverCallInfos[keyserverID]; - - const boundCallServerEndpoint = createBoundServerCallsSelector( - keyserverID, - )({ - dispatch, - currentUserInfo, - cookie, - urlPrefix, - sessionID, - isSocketConnected, - lastCommunicatedPlatformDetails, - keyserverID, - }); - - return boundCallServerEndpoint( - endpoint, - requests[keyserverID], - options, - ); +) => Args => Promise; +function useBindCallKeyserverEndpointSelector( + dispatch: Dispatch, + currentUserInfo: ?CurrentUserInfo, + keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo }, +): BindCallKeyserverSelector { + return React.useMemo( + () => + _memoize( + ( + keyserverCall: ActionFunc, + ): (Args => Promise) => { + const callKeyserverEndpoint = ( + endpoint: Endpoint, + requests: { +[keyserverID: string]: ?{ +[string]: mixed } }, + options?: ?CallServerEndpointOptions, + ) => { + const bindCallKeyserverEndpoint = (keyserverID: string) => { + const { + cookie, + urlPrefix, + sessionID, + isSocketConnected, + lastCommunicatedPlatformDetails, + } = keyserverCallInfos[keyserverID]; + + const boundCallServerEndpoint = createBoundServerCallsSelector( + keyserverID, + )({ + dispatch, + currentUserInfo, + cookie, + urlPrefix, + sessionID, + isSocketConnected, + lastCommunicatedPlatformDetails, + keyserverID, + }); + + return boundCallServerEndpoint( + endpoint, + requests[keyserverID], + options, + ); + }; + + const promises: { [string]: Promise } = {}; + for (const keyserverID in requests) { + promises[keyserverID] = bindCallKeyserverEndpoint(keyserverID); + } + return promiseAll(promises); }; - - const promises: { [string]: Promise } = {}; - for (const keyserverID in requests) { - promises[keyserverID] = bindCallKeyserverEndpoint(keyserverID); - } - return promiseAll(promises); - }; - const keyserverIDs = Object.keys(keyserverCallInfos); - return keyserverCall(callKeyserverEndpoint, keyserverIDs); - }, - ); - }, -); + const keyserverIDs = Object.keys(keyserverCallInfos); + return keyserverCall(callKeyserverEndpoint, keyserverIDs); + }, + ), + [dispatch, currentUserInfo, keyserverCallInfos], + ); +} export type KeyserverCallParamOverride = Partial<{ +dispatch: Dispatch, +currentUserInfo: ?CurrentUserInfo, +keyserverInfos: { +[keyserverID: string]: KeyserverInfoPartial }, }>; function useKeyserverCall( keyserverCall: ActionFunc, paramOverride?: ?KeyserverCallParamOverride, ): Args => Promise { - const dispatch = useDispatch(); - const currentUserInfo = useSelector(state => state.currentUserInfo); + const baseDispatch = useDispatch(); + const baseCurrentUserInfo = useSelector(state => state.currentUserInfo); const keyserverInfos = useSelector( state => state.keyserverStore.keyserverInfos, ); const baseCombinedInfo = { - dispatch, + dispatch: baseDispatch, + currentUserInfo: baseCurrentUserInfo, keyserverInfos, - currentUserInfo, ...paramOverride, }; - const { keyserverInfos: keyserverInfoPartials, ...restCombinedInfo } = - baseCombinedInfo; + const { + dispatch, + currentUserInfo, + keyserverInfos: keyserverInfoPartials, + } = baseCombinedInfo; const keyserverCallInfos = useKeyserverCallInfos(keyserverInfoPartials); - const bindCallKeyserverEndpointToAction = bindCallKeyserverEndpointSelector({ - ...restCombinedInfo, - keyserverCallInfos, - }); + const bindCallKeyserverEndpointToAction = + useBindCallKeyserverEndpointSelector( + dispatch, + currentUserInfo, + keyserverCallInfos, + ); + return React.useMemo( () => bindCallKeyserverEndpointToAction(keyserverCall), [bindCallKeyserverEndpointToAction, keyserverCall], ); } export { useKeyserverCall };