diff --git a/native/components/auto-join-community-handler.react.js b/native/components/auto-join-community-handler.react.js
--- a/native/components/auto-join-community-handler.react.js
+++ b/native/components/auto-join-community-handler.react.js
@@ -1,23 +1,30 @@
 // @flow
 
 import invariant from 'invariant';
+import _pickBy from 'lodash/fp/pickBy.js';
 import * as React from 'react';
 
-import {
-  joinThreadActionTypes,
-  useJoinThread,
-} from 'lib/actions/thread-actions.js';
 import { NeynarClientContext } from 'lib/components/neynar-client-provider.react.js';
 import blobService from 'lib/facts/blob-service.js';
 import { useIsLoggedInToIdentityAndAuthoritativeKeyserver } from 'lib/hooks/account-hooks.js';
 import { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
-import { farcasterChannelTagBlobHash } from 'lib/shared/community-utils.js';
+import {
+  farcasterChannelTagBlobHash,
+  useJoinCommunity,
+} from 'lib/shared/community-utils.js';
 import type { AuthMetadata } from 'lib/shared/identity-client-context.js';
 import { IdentityClientContext } from 'lib/shared/identity-client-context.js';
+import type { KeyserverOverride } from 'lib/shared/invite-links.js';
+import type {
+  OngoingJoinCommunityData,
+  JoinCommunityStep,
+} from 'lib/types/community-types.js';
+import type { CalendarQuery } from 'lib/types/entry-types.js';
+import type { SetState } from 'lib/types/hook-types.js';
 import { defaultThreadSubscription } from 'lib/types/subscription-types.js';
 import { getBlobFetchableURL } from 'lib/utils/blob-service.js';
 import { useCurrentUserFID } from 'lib/utils/farcaster-utils.js';
-import { useDispatchActionPromise } from 'lib/utils/redux-promise-utils.js';
+import { promiseAll } from 'lib/utils/promises.js';
 import {
   usingCommServicesAccessToken,
   createDefaultHTTPRequestHeaders,
@@ -27,6 +34,16 @@
 import { NavContext } from '../navigation/navigation-context.js';
 import { useSelector } from '../redux/redux-utils.js';
 
+type CommunityToAutoJoin = {
+  +communityID: string,
+  +keyserverOverride: ?KeyserverOverride,
+  +joinStatus: 'inactive' | 'joining' | 'joined',
+};
+
+type CommunitiesToAutoJoin = {
+  +[communityID: string]: CommunityToAutoJoin,
+};
+
 function AutoJoinCommunityHandler(): React.Node {
   const isActive = useSelector(state => state.lifecycleState !== 'background');
 
@@ -49,37 +66,24 @@
     }),
   );
 
-  const joinThread = useJoinThread();
-
-  const joinThreadActionPromise = React.useCallback(
-    async (communityID: string) => {
-      const query = calendarQuery();
-
-      return await joinThread({
-        threadID: communityID,
-        calendarQuery: {
-          startDate: query.startDate,
-          endDate: query.endDate,
-          filters: [
-            ...query.filters,
-            { type: 'threads', threadIDs: [communityID] },
-          ],
-        },
-        defaultSubscription: defaultThreadSubscription,
-      });
-    },
-    [calendarQuery, joinThread],
-  );
-
-  const dispatchActionPromise = useDispatchActionPromise();
-
   const threadInfos = useSelector(state => state.threadStore.threadInfos);
 
   const keyserverInfos = useSelector(
     state => state.keyserverStore.keyserverInfos,
   );
 
+  const [communitiesToAutoJoin, setCommunitiesToAutoJoin] =
+    React.useState<?CommunitiesToAutoJoin>();
+
+  const prevCanQueryRef = React.useRef<?boolean>();
+  const canQuery = loggedIn;
+
   React.useEffect(() => {
+    if (canQuery === prevCanQueryRef.current) {
+      return;
+    }
+
+    prevCanQueryRef.current = canQuery;
     if (!loggedIn || !isActive || !fid || !neynarClient || !threadInfos) {
       return;
     }
@@ -108,51 +112,186 @@
         channel => channel.id,
       );
 
-      const promises = followedFarcasterChannelIDs.map(async channelID => {
-        const blobHash = farcasterChannelTagBlobHash(channelID);
-        const blobURL = getBlobFetchableURL(blobHash);
+      const promises: { [string]: Promise<?CommunityToAutoJoin> } = {};
 
-        const blobResult = await fetch(blobURL, {
-          method: blobService.httpEndpoints.GET_BLOB.method,
-          headers,
-        });
+      for (const channelID of followedFarcasterChannelIDs) {
+        promises[channelID] = (async () => {
+          const blobHash = farcasterChannelTagBlobHash(channelID);
+          const blobURL = getBlobFetchableURL(blobHash);
 
-        if (blobResult.status !== 200) {
-          return;
-        }
+          const blobResult = await fetch(blobURL, {
+            method: blobService.httpEndpoints.GET_BLOB.method,
+            headers,
+          });
 
-        const { commCommunityID } = await blobResult.json();
-        const keyserverID = extractKeyserverIDFromID(commCommunityID);
+          if (blobResult.status !== 200) {
+            return null;
+          }
 
-        if (!keyserverInfos[keyserverID]) {
-          return;
-        }
+          const { commCommunityID, keyserverURL } = await blobResult.json();
+          const keyserverID = extractKeyserverIDFromID(commCommunityID);
 
-        // The user is already in the community
-        if (threadInfos[commCommunityID]) {
-          return;
-        }
+          // The user is already in the community
+          if (threadInfos[commCommunityID]) {
+            return null;
+          }
+
+          const keyserverOverride = !keyserverInfos[keyserverID]
+            ? {
+                keyserverID,
+                keyserverURL: keyserverURL.replace(/\/$/, ''),
+              }
+            : null;
+
+          return {
+            communityID: commCommunityID,
+            keyserverOverride,
+            joinStatus: 'inactive',
+          };
+        })();
+      }
+
+      const communitiesObj = await promiseAll(promises);
 
-        void dispatchActionPromise(
-          joinThreadActionTypes,
-          joinThreadActionPromise(commCommunityID),
-        );
-      });
+      const filteredCommunitiesObj = _pickBy(Boolean)(communitiesObj);
 
-      await Promise.all(promises);
+      const communitesToJoin: { ...CommunitiesToAutoJoin } = {};
+
+      for (const key in filteredCommunitiesObj) {
+        const communityID = filteredCommunitiesObj[key].communityID;
+        communitesToJoin[communityID] = filteredCommunitiesObj[key];
+      }
+
+      setCommunitiesToAutoJoin(communitesToJoin);
     })();
   }, [
     threadInfos,
-    dispatchActionPromise,
     fid,
     isActive,
-    joinThreadActionPromise,
     loggedIn,
     neynarClient,
     getAuthMetadata,
     keyserverInfos,
+    canQuery,
   ]);
 
+  const joinHandlers = React.useMemo(() => {
+    if (!communitiesToAutoJoin) {
+      return null;
+    }
+
+    return Object.keys(communitiesToAutoJoin).map(id => {
+      const communityToAutoJoin = communitiesToAutoJoin[id];
+
+      const { communityID, keyserverOverride, joinStatus } =
+        communityToAutoJoin;
+
+      if (joinStatus === 'joined') {
+        return null;
+      }
+
+      return (
+        <JoinHandler
+          key={communityID}
+          communityID={communityID}
+          keyserverOverride={keyserverOverride}
+          calendarQuery={calendarQuery}
+          communitiesToAutoJoin={communitiesToAutoJoin}
+          setCommunitiesToAutoJoin={setCommunitiesToAutoJoin}
+        />
+      );
+    });
+  }, [calendarQuery, communitiesToAutoJoin]);
+
+  return joinHandlers;
+}
+
+type JoinHandlerProps = {
+  +communityID: string,
+  +keyserverOverride: ?KeyserverOverride,
+  +calendarQuery: () => CalendarQuery,
+  +communitiesToAutoJoin: CommunitiesToAutoJoin,
+  +setCommunitiesToAutoJoin: SetState<?CommunitiesToAutoJoin>,
+};
+function JoinHandler(props: JoinHandlerProps) {
+  const {
+    communityID,
+    keyserverOverride,
+    calendarQuery,
+    communitiesToAutoJoin,
+    setCommunitiesToAutoJoin,
+  } = props;
+
+  const [ongoingJoinData, setOngoingJoinData] =
+    React.useState<?OngoingJoinCommunityData>(null);
+
+  const [step, setStep] = React.useState<JoinCommunityStep>('inactive');
+
+  const joinCommunity = useJoinCommunity({
+    communityID,
+    keyserverOverride,
+    calendarQuery,
+    ongoingJoinData,
+    setOngoingJoinData,
+    step,
+    setStep,
+    defaultSubscription: defaultThreadSubscription,
+  });
+
+  React.useEffect(() => {
+    const joinStatus = communitiesToAutoJoin[communityID]?.joinStatus;
+    if (joinStatus !== 'inactive') {
+      return;
+    }
+
+    void joinCommunity();
+  }, [
+    communitiesToAutoJoin,
+    communityID,
+    joinCommunity,
+    setCommunitiesToAutoJoin,
+  ]);
+
+  React.useEffect(() => {
+    if (step !== 'add_keyserver') {
+      return;
+    }
+
+    setCommunitiesToAutoJoin(prev => {
+      if (!prev) {
+        return null;
+      }
+
+      return {
+        ...prev,
+        [communityID]: {
+          ...prev[communityID],
+          joinStatus: 'joining',
+        },
+      };
+    });
+  }, [communityID, setCommunitiesToAutoJoin, step]);
+
+  React.useEffect(() => {
+    if (step !== 'finished') {
+      return;
+    }
+
+    setCommunitiesToAutoJoin(prev => {
+      if (!prev) {
+        return null;
+      }
+
+      return {
+        ...prev,
+        [communityID]: {
+          ...prev[communityID],
+          joinStatus: 'joined',
+        },
+      };
+    });
+  }, [communityID, step, setCommunitiesToAutoJoin]);
+
   return null;
 }