diff --git a/lib/utils/keyserver-call.js b/lib/utils/keyserver-call.js --- a/lib/utils/keyserver-call.js +++ b/lib/utils/keyserver-call.js @@ -1,14 +1,22 @@ // @flow +import _memoize from 'lodash/memoize.js'; import * as React from 'react'; import { useDispatch } from 'react-redux'; +import { createSelector } from 'reselect'; import { bindCookieAndUtilsIntoCallServerEndpoint } from './action-utils.js'; -import type { CallServerEndpointOptions } from './call-server-endpoint.js'; +import type { BindServerCallsParams } from './action-utils.js'; +import type { + CallServerEndpoint, + CallServerEndpointOptions, +} from './call-server-endpoint.js'; import { useSelector } from './redux-utils.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 { ConnectionStatus } from '../types/socket-types.js'; import type { CurrentUserInfo } from '../types/user-types.js'; export type CallKeyserverEndpoint> = ( @@ -34,74 +42,130 @@ +config: KeyserverCallConfig, }; -export type ExtractAndBindCookieAndUtilsParams< - Args: $ReadOnlyArray, - Return, -> = { +// _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.connectionStatus, + (state: BindServerCallsParams) => state.lastCommunicatedPlatformDetails, + ( + dispatch: Dispatch, + cookie: ?string, + urlPrefix: string, + sessionID: ?string, + currentUserInfo: ?CurrentUserInfo, + connectionStatus: ConnectionStatus, + lastCommunicatedPlatformDetails: ?PlatformDetails, + ) => + bindCookieAndUtilsIntoCallServerEndpoint({ + dispatch, + cookie, + urlPrefix, + sessionID, + currentUserInfo, + connectionStatus, + lastCommunicatedPlatformDetails, + }), + ); + +type CreateBoundServerCallsSelectorType = + string => BindServerCallsParams => CallServerEndpoint; +const createBoundServerCallsSelector: CreateBoundServerCallsSelectorType = + (_memoize(baseCreateBoundServerCallsSelector): any); + +export type BindKeyserverCallParams = { +dispatch: Dispatch, +currentUserInfo: ?CurrentUserInfo, +keyserverInfos: { +[keyserverID: string]: KeyserverInfo }, - +keyserverCall: KeyserverCall, }; -function getCallKeyserverEndpoint, Return>( - params: ExtractAndBindCookieAndUtilsParams, -): CallKeyserverEndpoint { - const { dispatch, keyserverInfos, currentUserInfo, keyserverCall } = params; - - const { config: serverCallConfig } = keyserverCall; +export type GetCallKeyserverEndpointParams> = { + ...BindKeyserverCallParams, + +keyserverCallConfig: KeyserverCallConfig, +}; - return ( - endpoint: Endpoint, - data: Object, - args: Args, - options?: ?CallServerEndpointOptions, +const bindCallKeyserverEndpointSelector = createSelector( + (state: BindKeyserverCallParams) => state.dispatch, + (state: BindKeyserverCallParams) => state.currentUserInfo, + (state: BindKeyserverCallParams) => state.keyserverInfos, + ( + dispatch: Dispatch, + currentUserInfo: ?CurrentUserInfo, + keyserverInfos: { +[keyserverID: string]: KeyserverInfo }, ) => { - // TODO - if (serverCallConfig.keyserverSelection === 'fanout') { - return Promise.resolve(undefined); - } - const keyserverID = serverCallConfig.keyserverIDExtractor(...args); - const { - cookie, - urlPrefix, - sessionID, - connection, - lastCommunicatedPlatformDetails, - } = keyserverInfos[keyserverID]; - - const boundCallServerEndpoint = bindCookieAndUtilsIntoCallServerEndpoint({ - dispatch, - currentUserInfo, - cookie, - urlPrefix, - sessionID, - connectionStatus: connection.status, - lastCommunicatedPlatformDetails, - }); - return boundCallServerEndpoint(endpoint, data, options); - }; -} + return _memoize( + , Return>( + keyserverCall: KeyserverCall, + ): ((...Args) => Promise) => { + const callKeyserverEndpoint = ( + endpoint: Endpoint, + data: Object, + args: Args, + options?: ?CallServerEndpointOptions, + ) => { + // TODO + if (keyserverCall.config.keyserverSelection === 'fanout') { + return Promise.resolve(undefined); + } + const keyserverID = keyserverCall.config.keyserverIDExtractor( + ...args, + ); + const { + cookie, + urlPrefix, + sessionID, + connection, + lastCommunicatedPlatformDetails, + } = keyserverInfos[keyserverID]; + + const boundCallServerEndpoint = createBoundServerCallsSelector( + keyserverID, + )({ + dispatch, + currentUserInfo, + cookie, + urlPrefix, + sessionID, + connectionStatus: connection.status, + lastCommunicatedPlatformDetails, + }); + + return boundCallServerEndpoint(endpoint, data, options); + }; + + return keyserverCall.actionFunc(callKeyserverEndpoint); + }, + ); + }, +); function useKeyserverCall, Return>( keyserverCall: KeyserverCall, - paramOverride?: ?$Shape>, + paramOverride?: ?$Shape, ): (...Args) => Promise { const dispatch = useDispatch(); const keyserverInfos = useSelector( state => state.keyserverStore.keyserverInfos, ); const currentUserInfo = useSelector(state => state.currentUserInfo); - return React.useMemo(() => { - const callKeyserverEndpoint = getCallKeyserverEndpoint({ - dispatch, - currentUserInfo, - keyserverInfos, - keyserverCall, - ...paramOverride, - }); - return keyserverCall.actionFunc(callKeyserverEndpoint); - }, [dispatch, currentUserInfo, keyserverInfos, keyserverCall, paramOverride]); + const bindCallKeyserverEndpointToAction = bindCallKeyserverEndpointSelector({ + dispatch, + keyserverInfos, + currentUserInfo, + ...paramOverride, + }); + return React.useMemo( + () => bindCallKeyserverEndpointToAction(keyserverCall), + [bindCallKeyserverEndpointToAction, keyserverCall], + ); } export { useKeyserverCall };