Update DM header to match new Layout style (#8846)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,87 +48,54 @@ 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,
|
{profile && moderation && blockInfo ? (
|
||||||
a.align_start,
|
<HeaderReady
|
||||||
a.gap_sm,
|
profile={profile}
|
||||||
gtTablet ? a.pl_lg : a.pl_xl,
|
moderation={moderation}
|
||||||
a.pr_lg,
|
blockInfo={blockInfo}
|
||||||
a.py_sm,
|
/>
|
||||||
]}>
|
) : (
|
||||||
<TouchableOpacity
|
<>
|
||||||
testID="conversationHeaderBackBtn"
|
<View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}>
|
||||||
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}>
|
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
{width: 120, height: 16},
|
{width: PFP_SIZE, height: PFP_SIZE},
|
||||||
a.rounded_xs,
|
a.rounded_full,
|
||||||
t.atoms.bg_contrast_25,
|
|
||||||
a.mt_xs,
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
{width: 175, height: 12},
|
|
||||||
a.rounded_xs,
|
|
||||||
t.atoms.bg_contrast_25,
|
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>
|
|
||||||
|
|
||||||
<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 style={[a.flex_1]}>
|
||||||
</View>
|
|
||||||
<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,15 +194,19 @@ function HeaderReady({
|
|||||||
</View>
|
</View>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{isConvoActive(convoState) && (
|
<View style={[{minHeight: PFP_SIZE}, a.justify_center]}>
|
||||||
<ConvoMenu
|
<Layout.Header.Slot>
|
||||||
convo={convoState.convo}
|
{isConvoActive(convoState) && (
|
||||||
profile={profile}
|
<ConvoMenu
|
||||||
currentScreen="conversation"
|
convo={convoState.convo}
|
||||||
blockInfo={blockInfo}
|
profile={profile}
|
||||||
latestReportableMessage={latestReportableMessage}
|
currentScreen="conversation"
|
||||||
/>
|
blockInfo={blockInfo}
|
||||||
)}
|
latestReportableMessage={latestReportableMessage}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Layout.Header.Slot>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
|
|||||||
Reference in New Issue
Block a user