An ATproto social media client -- with an independent Appview.
at main 3.1 kB view raw
1import {createContext, useContext, useMemo} from 'react' 2import {type GestureResponderEvent, type Insets, type View} from 'react-native' 3 4import {useHaptics} from '#/lib/haptics' 5import {atoms as a, useTheme} from '#/alf' 6import {Button, type ButtonProps} from '#/components/Button' 7import {type Props as SVGIconProps} from '#/components/icons/common' 8import {Text, type TextProps} from '#/components/Typography' 9 10export const DEFAULT_HITSLOP = {top: 5, bottom: 10, left: 10, right: 10} 11 12const PostControlContext = createContext<{ 13 big?: boolean 14 active?: boolean 15 color?: {color: string} 16}>({}) 17PostControlContext.displayName = 'PostControlContext' 18 19// Base button style, which the the other ones extend 20export function PostControlButton({ 21 ref, 22 onPress, 23 onLongPress, 24 children, 25 big, 26 active, 27 activeColor, 28 ...props 29}: Omit<ButtonProps, 'hitSlop'> & { 30 ref?: React.Ref<View> 31 active?: boolean 32 big?: boolean 33 color?: string 34 activeColor?: string 35 hitSlop?: Insets 36}) { 37 const t = useTheme() 38 const playHaptic = useHaptics() 39 40 const ctx = useMemo( 41 () => ({ 42 big, 43 active, 44 color: { 45 color: activeColor && active ? activeColor : t.palette.contrast_500, 46 }, 47 }), 48 [big, active, activeColor, t.palette.contrast_500], 49 ) 50 51 const style = useMemo( 52 () => [ 53 a.flex_row, 54 a.align_center, 55 a.gap_xs, 56 a.bg_transparent, 57 {padding: 5}, 58 ], 59 [], 60 ) 61 62 const handlePress = useMemo(() => { 63 if (!onPress) return 64 return (evt: GestureResponderEvent) => { 65 playHaptic('Light') 66 onPress(evt) 67 } 68 }, [onPress, playHaptic]) 69 70 const handleLongPress = useMemo(() => { 71 if (!onLongPress) return 72 return (evt: GestureResponderEvent) => { 73 playHaptic('Heavy') 74 onLongPress(evt) 75 } 76 }, [onLongPress, playHaptic]) 77 78 return ( 79 <Button 80 ref={ref} 81 onPress={handlePress} 82 onLongPress={handleLongPress} 83 style={style} 84 hoverStyle={t.atoms.bg_contrast_25} 85 shape="round" 86 variant="ghost" 87 color="secondary" 88 {...props} 89 hitSlop={{ 90 ...DEFAULT_HITSLOP, 91 ...(props.hitSlop || {}), 92 }}> 93 {typeof children === 'function' ? ( 94 args => ( 95 <PostControlContext.Provider value={ctx}> 96 {children(args)} 97 </PostControlContext.Provider> 98 ) 99 ) : ( 100 <PostControlContext.Provider value={ctx}> 101 {children} 102 </PostControlContext.Provider> 103 )} 104 </Button> 105 ) 106} 107 108export function PostControlButtonIcon({ 109 icon: Comp, 110 style, 111 ...rest 112}: SVGIconProps & { 113 icon: React.ComponentType<SVGIconProps> 114}) { 115 const {big, color} = useContext(PostControlContext) 116 117 return ( 118 <Comp 119 style={[color, a.pointer_events_none, style]} 120 {...rest} 121 width={big ? 22 : 18} 122 /> 123 ) 124} 125 126export function PostControlButtonText({style, ...props}: TextProps) { 127 const {big, active, color} = useContext(PostControlContext) 128 129 return ( 130 <Text 131 style={[color, big ? a.text_md : a.text_sm, active && a.font_bold, style]} 132 {...props} 133 /> 134 ) 135}