diff --git a/web/app.react.js b/web/app.react.js
--- a/web/app.react.js
+++ b/web/app.react.js
@@ -37,6 +37,7 @@
import { initOpaque } from './crypto/opaque-utils.js';
import electron from './electron.js';
import InputStateContainer from './input/input-state-container.react.js';
+import InviteLinkHandler from './invite-links/invite-link-handler.react.js';
import LoadingIndicator from './loading-indicator.react.js';
import { MenuProvider } from './menu-provider.react.js';
import UpdateModalHandler from './modals/update-modal.react.js';
@@ -169,6 +170,7 @@
+
{content}
{this.props.modals}
diff --git a/web/invite-links/invite-link-handler.react.js b/web/invite-links/invite-link-handler.react.js
new file mode 100644
--- /dev/null
+++ b/web/invite-links/invite-link-handler.react.js
@@ -0,0 +1,63 @@
+// @flow
+
+import * as React from 'react';
+import { useDispatch } from 'react-redux';
+
+import {
+ verifyInviteLink,
+ verifyInviteLinkActionTypes,
+} from 'lib/actions/link-actions.js';
+import { useModalContext } from 'lib/components/modal-provider.react.js';
+import { isLoggedIn } from 'lib/selectors/user-selectors.js';
+import {
+ useDispatchActionPromise,
+ useServerCall,
+} from 'lib/utils/action-utils.js';
+
+import AcceptInviteModal from './accept-invite-modal.react.js';
+import { updateNavInfoActionType } from '../redux/action-types.js';
+import { useSelector } from '../redux/redux-utils.js';
+
+function InviteLinkHandler(): null {
+ const inviteSecret = useSelector(state => state.navInfo.inviteSecret);
+ const loggedIn = useSelector(isLoggedIn);
+
+ const dispatchActionPromise = useDispatchActionPromise();
+ const dispatch = useDispatch();
+ const validateLink = useServerCall(verifyInviteLink);
+ const { pushModal } = useModalContext();
+ React.useEffect(() => {
+ if (!inviteSecret || !loggedIn) {
+ return;
+ }
+ dispatch({
+ type: updateNavInfoActionType,
+ payload: { inviteSecret: null },
+ });
+ const validateLinkPromise = validateLink({ secret: inviteSecret });
+ dispatchActionPromise(verifyInviteLinkActionTypes, validateLinkPromise);
+ (async () => {
+ const result = await validateLinkPromise;
+ if (result.status === 'already_joined') {
+ return;
+ }
+ pushModal(
+ ,
+ );
+ })();
+ }, [
+ dispatch,
+ dispatchActionPromise,
+ inviteSecret,
+ loggedIn,
+ pushModal,
+ validateLink,
+ ]);
+
+ return null;
+}
+
+export default InviteLinkHandler;