mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {useEffect, useState, useMemo} from 'react'
2import EventEmitter from 'eventemitter3'
3import {AppBskyActorDefs} from '@atproto/api'
4import {batchedUpdates} from '#/lib/batchedUpdates'
5import {findAllProfilesInQueryData as findAllProfilesInListMembersQueryData} from '../queries/list-members'
6import {findAllProfilesInQueryData as findAllProfilesInMyBlockedAccountsQueryData} from '../queries/my-blocked-accounts'
7import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '../queries/my-muted-accounts'
8import {findAllProfilesInQueryData as findAllProfilesInPostLikedByQueryData} from '../queries/post-liked-by'
9import {findAllProfilesInQueryData as findAllProfilesInPostRepostedByQueryData} from '../queries/post-reposted-by'
10import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '../queries/profile'
11import {findAllProfilesInQueryData as findAllProfilesInProfileFollowersQueryData} from '../queries/profile-followers'
12import {findAllProfilesInQueryData as findAllProfilesInProfileFollowsQueryData} from '../queries/profile-follows'
13import {findAllProfilesInQueryData as findAllProfilesInSuggestedFollowsQueryData} from '../queries/suggested-follows'
14import {Shadow, castAsShadow} from './types'
15import {queryClient} from 'lib/react-query'
16export type {Shadow} from './types'
17
18export interface ProfileShadow {
19 followingUri: string | undefined
20 muted: boolean | undefined
21 blockingUri: string | undefined
22}
23
24type ProfileView =
25 | AppBskyActorDefs.ProfileView
26 | AppBskyActorDefs.ProfileViewBasic
27 | AppBskyActorDefs.ProfileViewDetailed
28
29const shadows: WeakMap<ProfileView, Partial<ProfileShadow>> = new WeakMap()
30const emitter = new EventEmitter()
31
32export function useProfileShadow(profile: ProfileView): Shadow<ProfileView> {
33 const [shadow, setShadow] = useState(() => shadows.get(profile))
34 const [prevPost, setPrevPost] = useState(profile)
35 if (profile !== prevPost) {
36 setPrevPost(profile)
37 setShadow(shadows.get(profile))
38 }
39
40 useEffect(() => {
41 function onUpdate() {
42 setShadow(shadows.get(profile))
43 }
44 emitter.addListener(profile.did, onUpdate)
45 return () => {
46 emitter.removeListener(profile.did, onUpdate)
47 }
48 }, [profile])
49
50 return useMemo(() => {
51 if (shadow) {
52 return mergeShadow(profile, shadow)
53 } else {
54 return castAsShadow(profile)
55 }
56 }, [profile, shadow])
57}
58
59export function updateProfileShadow(
60 did: string,
61 value: Partial<ProfileShadow>,
62) {
63 const cachedProfiles = findProfilesInCache(did)
64 for (let post of cachedProfiles) {
65 shadows.set(post, {...shadows.get(post), ...value})
66 }
67 batchedUpdates(() => {
68 emitter.emit(did, value)
69 })
70}
71
72function mergeShadow(
73 profile: ProfileView,
74 shadow: Partial<ProfileShadow>,
75): Shadow<ProfileView> {
76 return castAsShadow({
77 ...profile,
78 viewer: {
79 ...(profile.viewer || {}),
80 following:
81 'followingUri' in shadow
82 ? shadow.followingUri
83 : profile.viewer?.following,
84 muted: 'muted' in shadow ? shadow.muted : profile.viewer?.muted,
85 blocking:
86 'blockingUri' in shadow ? shadow.blockingUri : profile.viewer?.blocking,
87 },
88 })
89}
90
91function* findProfilesInCache(did: string): Generator<ProfileView, void> {
92 yield* findAllProfilesInListMembersQueryData(queryClient, did)
93 yield* findAllProfilesInMyBlockedAccountsQueryData(queryClient, did)
94 yield* findAllProfilesInMyMutedAccountsQueryData(queryClient, did)
95 yield* findAllProfilesInPostLikedByQueryData(queryClient, did)
96 yield* findAllProfilesInPostRepostedByQueryData(queryClient, did)
97 yield* findAllProfilesInProfileQueryData(queryClient, did)
98 yield* findAllProfilesInProfileFollowersQueryData(queryClient, did)
99 yield* findAllProfilesInProfileFollowsQueryData(queryClient, did)
100 yield* findAllProfilesInSuggestedFollowsQueryData(queryClient, did)
101}