diff --git a/lib/actions/holder-actions.js b/lib/actions/holder-actions.js --- a/lib/actions/holder-actions.js +++ b/lib/actions/holder-actions.js @@ -229,4 +229,4 @@ ); } -export { useClearAllHolders, useProcessBlobHolders }; +export { processHoldersAction, useClearAllHolders, useProcessBlobHolders }; diff --git a/lib/handlers/holders-handler.react.js b/lib/handlers/holders-handler.react.js new file mode 100644 --- /dev/null +++ b/lib/handlers/holders-handler.react.js @@ -0,0 +1,78 @@ +// @flow + +import invariant from 'invariant'; +import _debounce from 'lodash/debounce.js'; +import * as React from 'react'; + +import { + processHoldersAction, + processHoldersActionTypes, +} from '../actions/holder-actions.js'; +import type { ProcessHoldersStartedPayload } from '../actions/holder-actions.js'; +import { isLoggedIn } from '../selectors/user-selectors.js'; +import { IdentityClientContext } from '../shared/identity-client-context.js'; +import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; +import { useSelector } from '../utils/redux-utils.js'; + +const retryInterval = 5000; // ms + +function HoldersHandler(): React.Node { + const dispatchActionPromise = useDispatchActionPromise(); + const identityContext = React.useContext(IdentityClientContext); + const getAuthMetadata = identityContext?.getAuthMetadata; + + const loggedIn = useSelector(isLoggedIn); + const storedHolders = useSelector(state => state.holderStore.storedHolders); + + const itemsToProcess = React.useMemo(() => { + const holdersToAdd = [], + holdersToRemove = []; + + for (const blobHash in storedHolders) { + const { status, holder } = storedHolders[blobHash]; + if (status === 'NOT_ESTABLISHED') { + holdersToAdd.push({ blobHash, holder }); + } else if (status === 'NOT_REMOVED') { + holdersToRemove.push({ blobHash, holder }); + } + } + return { holdersToAdd, holdersToRemove }; + }, [storedHolders]); + + const performHoldersProcessing: ( + input: ProcessHoldersStartedPayload, + ) => Promise = React.useMemo( + () => + _debounce(async (input: ProcessHoldersStartedPayload) => { + invariant(getAuthMetadata, 'Identity context not set'); + const authMetadata = await getAuthMetadata(); + + void dispatchActionPromise( + processHoldersActionTypes, + processHoldersAction(input, authMetadata), + undefined, + input, + ); + }, retryInterval), + [getAuthMetadata, dispatchActionPromise], + ); + + const shouldStartProcessing = + itemsToProcess.holdersToAdd.length !== 0 || + itemsToProcess.holdersToRemove.length !== 0; + + React.useEffect(() => { + if (!loggedIn || !shouldStartProcessing) { + return; + } + + void performHoldersProcessing(itemsToProcess); + }, [ + itemsToProcess, + loggedIn, + performHoldersProcessing, + shouldStartProcessing, + ]); +} + +export { HoldersHandler }; diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -36,6 +36,7 @@ import { StaffContextProvider } from 'lib/components/staff-provider.react.js'; import { UserIdentityCacheProvider } from 'lib/components/user-identity-cache.react.js'; import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js'; +import { HoldersHandler } from 'lib/handlers/holders-handler.react.js'; import { InitialStateSharingHandler } from 'lib/handlers/initial-state-sharing-handler.react.js'; import { TunnelbrokerDeviceTokenHandler } from 'lib/handlers/tunnelbroker-device-token-handler.react.js'; import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js'; @@ -295,6 +296,7 @@ + ); let navigation; diff --git a/web/app.react.js b/web/app.react.js --- a/web/app.react.js +++ b/web/app.react.js @@ -25,6 +25,7 @@ import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js'; import { StaffContextProvider } from 'lib/components/staff-provider.react.js'; import { DBOpsHandler } from 'lib/handlers/db-ops-handler.react.js'; +import { HoldersHandler } from 'lib/handlers/holders-handler.react.js'; import { TunnelbrokerDeviceTokenHandler } from 'lib/handlers/tunnelbroker-device-token-handler.react.js'; import { UserInfosHandler } from 'lib/handlers/user-infos-handler.react.js'; import { IdentitySearchProvider } from 'lib/identity-search/identity-search-context.js'; @@ -258,6 +259,7 @@ + {content}