Skip to content

Commit e85be4e

Browse files
committed
[Fix] Call get-membership-channel only once per path name
1 parent baf006a commit e85be4e

6 files changed

Lines changed: 70 additions & 16 deletions

File tree

web/components/messaging/messages-icon.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
import clsx from 'clsx'
2+
import {PrivateMessageChannel} from 'common/supabase/private-messages'
23
import {PrivateUser} from 'common/user'
34
import {getNotificationDestinationsForUser} from 'common/user-notification-preferences'
45
import {usePathname} from 'next/navigation'
6+
import {createContext, ReactNode, useContext} from 'react'
57
import {BiEnvelope, BiSolidEnvelope} from 'react-icons/bi'
68
import {Row} from 'web/components/layout/row'
79
import {useUnseenPrivateMessageChannels} from 'web/hooks/use-private-messages'
810
import {usePrivateUser} from 'web/hooks/use-user'
911

12+
// Shared unseen-channels state so the desktop sidebar icon and the mobile
13+
// bottom-nav icon (both mounted at once) consume a single fetch instead of each
14+
// running their own `useUnseenPrivateMessageChannels`.
15+
const UnseenMessageChannelsContext = createContext<PrivateMessageChannel[]>([])
16+
17+
export function UnseenMessageChannelsProvider(props: {children: ReactNode}) {
18+
const {children} = props
19+
const privateUser = usePrivateUser()
20+
// The hook is always called (no conditional hooks); `enabled` gates the fetch
21+
// so signed-out users don't hit the authed endpoint.
22+
const {unseenChannels} = useUnseenPrivateMessageChannels(false, !!privateUser)
23+
return (
24+
<UnseenMessageChannelsContext.Provider value={unseenChannels}>
25+
{children}
26+
</UnseenMessageChannelsContext.Provider>
27+
)
28+
}
29+
1030
export function UnseenMessagesBubble(props: {className?: string}) {
1131
const {className} = props
1232
const privateUser = usePrivateUser()
@@ -50,7 +70,7 @@ function InternalUnseenMessagesBubble(props: {
5070
}) {
5171
const {privateUser, className, bubbleClassName} = props
5272

53-
const {unseenChannels} = useUnseenPrivateMessageChannels(false)
73+
const unseenChannels = useContext(UnseenMessageChannelsContext)
5474
const pathName = usePathname()
5575

5676
const {sendToBrowser} = getNotificationDestinationsForUser(privateUser, 'new_message')
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {createContext, ReactNode, useContext} from 'react'
2+
import {useSortedPrivateMessageMemberships} from 'web/hooks/use-private-messages'
3+
import {useUser} from 'web/hooks/use-user'
4+
5+
type ChannelMembershipsValue = ReturnType<typeof useSortedPrivateMessageMemberships>
6+
7+
const PrivateMessageMembershipsContext = createContext<ChannelMembershipsValue>({
8+
channels: undefined,
9+
memberIdsByChannelId: undefined,
10+
})
11+
12+
export const usePrivateMessageMembershipsContext = () =>
13+
useContext(PrivateMessageMembershipsContext)
14+
15+
// Fetches the current user's channel memberships once and shares them via
16+
// context so the many SendMessageButtons on a page (one per profile card) don't
17+
// each call get-channel-memberships.
18+
export function PrivateMessageMembershipsProvider(props: {children: ReactNode}) {
19+
const {children} = props
20+
const currentUser = useUser()
21+
const channelMemberships = useSortedPrivateMessageMemberships(currentUser?.id)
22+
return (
23+
<PrivateMessageMembershipsContext.Provider value={channelMemberships}>
24+
{children}
25+
</PrivateMessageMembershipsContext.Provider>
26+
)
27+
}

web/components/messaging/send-message-button.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import {CommentInputTextArea} from 'web/components/comments/comment-input'
1111
import {Col} from 'web/components/layout/col'
1212
import {Modal, MODAL_CLASS} from 'web/components/layout/modal'
1313
import {EmailVerificationPrompt} from 'web/components/messaging/email-verification-prompt'
14+
import {usePrivateMessageMembershipsContext} from 'web/components/messaging/private-message-memberships-context'
1415
import {useTextEditor} from 'web/components/widgets/editor'
1516
import {Tooltip} from 'web/components/widgets/tooltip'
1617
import {useFirebaseUser} from 'web/hooks/use-firebase-user'
17-
import {useSortedPrivateMessageMemberships} from 'web/hooks/use-private-messages'
1818
import {usePrivateUser, useUser} from 'web/hooks/use-user'
1919
import {api} from 'web/lib/api'
2020
import {firebaseLogin} from 'web/lib/firebase/users'
@@ -50,8 +50,7 @@ export const SendMessageButton = (props: {
5050
const router = useRouter()
5151
const privateUser = usePrivateUser()
5252
const currentUser = useUser()
53-
const channelMemberships = useSortedPrivateMessageMemberships(currentUser?.id)
54-
const {memberIdsByChannelId} = channelMemberships
53+
const {memberIdsByChannelId} = usePrivateMessageMembershipsContext()
5554
const t = useT()
5655

5756
const [openComposeModal, setOpenComposeModal] = useState(false)

web/hooks/use-api-getter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ const promiseCache: Record<string, Promise<any> | undefined> = {}
1212
export const useAPIGetter = <P extends APIPath>(
1313
path: P,
1414
props: APIParams<P> | undefined,
15-
ingoreDependencies?: string[],
15+
ignoreDependencies?: string[],
1616
) => {
1717
const propsStringToTriggerRefresh = JSON.stringify(
18-
deepCopyWithoutKeys(props, ingoreDependencies || []),
18+
deepCopyWithoutKeys(props, ignoreDependencies || []),
1919
)
2020

2121
// Key caching and in-flight dedup on the dependency-filtered props so that

web/hooks/use-private-messages.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export function usePrivateMessages(channelId: number, limit: number, userId: str
5858
return {messages, fetchMessages, setMessages}
5959
}
6060

61-
export const useUnseenPrivateMessageChannels = (ignorePageSeenTime: boolean) => {
61+
export const useUnseenPrivateMessageChannels = (ignorePageSeenTime: boolean, enabled = true) => {
6262
const pathName = usePathname()
6363
const lastSeenMessagesPageTime = useLastSeenMessagesPageTime() // ms for now
6464
const [lastSeenChatTimeByChannelId, setLastSeenChatTimeByChannelId] = useState<
@@ -67,12 +67,14 @@ export const useUnseenPrivateMessageChannels = (ignorePageSeenTime: boolean) =>
6767

6868
const {data, refresh} = useAPIGetter(
6969
'get-channel-memberships',
70-
{
71-
lastUpdatedTime: ignorePageSeenTime
72-
? new Date(0).toISOString()
73-
: millisToTs(lastSeenMessagesPageTime),
74-
limit: 100,
75-
},
70+
enabled
71+
? {
72+
lastUpdatedTime: ignorePageSeenTime
73+
? new Date(0).toISOString()
74+
: millisToTs(lastSeenMessagesPageTime),
75+
limit: 100,
76+
}
77+
: undefined,
7678
['lastUpdatedTime'],
7779
)
7880
const {channels} = data ?? {

web/pages/_app.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {useEffect, useState} from 'react'
2121
import {AuthProvider, AuthUser} from 'web/components/auth-context'
2222
import {ErrorBoundary} from 'web/components/error-boundary'
2323
import {LiveRegionProvider} from 'web/components/live-region'
24+
import {UnseenMessageChannelsProvider} from 'web/components/messaging/messages-icon'
25+
import {PrivateMessageMembershipsProvider} from 'web/components/messaging/private-message-memberships-context'
2426
import {ChoicesProvider} from 'web/hooks/use-choices'
2527
import {useFontPreferenceManager} from 'web/hooks/use-font-preference'
2628
import {useHasLoaded} from 'web/hooks/use-has-loaded'
@@ -237,9 +239,13 @@ function MyApp(props: AppProps<PageProps>) {
237239
<ChoicesProvider>
238240
<PinnedQuestionIdsProvider>
239241
<HiddenProfilesProvider>
240-
<WebPush />
241-
<AndroidPush />
242-
<Component {...pageProps} />
242+
<UnseenMessageChannelsProvider>
243+
<PrivateMessageMembershipsProvider>
244+
<WebPush />
245+
<AndroidPush />
246+
<Component {...pageProps} />
247+
</PrivateMessageMembershipsProvider>
248+
</UnseenMessageChannelsProvider>
243249
</HiddenProfilesProvider>
244250
</PinnedQuestionIdsProvider>
245251
</ChoicesProvider>

0 commit comments

Comments
 (0)