Skip to content

Commit

Permalink
[lib] Batch BaseAutoJoinCommunityHandler
Browse files Browse the repository at this point in the history
Summary: This diff updates `BaseAutoJoinCommunityHandler` to work in batches of 5, and to avoid triggering a `joinCommunity` for a single community more than once.

Test Plan: Varun tested this in his local environment, where he has set up a reproduction of the production environment where we have a lot of communities tagged with Farcaster channels, and he tested signing in as a user with a lot of channels

Reviewers: varun, will

Reviewed By: varun

Subscribers: tomek

Differential Revision: https://phab.comm.dev/D13583
  • Loading branch information
Ashoat committed Oct 3, 2024
1 parent 81d2447 commit 4c5fc78
Showing 1 changed file with 111 additions and 64 deletions.
175 changes: 111 additions & 64 deletions lib/components/base-auto-join-community-handler.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,26 @@ import {
usingCommServicesAccessToken,
createDefaultHTTPRequestHeaders,
} from '../utils/services-utils.js';
import sleep from '../utils/sleep.js';

type JoinStatus = 'inactive' | 'joining' | 'joined';

type CommunityToAutoJoin = {
+batch: number,
+communityID: string,
+keyserverOverride: ?KeyserverOverride,
+joinStatus: 'inactive' | 'joining' | 'joined',
+joinStatus: JoinStatus,
};

type CommunitiesToAutoJoin = {
type CommunityDatas = {
+[communityID: string]: CommunityToAutoJoin,
};

type CommunitiesToAutoJoin = {
+curBatch: number,
+communityDatas: CommunityDatas,
};

type Props = {
+calendarQuery: () => CalendarQuery,
};
Expand All @@ -66,7 +75,7 @@ function BaseAutoJoinCommunityHandler(props: Props): React.Node {
state => state.keyserverStore.keyserverInfos,
);

const [communitiesToAutoJoin, setCommunitiesToAutoJoin] =
const [communitiesToAutoJoin, baseSetCommunitiesToAutoJoin] =
React.useState<?CommunitiesToAutoJoin>();

const prevCanQueryRef = React.useRef<?boolean>();
Expand Down Expand Up @@ -106,7 +115,9 @@ function BaseAutoJoinCommunityHandler(props: Props): React.Node {
channel => channel.id,
);

const promises: { [string]: Promise<?CommunityToAutoJoin> } = {};
const promises: {
[string]: Promise<?$Diff<CommunityToAutoJoin, { +batch: number }>>,
} = {};

for (const channelID of followedFarcasterChannelIDs) {
promises[channelID] = (async () => {
Expand Down Expand Up @@ -149,14 +160,19 @@ function BaseAutoJoinCommunityHandler(props: Props): React.Node {

const filteredCommunitiesObj = _pickBy(Boolean)(communitiesObj);

const communitesToJoin: { ...CommunitiesToAutoJoin } = {};
const communityDatas: { ...CommunityDatas } = {};

let i = 0;
for (const key in filteredCommunitiesObj) {
const communityID = filteredCommunitiesObj[key].communityID;
communitesToJoin[communityID] = filteredCommunitiesObj[key];
const communityObject = filteredCommunitiesObj[key];
const communityID = communityObject.communityID;
communityDatas[communityID] = {
...communityObject,
batch: Math.floor(i++ / 5),
};
}

setCommunitiesToAutoJoin(communitesToJoin);
baseSetCommunitiesToAutoJoin({ communityDatas, curBatch: 0 });
})();
}, [
threadInfos,
Expand All @@ -169,18 +185,66 @@ function BaseAutoJoinCommunityHandler(props: Props): React.Node {
canQuery,
]);

const potentiallyIncrementBatch: (
?CommunitiesToAutoJoin,
) => ?CommunitiesToAutoJoin = React.useCallback(input => {
if (!input) {
return input;
}

let shouldIncrementBatch = false;
const { curBatch, communityDatas } = input;
for (const communityToAutoJoin of Object.values(communityDatas)) {
const { batch, joinStatus } = communityToAutoJoin;

if (batch !== curBatch) {
continue;
}

if (joinStatus !== 'joined') {
// One of the current batch isn't complete yet
return input;
}

// We have at least one complete in the current batch
shouldIncrementBatch = true;
}

// If we get here, all of the current batch is complete
if (shouldIncrementBatch) {
return { communityDatas, curBatch: curBatch + 1 };
}

return input;
}, []);

const setCommunitiesToAutoJoin: SetState<?CommunitiesToAutoJoin> =
React.useCallback(
next => {
if (typeof next !== 'function') {
baseSetCommunitiesToAutoJoin(potentiallyIncrementBatch(next));
return;
}
baseSetCommunitiesToAutoJoin(prev => {
const result = next(prev);
return potentiallyIncrementBatch(result);
});
},
[potentiallyIncrementBatch],
);

const joinHandlers = React.useMemo(() => {
if (!communitiesToAutoJoin) {
return null;
}

return Object.keys(communitiesToAutoJoin).map(id => {
const communityToAutoJoin = communitiesToAutoJoin[id];
const { curBatch, communityDatas } = communitiesToAutoJoin;

const { communityID, keyserverOverride, joinStatus } =
communityToAutoJoin;
return Object.values(communityDatas).map(communityData => {
const { batch, communityID, keyserverOverride, joinStatus } =
communityData;

if (joinStatus === 'joined') {
if (batch !== curBatch || joinStatus === 'joined') {
return null;
}

Expand All @@ -190,12 +254,12 @@ function BaseAutoJoinCommunityHandler(props: Props): React.Node {
communityID={communityID}
keyserverOverride={keyserverOverride}
calendarQuery={calendarQuery}
communitiesToAutoJoin={communitiesToAutoJoin}
joinStatus={joinStatus}
setCommunitiesToAutoJoin={setCommunitiesToAutoJoin}
/>
);
});
}, [calendarQuery, communitiesToAutoJoin]);
}, [calendarQuery, communitiesToAutoJoin, setCommunitiesToAutoJoin]);

return joinHandlers;
}
Expand All @@ -204,7 +268,7 @@ type JoinHandlerProps = {
+communityID: string,
+keyserverOverride: ?KeyserverOverride,
+calendarQuery: () => CalendarQuery,
+communitiesToAutoJoin: CommunitiesToAutoJoin,
+joinStatus: JoinStatus,
+setCommunitiesToAutoJoin: SetState<?CommunitiesToAutoJoin>,
};

Expand All @@ -213,7 +277,7 @@ function JoinHandler(props: JoinHandlerProps) {
communityID,
keyserverOverride,
calendarQuery,
communitiesToAutoJoin,
joinStatus,
setCommunitiesToAutoJoin,
} = props;

Expand All @@ -233,59 +297,42 @@ function JoinHandler(props: JoinHandlerProps) {
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;
}
const setJoinStatus = React.useCallback(
(newJoinStatus: JoinStatus) => {
setCommunitiesToAutoJoin(prev => {
if (!prev) {
return null;
}

return {
...prev,
[communityID]: {
...prev[communityID],
joinStatus: 'joining',
},
};
});
}, [communityID, setCommunitiesToAutoJoin, step]);
return {
...prev,
communityDatas: {
...prev.communityDatas,
[communityID]: {
...prev.communityDatas[communityID],
joinStatus: newJoinStatus,
},
},
};
});
},
[communityID, setCommunitiesToAutoJoin],
);

React.useEffect(() => {
if (step !== 'finished') {
if (joinStatus !== 'inactive') {
return;
}

setCommunitiesToAutoJoin(prev => {
if (!prev) {
return null;
void (async () => {
try {
setJoinStatus('joining');
await sleep(1000);
await joinCommunity();
} finally {
setJoinStatus('joined');
}

return {
...prev,
[communityID]: {
...prev[communityID],
joinStatus: 'joined',
},
};
});
}, [communityID, step, setCommunitiesToAutoJoin]);
})();
}, [joinStatus, communityID, setJoinStatus, joinCommunity]);

return null;
}
Expand Down

0 comments on commit 4c5fc78

Please sign in to comment.