tangled
alpha
login
or
join now
stream.place
/
streamplace
74
fork
atom
Live video on the AT Protocol
74
fork
atom
overview
issues
1
pulls
pipelines
profile cache
Natalie B.
1 week ago
f1ce0f74
997c3a36
+60
-14
3 changed files
expand all
collapse all
unified
split
js
components
src
context
profile-cache.tsx
hooks
useAvatars.tsx
streamplace-provider
index.tsx
+39
js/components/src/context/profile-cache.tsx
···
1
1
+
import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
2
2
+
import { createContext, useContext, useRef, useState } from "react";
3
3
+
4
4
+
interface ProfileCacheContextValue {
5
5
+
profiles: Record<string, ProfileViewDetailed>;
6
6
+
inFlight: React.MutableRefObject<Set<string>>;
7
7
+
addProfiles: (profiles: Record<string, ProfileViewDetailed>) => void;
8
8
+
}
9
9
+
10
10
+
export const ProfileCacheContext =
11
11
+
createContext<ProfileCacheContextValue | null>(null);
12
12
+
13
13
+
export function ProfileCacheProvider({
14
14
+
children,
15
15
+
}: {
16
16
+
children: React.ReactNode;
17
17
+
}) {
18
18
+
const [profiles, setProfiles] = useState<Record<string, ProfileViewDetailed>>(
19
19
+
{},
20
20
+
);
21
21
+
const inFlight = useRef<Set<string>>(new Set());
22
22
+
23
23
+
const addProfiles = (newProfiles: Record<string, ProfileViewDetailed>) =>
24
24
+
setProfiles((prev) => ({ ...prev, ...newProfiles }));
25
25
+
26
26
+
return (
27
27
+
<ProfileCacheContext.Provider value={{ profiles, inFlight, addProfiles }}>
28
28
+
{children}
29
29
+
</ProfileCacheContext.Provider>
30
30
+
);
31
31
+
}
32
32
+
33
33
+
export function useProfileCache(): ProfileCacheContextValue {
34
34
+
const ctx = useContext(ProfileCacheContext);
35
35
+
if (!ctx) {
36
36
+
throw new Error("useProfileCache must be used within ProfileCacheProvider");
37
37
+
}
38
38
+
return ctx;
39
39
+
}
+13
-9
js/components/src/hooks/useAvatars.tsx
···
1
1
import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
2
2
-
import { useEffect, useMemo, useRef, useState } from "react";
2
2
+
import { useEffect, useMemo } from "react";
3
3
+
import { useProfileCache } from "../context/profile-cache";
3
4
import { usePDSAgent } from "../streamplace-store/xrpc";
4
5
5
6
export function useAvatars(
6
7
dids: string[],
7
8
): Record<string, ProfileViewDetailed> {
8
8
-
let agent = usePDSAgent();
9
9
-
const [profiles, setProfiles] = useState<Record<string, ProfileViewDetailed>>(
10
10
-
{},
11
11
-
);
12
12
-
const inFlight = useRef<Set<string>>(new Set());
9
9
+
const agent = usePDSAgent();
10
10
+
const { profiles, inFlight, addProfiles } = useProfileCache();
13
11
14
12
const missingDids = useMemo(
15
13
() =>
16
14
dids.filter((did) => !(did in profiles) && !inFlight.current.has(did)),
17
17
-
[dids, profiles],
15
15
+
[dids, profiles, inFlight],
18
16
);
19
17
20
18
useEffect(() => {
···
29
27
result.data.profiles.forEach((p) => {
30
28
newProfiles[p.did] = p;
31
29
});
32
32
-
setProfiles((prev) => ({ ...prev, ...newProfiles }));
30
30
+
addProfiles(newProfiles);
33
31
} catch (e) {
34
32
console.error("Failed to fetch profiles", e);
35
33
} finally {
···
40
38
fetchProfiles();
41
39
}, [missingDids, agent]);
42
40
43
43
-
return profiles;
41
41
+
return useMemo(
42
42
+
() =>
43
43
+
Object.fromEntries(
44
44
+
dids.filter((d) => d in profiles).map((d) => [d, profiles[d]]),
45
45
+
),
46
46
+
[dids, profiles],
47
47
+
);
44
48
}
+8
-5
js/components/src/streamplace-provider/index.tsx
···
1
1
import { SessionManager } from "@atproto/api/dist/session-manager";
2
2
import { useEffect, useRef } from "react";
3
3
+
import { ProfileCacheProvider } from "../context/profile-cache";
3
4
import { useDocumentTitle } from "../hooks";
4
5
import {
5
6
useBrandingAutoFetch,
···
32
33
33
34
return (
34
35
<StreamplaceContext.Provider value={{ store: store }}>
35
35
-
<BrandingFetcher>
36
36
-
<ChatProfileCreator oauthSession={oauthSession}>
37
37
-
<Poller>{children}</Poller>
38
38
-
</ChatProfileCreator>
39
39
-
</BrandingFetcher>
36
36
+
<ProfileCacheProvider>
37
37
+
<BrandingFetcher>
38
38
+
<ChatProfileCreator oauthSession={oauthSession}>
39
39
+
<Poller>{children}</Poller>
40
40
+
</ChatProfileCreator>
41
41
+
</BrandingFetcher>
42
42
+
</ProfileCacheProvider>
40
43
</StreamplaceContext.Provider>
41
44
);
42
45
}