Skip to content

Commit 77433cc

Browse files
authored
Updating following list cache on follow/unfollow (#24567)
ref https://linear.app/ghost/issue/PROD-2382 - caching accounts info from explore, suggested accounts and followers list - using the cached account info to update following list on follow/unfollow actions
1 parent fa03570 commit 77433cc

File tree

2 files changed

+109
-11
lines changed

2 files changed

+109
-11
lines changed

apps/admin-x-activitypub/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tryghost/admin-x-activitypub",
3-
"version": "0.9.15",
3+
"version": "0.9.16",
44
"license": "MIT",
55
"repository": {
66
"type": "git",

apps/admin-x-activitypub/src/hooks/use-activity-pub-queries.ts

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -993,9 +993,41 @@ export function useUnfollowMutationForUser(handle: string, onSuccess: () => void
993993
};
994994
});
995995

996-
// Invalidate the follows query cache for the account performing the unfollow
997-
const accountFollowsQueryKey = QUERY_KEYS.accountFollows(handle, 'following');
998-
queryClient.invalidateQueries({queryKey: accountFollowsQueryKey});
996+
const followingHandle = handle === 'index' ? 'me' : handle;
997+
const accountFollowsQueryKey = QUERY_KEYS.accountFollows(followingHandle, 'following');
998+
const existingData = queryClient.getQueryData(accountFollowsQueryKey);
999+
1000+
// Update the following list cache if it exists, otherwise invalidate
1001+
if (existingData) {
1002+
queryClient.setQueryData(accountFollowsQueryKey, (oldData?: {
1003+
pages: Array<{
1004+
accounts: Array<{
1005+
id: string;
1006+
name: string;
1007+
handle: string;
1008+
avatarUrl: string;
1009+
blockedByMe: boolean;
1010+
domainBlockedByMe: boolean;
1011+
isFollowing: boolean;
1012+
}>;
1013+
}>;
1014+
}) => {
1015+
if (!oldData?.pages) {
1016+
return oldData;
1017+
}
1018+
1019+
return {
1020+
...oldData,
1021+
pages: oldData.pages.map(page => ({
1022+
...page,
1023+
accounts: page.accounts.filter(account => account.handle !== fullHandle)
1024+
}))
1025+
};
1026+
});
1027+
} else {
1028+
// Cache doesn't exist, invalidate to fetch fresh data when needed
1029+
queryClient.invalidateQueries({queryKey: accountFollowsQueryKey});
1030+
}
9991031

10001032
// Update explore profiles cache
10011033
queryClient.setQueryData(QUERY_KEYS.exploreProfiles(handle), (current: {pages: Array<{results: Record<string, { categoryName: string; sites: Account[] }>}>} | undefined) => {
@@ -1109,12 +1141,55 @@ export function useFollowMutationForUser(handle: string, onSuccess: () => void,
11091141

11101142
const profileFollowersQueryKey = QUERY_KEYS.accountFollows(fullHandle, 'followers');
11111143

1112-
// Invalidate the follows query cache for the account performing the follow
1113-
// because we cannot directly add to it due to potentially incompatible data
1114-
// shapes
1115-
const accountFollowsQueryKey = QUERY_KEYS.accountFollows(handle, 'following');
1144+
const followingHandle = handle === 'index' ? 'me' : handle;
1145+
const accountFollowsQueryKey = QUERY_KEYS.accountFollows(followingHandle, 'following');
1146+
const existingData = queryClient.getQueryData(accountFollowsQueryKey);
1147+
1148+
// Update the following list cache if it exists, otherwise invalidate
1149+
if (existingData) {
1150+
queryClient.setQueryData(accountFollowsQueryKey, (oldData?: {
1151+
pages: Array<{
1152+
accounts: Array<{
1153+
id: string;
1154+
name: string;
1155+
handle: string;
1156+
avatarUrl: string;
1157+
blockedByMe: boolean;
1158+
domainBlockedByMe: boolean;
1159+
isFollowing: boolean;
1160+
}>;
1161+
}>;
1162+
}) => {
1163+
if (!oldData?.pages?.[0]) {
1164+
return oldData;
1165+
}
1166+
1167+
const followedAccount = queryClient.getQueryData<Account>(QUERY_KEYS.account(fullHandle === 'me' ? 'index' : fullHandle));
1168+
if (!followedAccount) {
1169+
return oldData;
1170+
}
11161171

1117-
queryClient.invalidateQueries({queryKey: accountFollowsQueryKey});
1172+
const newFollowing = {
1173+
id: followedAccount.id,
1174+
name: followedAccount.name,
1175+
handle: followedAccount.handle,
1176+
avatarUrl: followedAccount.avatarUrl,
1177+
blockedByMe: followedAccount.blockedByMe,
1178+
domainBlockedByMe: followedAccount.domainBlockedByMe,
1179+
isFollowing: true
1180+
};
1181+
1182+
return {
1183+
...oldData,
1184+
pages: [{
1185+
...oldData.pages[0],
1186+
accounts: [newFollowing, ...oldData.pages[0].accounts]
1187+
}, ...oldData.pages.slice(1)]
1188+
};
1189+
});
1190+
} else {
1191+
queryClient.invalidateQueries({queryKey: accountFollowsQueryKey});
1192+
}
11181193

11191194
// Update explore profiles cache
11201195
queryClient.setQueryData(QUERY_KEYS.exploreProfiles(handle), (current: {pages: Array<{results: Record<string, { categoryName: string; sites: Account[] }>}>} | undefined) => {
@@ -1506,13 +1581,24 @@ export function useAccountForUser(handle: string, profileHandle: string) {
15061581
}
15071582

15081583
export function useAccountFollowsForUser(profileHandle: string, type: AccountFollowsType) {
1584+
const queryClient = useQueryClient();
1585+
15091586
return useInfiniteQuery({
15101587
queryKey: QUERY_KEYS.accountFollows(profileHandle, type),
15111588
async queryFn({pageParam}: {pageParam?: string}) {
15121589
const siteUrl = await getSiteUrl();
15131590
const api = createActivityPubAPI('index', siteUrl);
15141591

1515-
return api.getAccountFollows(profileHandle, type, pageParam);
1592+
const response = await api.getAccountFollows(profileHandle, type, pageParam);
1593+
1594+
// Cache individual account data for follow mutations
1595+
if (response.accounts) {
1596+
response.accounts.forEach((account) => {
1597+
queryClient.setQueryData(QUERY_KEYS.account(account.handle), account);
1598+
});
1599+
}
1600+
1601+
return response;
15161602
},
15171603
getNextPageParam(prevPage) {
15181604
return prevPage.next;
@@ -2374,6 +2460,11 @@ export function useExploreProfilesForUser(handle: string) {
23742460
const fetchExploreProfilesFromJSON = useCallback(async () => {
23752461
const accounts = await fetchAndFilterAccounts();
23762462

2463+
// Cache account data for follow mutations
2464+
accounts.forEach((account: Account) => {
2465+
queryClient.setQueryData(QUERY_KEYS.account(account.handle), account);
2466+
});
2467+
23772468
const results = {
23782469
uncategorized: {
23792470
categoryName: 'Recommended',
@@ -2385,7 +2476,7 @@ export function useExploreProfilesForUser(handle: string) {
23852476
results,
23862477
nextPage: undefined
23872478
};
2388-
}, [fetchAndFilterAccounts]);
2479+
}, [fetchAndFilterAccounts, queryClient]);
23892480

23902481
const exploreProfilesQuery = useInfiniteQuery({
23912482
queryKey,
@@ -2454,6 +2545,13 @@ export function useSuggestedProfilesForUser(handle: string, limit = 3) {
24542545
.sort(() => Math.random() - 0.5)
24552546
.slice(0, limit);
24562547

2548+
// Cache account data for follow mutations
2549+
if (randomAccounts.length > 0) {
2550+
randomAccounts.forEach((account: Account) => {
2551+
queryClient.setQueryData(QUERY_KEYS.account(account.handle), account);
2552+
});
2553+
}
2554+
24572555
return randomAccounts.length > 0 ? randomAccounts : null;
24582556
},
24592557
retry: false,

0 commit comments

Comments
 (0)