Page MenuHomePhabricator

D10992.id36991.diff
No OneTemporary

D10992.id36991.diff

diff --git a/lib/hooks/invite-links.js b/lib/hooks/invite-links.js
--- a/lib/hooks/invite-links.js
+++ b/lib/hooks/invite-links.js
@@ -3,6 +3,7 @@
import invariant from 'invariant';
import * as React from 'react';
+import { addKeyserverActionType } from '../actions/keyserver-actions.js';
import {
useCreateOrUpdatePublicLink,
createOrUpdatePublicLinkActionTypes,
@@ -13,16 +14,23 @@
joinThreadActionTypes,
useJoinThread,
} from '../actions/thread-actions.js';
+import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
import { createLoadingStatusSelector } from '../selectors/loading-selectors.js';
+import { isLoggedInToKeyserver } from '../selectors/user-selectors.js';
+import type { KeyserverOverride } from '../shared/invite-links.js';
+import { useIsKeyserverURLValid } from '../shared/keyserver-utils.js';
+import { callSingleKeyserverEndpointTimeout } from '../shared/timeouts.js';
import type { CalendarQuery } from '../types/entry-types.js';
import type { SetState } from '../types/hook-types.js';
+import { defaultKeyserverInfo } from '../types/keyserver-types.js';
import type {
InviteLink,
InviteLinkVerificationResponse,
} from '../types/link-types.js';
import type { LoadingStatus } from '../types/loading-types.js';
+import type { ThreadJoinPayload } from '../types/thread-types.js';
import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
-import { useSelector } from '../utils/redux-utils.js';
+import { useDispatch, useSelector } from '../utils/redux-utils.js';
const createOrUpdatePublicLinkStatusSelector = createLoadingStatusSelector(
createOrUpdatePublicLinkActionTypes,
@@ -116,6 +124,7 @@
type AcceptInviteLinkParams = {
+verificationResponse: InviteLinkVerificationResponse,
+inviteSecret: string,
+ +keyserverOverride: ?KeyserverOverride,
+calendarQuery: () => CalendarQuery,
+onFinish: () => mixed,
+onInvalidLinkDetected: () => mixed,
@@ -127,6 +136,7 @@
const {
verificationResponse,
inviteSecret,
+ keyserverOverride,
calendarQuery,
onFinish,
onInvalidLinkDetected,
@@ -138,41 +148,151 @@
}
}, [onFinish, verificationResponse.status]);
+ const dispatch = useDispatch();
const callJoinThread = useJoinThread();
+
const communityID = verificationResponse.community?.id;
- const createJoinCommunityAction = React.useCallback(async () => {
- invariant(
- communityID,
- 'CommunityID should be present while calling this function',
- );
- const query = calendarQuery();
- try {
- const result = await callJoinThread({
- threadID: communityID,
- calendarQuery: {
- startDate: query.startDate,
- endDate: query.endDate,
- filters: [
- ...query.filters,
- { type: 'threads', threadIDs: [communityID] },
- ],
+ let keyserverID = keyserverOverride?.keyserverID;
+ if (!keyserverID && communityID) {
+ keyserverID = extractKeyserverIDFromID(communityID);
+ }
+
+ const isKeyserverKnown = useSelector(state =>
+ keyserverID ? !!state.keyserverStore.keyserverInfos[keyserverID] : false,
+ );
+ const isAuthenticated = useSelector(isLoggedInToKeyserver(keyserverID));
+
+ const keyserverURL = keyserverOverride?.keyserverURL;
+ const isKeyserverURLValid = useIsKeyserverURLValid(keyserverURL);
+
+ const [joinCommunityPromiseFunctions, setJoinCommunityPromiseFunctions] =
+ React.useState<?{
+ +resolve: ThreadJoinPayload => mixed,
+ +reject: () => mixed,
+ +communityID: string,
+ }>(null);
+ const timeoutRef = React.useRef<?TimeoutID>();
+
+ React.useEffect(() => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ }, []);
+
+ const createJoinCommunityAction = React.useCallback(() => {
+ return new Promise<ThreadJoinPayload>((resolve, reject) => {
+ if (
+ !keyserverID ||
+ !communityID ||
+ keyserverID !== extractKeyserverIDFromID(communityID)
+ ) {
+ reject();
+ onInvalidLinkDetected();
+ setJoinCommunityPromiseFunctions(null);
+ return;
+ }
+ const timeoutID = setTimeout(() => {
+ reject();
+ setJoinCommunityPromiseFunctions(oldFunctions => {
+ if (oldFunctions) {
+ onInvalidLinkDetected();
+ }
+ return null;
+ });
+ }, callSingleKeyserverEndpointTimeout);
+ timeoutRef.current = timeoutID;
+ const resolveAndClearTimeout = (result: ThreadJoinPayload) => {
+ clearTimeout(timeoutID);
+ resolve(result);
+ };
+ const rejectAndClearTimeout = () => {
+ clearTimeout(timeoutID);
+ reject();
+ };
+ setJoinCommunityPromiseFunctions({
+ resolve: resolveAndClearTimeout,
+ reject: rejectAndClearTimeout,
+ communityID,
+ });
+ });
+ }, [communityID, keyserverID, onInvalidLinkDetected]);
+
+ React.useEffect(() => {
+ void (async () => {
+ if (!joinCommunityPromiseFunctions || isKeyserverKnown) {
+ return;
+ }
+ const isValid = await isKeyserverURLValid();
+ if (!isValid || !keyserverURL) {
+ onInvalidLinkDetected();
+ joinCommunityPromiseFunctions.reject();
+ setJoinCommunityPromiseFunctions(null);
+ return;
+ }
+ dispatch({
+ type: addKeyserverActionType,
+ payload: {
+ keyserverAdminUserID: keyserverID,
+ newKeyserverInfo: defaultKeyserverInfo(keyserverURL),
},
- inviteLinkSecret: inviteSecret,
});
- onFinish();
- return result;
- } catch (e) {
- onInvalidLinkDetected();
- throw e;
- }
+ })();
+ }, [
+ dispatch,
+ isKeyserverKnown,
+ isKeyserverURLValid,
+ joinCommunityPromiseFunctions,
+ keyserverID,
+ keyserverURL,
+ onInvalidLinkDetected,
+ ]);
+
+ React.useEffect(() => {
+ void (async () => {
+ if (!joinCommunityPromiseFunctions || !isAuthenticated) {
+ return;
+ }
+ invariant(communityID, 'CommunityID should be set');
+ const query = calendarQuery();
+
+ if (communityID !== joinCommunityPromiseFunctions.communityID) {
+ joinCommunityPromiseFunctions.reject();
+ return;
+ }
+
+ try {
+ const result = await callJoinThread({
+ threadID: communityID,
+ calendarQuery: {
+ startDate: query.startDate,
+ endDate: query.endDate,
+ filters: [
+ ...query.filters,
+ { type: 'threads', threadIDs: [communityID] },
+ ],
+ },
+ inviteLinkSecret: inviteSecret,
+ });
+ onFinish();
+ joinCommunityPromiseFunctions.resolve(result);
+ } catch (e) {
+ onInvalidLinkDetected();
+ joinCommunityPromiseFunctions.reject();
+ } finally {
+ setJoinCommunityPromiseFunctions(null);
+ }
+ })();
}, [
calendarQuery,
callJoinThread,
communityID,
inviteSecret,
+ isAuthenticated,
+ joinCommunityPromiseFunctions,
onFinish,
onInvalidLinkDetected,
]);
+
const dispatchActionPromise = useDispatchActionPromise();
const joinCommunity = React.useCallback(() => {
void dispatchActionPromise(
diff --git a/lib/selectors/user-selectors.js b/lib/selectors/user-selectors.js
--- a/lib/selectors/user-selectors.js
+++ b/lib/selectors/user-selectors.js
@@ -143,9 +143,12 @@
);
const isLoggedInToKeyserver: (
- keyserverID: string,
+ keyserverID: ?string,
) => (state: BaseAppState<>) => boolean = _memoize(
- (keyserverID: string) => (state: BaseAppState<>) => {
+ (keyserverID: ?string) => (state: BaseAppState<>) => {
+ if (!keyserverID) {
+ return false;
+ }
const cookie = state.keyserverStore.keyserverInfos[keyserverID]?.cookie;
return !!cookie && cookie.startsWith('user=');
},
diff --git a/native/navigation/deep-links-context-provider.react.js b/native/navigation/deep-links-context-provider.react.js
--- a/native/navigation/deep-links-context-provider.react.js
+++ b/native/navigation/deep-links-context-provider.react.js
@@ -161,6 +161,7 @@
params: {
invitationDetails: result,
secret,
+ keyserverOverride,
},
});
})();
diff --git a/native/navigation/invite-link-modal.react.js b/native/navigation/invite-link-modal.react.js
--- a/native/navigation/invite-link-modal.react.js
+++ b/native/navigation/invite-link-modal.react.js
@@ -4,6 +4,7 @@
import { View, Text, ActivityIndicator } from 'react-native';
import { useAcceptInviteLink } from 'lib/hooks/invite-links.js';
+import type { KeyserverOverride } from 'lib/shared/invite-links';
import type { InviteLinkVerificationResponse } from 'lib/types/link-types.js';
import { nonThreadCalendarQuery } from './nav-selectors.js';
@@ -18,6 +19,7 @@
export type InviteLinkModalParams = {
+invitationDetails: InviteLinkVerificationResponse,
+secret: string,
+ +keyserverOverride?: ?KeyserverOverride,
};
type Props = {
@@ -27,7 +29,7 @@
function InviteLinkModal(props: Props): React.Node {
const styles = useStyles(unboundStyles);
- const { invitationDetails, secret } = props.route.params;
+ const { invitationDetails, secret, keyserverOverride } = props.route.params;
const navContext = React.useContext(NavContext);
const calendarQuery = useSelector(state =>
@@ -49,6 +51,7 @@
const { joinCommunity, joinThreadLoadingStatus } = useAcceptInviteLink({
verificationResponse: invitationDetails,
inviteSecret: secret,
+ keyserverOverride,
calendarQuery,
onFinish: props.navigation.goBack,
onInvalidLinkDetected,
diff --git a/web/invite-links/accept-invite-modal.react.js b/web/invite-links/accept-invite-modal.react.js
--- a/web/invite-links/accept-invite-modal.react.js
+++ b/web/invite-links/accept-invite-modal.react.js
@@ -5,6 +5,7 @@
import ModalOverlay from 'lib/components/modal-overlay.react.js';
import { useModalContext } from 'lib/components/modal-provider.react.js';
import { useAcceptInviteLink } from 'lib/hooks/invite-links.js';
+import type { KeyserverOverride } from 'lib/shared/invite-links.js';
import { type InviteLinkVerificationResponse } from 'lib/types/link-types.js';
import css from './accept-invite-modal.css';
@@ -15,10 +16,11 @@
type Props = {
+verificationResponse: InviteLinkVerificationResponse,
+inviteSecret: string,
+ +keyserverOverride?: ?KeyserverOverride,
};
function AcceptInviteModal(props: Props): React.Node {
- const { verificationResponse, inviteSecret } = props;
+ const { verificationResponse, inviteSecret, keyserverOverride } = props;
const [isLinkValid, setIsLinkValid] = React.useState(
verificationResponse.status === 'valid',
);
@@ -32,6 +34,7 @@
const { joinCommunity, joinThreadLoadingStatus } = useAcceptInviteLink({
verificationResponse,
inviteSecret,
+ keyserverOverride,
calendarQuery,
onFinish: popModal,
onInvalidLinkDetected,
diff --git a/web/invite-links/invite-link-handler.react.js b/web/invite-links/invite-link-handler.react.js
--- a/web/invite-links/invite-link-handler.react.js
+++ b/web/invite-links/invite-link-handler.react.js
@@ -89,6 +89,7 @@
<AcceptInviteModal
verificationResponse={result}
inviteSecret={secret}
+ keyserverOverride={keyserverOverride}
/>,
);
})();

File Metadata

Mime Type
text/plain
Expires
Tue, Dec 24, 5:39 PM (9 h, 18 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2698545
Default Alt Text
D10992.id36991.diff (11 KB)

Event Timeline