diff --git a/native/components/auto-join-community-handler.react.js b/lib/components/base-auto-join-community-handler.react.js
similarity index 82%
copy from native/components/auto-join-community-handler.react.js
copy to lib/components/base-auto-join-community-handler.react.js
index 3cd2b0aa7..638e3811f 100644
--- a/native/components/auto-join-community-handler.react.js
+++ b/lib/components/base-auto-join-community-handler.react.js
@@ -1,298 +1,293 @@
// @flow
import invariant from 'invariant';
import _pickBy from 'lodash/fp/pickBy.js';
import * as React from 'react';
-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 { NeynarClientContext } from '../components/neynar-client-provider.react.js';
+import blobService from '../facts/blob-service.js';
+import { useIsLoggedInToIdentityAndAuthoritativeKeyserver } from '../hooks/account-hooks.js';
+import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-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';
+} from '../shared/community-utils.js';
+import type { AuthMetadata } from '../shared/identity-client-context.js';
+import { IdentityClientContext } from '../shared/identity-client-context.js';
+import type { KeyserverOverride } from '../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 { promiseAll } from 'lib/utils/promises.js';
+} from '../types/community-types.js';
+import type { CalendarQuery } from '../types/entry-types.js';
+import type { SetState } from '../types/hook-types.js';
+import { defaultThreadSubscription } from '../types/subscription-types.js';
+import { getBlobFetchableURL } from '../utils/blob-service.js';
+import { useCurrentUserFID } from '../utils/farcaster-utils.js';
+import { promiseAll } from '../utils/promises.js';
+import { useSelector } from '../utils/redux-utils.js';
import {
usingCommServicesAccessToken,
createDefaultHTTPRequestHeaders,
-} from 'lib/utils/services-utils.js';
-
-import { nonThreadCalendarQuery } from '../navigation/nav-selectors.js';
-import { NavContext } from '../navigation/navigation-context.js';
-import { useSelector } from '../redux/redux-utils.js';
+} from '../utils/services-utils.js';
type CommunityToAutoJoin = {
+communityID: string,
+keyserverOverride: ?KeyserverOverride,
+joinStatus: 'inactive' | 'joining' | 'joined',
};
type CommunitiesToAutoJoin = {
+[communityID: string]: CommunityToAutoJoin,
};
-function AutoJoinCommunityHandler(): React.Node {
+type Props = {
+ +calendarQuery: () => CalendarQuery,
+};
+
+function BaseAutoJoinCommunityHandler(props: Props): React.Node {
+ const { calendarQuery } = props;
+
const isActive = useSelector(state => state.lifecycleState !== 'background');
const loggedIn = useIsLoggedInToIdentityAndAuthoritativeKeyserver();
const fid = useCurrentUserFID();
const neynarClient = React.useContext(NeynarClientContext)?.client;
- const navContext = React.useContext(NavContext);
-
const identityClientContext = React.useContext(IdentityClientContext);
invariant(identityClientContext, 'IdentityClientContext should be set');
const { getAuthMetadata } = identityClientContext;
- const calendarQuery = useSelector(state =>
- nonThreadCalendarQuery({
- redux: state,
- navContext,
- }),
- );
-
const threadInfos = useSelector(state => state.threadStore.threadInfos);
const keyserverInfos = useSelector(
state => state.keyserverStore.keyserverInfos,
);
const [communitiesToAutoJoin, setCommunitiesToAutoJoin] =
React.useState();
const prevCanQueryRef = React.useRef();
const canQuery = loggedIn;
React.useEffect(() => {
if (canQuery === prevCanQueryRef.current) {
return;
}
prevCanQueryRef.current = canQuery;
if (!loggedIn || !isActive || !fid || !neynarClient || !threadInfos) {
return;
}
void (async () => {
const authMetadataPromise: Promise = (async () => {
if (!usingCommServicesAccessToken) {
return undefined;
}
return await getAuthMetadata();
})();
const followedFarcasterChannelsPromise =
neynarClient.fetchFollowedFarcasterChannels(fid);
const [authMetadata, followedFarcasterChannels] = await Promise.all([
authMetadataPromise,
followedFarcasterChannelsPromise,
]);
const headers = authMetadata
? createDefaultHTTPRequestHeaders(authMetadata)
: {};
const followedFarcasterChannelIDs = followedFarcasterChannels.map(
channel => channel.id,
);
const promises: { [string]: Promise } = {};
for (const channelID of followedFarcasterChannelIDs) {
promises[channelID] = (async () => {
const blobHash = farcasterChannelTagBlobHash(channelID);
const blobURL = getBlobFetchableURL(blobHash);
const blobResult = await fetch(blobURL, {
method: blobService.httpEndpoints.GET_BLOB.method,
headers,
});
if (blobResult.status !== 200) {
return null;
}
const { commCommunityID, keyserverURL } = await blobResult.json();
const keyserverID = extractKeyserverIDFromID(commCommunityID);
// 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);
const filteredCommunitiesObj = _pickBy(Boolean)(communitiesObj);
const communitesToJoin: { ...CommunitiesToAutoJoin } = {};
for (const key in filteredCommunitiesObj) {
const communityID = filteredCommunitiesObj[key].communityID;
communitesToJoin[communityID] = filteredCommunitiesObj[key];
}
setCommunitiesToAutoJoin(communitesToJoin);
})();
}, [
threadInfos,
fid,
isActive,
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 (
);
});
}, [calendarQuery, communitiesToAutoJoin]);
return joinHandlers;
}
type JoinHandlerProps = {
+communityID: string,
+keyserverOverride: ?KeyserverOverride,
+calendarQuery: () => CalendarQuery,
+communitiesToAutoJoin: CommunitiesToAutoJoin,
+setCommunitiesToAutoJoin: SetState,
};
+
function JoinHandler(props: JoinHandlerProps) {
const {
communityID,
keyserverOverride,
calendarQuery,
communitiesToAutoJoin,
setCommunitiesToAutoJoin,
} = props;
const [ongoingJoinData, setOngoingJoinData] =
React.useState(null);
const [step, setStep] = React.useState('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;
}
-export { AutoJoinCommunityHandler };
+export { BaseAutoJoinCommunityHandler };
diff --git a/native/components/auto-join-community-handler.react.js b/native/components/auto-join-community-handler.react.js
index 3cd2b0aa7..8cb46eb5d 100644
--- a/native/components/auto-join-community-handler.react.js
+++ b/native/components/auto-join-community-handler.react.js
@@ -1,298 +1,24 @@
// @flow
-import invariant from 'invariant';
-import _pickBy from 'lodash/fp/pickBy.js';
import * as React from 'react';
-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,
- 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 { promiseAll } from 'lib/utils/promises.js';
-import {
- usingCommServicesAccessToken,
- createDefaultHTTPRequestHeaders,
-} from 'lib/utils/services-utils.js';
+import { BaseAutoJoinCommunityHandler } from 'lib/components/base-auto-join-community-handler.react.js';
import { nonThreadCalendarQuery } from '../navigation/nav-selectors.js';
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');
-
- const loggedIn = useIsLoggedInToIdentityAndAuthoritativeKeyserver();
-
- const fid = useCurrentUserFID();
-
- const neynarClient = React.useContext(NeynarClientContext)?.client;
-
const navContext = React.useContext(NavContext);
- const identityClientContext = React.useContext(IdentityClientContext);
- invariant(identityClientContext, 'IdentityClientContext should be set');
- const { getAuthMetadata } = identityClientContext;
-
const calendarQuery = useSelector(state =>
nonThreadCalendarQuery({
redux: state,
navContext,
}),
);
- const threadInfos = useSelector(state => state.threadStore.threadInfos);
-
- const keyserverInfos = useSelector(
- state => state.keyserverStore.keyserverInfos,
- );
-
- const [communitiesToAutoJoin, setCommunitiesToAutoJoin] =
- React.useState();
-
- const prevCanQueryRef = React.useRef();
- const canQuery = loggedIn;
-
- React.useEffect(() => {
- if (canQuery === prevCanQueryRef.current) {
- return;
- }
-
- prevCanQueryRef.current = canQuery;
- if (!loggedIn || !isActive || !fid || !neynarClient || !threadInfos) {
- return;
- }
-
- void (async () => {
- const authMetadataPromise: Promise = (async () => {
- if (!usingCommServicesAccessToken) {
- return undefined;
- }
- return await getAuthMetadata();
- })();
-
- const followedFarcasterChannelsPromise =
- neynarClient.fetchFollowedFarcasterChannels(fid);
-
- const [authMetadata, followedFarcasterChannels] = await Promise.all([
- authMetadataPromise,
- followedFarcasterChannelsPromise,
- ]);
-
- const headers = authMetadata
- ? createDefaultHTTPRequestHeaders(authMetadata)
- : {};
-
- const followedFarcasterChannelIDs = followedFarcasterChannels.map(
- channel => channel.id,
- );
-
- const promises: { [string]: Promise } = {};
-
- for (const channelID of followedFarcasterChannelIDs) {
- promises[channelID] = (async () => {
- const blobHash = farcasterChannelTagBlobHash(channelID);
- const blobURL = getBlobFetchableURL(blobHash);
-
- const blobResult = await fetch(blobURL, {
- method: blobService.httpEndpoints.GET_BLOB.method,
- headers,
- });
-
- if (blobResult.status !== 200) {
- return null;
- }
-
- const { commCommunityID, keyserverURL } = await blobResult.json();
- const keyserverID = extractKeyserverIDFromID(commCommunityID);
-
- // 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);
-
- const filteredCommunitiesObj = _pickBy(Boolean)(communitiesObj);
-
- const communitesToJoin: { ...CommunitiesToAutoJoin } = {};
-
- for (const key in filteredCommunitiesObj) {
- const communityID = filteredCommunitiesObj[key].communityID;
- communitesToJoin[communityID] = filteredCommunitiesObj[key];
- }
-
- setCommunitiesToAutoJoin(communitesToJoin);
- })();
- }, [
- threadInfos,
- fid,
- isActive,
- 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 (
-
- );
- });
- }, [calendarQuery, communitiesToAutoJoin]);
-
- return joinHandlers;
-}
-
-type JoinHandlerProps = {
- +communityID: string,
- +keyserverOverride: ?KeyserverOverride,
- +calendarQuery: () => CalendarQuery,
- +communitiesToAutoJoin: CommunitiesToAutoJoin,
- +setCommunitiesToAutoJoin: SetState,
-};
-function JoinHandler(props: JoinHandlerProps) {
- const {
- communityID,
- keyserverOverride,
- calendarQuery,
- communitiesToAutoJoin,
- setCommunitiesToAutoJoin,
- } = props;
-
- const [ongoingJoinData, setOngoingJoinData] =
- React.useState(null);
-
- const [step, setStep] = React.useState('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;
+ return ;
}
export { AutoJoinCommunityHandler };