Update chat requests header (#10446)
This commit is contained in:
@@ -139,11 +139,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/dialogs/GifSelect.tsx": {
|
|
||||||
"@typescript-eslint/no-explicit-any": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/components/dialogs/LanguageSelectDialog.tsx": {
|
"src/components/dialogs/LanguageSelectDialog.tsx": {
|
||||||
"@typescript-eslint/no-explicit-any": {
|
"@typescript-eslint/no-explicit-any": {
|
||||||
"count": 1
|
"count": 1
|
||||||
@@ -579,21 +574,6 @@
|
|||||||
"count": 3
|
"count": 3
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/view/com/lightbox/ImageViewing/@types/index.ts": {
|
|
||||||
"@typescript-eslint/no-explicit-any": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.android.tsx": {
|
|
||||||
"@typescript-eslint/no-explicit-any": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/view/com/lightbox/ImageViewing/index.tsx": {
|
|
||||||
"@typescript-eslint/no-explicit-any": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/view/com/lists/ListMembers.tsx": {
|
"src/view/com/lists/ListMembers.tsx": {
|
||||||
"@typescript-eslint/no-explicit-any": {
|
"@typescript-eslint/no-explicit-any": {
|
||||||
"count": 3
|
"count": 3
|
||||||
@@ -709,11 +689,6 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/view/com/util/fab/FABInner.tsx": {
|
|
||||||
"@typescript-eslint/no-explicit-any": {
|
|
||||||
"count": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"src/view/screens/Debug.tsx": {
|
"src/view/screens/Debug.tsx": {
|
||||||
"@typescript-eslint/no-explicit-any": {
|
"@typescript-eslint/no-explicit-any": {
|
||||||
"count": 1
|
"count": 1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
import {useCallback, useEffect, useMemo, 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 ChatBskyActorDefs, type ChatBskyConvoDefs} from '@atproto/api'
|
import {type ChatBskyConvoDefs} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {Trans} from '@lingui/react/macro'
|
import {Trans} from '@lingui/react/macro'
|
||||||
@@ -19,7 +19,6 @@ import {MESSAGE_SCREEN_POLL_INTERVAL} from '#/state/messages/convo/const'
|
|||||||
import {useMessagesEventBus} from '#/state/messages/events'
|
import {useMessagesEventBus} from '#/state/messages/events'
|
||||||
import {useLeftConvos} from '#/state/queries/messages/leave-conversation'
|
import {useLeftConvos} from '#/state/queries/messages/leave-conversation'
|
||||||
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
|
import {useListConvosQuery} from '#/state/queries/messages/list-conversations'
|
||||||
import {useSession} from '#/state/session'
|
|
||||||
import {List, type ListRef} from '#/view/com/util/List'
|
import {List, type ListRef} from '#/view/com/util/List'
|
||||||
import {ChatListLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {ChatListLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||||
@@ -41,30 +40,19 @@ import {Text} from '#/components/Typography'
|
|||||||
import {useAgeAssurance} from '#/ageAssurance'
|
import {useAgeAssurance} from '#/ageAssurance'
|
||||||
import {IS_NATIVE} from '#/env'
|
import {IS_NATIVE} from '#/env'
|
||||||
import {ChatListItem} from './components/ChatListItem'
|
import {ChatListItem} from './components/ChatListItem'
|
||||||
import {InboxPreview} from './components/InboxPreview'
|
import {InboxRequests} from './components/InboxRequests'
|
||||||
|
|
||||||
type ListItem =
|
type ListItem = {
|
||||||
| {
|
type: 'CONVERSATION'
|
||||||
type: 'INBOX'
|
conversation: ChatBskyConvoDefs.ConvoView
|
||||||
count: number
|
}
|
||||||
profiles: ChatBskyActorDefs.ProfileViewBasic[]
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'CONVERSATION'
|
|
||||||
conversation: ChatBskyConvoDefs.ConvoView
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderItem({item}: {item: ListItem}) {
|
function renderItem({item}: {item: ListItem}) {
|
||||||
switch (item.type) {
|
return <ChatListItem convo={item.conversation} />
|
||||||
case 'INBOX':
|
|
||||||
return <InboxPreview profiles={item.profiles} />
|
|
||||||
case 'CONVERSATION':
|
|
||||||
return <ChatListItem convo={item.conversation} />
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function keyExtractor(item: ListItem) {
|
function keyExtractor(item: ListItem) {
|
||||||
return item.type === 'INBOX' ? 'INBOX' : item.conversation.id
|
return item.conversation.id
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<MessagesTabNavigatorParams, 'Messages'>
|
type Props = NativeStackScreenProps<MessagesTabNavigatorParams, 'Messages'>
|
||||||
@@ -99,7 +87,6 @@ export function MessagesScreen(props: Props) {
|
|||||||
export function MessagesScreenInner({navigation, route}: Props) {
|
export function MessagesScreenInner({navigation, route}: Props) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {currentAccount} = useSession()
|
|
||||||
const newChatControl = useDialogControl()
|
const newChatControl = useDialogControl()
|
||||||
const scrollElRef: ListRef = useAnimatedRef()
|
const scrollElRef: ListRef = useAnimatedRef()
|
||||||
const pushToConversation = route.params?.pushToConversation
|
const pushToConversation = route.params?.pushToConversation
|
||||||
@@ -147,7 +134,11 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
refetch,
|
refetch,
|
||||||
} = useListConvosQuery({status: 'accepted'})
|
} = useListConvosQuery({status: 'accepted'})
|
||||||
|
|
||||||
const {data: inboxData, refetch: refetchInbox} = useListConvosQuery({
|
const {
|
||||||
|
data: inboxData,
|
||||||
|
refetch: refetchInbox,
|
||||||
|
hasNextPage: hasMoreRequests,
|
||||||
|
} = useListConvosQuery({
|
||||||
status: 'request',
|
status: 'request',
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -165,15 +156,6 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
!convo.muted &&
|
!convo.muted &&
|
||||||
convo.members.every(member => member.handle !== 'missing.invalid'),
|
convo.members.every(member => member.handle !== 'missing.invalid'),
|
||||||
) ?? []
|
) ?? []
|
||||||
const hasInboxConvos = inboxAllConvos?.length > 0
|
|
||||||
|
|
||||||
const inboxUnreadConvos = inboxAllConvos.filter(
|
|
||||||
convo => convo.unreadCount > 0,
|
|
||||||
)
|
|
||||||
|
|
||||||
const inboxUnreadConvoMembers = inboxUnreadConvos
|
|
||||||
.map(x => x.members.find(y => y.did !== currentAccount?.did))
|
|
||||||
.filter(x => !!x)
|
|
||||||
|
|
||||||
const conversations = useMemo(() => {
|
const conversations = useMemo(() => {
|
||||||
if (data?.pages) {
|
if (data?.pages) {
|
||||||
@@ -183,15 +165,6 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
.filter(convo => !leftConvos.includes(convo.id))
|
.filter(convo => !leftConvos.includes(convo.id))
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...(hasInboxConvos
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
type: 'INBOX' as const,
|
|
||||||
count: inboxUnreadConvoMembers.length,
|
|
||||||
profiles: inboxUnreadConvoMembers.slice(0, 3),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
...conversations.map(
|
...conversations.map(
|
||||||
convo =>
|
convo =>
|
||||||
({
|
({
|
||||||
@@ -202,7 +175,7 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
] satisfies ListItem[]
|
] satisfies ListItem[]
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
}, [data, leftConvos, hasInboxConvos, inboxUnreadConvoMembers])
|
}, [data, leftConvos])
|
||||||
|
|
||||||
const onRefresh = useCallback(async () => {
|
const onRefresh = useCallback(async () => {
|
||||||
setIsPTRing(true)
|
setIsPTRing(true)
|
||||||
@@ -258,11 +231,12 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
if (activeConversations.length === 0) {
|
if (activeConversations.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Layout.Screen>
|
<Layout.Screen>
|
||||||
<Header newChatControl={newChatControl} />
|
<Header
|
||||||
|
newChatControl={newChatControl}
|
||||||
|
requestsCount={inboxAllConvos.length}
|
||||||
|
hasMoreRequests={hasMoreRequests}
|
||||||
|
/>
|
||||||
<Layout.Center>
|
<Layout.Center>
|
||||||
{!isLoading && hasInboxConvos && (
|
|
||||||
<InboxPreview profiles={inboxUnreadConvoMembers} />
|
|
||||||
)}
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<ChatListLoadingPlaceholder />
|
<ChatListLoadingPlaceholder />
|
||||||
) : (
|
) : (
|
||||||
@@ -338,7 +312,11 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen testID="messagesScreen">
|
<Layout.Screen testID="messagesScreen">
|
||||||
<Header newChatControl={newChatControl} />
|
<Header
|
||||||
|
newChatControl={newChatControl}
|
||||||
|
requestsCount={inboxAllConvos.length}
|
||||||
|
hasMoreRequests={hasMoreRequests}
|
||||||
|
/>
|
||||||
<NewChat onNewChat={onNewChat} control={newChatControl} />
|
<NewChat onNewChat={onNewChat} control={newChatControl} />
|
||||||
<List
|
<List
|
||||||
ref={scrollElRef}
|
ref={scrollElRef}
|
||||||
@@ -367,7 +345,15 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function Header({newChatControl}: {newChatControl: DialogControlProps}) {
|
function Header({
|
||||||
|
newChatControl,
|
||||||
|
requestsCount,
|
||||||
|
hasMoreRequests,
|
||||||
|
}: {
|
||||||
|
newChatControl: DialogControlProps
|
||||||
|
requestsCount: number
|
||||||
|
hasMoreRequests: boolean
|
||||||
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const requireEmailVerification = useRequireEmailVerification()
|
const requireEmailVerification = useRequireEmailVerification()
|
||||||
@@ -383,6 +369,10 @@ function Header({newChatControl}: {newChatControl: DialogControlProps}) {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const requestsLink = (
|
||||||
|
<InboxRequests count={requestsCount} more={hasMoreRequests} />
|
||||||
|
)
|
||||||
|
|
||||||
const settingsLink = (
|
const settingsLink = (
|
||||||
<Link
|
<Link
|
||||||
to="/messages/settings"
|
to="/messages/settings"
|
||||||
@@ -407,6 +397,7 @@ function Header({newChatControl}: {newChatControl: DialogControlProps}) {
|
|||||||
</Layout.Header.Content>
|
</Layout.Header.Content>
|
||||||
|
|
||||||
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
<View style={[a.flex_row, a.align_center, a.gap_sm]}>
|
||||||
|
{requestsLink}
|
||||||
{settingsLink}
|
{settingsLink}
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`New chat`)}
|
label={_(msg`New chat`)}
|
||||||
@@ -424,11 +415,12 @@ function Header({newChatControl}: {newChatControl: DialogControlProps}) {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Layout.Header.MenuButton />
|
<Layout.Header.MenuButton />
|
||||||
<Layout.Header.Content>
|
<Layout.Header.Content align="left">
|
||||||
<Layout.Header.TitleText>
|
<Layout.Header.TitleText>
|
||||||
<Trans>Chats</Trans>
|
<Trans>Chats</Trans>
|
||||||
</Layout.Header.TitleText>
|
</Layout.Header.TitleText>
|
||||||
</Layout.Header.Content>
|
</Layout.Header.Content>
|
||||||
|
{requestsLink}
|
||||||
<Layout.Header.Slot>{settingsLink}</Layout.Header.Slot>
|
<Layout.Header.Slot>{settingsLink}</Layout.Header.Slot>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import {
|
|||||||
type ChatBskyConvoDefs,
|
type ChatBskyConvoDefs,
|
||||||
type ChatBskyConvoListConvos,
|
type ChatBskyConvoListConvos,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {Trans, useLingui} from '@lingui/react/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
|
||||||
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
import {useFocusEffect, useNavigation} from '@react-navigation/native'
|
||||||
import {
|
import {
|
||||||
type InfiniteData,
|
type InfiniteData,
|
||||||
@@ -50,11 +48,11 @@ import {RequestListItem} from './components/RequestListItem'
|
|||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'MessagesInbox'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'MessagesInbox'>
|
||||||
|
|
||||||
export function MessagesInboxScreen(props: Props) {
|
export function MessagesInboxScreen(props: Props) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const aaCopy = useAgeAssuranceCopy()
|
const aaCopy = useAgeAssuranceCopy()
|
||||||
return (
|
return (
|
||||||
<AgeRestrictedScreen
|
<AgeRestrictedScreen
|
||||||
screenTitle={_(msg`Chat requests`)}
|
screenTitle={l`Chat requests`}
|
||||||
infoText={aaCopy.chatsInfoText}>
|
infoText={aaCopy.chatsInfoText}>
|
||||||
<MessagesInboxScreenInner {...props} />
|
<MessagesInboxScreenInner {...props} />
|
||||||
</AgeRestrictedScreen>
|
</AgeRestrictedScreen>
|
||||||
@@ -126,7 +124,7 @@ function RequestList({
|
|||||||
conversations: ChatBskyConvoDefs.ConvoView[]
|
conversations: ChatBskyConvoDefs.ConvoView[]
|
||||||
hasUnreadConvos: boolean
|
hasUnreadConvos: boolean
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const navigation = useNavigation<NavigationProp>()
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
@@ -207,15 +205,15 @@ function RequestList({
|
|||||||
t.atoms.text_contrast_medium,
|
t.atoms.text_contrast_medium,
|
||||||
{maxWidth: 360},
|
{maxWidth: 360},
|
||||||
]}>
|
]}>
|
||||||
{cleanError(error) || _(msg`Failed to load conversations`)}
|
{cleanError(error) || l`Failed to load conversations`}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Reload conversations`)}
|
label={l`Reload conversations`}
|
||||||
size="small"
|
size="small"
|
||||||
color="secondary_inverted"
|
color="secondary_inverted"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
onPress={() => refetch()}>
|
onPress={() => void refetch()}>
|
||||||
<ButtonText>
|
<ButtonText>
|
||||||
<Trans>Retry</Trans>
|
<Trans>Retry</Trans>
|
||||||
</ButtonText>
|
</ButtonText>
|
||||||
@@ -249,7 +247,7 @@ function RequestList({
|
|||||||
variant="solid"
|
variant="solid"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
size="small"
|
size="small"
|
||||||
label={_(msg`Go back`)}
|
label={l`Go back`}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (navigation.canGoBack()) {
|
if (navigation.canGoBack()) {
|
||||||
navigation.goBack()
|
navigation.goBack()
|
||||||
@@ -278,8 +276,8 @@ function RequestList({
|
|||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
keyExtractor={keyExtractor}
|
keyExtractor={keyExtractor}
|
||||||
refreshing={isPTRing}
|
refreshing={isPTRing}
|
||||||
onRefresh={onRefresh}
|
onRefresh={() => void onRefresh()}
|
||||||
onEndReached={onEndReached}
|
onEndReached={() => void onEndReached()}
|
||||||
ListFooterComponent={
|
ListFooterComponent={
|
||||||
<ListFooter
|
<ListFooter
|
||||||
isFetchingNextPage={isFetchingNextPage}
|
isFetchingNextPage={isFetchingNextPage}
|
||||||
@@ -309,16 +307,16 @@ function renderItem({item}: {item: ChatBskyConvoDefs.ConvoView}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MarkAllReadFAB() {
|
function MarkAllReadFAB() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {mutate: markAllRead} = useUpdateAllRead('request', {
|
const {mutate: markAllRead} = useUpdateAllRead('request', {
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
Toast.show(_(msg`Marked all as read`), {
|
Toast.show(l`Marked all as read`, {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
Toast.show(_(msg`Failed to mark all requests as read`), {
|
Toast.show(l`Failed to mark all requests as read`, {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -330,22 +328,22 @@ function MarkAllReadFAB() {
|
|||||||
onPress={() => markAllRead()}
|
onPress={() => markAllRead()}
|
||||||
icon={<CheckIcon size="lg" fill={t.palette.white} />}
|
icon={<CheckIcon size="lg" fill={t.palette.white} />}
|
||||||
accessibilityRole="button"
|
accessibilityRole="button"
|
||||||
accessibilityLabel={_(msg`Mark all as read`)}
|
accessibilityLabel={l`Mark all as read`}
|
||||||
accessibilityHint=""
|
accessibilityHint=""
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function MarkAsReadHeaderButton() {
|
function MarkAsReadHeaderButton() {
|
||||||
const {_} = useLingui()
|
const {t: l} = useLingui()
|
||||||
const {mutate: markAllRead} = useUpdateAllRead('request', {
|
const {mutate: markAllRead} = useUpdateAllRead('request', {
|
||||||
onMutate: () => {
|
onMutate: () => {
|
||||||
Toast.show(_(msg`Marked all as read`), {
|
Toast.show(l`Marked all as read`, {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
Toast.show(_(msg`Failed to mark all requests as read`), {
|
Toast.show(l`Failed to mark all requests as read`, {
|
||||||
type: 'error',
|
type: 'error',
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -353,7 +351,7 @@ function MarkAsReadHeaderButton() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
label={_(msg`Mark all as read`)}
|
label={l`Mark all as read`}
|
||||||
size="small"
|
size="small"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
import {View} from 'react-native'
|
|
||||||
import {type ChatBskyActorDefs} from '@atproto/api'
|
|
||||||
import {msg} from '@lingui/core/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
import {Trans} from '@lingui/react/macro'
|
|
||||||
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
|
||||||
import {AvatarStack} from '#/components/AvatarStack'
|
|
||||||
import {ButtonIcon, ButtonText} from '#/components/Button'
|
|
||||||
import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRightIcon} from '#/components/icons/Arrow'
|
|
||||||
import {Envelope_Stroke2_Corner2_Rounded as EnvelopeIcon} from '#/components/icons/Envelope'
|
|
||||||
import {Link} from '#/components/Link'
|
|
||||||
|
|
||||||
export function InboxPreview({
|
|
||||||
profiles,
|
|
||||||
}: {
|
|
||||||
profiles: ChatBskyActorDefs.ProfileViewBasic[]
|
|
||||||
}) {
|
|
||||||
const {_} = useLingui()
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
label={_(msg`Chat request inbox`)}
|
|
||||||
style={[
|
|
||||||
a.flex_1,
|
|
||||||
a.px_xl,
|
|
||||||
a.py_sm,
|
|
||||||
a.flex_row,
|
|
||||||
a.align_center,
|
|
||||||
a.gap_md,
|
|
||||||
a.border_t,
|
|
||||||
{marginTop: a.border_t.borderTopWidth * -1},
|
|
||||||
a.border_b,
|
|
||||||
t.atoms.border_contrast_low,
|
|
||||||
{minHeight: 44},
|
|
||||||
a.rounded_0,
|
|
||||||
]}
|
|
||||||
to="/messages/inbox"
|
|
||||||
color="secondary"
|
|
||||||
variant="solid">
|
|
||||||
<View style={[a.relative]}>
|
|
||||||
<ButtonIcon icon={EnvelopeIcon} size="lg" />
|
|
||||||
{profiles.length > 0 && (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.absolute,
|
|
||||||
a.rounded_full,
|
|
||||||
a.z_20,
|
|
||||||
{
|
|
||||||
top: -4,
|
|
||||||
right: -5,
|
|
||||||
width: 10,
|
|
||||||
height: 10,
|
|
||||||
backgroundColor: t.palette.primary_500,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
<ButtonText
|
|
||||||
style={[a.flex_1, a.font_semi_bold, a.text_left]}
|
|
||||||
numberOfLines={1}>
|
|
||||||
<Trans>Chat requests</Trans>
|
|
||||||
</ButtonText>
|
|
||||||
<AvatarStack
|
|
||||||
profiles={profiles}
|
|
||||||
backgroundColor={t.atoms.bg_contrast_25.backgroundColor}
|
|
||||||
/>
|
|
||||||
<ButtonIcon icon={ArrowRightIcon} size="lg" />
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {plural} from '@lingui/core/macro'
|
||||||
|
import {useLingui} from '@lingui/react/macro'
|
||||||
|
|
||||||
|
import {UNREAD_LIMIT} from '#/state/queries/messages/list-conversations'
|
||||||
|
import {atoms as a} from '#/alf'
|
||||||
|
import {InlineLinkText} from '#/components/Link'
|
||||||
|
|
||||||
|
export function InboxRequests({count, more}: {count: number; more: boolean}) {
|
||||||
|
const {t: l} = useLingui()
|
||||||
|
|
||||||
|
if (count < 1) return null
|
||||||
|
|
||||||
|
const label =
|
||||||
|
count >= UNREAD_LIMIT && more
|
||||||
|
? l({
|
||||||
|
message: `${count}+ requests`,
|
||||||
|
comment: 'Displayed when the number of requests is greater than 20',
|
||||||
|
})
|
||||||
|
: plural(count, {
|
||||||
|
one: '# request',
|
||||||
|
other: '# requests',
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InlineLinkText
|
||||||
|
label={label}
|
||||||
|
to="/messages/inbox"
|
||||||
|
style={[a.text_md, a.font_medium]}>
|
||||||
|
{label}
|
||||||
|
</InlineLinkText>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import {parseConvoView} from '#/components/dms/util'
|
|||||||
import {useLeftConvos} from './leave-conversation'
|
import {useLeftConvos} from './leave-conversation'
|
||||||
|
|
||||||
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 = (
|
||||||
@@ -102,7 +103,10 @@ export function ListConvosProviderInner({
|
|||||||
}: {
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
const {refetch, data} = useListConvosQuery({readState: 'unread', limit: 20})
|
const {refetch, data} = useListConvosQuery({
|
||||||
|
readState: 'unread',
|
||||||
|
limit: UNREAD_LIMIT,
|
||||||
|
})
|
||||||
const messagesBus = useMessagesEventBus()
|
const messagesBus = useMessagesEventBus()
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
const {currentConvoId} = useCurrentConvoId()
|
const {currentConvoId} = useCurrentConvoId()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {type ComponentProps, type JSX} from 'react'
|
import {type ComponentProps, type JSX} from 'react'
|
||||||
import {
|
import {
|
||||||
|
type GestureResponderEvent,
|
||||||
type Pressable,
|
type Pressable,
|
||||||
type StyleProp,
|
type StyleProp,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@@ -49,7 +50,7 @@ export function FABInner({testID, icon, onPress, style, ...props}: FABProps) {
|
|||||||
onPress?.(evt)
|
onPress?.(evt)
|
||||||
playHaptic('Light')
|
playHaptic('Light')
|
||||||
}}
|
}}
|
||||||
onLongPress={ios((evt: any) => {
|
onLongPress={ios((evt: GestureResponderEvent) => {
|
||||||
onPress?.(evt)
|
onPress?.(evt)
|
||||||
playHaptic('Heavy')
|
playHaptic('Heavy')
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user