Refine hitslop

This commit is contained in:
Eric Bailey
2025-09-04 15:35:59 -05:00
parent c23bdac0dc
commit 0138e8368a
6 changed files with 47 additions and 12 deletions
@@ -1,4 +1,5 @@
import {memo} from 'react' import {memo} from 'react'
import {type Insets} from 'react-native'
import {type AppBskyFeedDefs} from '@atproto/api' import {type AppBskyFeedDefs} 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'
@@ -18,10 +19,12 @@ export const BookmarkButton = memo(function BookmarkButton({
post, post,
big, big,
logContext, logContext,
hitSlop,
}: { }: {
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<AppBskyFeedDefs.PostView>
big?: boolean big?: boolean
logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo' logContext: 'FeedItem' | 'PostThreadItem' | 'Post' | 'ImmersiveVideo'
hitSlop?: Insets
}): React.ReactNode { }): React.ReactNode {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
@@ -122,7 +125,8 @@ export const BookmarkButton = memo(function BookmarkButton({
? _(msg`Remove from saved posts`) ? _(msg`Remove from saved posts`)
: _(msg`Add to saved posts`) : _(msg`Add to saved posts`)
} }
onPress={onHandlePress}> onPress={onHandlePress}
hitSlop={hitSlop}>
<PostControlButtonIcon <PostControlButtonIcon
fill={isBookmarked ? t.palette.primary_500 : undefined} fill={isBookmarked ? t.palette.primary_500 : undefined}
icon={isBookmarked ? BookmarkFilled : Bookmark} icon={isBookmarked ? BookmarkFilled : Bookmark}
@@ -1,13 +1,14 @@
import {createContext, useContext, useMemo} from 'react' import {createContext, useContext, useMemo} from 'react'
import {type GestureResponderEvent, type View} from 'react-native' import {type GestureResponderEvent, type Insets, type View} from 'react-native'
import {POST_CTRL_HITSLOP} from '#/lib/constants'
import {useHaptics} from '#/lib/haptics' import {useHaptics} from '#/lib/haptics'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button, type ButtonProps} from '#/components/Button' import {Button, type ButtonProps} from '#/components/Button'
import {type Props as SVGIconProps} from '#/components/icons/common' import {type Props as SVGIconProps} from '#/components/icons/common'
import {Text, type TextProps} from '#/components/Typography' import {Text, type TextProps} from '#/components/Typography'
export const DEFAULT_HITSLOP = {top: 5, bottom: 10, left: 10, right: 10}
const PostControlContext = createContext<{ const PostControlContext = createContext<{
big?: boolean big?: boolean
active?: boolean active?: boolean
@@ -25,12 +26,13 @@ export function PostControlButton({
active, active,
activeColor, activeColor,
...props ...props
}: ButtonProps & { }: Omit<ButtonProps, 'hitSlop'> & {
ref?: React.Ref<View> ref?: React.Ref<View>
active?: boolean active?: boolean
big?: boolean big?: boolean
color?: string color?: string
activeColor?: string activeColor?: string
hitSlop?: Insets
}) { }) {
const t = useTheme() const t = useTheme()
const playHaptic = useHaptics() const playHaptic = useHaptics()
@@ -83,8 +85,11 @@ export function PostControlButton({
shape="round" shape="round"
variant="ghost" variant="ghost"
color="secondary" color="secondary"
hitSlop={POST_CTRL_HITSLOP} {...props}
{...props}> hitSlop={{
...DEFAULT_HITSLOP,
...(props.hitSlop || {}),
}}>
{typeof children === 'function' ? ( {typeof children === 'function' ? (
args => ( args => (
<PostControlContext.Provider value={ctx}> <PostControlContext.Provider value={ctx}>
@@ -1,4 +1,5 @@
import {memo, useMemo, useState} from 'react' import {memo, useMemo, useState} from 'react'
import {type Insets} from 'react-native'
import { import {
type AppBskyFeedDefs, type AppBskyFeedDefs,
type AppBskyFeedPost, type AppBskyFeedPost,
@@ -28,6 +29,7 @@ let PostMenuButton = ({
timestamp, timestamp,
threadgateRecord, threadgateRecord,
onShowLess, onShowLess,
hitSlop,
}: { }: {
testID: string testID: string
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<AppBskyFeedDefs.PostView>
@@ -39,6 +41,7 @@ let PostMenuButton = ({
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: AppBskyFeedThreadgate.Record
onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void onShowLess?: (interaction: AppBskyFeedDefs.Interaction) => void
hitSlop?: Insets
}): React.ReactNode => { }): React.ReactNode => {
const {_} = useLingui() const {_} = useLingui()
@@ -66,7 +69,8 @@ let PostMenuButton = ({
testID="postDropdownBtn" testID="postDropdownBtn"
big={big} big={big}
label={props.accessibilityLabel} label={props.accessibilityLabel}
{...props}> {...props}
hitSlop={hitSlop}>
<PostControlButtonIcon icon={DotsHorizontal} /> <PostControlButtonIcon icon={DotsHorizontal} />
</PostControlButton> </PostControlButton>
) )
@@ -1,4 +1,5 @@
import {memo, useMemo, useState} from 'react' import {memo, useMemo, useState} from 'react'
import {type Insets} from 'react-native'
import { import {
type AppBskyFeedDefs, type AppBskyFeedDefs,
type AppBskyFeedPost, type AppBskyFeedPost,
@@ -34,6 +35,7 @@ let ShareMenuButton = ({
timestamp, timestamp,
threadgateRecord, threadgateRecord,
onShare, onShare,
hitSlop,
}: { }: {
testID: string testID: string
post: Shadow<AppBskyFeedDefs.PostView> post: Shadow<AppBskyFeedDefs.PostView>
@@ -43,6 +45,7 @@ let ShareMenuButton = ({
timestamp: string timestamp: string
threadgateRecord?: AppBskyFeedThreadgate.Record threadgateRecord?: AppBskyFeedThreadgate.Record
onShare: () => void onShare: () => void
hitSlop?: Insets
}): React.ReactNode => { }): React.ReactNode => {
const {_} = useLingui() const {_} = useLingui()
const gate = useGate() const gate = useGate()
@@ -92,7 +95,8 @@ let ShareMenuButton = ({
big={big} big={big}
label={props.accessibilityLabel} label={props.accessibilityLabel}
{...props} {...props}
onLongPress={native(onNativeLongPress)}> onLongPress={native(onNativeLongPress)}
hitSlop={hitSlop}>
<PostControlButtonIcon icon={ShareIcon} /> <PostControlButtonIcon icon={ShareIcon} />
</PostControlButton> </PostControlButton>
) )
+22 -3
View File
@@ -25,7 +25,7 @@ import {
useProgressGuideControls, useProgressGuideControls,
} from '#/state/shell/progress-guide' } from '#/state/shell/progress-guide'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useBreakpoints} from '#/alf' import {atoms as a, flatten, useBreakpoints} from '#/alf'
import {Reply as Bubble} from '#/components/icons/Reply' import {Reply as Bubble} from '#/components/icons/Reply'
import {formatPostStatCount} from '#/components/PostControls/util' import {formatPostStatCount} from '#/components/PostControls/util'
import {BookmarkButton} from './BookmarkButton' import {BookmarkButton} from './BookmarkButton'
@@ -187,6 +187,11 @@ let PostControls = ({
}) })
} }
const secondaryControlSpacingStyles = flatten([
a.gap_xs,
(big || gtPhone) && a.gap_sm,
])
return ( return (
<View <View
style={[ style={[
@@ -289,8 +294,15 @@ let PostControls = ({
{/* Spacer! */} {/* Spacer! */}
<View /> <View />
</View> </View>
<View style={[a.flex_row, a.justify_end, (big || gtPhone) && a.gap_sm]}> <View style={[a.flex_row, a.justify_end, secondaryControlSpacingStyles]}>
<BookmarkButton post={post} big={big} logContext={logContext} /> <BookmarkButton
post={post}
big={big}
logContext={logContext}
hitSlop={{
right: secondaryControlSpacingStyles.gap / 2,
}}
/>
<ShareMenuButton <ShareMenuButton
testID="postShareBtn" testID="postShareBtn"
post={post} post={post}
@@ -300,6 +312,10 @@ let PostControls = ({
timestamp={post.indexedAt} timestamp={post.indexedAt}
threadgateRecord={threadgateRecord} threadgateRecord={threadgateRecord}
onShare={onShare} onShare={onShare}
hitSlop={{
left: secondaryControlSpacingStyles.gap / 2,
right: secondaryControlSpacingStyles.gap / 2,
}}
/> />
<PostMenuButton <PostMenuButton
testID="postDropdownBtn" testID="postDropdownBtn"
@@ -312,6 +328,9 @@ let PostControls = ({
timestamp={post.indexedAt} timestamp={post.indexedAt}
threadgateRecord={threadgateRecord} threadgateRecord={threadgateRecord}
onShowLess={onShowLess} onShowLess={onShowLess}
hitSlop={{
left: secondaryControlSpacingStyles.gap / 2,
}}
/> />
</View> </View>
</View> </View>
-1
View File
@@ -124,7 +124,6 @@ export const createHitslop = (size: number): Insets => ({
export const HITSLOP_10 = createHitslop(10) export const HITSLOP_10 = createHitslop(10)
export const HITSLOP_20 = createHitslop(20) export const HITSLOP_20 = createHitslop(20)
export const HITSLOP_30 = createHitslop(30) export const HITSLOP_30 = createHitslop(30)
export const POST_CTRL_HITSLOP = {top: 5, bottom: 10, left: 10, right: 10}
export const LANG_DROPDOWN_HITSLOP = {top: 10, bottom: 10, left: 4, right: 4} export const LANG_DROPDOWN_HITSLOP = {top: 10, bottom: 10, left: 4, right: 4}
export const BACK_HITSLOP = HITSLOP_30 export const BACK_HITSLOP = HITSLOP_30
export const MAX_POST_LINES = 25 export const MAX_POST_LINES = 25