diff --git a/lib/keyserver-conn/keyserver-call-infos.js b/lib/keyserver-conn/keyserver-call-infos.js new file mode 100644 index 000000000..476ef132d --- /dev/null +++ b/lib/keyserver-conn/keyserver-call-infos.js @@ -0,0 +1,56 @@ +// @flow + +import { createSelector } from 'reselect'; + +import { useDerivedObject } from '../hooks/objects.js'; +import type { PlatformDetails } from '../types/device-types.js'; +import type { KeyserverInfo } from '../types/keyserver-types.js'; + +export type KeyserverInfoPartial = $ReadOnly<{ + ...Partial, + +urlPrefix: $PropertyType, +}>; + +export 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, + ); +} + +export { useKeyserverCallInfos }; diff --git a/lib/utils/keyserver-call.js b/lib/utils/keyserver-call.js index 8970f7913..ae8ad5891 100644 --- a/lib/utils/keyserver-call.js +++ b/lib/utils/keyserver-call.js @@ -1,196 +1,148 @@ // @flow import _memoize from 'lodash/memoize.js'; import * as React from 'react'; -import { createSelector } from 'reselect'; -import type { - CallServerEndpoint, - CallServerEndpointOptions, -} from './call-server-endpoint.js'; +import type { 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 { useCallKeyserverEndpointContext } from '../keyserver-conn/call-keyserver-endpoint-provider.react.js'; -import type { PlatformDetails } from '../types/device-types.js'; +import { + useKeyserverCallInfos, + type KeyserverInfoPartial, + type KeyserverCallInfo, +} from '../keyserver-conn/keyserver-call-infos.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 }>; 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; -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 BindCallKeyserverSelector = ( keyserverCall: ActionFunc, ) => Args => Promise; function useBindCallKeyserverEndpointSelector( dispatch: Dispatch, currentUserInfo: ?CurrentUserInfo, keyserverCallInfos: { +[keyserverID: string]: KeyserverCallInfo }, ): BindCallKeyserverSelector { const { createCallSingleKeyserverEndpointSelector } = useCallKeyserverEndpointContext(); - const getCallSingleKeyserverEndpointSelector = React.useMemo( - () => _memoize(createCallSingleKeyserverEndpointSelector), - [createCallSingleKeyserverEndpointSelector], - ); + const getCallSingleKeyserverEndpointSelector: typeof createCallSingleKeyserverEndpointSelector = + React.useMemo( + () => _memoize(createCallSingleKeyserverEndpointSelector), + [createCallSingleKeyserverEndpointSelector], + ); return React.useMemo( () => _memoize( ( keyserverCall: ActionFunc, ): (Args => Promise) => { const callKeyserverEndpoint = ( endpoint: Endpoint, requests: { +[keyserverID: string]: ?{ +[string]: mixed } }, options?: ?CallServerEndpointOptions, ) => { - const bindCallKeyserverEndpoint = (keyserverID: string) => { + const makeCallToSingleKeyserver = (keyserverID: string) => { const { cookie, urlPrefix, sessionID, isSocketConnected, lastCommunicatedPlatformDetails, } = keyserverCallInfos[keyserverID]; const boundCallServerEndpoint = getCallSingleKeyserverEndpointSelector(keyserverID)({ dispatch, currentUserInfo, cookie, urlPrefix, sessionID, isSocketConnected, lastCommunicatedPlatformDetails, }); return boundCallServerEndpoint( endpoint, requests[keyserverID], options, ); }; - const promises: { [string]: Promise } = {}; + const promises: { [string]: Promise } = {}; for (const keyserverID in requests) { - promises[keyserverID] = bindCallKeyserverEndpoint(keyserverID); + promises[keyserverID] = makeCallToSingleKeyserver(keyserverID); } return promiseAll(promises); }; const keyserverIDs = Object.keys(keyserverCallInfos); return keyserverCall(callKeyserverEndpoint, keyserverIDs); }, ), [ dispatch, currentUserInfo, keyserverCallInfos, getCallSingleKeyserverEndpointSelector, ], ); } export type KeyserverCallParamOverride = Partial<{ +dispatch: Dispatch, +currentUserInfo: ?CurrentUserInfo, +keyserverInfos: { +[keyserverID: string]: KeyserverInfoPartial }, }>; function useKeyserverCall( keyserverCall: ActionFunc, paramOverride?: ?KeyserverCallParamOverride, ): Args => Promise { const baseDispatch = useDispatch(); const baseCurrentUserInfo = useSelector(state => state.currentUserInfo); const keyserverInfos = useSelector( state => state.keyserverStore.keyserverInfos, ); const baseCombinedInfo = { dispatch: baseDispatch, currentUserInfo: baseCurrentUserInfo, keyserverInfos, ...paramOverride, }; const { dispatch, currentUserInfo, keyserverInfos: keyserverInfoPartials, } = baseCombinedInfo; const keyserverCallInfos = useKeyserverCallInfos(keyserverInfoPartials); const bindCallKeyserverEndpointToAction = useBindCallKeyserverEndpointSelector( dispatch, currentUserInfo, keyserverCallInfos, ); return React.useMemo( () => bindCallKeyserverEndpointToAction(keyserverCall), [bindCallKeyserverEndpointToAction, keyserverCall], ); } export { useKeyserverCall };