Page MenuHomePhabricator

D12706.diff
No OneTemporary

D12706.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
@@ -2,27 +2,23 @@
import * as React from 'react';
-import { addKeyserverActionType } from '../actions/keyserver-actions.js';
import {
useCreateOrUpdatePublicLink,
createOrUpdatePublicLinkActionTypes,
useDisableInviteLink,
disableInviteLinkLinkActionTypes,
} from '../actions/link-actions.js';
-import {
- joinThreadActionTypes,
- useJoinThread,
-} from '../actions/thread-actions.js';
import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
import { createLoadingStatusSelector } from '../selectors/loading-selectors.js';
import { threadInfoSelector } from '../selectors/thread-selectors.js';
-import { isLoggedInToKeyserver } from '../selectors/user-selectors.js';
+import { useJoinCommunity } from '../shared/community-utils.js';
import type { KeyserverOverride } from '../shared/invite-links.js';
-import { useIsKeyserverURLValid } from '../shared/keyserver-utils.js';
-import { permissionsAndAuthRelatedRequestTimeout } from '../shared/timeouts.js';
+import type {
+ OngoingJoinCommunityData,
+ JoinCommunityStep,
+} from '../types/community-types.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,
@@ -30,7 +26,7 @@
import type { LoadingStatus } from '../types/loading-types.js';
import type { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
import { useDispatchActionPromise } from '../utils/redux-promise-utils.js';
-import { useDispatch, useSelector } from '../utils/redux-utils.js';
+import { useSelector } from '../utils/redux-utils.js';
const createOrUpdatePublicLinkStatusSelector = createLoadingStatusSelector(
createOrUpdatePublicLinkActionTypes,
@@ -178,223 +174,29 @@
navigateToThread,
} = params;
- const dispatch = useDispatch();
- const callJoinThread = useJoinThread();
-
const communityID = verificationResponse.community?.id;
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 [ongoingJoinData, setOngoingJoinData] = React.useState<?{
- +resolve: () => mixed,
- +reject: () => mixed,
- +communityID: string,
- +threadID: ?string,
- }>(null);
- const timeoutRef = React.useRef<?TimeoutID>();
+ const [ongoingJoinData, setOngoingJoinData] =
+ React.useState<?OngoingJoinCommunityData>(null);
- React.useEffect(() => {
- return () => {
- if (timeoutRef.current) {
- clearTimeout(timeoutRef.current);
- }
- };
- }, []);
- const [step, setStep] = React.useState<
- | 'inactive'
- | 'add_keyserver'
- | 'auth_to_keyserver'
- | 'join_community'
- | 'join_thread'
- | 'finished',
- >('inactive');
+ const [step, setStep] = React.useState<JoinCommunityStep>('inactive');
- const createJoinPromise = React.useCallback(() => {
- return new Promise<void>((resolve, reject) => {
- if (
- !keyserverID ||
- !communityID ||
- keyserverID !== extractKeyserverIDFromID(communityID)
- ) {
- reject();
- setLinkStatus('invalid');
- setOngoingJoinData(null);
- return;
- }
- const timeoutID = setTimeout(() => {
- reject();
- setOngoingJoinData(oldData => {
- if (oldData) {
- setLinkStatus('timed_out');
- }
- return null;
- });
- }, permissionsAndAuthRelatedRequestTimeout);
- timeoutRef.current = timeoutID;
- const resolveAndClearTimeout = () => {
- clearTimeout(timeoutID);
- resolve();
- };
- const rejectAndClearTimeout = () => {
- clearTimeout(timeoutID);
- reject();
- };
- setOngoingJoinData({
- resolve: resolveAndClearTimeout,
- reject: rejectAndClearTimeout,
- communityID,
- threadID: verificationResponse.thread?.id,
- });
- setStep('add_keyserver');
- });
- }, [
+ const joinCommunity = useJoinCommunity({
communityID,
- keyserverID,
- setLinkStatus,
- verificationResponse.thread?.id,
- ]);
-
- React.useEffect(() => {
- void (async () => {
- if (!ongoingJoinData || step !== 'add_keyserver') {
- return;
- }
- if (isKeyserverKnown) {
- setStep('auth_to_keyserver');
- return;
- }
-
- const isValid = await isKeyserverURLValid();
- if (!isValid || !keyserverURL) {
- setLinkStatus('invalid');
- ongoingJoinData.reject();
- setOngoingJoinData(null);
- return;
- }
- dispatch({
- type: addKeyserverActionType,
- payload: {
- keyserverAdminUserID: keyserverID,
- newKeyserverInfo: defaultKeyserverInfo(keyserverURL),
- },
- });
- })();
- }, [
- dispatch,
- isKeyserverKnown,
- isKeyserverURLValid,
- keyserverID,
- keyserverURL,
- ongoingJoinData,
- setLinkStatus,
- step,
- ]);
-
- React.useEffect(() => {
- if (step === 'auth_to_keyserver' && ongoingJoinData && isAuthenticated) {
- setStep('join_community');
- }
- }, [isAuthenticated, ongoingJoinData, step]);
-
- const dispatchActionPromise = useDispatchActionPromise();
- React.useEffect(() => {
- void (async () => {
- if (!ongoingJoinData || step !== 'join_community') {
- return;
- }
- const threadID = ongoingJoinData.communityID;
- const query = calendarQuery();
- const joinThreadPromise = callJoinThread({
- threadID,
- calendarQuery: {
- startDate: query.startDate,
- endDate: query.endDate,
- filters: [
- ...query.filters,
- { type: 'threads', threadIDs: [threadID] },
- ],
- },
- inviteLinkSecret: inviteSecret,
- });
- void dispatchActionPromise(joinThreadActionTypes, joinThreadPromise);
-
- try {
- await joinThreadPromise;
- setStep('join_thread');
- } catch (e) {
- setLinkStatus(status => (status === 'valid' ? 'invalid' : status));
- ongoingJoinData.reject();
- setOngoingJoinData(null);
- }
- })();
- }, [
+ keyserverOverride,
calendarQuery,
- callJoinThread,
- dispatchActionPromise,
- inviteSecret,
ongoingJoinData,
- setLinkStatus,
+ setOngoingJoinData,
step,
- ]);
-
- React.useEffect(() => {
- void (async () => {
- if (!ongoingJoinData || step !== 'join_thread') {
- return;
- }
- const threadID = ongoingJoinData.threadID;
- if (!threadID) {
- setStep('finished');
- ongoingJoinData.resolve();
- setOngoingJoinData(null);
- return;
- }
-
- const query = calendarQuery();
- const joinThreadPromise = callJoinThread({
- threadID,
- calendarQuery: {
- startDate: query.startDate,
- endDate: query.endDate,
- filters: [
- ...query.filters,
- { type: 'threads', threadIDs: [threadID] },
- ],
- },
- inviteLinkSecret: inviteSecret,
- });
- void dispatchActionPromise(joinThreadActionTypes, joinThreadPromise);
-
- try {
- await joinThreadPromise;
- setStep('finished');
- ongoingJoinData.resolve();
- } catch (e) {
- setLinkStatus(status => (status === 'valid' ? 'invalid' : status));
- ongoingJoinData.reject();
- } finally {
- setOngoingJoinData(null);
- }
- })();
- }, [
- calendarQuery,
- callJoinThread,
- dispatchActionPromise,
+ setStep,
inviteSecret,
- ongoingJoinData,
setLinkStatus,
- step,
- ]);
+ threadID: verificationResponse.thread?.id,
+ });
const threadInfos = useSelector(threadInfoSelector);
React.useEffect(() => {
@@ -430,10 +232,10 @@
return React.useMemo(
() => ({
- join: createJoinPromise,
+ join: joinCommunity,
joinLoadingStatus,
}),
- [createJoinPromise, joinLoadingStatus],
+ [joinCommunity, joinLoadingStatus],
);
}
diff --git a/lib/shared/community-utils.js b/lib/shared/community-utils.js
--- a/lib/shared/community-utils.js
+++ b/lib/shared/community-utils.js
@@ -9,14 +9,31 @@
deleteFarcasterChannelTagActionTypes,
useDeleteFarcasterChannelTag,
} from '../actions/community-actions.js';
+import { addKeyserverActionType } from '../actions/keyserver-actions.js';
+import {
+ joinThreadActionTypes,
+ useJoinThread,
+} from '../actions/thread-actions.js';
+import type { LinkStatus } from '../hooks/invite-links.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 { permissionsAndAuthRelatedRequestTimeout } from '../shared/timeouts.js';
+import type {
+ JoinCommunityStep,
+ OngoingJoinCommunityData,
+} from '../types/community-types.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 { ThreadInfo } from '../types/minimally-encoded-thread-permissions-types.js';
import { threadPermissions } from '../types/thread-permission-types.js';
import { threadTypes } from '../types/thread-types-enum.js';
import { useCurrentUserFID } from '../utils/farcaster-utils.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 tagFarcasterChannelCopy = {
DESCRIPTION:
@@ -160,6 +177,246 @@
);
}
+type UseJoinCommunityParams = {
+ +communityID: ?string,
+ +keyserverOverride: ?KeyserverOverride,
+ +calendarQuery: () => CalendarQuery,
+ +ongoingJoinData: ?OngoingJoinCommunityData,
+ +setOngoingJoinData: SetState<?OngoingJoinCommunityData>,
+ +step: JoinCommunityStep,
+ +setStep: SetState<JoinCommunityStep>,
+ +inviteSecret: string,
+ +setLinkStatus: SetState<LinkStatus>,
+ +threadID?: string,
+};
+function useJoinCommunity(params: UseJoinCommunityParams): () => Promise<void> {
+ const {
+ communityID,
+ keyserverOverride,
+ calendarQuery,
+ ongoingJoinData,
+ setOngoingJoinData,
+ step,
+ setStep,
+ inviteSecret,
+ setLinkStatus,
+ threadID,
+ } = params;
+
+ const dispatch = useDispatch();
+ const dispatchActionPromise = useDispatchActionPromise();
+ const callJoinThread = useJoinThread();
+
+ 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 timeoutRef = React.useRef<?TimeoutID>();
+
+ React.useEffect(() => {
+ return () => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ };
+ }, []);
+
+ const createJoinPromise = React.useCallback(() => {
+ return new Promise<void>((resolve, reject) => {
+ if (
+ !keyserverID ||
+ !communityID ||
+ keyserverID !== extractKeyserverIDFromID(communityID)
+ ) {
+ reject();
+ setLinkStatus('invalid');
+ setOngoingJoinData(null);
+ return;
+ }
+ const timeoutID = setTimeout(() => {
+ reject();
+ setOngoingJoinData(oldData => {
+ if (oldData) {
+ setLinkStatus('timed_out');
+ }
+ return null;
+ });
+ }, permissionsAndAuthRelatedRequestTimeout);
+ timeoutRef.current = timeoutID;
+ const resolveAndClearTimeout = () => {
+ clearTimeout(timeoutID);
+ resolve();
+ };
+ const rejectAndClearTimeout = () => {
+ clearTimeout(timeoutID);
+ reject();
+ };
+ setOngoingJoinData({
+ resolve: resolveAndClearTimeout,
+ reject: rejectAndClearTimeout,
+ communityID,
+ threadID,
+ });
+ setStep('add_keyserver');
+ });
+ }, [
+ communityID,
+ keyserverID,
+ setLinkStatus,
+ setOngoingJoinData,
+ setStep,
+ threadID,
+ ]);
+
+ React.useEffect(() => {
+ void (async () => {
+ if (!ongoingJoinData || step !== 'add_keyserver') {
+ return;
+ }
+ if (isKeyserverKnown) {
+ setStep('auth_to_keyserver');
+ return;
+ }
+
+ const isValid = await isKeyserverURLValid();
+ if (!isValid || !keyserverURL) {
+ setLinkStatus('invalid');
+ ongoingJoinData.reject();
+ setOngoingJoinData(null);
+ return;
+ }
+ dispatch({
+ type: addKeyserverActionType,
+ payload: {
+ keyserverAdminUserID: keyserverID,
+ newKeyserverInfo: defaultKeyserverInfo(keyserverURL),
+ },
+ });
+ })();
+ }, [
+ dispatch,
+ isKeyserverKnown,
+ isKeyserverURLValid,
+ keyserverID,
+ keyserverURL,
+ ongoingJoinData,
+ setLinkStatus,
+ setOngoingJoinData,
+ setStep,
+ step,
+ ]);
+
+ React.useEffect(() => {
+ if (step === 'auth_to_keyserver' && ongoingJoinData && isAuthenticated) {
+ setStep('join_community');
+ }
+ }, [isAuthenticated, ongoingJoinData, setStep, step]);
+
+ React.useEffect(() => {
+ void (async () => {
+ if (!ongoingJoinData || step !== 'join_community') {
+ return;
+ }
+ const communityThreadID = ongoingJoinData.communityID;
+ const query = calendarQuery();
+ const joinThreadPromise = callJoinThread({
+ threadID: communityThreadID,
+ calendarQuery: {
+ startDate: query.startDate,
+ endDate: query.endDate,
+ filters: [
+ ...query.filters,
+ { type: 'threads', threadIDs: [communityThreadID] },
+ ],
+ },
+ inviteLinkSecret: inviteSecret,
+ });
+ void dispatchActionPromise(joinThreadActionTypes, joinThreadPromise);
+
+ try {
+ await joinThreadPromise;
+ setStep('join_thread');
+ } catch (e) {
+ setLinkStatus(status => (status === 'valid' ? 'invalid' : status));
+ ongoingJoinData.reject();
+ setOngoingJoinData(null);
+ }
+ })();
+ }, [
+ calendarQuery,
+ callJoinThread,
+ dispatchActionPromise,
+ inviteSecret,
+ ongoingJoinData,
+ setLinkStatus,
+ setOngoingJoinData,
+ setStep,
+ step,
+ ]);
+
+ React.useEffect(() => {
+ void (async () => {
+ if (!ongoingJoinData || step !== 'join_thread') {
+ return;
+ }
+ if (!threadID) {
+ setStep('finished');
+ ongoingJoinData.resolve();
+ setOngoingJoinData(null);
+ return;
+ }
+
+ const query = calendarQuery();
+ const joinThreadPromise = callJoinThread({
+ threadID,
+ calendarQuery: {
+ startDate: query.startDate,
+ endDate: query.endDate,
+ filters: [
+ ...query.filters,
+ { type: 'threads', threadIDs: [threadID] },
+ ],
+ },
+ inviteLinkSecret: inviteSecret,
+ });
+ void dispatchActionPromise(joinThreadActionTypes, joinThreadPromise);
+
+ try {
+ await joinThreadPromise;
+ setStep('finished');
+ ongoingJoinData.resolve();
+ } catch (e) {
+ setLinkStatus(status => (status === 'valid' ? 'invalid' : status));
+ ongoingJoinData.reject();
+ } finally {
+ setOngoingJoinData(null);
+ }
+ })();
+ }, [
+ calendarQuery,
+ callJoinThread,
+ dispatchActionPromise,
+ inviteSecret,
+ ongoingJoinData,
+ setLinkStatus,
+ setOngoingJoinData,
+ setStep,
+ step,
+ threadID,
+ ]);
+
+ return createJoinPromise;
+}
+
export {
tagFarcasterChannelCopy,
tagFarcasterChannelErrorMessages,
@@ -167,4 +424,5 @@
useCreateFarcasterChannelTag,
useRemoveFarcasterChannelTag,
useCanManageFarcasterChannelTag,
+ useJoinCommunity,
};
diff --git a/lib/types/community-types.js b/lib/types/community-types.js
--- a/lib/types/community-types.js
+++ b/lib/types/community-types.js
@@ -52,3 +52,18 @@
export type DeleteFarcasterChannelTagPayload = {
+commCommunityID: string,
};
+
+export type OngoingJoinCommunityData = {
+ +resolve: () => mixed,
+ +reject: () => mixed,
+ +communityID: string,
+ +threadID: ?string,
+};
+
+export type JoinCommunityStep =
+ | 'inactive'
+ | 'add_keyserver'
+ | 'auth_to_keyserver'
+ | 'join_community'
+ | 'join_thread'
+ | 'finished';

File Metadata

Mime Type
text/plain
Expires
Sun, Dec 29, 6:19 AM (41 m, 3 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2730985
Default Alt Text
D12706.diff (17 KB)

Event Timeline