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 * 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 * 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 React, {useCallback} from 'react'
import {Keyboard, Pressable, View} from 'react-native' import {Keyboard, View} from 'react-native'
import {ChatBskyConvoDefs, ModerationCause} from '@atproto/api' import {type ChatBskyConvoDefs, type ModerationCause} from '@atproto/api'
import {msg, Trans} from '@lingui/macro' import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native' import {useNavigation} from '@react-navigation/native'
import {NavigationProp} from '#/lib/routes/types' import {type NavigationProp} from '#/lib/routes/types'
import {Shadow} from '#/state/cache/types' import {type Shadow} from '#/state/cache/types'
import { import {
useConvoQuery, useConvoQuery,
useMarkAsReadMutation, useMarkAsReadMutation,
@@ -14,11 +14,15 @@ import {
import {useMuteConvo} from '#/state/queries/messages/mute-conversation' import {useMuteConvo} from '#/state/queries/messages/mute-conversation'
import {useProfileBlockMutationQueue} from '#/state/queries/profile' import {useProfileBlockMutationQueue} from '#/state/queries/profile'
import * as Toast from '#/view/com/util/Toast' 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 {BlockedByListDialog} from '#/components/dms/BlockedByListDialog'
import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt' import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt'
import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt' import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt'
import {ReportDialog} from '#/components/dms/ReportDialog'
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft' 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 {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag' import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag'
import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' 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 {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker'
import * as Menu from '#/components/Menu' import * as Menu from '#/components/Menu'
import * as Prompt from '#/components/Prompt' import * as Prompt from '#/components/Prompt'
import * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '../icons/Bubble'
import {ReportDialog} from './ReportDialog'
let ConvoMenu = ({ let ConvoMenu = ({
convo, convo,
@@ -59,7 +61,6 @@ let ConvoMenu = ({
style?: ViewStyleProp['style'] style?: ViewStyleProp['style']
}): React.ReactNode => { }): React.ReactNode => {
const {_} = useLingui() const {_} = useLingui()
const t = useTheme()
const leaveConvoControl = Prompt.usePromptControl() const leaveConvoControl = Prompt.usePromptControl()
const reportControl = Prompt.usePromptControl() const reportControl = Prompt.usePromptControl()
@@ -73,22 +74,21 @@ let ConvoMenu = ({
{!hideTrigger && ( {!hideTrigger && (
<View style={[style]}> <View style={[style]}>
<Menu.Trigger label={_(msg`Chat settings`)}> <Menu.Trigger label={_(msg`Chat settings`)}>
{({props, state}) => ( {({props}) => (
<Pressable <Button
label={props.accessibilityLabel}
{...props} {...props}
onPress={() => { onPress={() => {
Keyboard.dismiss() Keyboard.dismiss()
props.onPress() props.onPress()
}} }}
style={[ size="small"
a.p_sm, color="secondary"
a.rounded_full, shape="round"
(state.hovered || state.pressed) && t.atoms.bg_contrast_25, variant="ghost"
// make sure pfp is in the middle style={[a.bg_transparent]}>
{marginLeft: -10}, <ButtonIcon icon={DotsHorizontal} size="md" />
]}> </Button>
<DotsHorizontal size="md" style={t.atoms.text} />
</Pressable>
)} )}
</Menu.Trigger> </Menu.Trigger>
</View> </View>
+21 -58
View File
@@ -1,48 +1,42 @@
import React, {useCallback} from 'react' import {useMemo} from 'react'
import {TouchableOpacity, View} from 'react-native' import {View} from 'react-native'
import { import {
type AppBskyActorDefs, type AppBskyActorDefs,
type ModerationCause, type ModerationCause,
type ModerationDecision, type ModerationDecision,
} from '@atproto/api' } from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {BACK_HITSLOP} from '#/lib/constants'
import {makeProfileLink} from '#/lib/routes/links' import {makeProfileLink} from '#/lib/routes/links'
import {type NavigationProp} from '#/lib/routes/types'
import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {isWeb} from '#/platform/detection' import {isWeb} from '#/platform/detection'
import {type Shadow} from '#/state/cache/profile-shadow' import {type Shadow} from '#/state/cache/profile-shadow'
import {isConvoActive, useConvo} from '#/state/messages/convo' import {isConvoActive, useConvo} from '#/state/messages/convo'
import {type ConvoItem} from '#/state/messages/convo/types' import {type ConvoItem} from '#/state/messages/convo/types'
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' 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 {ConvoMenu} from '#/components/dms/ConvoMenu'
import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2' import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2'
import * as Layout from '#/components/Layout'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import {PostAlerts} from '#/components/moderation/PostAlerts' import {PostAlerts} from '#/components/moderation/PostAlerts'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useSimpleVerificationState} from '#/components/verification' import {useSimpleVerificationState} from '#/components/verification'
import {VerificationCheck} from '#/components/verification/VerificationCheck' 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, profile,
moderation, moderation,
}: { }: {
profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed> profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed>
moderation?: ModerationDecision moderation?: ModerationDecision
}): React.ReactNode => { }) {
const t = useTheme() const t = useTheme()
const {_} = useLingui()
const {gtTablet} = useBreakpoints()
const navigation = useNavigation<NavigationProp>()
const blockInfo = React.useMemo(() => { const blockInfo = useMemo(() => {
if (!moderation) return if (!moderation) return
const modui = moderation.ui('profileView') const modui = moderation.ui('profileView')
const blocks = modui.alerts.filter(alert => alert.type === 'blocking') const blocks = modui.alerts.filter(alert => alert.type === 'blocking')
@@ -54,45 +48,12 @@ export let MessagesListHeader = ({
} }
}, [moderation]) }, [moderation])
const onPressBack = useCallback(() => {
if (navigation.canGoBack()) {
navigation.goBack()
} else {
navigation.navigate('Messages', {})
}
}, [navigation])
return ( return (
<View <Layout.Header.Outer>
style={[ <View style={[a.w_full, a.flex_row, a.gap_xs, a.align_start]}>
t.atoms.bg, <View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
t.atoms.border_contrast_low, <Layout.Header.BackButton />
a.border_b, </View>
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 ? ( {profile && moderation && blockInfo ? (
<HeaderReady <HeaderReady
profile={profile} profile={profile}
@@ -128,13 +89,13 @@ export let MessagesListHeader = ({
</View> </View>
</View> </View>
<View style={{width: 30}} /> <Layout.Header.Slot />
</> </>
)} )}
</View> </View>
</Layout.Header.Outer>
) )
} }
MessagesListHeader = React.memo(MessagesListHeader)
function HeaderReady({ function HeaderReady({
profile, profile,
@@ -181,15 +142,13 @@ function HeaderReady({
label={_(msg`View ${displayName}'s profile`)} label={_(msg`View ${displayName}'s profile`)}
style={[a.flex_row, a.align_start, a.gap_md, a.flex_1, a.pr_md]} style={[a.flex_row, a.align_start, a.gap_md, a.flex_1, a.pr_md]}
to={makeProfileLink(profile)}> to={makeProfileLink(profile)}>
<View style={[a.pt_2xs]}>
<PreviewableUserAvatar <PreviewableUserAvatar
size={PFP_SIZE} size={PFP_SIZE}
profile={profile} profile={profile}
moderation={moderation.ui('avatar')} moderation={moderation.ui('avatar')}
disableHoverCard={moderation.blocked} disableHoverCard={moderation.blocked}
/> />
</View> <View style={[a.flex_1]}>
<View style={a.flex_1}>
<View style={[a.flex_row, a.align_center]}> <View style={[a.flex_row, a.align_center]}>
<Text <Text
emoji emoji
@@ -215,7 +174,7 @@ function HeaderReady({
<Text <Text
style={[ style={[
t.atoms.text_contrast_medium, t.atoms.text_contrast_medium,
a.text_sm, a.text_xs,
web([a.leading_normal, {marginTop: -2}]), web([a.leading_normal, {marginTop: -2}]),
]} ]}
numberOfLines={1}> numberOfLines={1}>
@@ -235,6 +194,8 @@ function HeaderReady({
</View> </View>
</Link> </Link>
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
<Layout.Header.Slot>
{isConvoActive(convoState) && ( {isConvoActive(convoState) && (
<ConvoMenu <ConvoMenu
convo={convoState.convo} convo={convoState.convo}
@@ -244,6 +205,8 @@ function HeaderReady({
latestReportableMessage={latestReportableMessage} latestReportableMessage={latestReportableMessage}
/> />
)} )}
</Layout.Header.Slot>
</View>
</View> </View>
<View <View