Update DM header to match new Layout style (#8846)

This commit is contained in:
Samuel Newman
2025-08-25 19:23:08 +03:00
committed by GitHub
parent ecb77a9b96
commit 54f7af12ca
3 changed files with 88 additions and 125 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ export const BUTTON_VISUAL_ALIGNMENT_OFFSET = 3
/**
* Corresponds to the width of a small square or round button
*/
export const HEADER_SLOT_SIZE = 34
export const HEADER_SLOT_SIZE = 33
/**
* How far to shift the center column when in the tablet breakpoint
+20 -20
View File
@@ -1,12 +1,12 @@
import React, {useCallback} from 'react'
import {Keyboard, Pressable, View} from 'react-native'
import {ChatBskyConvoDefs, ModerationCause} from '@atproto/api'
import {Keyboard, View} from 'react-native'
import {type ChatBskyConvoDefs, type ModerationCause} from '@atproto/api'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from '#/lib/routes/types'
import {Shadow} from '#/state/cache/types'
import {type NavigationProp} from '#/lib/routes/types'
import {type Shadow} from '#/state/cache/types'
import {
useConvoQuery,
useMarkAsReadMutation,
@@ -14,11 +14,15 @@ import {
import {useMuteConvo} from '#/state/queries/messages/mute-conversation'
import {useProfileBlockMutationQueue} from '#/state/queries/profile'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
import {type ViewStyleProp} from '#/alf'
import {atoms as a} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {BlockedByListDialog} from '#/components/dms/BlockedByListDialog'
import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt'
import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt'
import {ReportDialog} from '#/components/dms/ReportDialog'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft'
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble'
import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag'
import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute'
@@ -30,9 +34,7 @@ import {
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
import * as Menu from '#/components/Menu'
import * as Prompt from '#/components/Prompt'
import * as bsky from '#/types/bsky'
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '../icons/Bubble'
import {ReportDialog} from './ReportDialog'
import type * as bsky from '#/types/bsky'
let ConvoMenu = ({
convo,
@@ -59,7 +61,6 @@ let ConvoMenu = ({
style?: ViewStyleProp['style']
}): React.ReactNode => {
const {_} = useLingui()
const t = useTheme()
const leaveConvoControl = Prompt.usePromptControl()
const reportControl = Prompt.usePromptControl()
@@ -73,22 +74,21 @@ let ConvoMenu = ({
{!hideTrigger && (
<View style={[style]}>
<Menu.Trigger label={_(msg`Chat settings`)}>
{({props, state}) => (
<Pressable
{({props}) => (
<Button
label={props.accessibilityLabel}
{...props}
onPress={() => {
Keyboard.dismiss()
props.onPress()
}}
style={[
a.p_sm,
a.rounded_full,
(state.hovered || state.pressed) && t.atoms.bg_contrast_25,
// make sure pfp is in the middle
{marginLeft: -10},
]}>
<DotsHorizontal size="md" style={t.atoms.text} />
</Pressable>
size="small"
color="secondary"
shape="round"
variant="ghost"
style={[a.bg_transparent]}>
<ButtonIcon icon={DotsHorizontal} size="md" />
</Button>
)}
</Menu.Trigger>
</View>
+67 -104
View File
@@ -1,48 +1,42 @@
import React, {useCallback} from 'react'
import {TouchableOpacity, View} from 'react-native'
import {useMemo} from 'react'
import {View} from 'react-native'
import {
type AppBskyActorDefs,
type ModerationCause,
type ModerationDecision,
} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {BACK_HITSLOP} from '#/lib/constants'
import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isWeb} from '#/platform/detection'
import {type Shadow} from '#/state/cache/profile-shadow'
import {isConvoActive, useConvo} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {atoms as a, useTheme, web} from '#/alf'
import {ConvoMenu} from '#/components/dms/ConvoMenu'
import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2'
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link'
import {PostAlerts} from '#/components/moderation/PostAlerts'
import {Text} from '#/components/Typography'
import {useSimpleVerificationState} from '#/components/verification'
import {VerificationCheck} from '#/components/verification/VerificationCheck'
const PFP_SIZE = isWeb ? 40 : 34
const PFP_SIZE = isWeb ? 40 : Layout.HEADER_SLOT_SIZE
export let MessagesListHeader = ({
export function MessagesListHeader({
profile,
moderation,
}: {
profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed>
moderation?: ModerationDecision
}): React.ReactNode => {
}) {
const t = useTheme()
const {_} = useLingui()
const {gtTablet} = useBreakpoints()
const navigation = useNavigation<NavigationProp>()
const blockInfo = React.useMemo(() => {
const blockInfo = useMemo(() => {
if (!moderation) return
const modui = moderation.ui('profileView')
const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
@@ -54,87 +48,54 @@ export let MessagesListHeader = ({
}
}, [moderation])
const onPressBack = useCallback(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.navigate('Messages', {})
}
}, [navigation])
return (
<View
style={[
t.atoms.bg,
t.atoms.border_contrast_low,
a.border_b,
a.flex_row,
a.align_start,
a.gap_sm,
gtTablet ? a.pl_lg : a.pl_xl,
a.pr_lg,
a.py_sm,
]}>
<TouchableOpacity
testID="conversationHeaderBackBtn"
onPress={onPressBack}
hitSlop={BACK_HITSLOP}
style={{width: 30, height: 30, marginTop: isWeb ? 6 : 4}}
accessibilityRole="button"
accessibilityLabel={_(msg`Back`)}
accessibilityHint="">
<FontAwesomeIcon
size={18}
icon="angle-left"
style={{
marginTop: 6,
}}
color={t.atoms.text.color}
/>
</TouchableOpacity>
{profile && moderation && blockInfo ? (
<HeaderReady
profile={profile}
moderation={moderation}
blockInfo={blockInfo}
/>
) : (
<>
<View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}>
<View
style={[
{width: PFP_SIZE, height: PFP_SIZE},
a.rounded_full,
t.atoms.bg_contrast_25,
]}
/>
<View style={a.gap_xs}>
<Layout.Header.Outer>
<View style={[a.w_full, a.flex_row, a.gap_xs, a.align_start]}>
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
<Layout.Header.BackButton />
</View>
{profile && moderation && blockInfo ? (
<HeaderReady
profile={profile}
moderation={moderation}
blockInfo={blockInfo}
/>
) : (
<>
<View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}>
<View
style={[
{width: 120, height: 16},
a.rounded_xs,
t.atoms.bg_contrast_25,
a.mt_xs,
]}
/>
<View
style={[
{width: 175, height: 12},
a.rounded_xs,
{width: PFP_SIZE, height: PFP_SIZE},
a.rounded_full,
t.atoms.bg_contrast_25,
]}
/>
<View style={a.gap_xs}>
<View
style={[
{width: 120, height: 16},
a.rounded_xs,
t.atoms.bg_contrast_25,
a.mt_xs,
]}
/>
<View
style={[
{width: 175, height: 12},
a.rounded_xs,
t.atoms.bg_contrast_25,
]}
/>
</View>
</View>
</View>
<View style={{width: 30}} />
</>
)}
</View>
<Layout.Header.Slot />
</>
)}
</View>
</Layout.Header.Outer>
)
}
MessagesListHeader = React.memo(MessagesListHeader)
function HeaderReady({
profile,
@@ -181,15 +142,13 @@ function HeaderReady({
label={_(msg`View ${displayName}'s profile`)}
style={[a.flex_row, a.align_start, a.gap_md, a.flex_1, a.pr_md]}
to={makeProfileLink(profile)}>
<View style={[a.pt_2xs]}>
<PreviewableUserAvatar
size={PFP_SIZE}
profile={profile}
moderation={moderation.ui('avatar')}
disableHoverCard={moderation.blocked}
/>
</View>
<View style={a.flex_1}>
<PreviewableUserAvatar
size={PFP_SIZE}
profile={profile}
moderation={moderation.ui('avatar')}
disableHoverCard={moderation.blocked}
/>
<View style={[a.flex_1]}>
<View style={[a.flex_row, a.align_center]}>
<Text
emoji
@@ -215,7 +174,7 @@ function HeaderReady({
<Text
style={[
t.atoms.text_contrast_medium,
a.text_sm,
a.text_xs,
web([a.leading_normal, {marginTop: -2}]),
]}
numberOfLines={1}>
@@ -235,15 +194,19 @@ function HeaderReady({
</View>
</Link>
{isConvoActive(convoState) && (
<ConvoMenu
convo={convoState.convo}
profile={profile}
currentScreen="conversation"
blockInfo={blockInfo}
latestReportableMessage={latestReportableMessage}
/>
)}
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
<Layout.Header.Slot>
{isConvoActive(convoState) && (
<ConvoMenu
convo={convoState.convo}
profile={profile}
currentScreen="conversation"
blockInfo={blockInfo}
latestReportableMessage={latestReportableMessage}
/>
)}
</Layout.Header.Slot>
</View>
</View>
<View