diff --git a/lib/components/sync-community-store-handler.react.js b/lib/components/sync-community-store-handler.react.js new file mode 100644 --- /dev/null +++ b/lib/components/sync-community-store-handler.react.js @@ -0,0 +1,76 @@ +// @flow + +import * as React from 'react'; + +import { + useFetchCommunityInfos, + fetchCommunityInfosActionTypes, +} from '../actions/community-actions.js'; +import { isLoggedIn } from '../selectors/user-selectors.js'; +import { threadTypeIsCommunityRoot } from '../types/thread-types-enum.js'; +import { FetchTimeout } from '../utils/errors.js'; +import { useDispatchActionPromise } from '../utils/redux-promise-utils.js'; +import { useSelector } from '../utils/redux-utils.js'; + +function SyncCommunityStoreHandler(): React.Node { + const loggedIn = useSelector(isLoggedIn); + const threadInfos = useSelector(state => state.threadStore.threadInfos); + const communityInfos = useSelector( + state => state.communityStore.communityInfos, + ); + const callFetchCommunityInfos = useFetchCommunityInfos(); + const dispatchActionPromise = useDispatchActionPromise(); + + const requestedCommunityInfosRef = React.useRef(new Set()); + + React.useEffect(() => { + if (!loggedIn) { + return; + } + + const communityRootThreads = Object.values(threadInfos).filter(thread => + threadTypeIsCommunityRoot(thread.type), + ); + + const missingCommunityInfos = communityRootThreads.filter( + thread => + !(thread.id in communityInfos) && + !requestedCommunityInfosRef.current.has(thread.id), + ); + + if (missingCommunityInfos.length === 0) { + return; + } + + missingCommunityInfos.forEach(thread => + requestedCommunityInfosRef.current.add(thread.id), + ); + + const fetchCommunityInfosPromise = (async () => { + try { + return await callFetchCommunityInfos(); + } catch (e) { + if (e instanceof FetchTimeout) { + missingCommunityInfos.forEach(thread => + requestedCommunityInfosRef.current.delete(thread.id), + ); + } + throw e; + } + })(); + void dispatchActionPromise( + fetchCommunityInfosActionTypes, + fetchCommunityInfosPromise, + ); + }, [ + callFetchCommunityInfos, + communityInfos, + dispatchActionPromise, + loggedIn, + threadInfos, + ]); + + return null; +} + +export default SyncCommunityStoreHandler; diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -34,6 +34,7 @@ import PrekeysHandler from 'lib/components/prekeys-handler.react.js'; import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js'; import { StaffContextProvider } from 'lib/components/staff-provider.react.js'; +import SyncCommunityStoreHandler from 'lib/components/sync-community-store-handler.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'; @@ -390,6 +391,7 @@ + {navigation} diff --git a/web/app.react.js b/web/app.react.js --- a/web/app.react.js +++ b/web/app.react.js @@ -24,6 +24,7 @@ import PlatformDetailsSynchronizer from 'lib/components/platform-details-synchronizer.react.js'; import { QRAuthProvider } from 'lib/components/qr-auth-provider.react.js'; import { StaffContextProvider } from 'lib/components/staff-provider.react.js'; +import SyncCommunityStoreHandler from 'lib/components/sync-community-store-handler.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'; @@ -258,6 +259,7 @@ + {content}