Get unread counts vs counting convos (#10878)
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {useAnimatedRef} from 'react-native-reanimated'
|
import {useAnimatedRef} from 'react-native-reanimated'
|
||||||
import {type ChatBskyActorGetStatus, ChatBskyConvoDefs} from '@atproto/api'
|
import {type ChatBskyActorGetStatus, type ChatBskyConvoDefs} from '@atproto/api'
|
||||||
import {Trans, useLingui} from '@lingui/react/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
import {useFocusEffect, useIsFocused} from '@react-navigation/native'
|
import {useFocusEffect, useIsFocused} from '@react-navigation/native'
|
||||||
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {type NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
@@ -17,6 +17,7 @@ import {listenSoftReset} from '#/state/events'
|
|||||||
import {MESSAGE_SCREEN_POLL_INTERVAL} from '#/state/messages/convo/const'
|
import {MESSAGE_SCREEN_POLL_INTERVAL} from '#/state/messages/convo/const'
|
||||||
import {useMessagesEventBus} from '#/state/messages/events'
|
import {useMessagesEventBus} from '#/state/messages/events'
|
||||||
import {useChatActorStatusQuery} from '#/state/queries/messages/get-status'
|
import {useChatActorStatusQuery} from '#/state/queries/messages/get-status'
|
||||||
|
import {useUnreadCountsQuery} from '#/state/queries/messages/get-unread-counts'
|
||||||
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
|
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
|
||||||
import {EmptyState} from '#/view/com/util/EmptyState'
|
import {EmptyState} from '#/view/com/util/EmptyState'
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
@@ -507,7 +508,6 @@ export function Header({
|
|||||||
}) {
|
}) {
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const aa = useAgeAssurance()
|
|
||||||
const requireEmailVerification = useRequireEmailVerification()
|
const requireEmailVerification = useRequireEmailVerification()
|
||||||
const {isWithinSplitView} = useIsWithinSplitView()
|
const {isWithinSplitView} = useIsWithinSplitView()
|
||||||
|
|
||||||
@@ -516,24 +516,8 @@ export function Header({
|
|||||||
// on repeated clicks, so navigate instead to dedupe by route + params.
|
// on repeated clicks, so navigate instead to dedupe by route + params.
|
||||||
const action = isWithinSplitView ? 'navigate' : 'push'
|
const action = isWithinSplitView ? 'navigate' : 'push'
|
||||||
|
|
||||||
const {data: unreadInboxData, hasNextPage: hasMoreRequests} =
|
const {data: unreadCounts} = useUnreadCountsQuery()
|
||||||
useListConvosQuery({
|
const requestCount = unreadCounts?.unreadRequestConvos ?? 0
|
||||||
status: 'request',
|
|
||||||
readState: 'unread',
|
|
||||||
kind: aa.flags.groupChatDisabled ? 'direct' : 'all',
|
|
||||||
})
|
|
||||||
|
|
||||||
const inboxAllConvos =
|
|
||||||
unreadInboxData?.pages
|
|
||||||
.flatMap(page => page.convos)
|
|
||||||
.filter(
|
|
||||||
convo =>
|
|
||||||
!convo.muted &&
|
|
||||||
convo.members.every(member => member.handle !== 'missing.invalid') &&
|
|
||||||
(ChatBskyConvoDefs.isGroupConvo(convo.kind)
|
|
||||||
? !aa.flags.groupChatDisabled
|
|
||||||
: true),
|
|
||||||
) ?? []
|
|
||||||
|
|
||||||
const openChatControl = useCallback(() => {
|
const openChatControl = useCallback(() => {
|
||||||
newChatControl.open()
|
newChatControl.open()
|
||||||
@@ -558,8 +542,7 @@ export function Header({
|
|||||||
|
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||||
<InboxRequests
|
<InboxRequests
|
||||||
count={inboxAllConvos.length}
|
count={requestCount}
|
||||||
more={hasMoreRequests}
|
|
||||||
variant="solid"
|
variant="solid"
|
||||||
action={action}
|
action={action}
|
||||||
/>
|
/>
|
||||||
@@ -593,11 +576,7 @@ export function Header({
|
|||||||
<Trans>Chats</Trans>
|
<Trans>Chats</Trans>
|
||||||
</Layout.Header.TitleText>
|
</Layout.Header.TitleText>
|
||||||
</Layout.Header.Content>
|
</Layout.Header.Content>
|
||||||
<InboxRequests
|
<InboxRequests count={requestCount} variant="ghost" />
|
||||||
count={inboxAllConvos.length}
|
|
||||||
more={hasMoreRequests}
|
|
||||||
variant="ghost"
|
|
||||||
/>
|
|
||||||
<Layout.Header.Slot>
|
<Layout.Header.Slot>
|
||||||
<Link
|
<Link
|
||||||
to="/messages/settings"
|
to="/messages/settings"
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
import {plural} from '@lingui/core/macro'
|
import {plural} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react/macro'
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
import {UNREAD_LIMIT} from '#/state/queries/messages/list-conversations'
|
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {ButtonIcon, ButtonText} from '#/components/Button'
|
import {ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {Inbox_Stroke2_Corner2_Rounded as InboxIcon} from '#/components/icons/Inbox'
|
import {Inbox_Stroke2_Corner2_Rounded as InboxIcon} from '#/components/icons/Inbox'
|
||||||
import {Link} from '#/components/Link'
|
import {Link} from '#/components/Link'
|
||||||
|
|
||||||
|
// The server caps unreadRequestConvos at 11, where 11 means "any more than 10".
|
||||||
|
const REQUEST_COUNT_CAP = 11
|
||||||
|
|
||||||
export function InboxRequests({
|
export function InboxRequests({
|
||||||
count,
|
count,
|
||||||
more,
|
|
||||||
variant,
|
variant,
|
||||||
action,
|
action,
|
||||||
}: {
|
}: {
|
||||||
count: number
|
count: number
|
||||||
more: boolean
|
|
||||||
variant?: 'ghost' | 'solid'
|
variant?: 'ghost' | 'solid'
|
||||||
action?: 'push' | 'navigate'
|
action?: 'push' | 'navigate'
|
||||||
}) {
|
}) {
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
const unread = count > 0
|
const unread = count > 0
|
||||||
|
const overflow = count >= REQUEST_COUNT_CAP
|
||||||
|
|
||||||
const label = !unread
|
const label = !unread
|
||||||
? l({
|
? l({
|
||||||
message: `Requests`,
|
message: `Requests`,
|
||||||
comment: 'Incoming message requests',
|
comment: 'Incoming message requests',
|
||||||
})
|
})
|
||||||
: count >= UNREAD_LIMIT && more
|
: overflow
|
||||||
? l({
|
? l({
|
||||||
message: `${count}+ requests`,
|
message: `10+ requests`,
|
||||||
comment: 'Displayed when the number of requests is greater than 20',
|
comment: 'Displayed when the number of requests is greater than 10',
|
||||||
})
|
})
|
||||||
: plural(count, {
|
: plural(count, {
|
||||||
one: '# request',
|
one: '# request',
|
||||||
@@ -52,11 +53,11 @@ export function InboxRequests({
|
|||||||
<ButtonIcon icon={InboxIcon} size="lg" />
|
<ButtonIcon icon={InboxIcon} size="lg" />
|
||||||
{unread && (
|
{unread && (
|
||||||
<ButtonText style={[a.text_md, a.font_bold]}>
|
<ButtonText style={[a.text_md, a.font_bold]}>
|
||||||
{count >= UNREAD_LIMIT && more
|
{overflow
|
||||||
? l({
|
? l({
|
||||||
message: `${count}+`,
|
message: `10+`,
|
||||||
comment:
|
comment:
|
||||||
'Displayed when the number of requests is greater than 20',
|
'Displayed when the number of requests is greater than 10',
|
||||||
})
|
})
|
||||||
: count}
|
: count}
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {useQuery} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {DM_SERVICE_HEADERS} from '#/lib/constants'
|
||||||
|
import {useAgent, useSession} from '#/state/session'
|
||||||
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
|
import {STALE} from '..'
|
||||||
|
|
||||||
|
const RQKEY_ROOT = 'convo-unread-counts'
|
||||||
|
export const RQKEY = (includeGroupChats: boolean) =>
|
||||||
|
[RQKEY_ROOT, includeGroupChats] as const
|
||||||
|
export const RQKEY_PARTIAL = [RQKEY_ROOT] as const
|
||||||
|
|
||||||
|
export function useUnreadCountsQuery() {
|
||||||
|
const agent = useAgent()
|
||||||
|
const {hasSession} = useSession()
|
||||||
|
const aa = useAgeAssurance()
|
||||||
|
const includeGroupChats = !aa.flags.groupChatDisabled
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: RQKEY(includeGroupChats),
|
||||||
|
queryFn: async () => {
|
||||||
|
const {data} = await agent.chat.bsky.convo.getUnreadCounts(
|
||||||
|
{includeGroupChats},
|
||||||
|
{headers: DM_SERVICE_HEADERS},
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
staleTime: STALE.SECONDS.FIFTEEN,
|
||||||
|
enabled: hasSession,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import {createContext, useCallback, useContext, useEffect, useMemo} from 'react'
|
import {useCallback, useEffect, useMemo} from 'react'
|
||||||
import {
|
import {
|
||||||
type ChatBskyActorDefs,
|
type ChatBskyActorDefs,
|
||||||
ChatBskyConvoDefs,
|
ChatBskyConvoDefs,
|
||||||
type ChatBskyConvoListConvos,
|
type ChatBskyConvoListConvos,
|
||||||
moderateProfile,
|
|
||||||
type ModerationOpts,
|
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {
|
import {
|
||||||
type InfiniteData,
|
type InfiniteData,
|
||||||
@@ -19,14 +17,14 @@ import throttle from 'lodash.throttle'
|
|||||||
import {DM_SERVICE_HEADERS} from '#/lib/constants'
|
import {DM_SERVICE_HEADERS} from '#/lib/constants'
|
||||||
import {useCurrentConvoId} from '#/state/messages/current-convo-id'
|
import {useCurrentConvoId} from '#/state/messages/current-convo-id'
|
||||||
import {useMessagesEventBus} from '#/state/messages/events'
|
import {useMessagesEventBus} from '#/state/messages/events'
|
||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
|
||||||
import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links'
|
import {invalidateJoinLinkPreviewsForConvo} from '#/state/queries/join-links'
|
||||||
import {useAgent, useSession} from '#/state/session'
|
import {useAgent, useSession} from '#/state/session'
|
||||||
import {parseConvoView} from '#/components/dms/util'
|
|
||||||
import {useAgeAssurance} from '#/ageAssurance'
|
|
||||||
import {type AgeAssuranceFlags} from '#/ageAssurance/types'
|
|
||||||
import * as bsky from '#/types/bsky'
|
import * as bsky from '#/types/bsky'
|
||||||
import {RQKEY as CONVO_KEY} from './conversation'
|
import {RQKEY as CONVO_KEY} from './conversation'
|
||||||
|
import {
|
||||||
|
RQKEY_PARTIAL as UNREAD_COUNTS_RQKEY_PARTIAL,
|
||||||
|
useUnreadCountsQuery,
|
||||||
|
} from './get-unread-counts'
|
||||||
import {
|
import {
|
||||||
type ConvoRequestListQueryData,
|
type ConvoRequestListQueryData,
|
||||||
optimisticDelete as optimisticDeleteRequest,
|
optimisticDelete as optimisticDeleteRequest,
|
||||||
@@ -37,7 +35,6 @@ import {
|
|||||||
import {listConvoMembersQueryKey} from './list-convo-members'
|
import {listConvoMembersQueryKey} from './list-convo-members'
|
||||||
|
|
||||||
const DEFAULT_LIMIT = 10
|
const DEFAULT_LIMIT = 10
|
||||||
export const UNREAD_LIMIT = 20
|
|
||||||
|
|
||||||
export const RQKEY_ROOT = 'convo-list'
|
export const RQKEY_ROOT = 'convo-list'
|
||||||
export const RQKEY = (
|
export const RQKEY = (
|
||||||
@@ -145,30 +142,11 @@ export function useListConvosQuery({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListConvosContext = createContext<{
|
|
||||||
accepted: ChatBskyConvoDefs.ConvoView[]
|
|
||||||
request: ChatBskyConvoDefs.ConvoView[]
|
|
||||||
} | null>(null)
|
|
||||||
ListConvosContext.displayName = 'ListConvosContext'
|
|
||||||
|
|
||||||
export function useListConvos() {
|
|
||||||
const ctx = useContext(ListConvosContext)
|
|
||||||
if (!ctx) {
|
|
||||||
throw new Error('useListConvos must be used within a ListConvosProvider')
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
const empty = {accepted: [], request: []}
|
|
||||||
export function ListConvosProvider({children}: {children: React.ReactNode}) {
|
export function ListConvosProvider({children}: {children: React.ReactNode}) {
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
|
|
||||||
if (!hasSession) {
|
if (!hasSession) {
|
||||||
return (
|
return <>{children}</>
|
||||||
<ListConvosContext.Provider value={empty}>
|
|
||||||
{children}
|
|
||||||
</ListConvosContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ListConvosProviderInner>{children}</ListConvosProviderInner>
|
return <ListConvosProviderInner>{children}</ListConvosProviderInner>
|
||||||
@@ -179,13 +157,6 @@ export function ListConvosProviderInner({
|
|||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
const aa = useAgeAssurance()
|
|
||||||
const {refetch, data} = useListConvosQuery({
|
|
||||||
readState: 'unread',
|
|
||||||
limit: UNREAD_LIMIT,
|
|
||||||
lockStatus: 'unlocked',
|
|
||||||
kind: aa.flags.groupChatDisabled ? 'direct' : 'all',
|
|
||||||
})
|
|
||||||
const messagesBus = useMessagesEventBus()
|
const messagesBus = useMessagesEventBus()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {currentConvoId} = useCurrentConvoId()
|
const {currentConvoId} = useCurrentConvoId()
|
||||||
@@ -193,7 +164,6 @@ export function ListConvosProviderInner({
|
|||||||
|
|
||||||
const debouncedRefetch = useMemo(() => {
|
const debouncedRefetch = useMemo(() => {
|
||||||
const refetchAndInvalidate = () => {
|
const refetchAndInvalidate = () => {
|
||||||
void refetch()
|
|
||||||
void queryClient.invalidateQueries({queryKey: [RQKEY_ROOT]})
|
void queryClient.invalidateQueries({queryKey: [RQKEY_ROOT]})
|
||||||
void queryClient.invalidateQueries({queryKey: [REQUESTS_RQKEY_ROOT]})
|
void queryClient.invalidateQueries({queryKey: [REQUESTS_RQKEY_ROOT]})
|
||||||
}
|
}
|
||||||
@@ -201,13 +171,31 @@ export function ListConvosProviderInner({
|
|||||||
leading: true,
|
leading: true,
|
||||||
trailing: true,
|
trailing: true,
|
||||||
})
|
})
|
||||||
}, [refetch, queryClient])
|
}, [queryClient])
|
||||||
|
|
||||||
|
// The unread badge count is derived from chat.bsky.convo.getUnreadCounts.
|
||||||
|
// Any chat log can change it, so refresh it (throttled) on every batch.
|
||||||
|
const debouncedInvalidateUnreadCounts = useMemo(() => {
|
||||||
|
return throttle(
|
||||||
|
() => {
|
||||||
|
void queryClient.invalidateQueries({
|
||||||
|
queryKey: UNREAD_COUNTS_RQKEY_PARTIAL,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
{leading: true, trailing: true},
|
||||||
|
)
|
||||||
|
}, [queryClient])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsub = messagesBus.on(
|
const unsub = messagesBus.on(
|
||||||
events => {
|
events => {
|
||||||
if (events.type !== 'logs') return
|
if (events.type !== 'logs') return
|
||||||
|
|
||||||
|
// Any log batch may change unread state (new message, read, accept,
|
||||||
|
// join request, etc.), so refresh the badge count for all of them.
|
||||||
|
debouncedInvalidateUnreadCounts()
|
||||||
|
|
||||||
function mutateMembers(
|
function mutateMembers(
|
||||||
convoId: string,
|
convoId: string,
|
||||||
fn: (
|
fn: (
|
||||||
@@ -851,117 +839,42 @@ export function ListConvosProviderInner({
|
|||||||
queryClient,
|
queryClient,
|
||||||
currentAccount?.did,
|
currentAccount?.did,
|
||||||
debouncedRefetch,
|
debouncedRefetch,
|
||||||
|
debouncedInvalidateUnreadCounts,
|
||||||
])
|
])
|
||||||
|
|
||||||
const ctx = useMemo(() => {
|
return <>{children}</>
|
||||||
const convos = data?.pages.flatMap(page => page.convos) ?? []
|
}
|
||||||
|
|
||||||
|
export function useUnreadMessageCount(): {
|
||||||
|
count: number
|
||||||
|
numUnread?: string
|
||||||
|
hasNew: boolean
|
||||||
|
} {
|
||||||
|
const {data} = useUnreadCountsQuery()
|
||||||
|
const accepted = data?.unreadAcceptedConvos ?? 0
|
||||||
|
const request = data?.unreadRequestConvos ?? 0
|
||||||
|
|
||||||
|
if (accepted > 0) {
|
||||||
|
const total = accepted + Math.min(request, 1)
|
||||||
return {
|
return {
|
||||||
accepted: convos.filter(conv => conv.status === 'accepted'),
|
count: total,
|
||||||
request: convos.filter(conv => conv.status === 'request'),
|
numUnread: total > 10 ? '10+' : String(total),
|
||||||
|
// only needed when numUnread is undefined
|
||||||
|
hasNew: false,
|
||||||
}
|
}
|
||||||
}, [data])
|
} else if (request > 0) {
|
||||||
|
return {
|
||||||
return (
|
count: 1,
|
||||||
<ListConvosContext.Provider value={ctx}>
|
numUnread: undefined,
|
||||||
{children}
|
hasNew: true,
|
||||||
</ListConvosContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useUnreadMessageCount() {
|
|
||||||
const {currentConvoId} = useCurrentConvoId()
|
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const {accepted, request} = useListConvos()
|
|
||||||
const moderationOpts = useModerationOpts()
|
|
||||||
const aa = useAgeAssurance()
|
|
||||||
|
|
||||||
return useMemo<{
|
|
||||||
count: number
|
|
||||||
numUnread?: string
|
|
||||||
hasNew: boolean
|
|
||||||
}>(() => {
|
|
||||||
const acceptedCount = calculateCount(
|
|
||||||
accepted,
|
|
||||||
currentAccount?.did,
|
|
||||||
currentConvoId,
|
|
||||||
moderationOpts,
|
|
||||||
aa.flags,
|
|
||||||
)
|
|
||||||
const requestCount = calculateCount(
|
|
||||||
request,
|
|
||||||
currentAccount?.did,
|
|
||||||
currentConvoId,
|
|
||||||
moderationOpts,
|
|
||||||
aa.flags,
|
|
||||||
)
|
|
||||||
if (acceptedCount > 0) {
|
|
||||||
const total = acceptedCount + Math.min(requestCount, 1)
|
|
||||||
return {
|
|
||||||
count: total,
|
|
||||||
numUnread: total > 10 ? '10+' : String(total),
|
|
||||||
// only needed when numUnread is undefined
|
|
||||||
hasNew: false,
|
|
||||||
}
|
|
||||||
} else if (requestCount > 0) {
|
|
||||||
return {
|
|
||||||
count: 1,
|
|
||||||
numUnread: undefined,
|
|
||||||
hasNew: true,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
count: 0,
|
|
||||||
numUnread: undefined,
|
|
||||||
hasNew: false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [
|
} else {
|
||||||
accepted,
|
return {
|
||||||
request,
|
count: 0,
|
||||||
currentAccount?.did,
|
numUnread: undefined,
|
||||||
currentConvoId,
|
hasNew: false,
|
||||||
moderationOpts,
|
}
|
||||||
aa.flags,
|
}
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateCount(
|
|
||||||
convos: ChatBskyConvoDefs.ConvoView[],
|
|
||||||
currentAccountDid: string | undefined,
|
|
||||||
currentConvoId: string | undefined,
|
|
||||||
moderationOpts: ModerationOpts | undefined,
|
|
||||||
flags: AgeAssuranceFlags,
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
convos
|
|
||||||
.filter(convo => convo.id !== currentConvoId)
|
|
||||||
.reduce((acc, convoView) => {
|
|
||||||
const convo = parseConvoView(convoView, currentAccountDid)
|
|
||||||
|
|
||||||
if (!convo || !moderationOpts) return acc
|
|
||||||
|
|
||||||
if (convo.kind === 'group' && flags.groupChatDisabled) return acc
|
|
||||||
|
|
||||||
const shouldIgnore =
|
|
||||||
convo.view.muted ||
|
|
||||||
!convo.primaryMember ||
|
|
||||||
moderateProfile(convo.primaryMember, moderationOpts).blocked ||
|
|
||||||
convo.primaryMember.handle === 'missing.invalid' ||
|
|
||||||
(convo.kind === 'group' && convo.details.lockStatus !== 'unlocked')
|
|
||||||
const unreadJoinRequestCount =
|
|
||||||
convo.kind === 'group'
|
|
||||||
? (convo.details.unreadJoinRequestCount ?? 0)
|
|
||||||
: 0
|
|
||||||
|
|
||||||
const unreadCount =
|
|
||||||
!shouldIgnore &&
|
|
||||||
(convo.view.unreadCount > 0 || unreadJoinRequestCount > 0)
|
|
||||||
? 1
|
|
||||||
: 0
|
|
||||||
|
|
||||||
return acc + unreadCount
|
|
||||||
}, 0) ?? 0
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConvoListQueryData = {
|
export type ConvoListQueryData = {
|
||||||
|
|||||||
Reference in New Issue
Block a user