mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-07-30 09:48:47 -04:00
[Fix] Call get-membership-channel only once per path name
This commit is contained in:
@@ -1,12 +1,32 @@
|
||||
import clsx from 'clsx'
|
||||
import {PrivateMessageChannel} from 'common/supabase/private-messages'
|
||||
import {PrivateUser} from 'common/user'
|
||||
import {getNotificationDestinationsForUser} from 'common/user-notification-preferences'
|
||||
import {usePathname} from 'next/navigation'
|
||||
import {createContext, ReactNode, useContext} from 'react'
|
||||
import {BiEnvelope, BiSolidEnvelope} from 'react-icons/bi'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {useUnseenPrivateMessageChannels} from 'web/hooks/use-private-messages'
|
||||
import {usePrivateUser} from 'web/hooks/use-user'
|
||||
|
||||
// Shared unseen-channels state so the desktop sidebar icon and the mobile
|
||||
// bottom-nav icon (both mounted at once) consume a single fetch instead of each
|
||||
// running their own `useUnseenPrivateMessageChannels`.
|
||||
const UnseenMessageChannelsContext = createContext<PrivateMessageChannel[]>([])
|
||||
|
||||
export function UnseenMessageChannelsProvider(props: {children: ReactNode}) {
|
||||
const {children} = props
|
||||
const privateUser = usePrivateUser()
|
||||
// The hook is always called (no conditional hooks); `enabled` gates the fetch
|
||||
// so signed-out users don't hit the authed endpoint.
|
||||
const {unseenChannels} = useUnseenPrivateMessageChannels(false, !!privateUser)
|
||||
return (
|
||||
<UnseenMessageChannelsContext.Provider value={unseenChannels}>
|
||||
{children}
|
||||
</UnseenMessageChannelsContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function UnseenMessagesBubble(props: {className?: string}) {
|
||||
const {className} = props
|
||||
const privateUser = usePrivateUser()
|
||||
@@ -50,7 +70,7 @@ function InternalUnseenMessagesBubble(props: {
|
||||
}) {
|
||||
const {privateUser, className, bubbleClassName} = props
|
||||
|
||||
const {unseenChannels} = useUnseenPrivateMessageChannels(false)
|
||||
const unseenChannels = useContext(UnseenMessageChannelsContext)
|
||||
const pathName = usePathname()
|
||||
|
||||
const {sendToBrowser} = getNotificationDestinationsForUser(privateUser, 'new_message')
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import {createContext, ReactNode, useContext} from 'react'
|
||||
import {useSortedPrivateMessageMemberships} from 'web/hooks/use-private-messages'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
|
||||
type ChannelMembershipsValue = ReturnType<typeof useSortedPrivateMessageMemberships>
|
||||
|
||||
const PrivateMessageMembershipsContext = createContext<ChannelMembershipsValue>({
|
||||
channels: undefined,
|
||||
memberIdsByChannelId: undefined,
|
||||
})
|
||||
|
||||
export const usePrivateMessageMembershipsContext = () =>
|
||||
useContext(PrivateMessageMembershipsContext)
|
||||
|
||||
// Fetches the current user's channel memberships once and shares them via
|
||||
// context so the many SendMessageButtons on a page (one per profile card) don't
|
||||
// each call get-channel-memberships.
|
||||
export function PrivateMessageMembershipsProvider(props: {children: ReactNode}) {
|
||||
const {children} = props
|
||||
const currentUser = useUser()
|
||||
const channelMemberships = useSortedPrivateMessageMemberships(currentUser?.id)
|
||||
return (
|
||||
<PrivateMessageMembershipsContext.Provider value={channelMemberships}>
|
||||
{children}
|
||||
</PrivateMessageMembershipsContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -11,10 +11,10 @@ import {CommentInputTextArea} from 'web/components/comments/comment-input'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Modal, MODAL_CLASS} from 'web/components/layout/modal'
|
||||
import {EmailVerificationPrompt} from 'web/components/messaging/email-verification-prompt'
|
||||
import {usePrivateMessageMembershipsContext} from 'web/components/messaging/private-message-memberships-context'
|
||||
import {useTextEditor} from 'web/components/widgets/editor'
|
||||
import {Tooltip} from 'web/components/widgets/tooltip'
|
||||
import {useFirebaseUser} from 'web/hooks/use-firebase-user'
|
||||
import {useSortedPrivateMessageMemberships} from 'web/hooks/use-private-messages'
|
||||
import {usePrivateUser, useUser} from 'web/hooks/use-user'
|
||||
import {api} from 'web/lib/api'
|
||||
import {firebaseLogin} from 'web/lib/firebase/users'
|
||||
@@ -50,8 +50,7 @@ export const SendMessageButton = (props: {
|
||||
const router = useRouter()
|
||||
const privateUser = usePrivateUser()
|
||||
const currentUser = useUser()
|
||||
const channelMemberships = useSortedPrivateMessageMemberships(currentUser?.id)
|
||||
const {memberIdsByChannelId} = channelMemberships
|
||||
const {memberIdsByChannelId} = usePrivateMessageMembershipsContext()
|
||||
const t = useT()
|
||||
|
||||
const [openComposeModal, setOpenComposeModal] = useState(false)
|
||||
|
||||
@@ -12,10 +12,10 @@ const promiseCache: Record<string, Promise<any> | undefined> = {}
|
||||
export const useAPIGetter = <P extends APIPath>(
|
||||
path: P,
|
||||
props: APIParams<P> | undefined,
|
||||
ingoreDependencies?: string[],
|
||||
ignoreDependencies?: string[],
|
||||
) => {
|
||||
const propsStringToTriggerRefresh = JSON.stringify(
|
||||
deepCopyWithoutKeys(props, ingoreDependencies || []),
|
||||
deepCopyWithoutKeys(props, ignoreDependencies || []),
|
||||
)
|
||||
|
||||
// Key caching and in-flight dedup on the dependency-filtered props so that
|
||||
|
||||
@@ -58,7 +58,7 @@ export function usePrivateMessages(channelId: number, limit: number, userId: str
|
||||
return {messages, fetchMessages, setMessages}
|
||||
}
|
||||
|
||||
export const useUnseenPrivateMessageChannels = (ignorePageSeenTime: boolean) => {
|
||||
export const useUnseenPrivateMessageChannels = (ignorePageSeenTime: boolean, enabled = true) => {
|
||||
const pathName = usePathname()
|
||||
const lastSeenMessagesPageTime = useLastSeenMessagesPageTime() // ms for now
|
||||
const [lastSeenChatTimeByChannelId, setLastSeenChatTimeByChannelId] = useState<
|
||||
@@ -67,12 +67,14 @@ export const useUnseenPrivateMessageChannels = (ignorePageSeenTime: boolean) =>
|
||||
|
||||
const {data, refresh} = useAPIGetter(
|
||||
'get-channel-memberships',
|
||||
{
|
||||
lastUpdatedTime: ignorePageSeenTime
|
||||
? new Date(0).toISOString()
|
||||
: millisToTs(lastSeenMessagesPageTime),
|
||||
limit: 100,
|
||||
},
|
||||
enabled
|
||||
? {
|
||||
lastUpdatedTime: ignorePageSeenTime
|
||||
? new Date(0).toISOString()
|
||||
: millisToTs(lastSeenMessagesPageTime),
|
||||
limit: 100,
|
||||
}
|
||||
: undefined,
|
||||
['lastUpdatedTime'],
|
||||
)
|
||||
const {channels} = data ?? {
|
||||
|
||||
@@ -21,6 +21,8 @@ import {useEffect, useState} from 'react'
|
||||
import {AuthProvider, AuthUser} from 'web/components/auth-context'
|
||||
import {ErrorBoundary} from 'web/components/error-boundary'
|
||||
import {LiveRegionProvider} from 'web/components/live-region'
|
||||
import {UnseenMessageChannelsProvider} from 'web/components/messaging/messages-icon'
|
||||
import {PrivateMessageMembershipsProvider} from 'web/components/messaging/private-message-memberships-context'
|
||||
import {ChoicesProvider} from 'web/hooks/use-choices'
|
||||
import {useFontPreferenceManager} from 'web/hooks/use-font-preference'
|
||||
import {useHasLoaded} from 'web/hooks/use-has-loaded'
|
||||
@@ -237,9 +239,13 @@ function MyApp(props: AppProps<PageProps>) {
|
||||
<ChoicesProvider>
|
||||
<PinnedQuestionIdsProvider>
|
||||
<HiddenProfilesProvider>
|
||||
<WebPush />
|
||||
<AndroidPush />
|
||||
<Component {...pageProps} />
|
||||
<UnseenMessageChannelsProvider>
|
||||
<PrivateMessageMembershipsProvider>
|
||||
<WebPush />
|
||||
<AndroidPush />
|
||||
<Component {...pageProps} />
|
||||
</PrivateMessageMembershipsProvider>
|
||||
</UnseenMessageChannelsProvider>
|
||||
</HiddenProfilesProvider>
|
||||
</PinnedQuestionIdsProvider>
|
||||
</ChoicesProvider>
|
||||
|
||||
Reference in New Issue
Block a user