mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Signed-off-by: Logan Johnson <loganj@squareup.com> Signed-off-by: npub1dpf98sl35hm9k65t8h6cvh5n6knn2msugh5k5nmysxwnw5wlh7uqn2wgp4 <685253c3f1a5f65b6a8b3df5865e93d5a7356e1c45e96a4f64819d3751dfbfb8@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz> Co-authored-by: npub1dpf98sl35hm9k65t8h6cvh5n6knn2msugh5k5nmysxwnw5wlh7uqn2wgp4 <685253c3f1a5f65b6a8b3df5865e93d5a7356e1c45e96a4f64819d3751dfbfb8@sprout-oss.stage.blox.sqprod.co> Co-authored-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
92 lines
3.0 KiB
TypeScript
92 lines
3.0 KiB
TypeScript
import type { Query, QueryClient } from "@tanstack/react-query";
|
|
|
|
import {
|
|
evictUsersBatchEntries,
|
|
profileQueryKey,
|
|
} from "@/features/profile/hooks";
|
|
import {
|
|
fetchAvatarDataUrl,
|
|
readSelfProfileCache,
|
|
resolveAvatarDataUrl,
|
|
writeSelfProfileCache,
|
|
} from "@/features/profile/lib/selfProfileStorage";
|
|
import type {
|
|
Profile,
|
|
UserProfileSummary,
|
|
UsersBatchResponse,
|
|
} from "@/shared/api/types";
|
|
import { getAvatarSnapshotUrl } from "@/shared/lib/animatedAvatar";
|
|
import { rewriteRelayUrl } from "@/shared/lib/mediaUrl";
|
|
|
|
function queryContainsPubkey(query: Query, pubkey: string): boolean {
|
|
return query.queryKey.includes(pubkey);
|
|
}
|
|
|
|
export async function refreshProfileCaches(
|
|
queryClient: QueryClient,
|
|
profile: Profile,
|
|
relayUrl: string,
|
|
): Promise<void> {
|
|
const pubkey = profile.pubkey.toLowerCase();
|
|
await queryClient.cancelQueries({
|
|
predicate: (query) =>
|
|
query.queryKey[0] === profileQueryKey[0] ||
|
|
(query.queryKey[0] === "user-profile" &&
|
|
queryContainsPubkey(query, pubkey)) ||
|
|
(query.queryKey[0] === "users-batch" &&
|
|
queryContainsPubkey(query, pubkey)),
|
|
});
|
|
|
|
queryClient.setQueryData(profileQueryKey, profile);
|
|
queryClient.setQueryData<Profile>(["user-profile", pubkey], profile);
|
|
evictUsersBatchEntries(queryClient, [pubkey]);
|
|
queryClient.setQueriesData<UsersBatchResponse>(
|
|
{
|
|
predicate: (query) =>
|
|
query.queryKey[0] === "users-batch" &&
|
|
queryContainsPubkey(query, pubkey),
|
|
},
|
|
(current) => {
|
|
if (!current?.profiles[pubkey]) return current;
|
|
return {
|
|
...current,
|
|
profiles: {
|
|
...current.profiles,
|
|
[pubkey]: {
|
|
...current.profiles[pubkey],
|
|
avatarUrl: profile.avatarUrl,
|
|
} satisfies UserProfileSummary,
|
|
},
|
|
};
|
|
},
|
|
);
|
|
// Search result pages also embed profile avatars, but their arbitrary query
|
|
// text/page shape makes a safe targeted rewrite brittle. Mark every search
|
|
// view stale; active searches refetch immediately and inactive ones refresh
|
|
// when next opened.
|
|
await queryClient.invalidateQueries({ queryKey: ["user-search"] });
|
|
|
|
const existing = readSelfProfileCache(relayUrl, profile.pubkey);
|
|
const baseCache = {
|
|
version: 1 as const,
|
|
displayName: profile.displayName,
|
|
avatarUrl: profile.avatarUrl,
|
|
about: profile.about,
|
|
avatarDataUrl: resolveAvatarDataUrl(profile.avatarUrl, null, existing),
|
|
updatedAt: Date.now(),
|
|
...(profile.hasProfileEvent && { hasProfileEvent: true as const }),
|
|
};
|
|
// Persist the canonical profile before attempting the optional image snapshot,
|
|
// so quitting during that fetch cannot leave the durable fallback stale.
|
|
writeSelfProfileCache(relayUrl, profile.pubkey, baseCache);
|
|
|
|
const snapshotUrl = getAvatarSnapshotUrl(profile.avatarUrl);
|
|
if (!snapshotUrl) return;
|
|
const fetched = await fetchAvatarDataUrl(rewriteRelayUrl(snapshotUrl));
|
|
if (!fetched) return;
|
|
writeSelfProfileCache(relayUrl, profile.pubkey, {
|
|
...baseCache,
|
|
avatarDataUrl: fetched,
|
|
});
|
|
}
|