Page MenuHomePhabricator

D12065.id.diff
No OneTemporary

D12065.id.diff

diff --git a/native/components/auto-join-community-handler.react.js b/native/components/auto-join-community-handler.react.js
new file mode 100644
--- /dev/null
+++ b/native/components/auto-join-community-handler.react.js
@@ -0,0 +1,161 @@
+// @flow
+
+import invariant from 'invariant';
+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 { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
+import { cookieSelector } from 'lib/selectors/keyserver-selectors.js';
+import { farcasterChannelTagBlobHash } 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 { authoritativeKeyserverID } from 'lib/utils/authoritative-keyserver.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 {
+ 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';
+
+function AutoJoinCommunityHandler(): React.Node {
+ const isActive = useSelector(state => state.lifecycleState !== 'background');
+
+ const cookie = useSelector(cookieSelector(authoritativeKeyserverID()));
+ const hasUserCookie = !!(cookie && cookie.startsWith('user='));
+ const currentUserID = useSelector(state => state.currentUserInfo?.id);
+ const loggedIn = !!currentUserID && hasUserCookie;
+
+ 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 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] },
+ ],
+ },
+ });
+ },
+ [calendarQuery, joinThread],
+ );
+
+ const dispatchActionPromise = useDispatchActionPromise();
+
+ const threadInfos = useSelector(state => state.threadStore.threadInfos);
+
+ const keyserverInfos = useSelector(
+ state => state.keyserverStore.keyserverInfos,
+ );
+
+ React.useEffect(() => {
+ if (!loggedIn || !isActive || !fid || !neynarClient || !threadInfos) {
+ return;
+ }
+
+ void (async () => {
+ const authMetadataPromise: Promise<?AuthMetadata> = (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 = followedFarcasterChannelIDs.map(async channelID => {
+ 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;
+ }
+
+ const { commCommunityID } = await blobResult.json();
+ const keyserverID = extractKeyserverIDFromID(commCommunityID);
+
+ if (!keyserverInfos[keyserverID]) {
+ return;
+ }
+
+ // The user is already in the community
+ if (threadInfos[commCommunityID]) {
+ return;
+ }
+
+ void dispatchActionPromise(
+ joinThreadActionTypes,
+ joinThreadActionPromise(commCommunityID),
+ );
+ });
+
+ await Promise.all(promises);
+ })();
+ }, [
+ threadInfos,
+ dispatchActionPromise,
+ fid,
+ isActive,
+ joinThreadActionPromise,
+ loggedIn,
+ neynarClient,
+ getAuthMetadata,
+ keyserverInfos,
+ ]);
+
+ return null;
+}
+
+export { AutoJoinCommunityHandler };
diff --git a/native/root.react.js b/native/root.react.js
--- a/native/root.react.js
+++ b/native/root.react.js
@@ -48,6 +48,7 @@
import ChatContextProvider from './chat/chat-context-provider.react.js';
import MessageEditingContextProvider from './chat/message-editing-context-provider.react.js';
import AccessTokenHandler from './components/access-token-handler.react.js';
+import { AutoJoinCommunityHandler } from './components/auto-join-community-handler.react.js';
import BackgroundIdentityLoginHandler from './components/background-identity-login-handler.react.js';
import ConnectFarcasterAlertHandler from './components/connect-farcaster-alert-handler.react.js';
import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js';
@@ -346,6 +347,7 @@
<PrekeysHandler />
<ReportHandler />
<FarcasterDataHandler />
+ <AutoJoinCommunityHandler />
</PersistedStateGate>
{navigation}
</RegistrationContextProvider>

File Metadata

Mime Type
text/plain
Expires
Sat, Sep 28, 3:06 AM (3 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2192845
Default Alt Text
D12065.id.diff (6 KB)

Event Timeline