Page MenuHomePhorge

D12714.1765198520.diff
No OneTemporary

Size
14 KB
Referenced Files
None
Subscribers
None

D12714.1765198520.diff

diff --git a/lib/components/base-auto-join-community-handler.react.js b/lib/components/base-auto-join-community-handler.react.js
new file mode 100644
--- /dev/null
+++ b/lib/components/base-auto-join-community-handler.react.js
@@ -0,0 +1,217 @@
+// @flow
+
+import invariant from 'invariant';
+import _pickBy from 'lodash/fp/pickBy.js';
+import * as React from 'react';
+
+import { NeynarClientContext } from '../components/neynar-client-provider.react.js';
+import blobService from '../facts/blob-service.js';
+import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { isLoggedInToIdentityAndAuthoritativeKeyserver } from '../selectors/user-selectors.js';
+import {
+ farcasterChannelTagBlobHash,
+ useJoinCommunity,
+} 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 '../types/community-types.js';
+import type { CalendarQuery } from '../types/entry-types.js';
+import { defaultThreadSubscription } from '../types/subscription-types.js';
+import { getBlobFetchableURL } from '../utils/blob-service.js';
+import { useCurrentUserFID } from '../utils/farcaster-utils.js';
+import { values } from '../utils/objects.js';
+import { promiseAll } from '../utils/promises.js';
+import { useSelector } from '../utils/redux-utils.js';
+import {
+ usingCommServicesAccessToken,
+ createDefaultHTTPRequestHeaders,
+} from '../utils/services-utils.js';
+
+type CommunityToAutoJoin = {
+ +communityID: string,
+ +keyserverOverride: ?KeyserverOverride,
+};
+
+type Props = {
+ +calendarQuery: () => CalendarQuery,
+};
+function BaseAutoJoinCommunityHandler(props: Props): React.Node {
+ const { calendarQuery } = props;
+
+ const isActive = useSelector(state => state.lifecycleState !== 'background');
+
+ const loggedIn = useSelector(isLoggedInToIdentityAndAuthoritativeKeyserver);
+
+ const fid = useCurrentUserFID();
+
+ const neynarClient = React.useContext(NeynarClientContext)?.client;
+
+ const identityClientContext = React.useContext(IdentityClientContext);
+ invariant(identityClientContext, 'IdentityClientContext should be set');
+ const { getAuthMetadata } = identityClientContext;
+
+ const threadInfos = useSelector(state => state.threadStore.threadInfos);
+
+ const keyserverInfos = useSelector(
+ state => state.keyserverStore.keyserverInfos,
+ );
+
+ const [communitiesToAutoJoin, setCommunitiesToAutoJoin] =
+ React.useState<?$ReadOnlyArray<CommunityToAutoJoin>>();
+
+ const prevCanQueryRef = React.useRef<?boolean>();
+ const canQuery = loggedIn;
+
+ React.useEffect(() => {
+ if (canQuery === prevCanQueryRef.current) {
+ setCommunitiesToAutoJoin(null);
+ return;
+ }
+
+ prevCanQueryRef.current = canQuery;
+ if (!loggedIn || !isActive || !fid || !neynarClient || !threadInfos) {
+ return;
+ }
+
+ console.log('running auto join effect');
+
+ 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: { [string]: Promise<?CommunityToAutoJoin> } = {};
+
+ 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,
+ };
+ })();
+ }
+
+ const communitiesObj = await promiseAll(promises);
+
+ const filteredCommunitiesObj = _pickBy(Boolean)(communitiesObj);
+
+ const communities = values(filteredCommunitiesObj);
+
+ if (communities.length === 0) {
+ return;
+ }
+
+ console.log('setting communities to auto join');
+ setCommunitiesToAutoJoin(communities);
+ })();
+ }, [
+ threadInfos,
+ fid,
+ isActive,
+ loggedIn,
+ neynarClient,
+ getAuthMetadata,
+ keyserverInfos,
+ canQuery,
+ ]);
+
+ const joinHandlers = React.useMemo(
+ () =>
+ communitiesToAutoJoin?.map((communityToAutoJoin, index) => (
+ <JoinHandler
+ key={index}
+ communityID={communityToAutoJoin.communityID}
+ keyserverOverride={communityToAutoJoin.keyserverOverride}
+ calendarQuery={calendarQuery}
+ />
+ )),
+ [calendarQuery, communitiesToAutoJoin],
+ );
+
+ return joinHandlers;
+}
+
+type JoinHandlerProps = {
+ +communityID: string,
+ +keyserverOverride: ?KeyserverOverride,
+ +calendarQuery: () => CalendarQuery,
+};
+function JoinHandler(props: JoinHandlerProps) {
+ const { communityID, keyserverOverride, calendarQuery } = props;
+
+ const [ongoingJoinData, setOngoingJoinData] =
+ React.useState<?OngoingJoinCommunityData>(null);
+
+ const [step, setStep] = React.useState<JoinCommunityStep>('inactive');
+
+ console.log('regenerating useJoinCommunity');
+
+ const joinCommunity = useJoinCommunity({
+ communityID,
+ keyserverOverride,
+ calendarQuery,
+ ongoingJoinData,
+ setOngoingJoinData,
+ step,
+ setStep,
+ defaultSubscription: defaultThreadSubscription,
+ });
+
+ React.useEffect(() => {
+ void joinCommunity();
+ }, [joinCommunity]);
+
+ return null;
+}
+
+export { BaseAutoJoinCommunityHandler };
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,29 @@
// @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 { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
import { isLoggedInToIdentityAndAuthoritativeKeyserver } from 'lib/selectors/user-selectors.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 { 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 { values } from 'lib/utils/objects.js';
+import { promiseAll } from 'lib/utils/promises.js';
import {
usingCommServicesAccessToken,
createDefaultHTTPRequestHeaders,
@@ -27,6 +33,11 @@
import { NavContext } from '../navigation/navigation-context.js';
import { useSelector } from '../redux/redux-utils.js';
+type CommunityToAutoJoin = {
+ +communityID: string,
+ +keyserverOverride: ?KeyserverOverride,
+};
+
function AutoJoinCommunityHandler(): React.Node {
const isActive = useSelector(state => state.lifecycleState !== 'background');
@@ -36,54 +47,35 @@
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] },
- ],
- },
- 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<?$ReadOnlyArray<CommunityToAutoJoin>>();
+
+ 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;
}
+ console.log('running auto join effect');
+
void (async () => {
const authMetadataPromise: Promise<?AuthMetadata> = (async () => {
if (!usingCommServicesAccessToken) {
@@ -108,51 +100,121 @@
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;
+ }
- void dispatchActionPromise(
- joinThreadActionTypes,
- joinThreadActionPromise(commCommunityID),
- );
- });
+ const keyserverOverride = !keyserverInfos[keyserverID]
+ ? {
+ keyserverID,
+ keyserverURL: keyserverURL.replace(/\/$/, ''),
+ }
+ : null;
- await Promise.all(promises);
+ return {
+ communityID: commCommunityID,
+ keyserverOverride,
+ };
+ })();
+ }
+
+ const communitiesObj = await promiseAll(promises);
+
+ const filteredCommunitiesObj = _pickBy(Boolean)(communitiesObj);
+
+ const communities = values(filteredCommunitiesObj);
+
+ if (communities.length === 0) {
+ return;
+ }
+
+ console.log('setting communities to auto join');
+ setCommunitiesToAutoJoin(communities);
})();
}, [
threadInfos,
- dispatchActionPromise,
fid,
isActive,
- joinThreadActionPromise,
loggedIn,
neynarClient,
getAuthMetadata,
keyserverInfos,
+ canQuery,
]);
+ const joinHandlers = React.useMemo(
+ () =>
+ communitiesToAutoJoin?.map(communityToAutoJoin => (
+ <JoinHandler
+ key={communityToAutoJoin.communityID}
+ communityID={communityToAutoJoin.communityID}
+ keyserverOverride={communityToAutoJoin.keyserverOverride}
+ />
+ )),
+ [communitiesToAutoJoin],
+ );
+
+ return joinHandlers;
+}
+
+type JoinHandlerProps = {
+ +communityID: string,
+ +keyserverOverride: ?KeyserverOverride,
+};
+function JoinHandler(props: JoinHandlerProps) {
+ const { communityID, keyserverOverride } = props;
+
+ const navContext = React.useContext(NavContext);
+
+ const calendarQuery = useSelector(state =>
+ nonThreadCalendarQuery({
+ redux: state,
+ navContext,
+ }),
+ );
+
+ const [ongoingJoinData, setOngoingJoinData] =
+ React.useState<?OngoingJoinCommunityData>(null);
+
+ const [step, setStep] = React.useState<JoinCommunityStep>('inactive');
+
+ console.log('regenerating useJoinCommunity');
+
+ const joinCommunity = useJoinCommunity({
+ communityID,
+ keyserverOverride,
+ calendarQuery,
+ ongoingJoinData,
+ setOngoingJoinData,
+ step,
+ setStep,
+ defaultSubscription: defaultThreadSubscription,
+ });
+
+ React.useEffect(() => {
+ void joinCommunity();
+ }, [joinCommunity]);
+
return null;
}

File Metadata

Mime Type
text/plain
Expires
Mon, Dec 8, 12:55 PM (6 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5848331
Default Alt Text
D12714.1765198520.diff (14 KB)

Event Timeline