this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at e28f6d2f370b4e882ed6f23d08ca0f8d94dbac5f 138 lines 3.7 kB view raw
1import {useCallback, useState} from 'react' 2import {StyleSheet, View} from 'react-native' 3import {DismissableLayer, FocusGuards, FocusScope} from 'radix-ui/internal' 4import {RemoveScrollBar} from 'react-remove-scroll-bar' 5 6import {useA11y} from '#/state/a11y' 7import {useModals} from '#/state/modals' 8import {type ComposerOpts, useComposerState} from '#/state/shell/composer' 9import { 10 EmojiPicker, 11 type EmojiPickerPosition, 12 type EmojiPickerState, 13} from '#/view/com/composer/text-input/web/EmojiPicker' 14import {atoms as a, flatten, useBreakpoints, useTheme} from '#/alf' 15import {ComposePost, useComposerCancelRef} from '../com/composer/Composer' 16 17const BOTTOM_BAR_HEIGHT = 61 18 19export function Composer({}: {winHeight: number}) { 20 const state = useComposerState() 21 const isActive = !!state 22 23 // rendering 24 // = 25 26 if (!isActive) { 27 return null 28 } 29 30 return ( 31 <> 32 <RemoveScrollBar /> 33 <Inner state={state} /> 34 </> 35 ) 36} 37 38function Inner({state}: {state: ComposerOpts}) { 39 const ref = useComposerCancelRef() 40 const {isModalActive} = useModals() 41 const t = useTheme() 42 const {gtMobile} = useBreakpoints() 43 const {reduceMotionEnabled} = useA11y() 44 const [pickerState, setPickerState] = useState<EmojiPickerState>({ 45 isOpen: false, 46 pos: {top: 0, left: 0, right: 0, bottom: 0, nextFocusRef: null}, 47 }) 48 49 const onOpenPicker = useCallback((pos: EmojiPickerPosition | undefined) => { 50 if (!pos) return 51 setPickerState({ 52 isOpen: true, 53 pos, 54 }) 55 }, []) 56 57 const onClosePicker = useCallback(() => { 58 setPickerState(prev => ({ 59 ...prev, 60 isOpen: false, 61 })) 62 }, []) 63 64 FocusGuards.useFocusGuards() 65 66 return ( 67 <FocusScope.FocusScope loop trapped asChild> 68 <DismissableLayer.DismissableLayer 69 role="dialog" 70 aria-modal 71 style={flatten([ 72 {position: 'fixed'}, 73 a.inset_0, 74 {backgroundColor: '#000c'}, 75 a.flex, 76 a.flex_col, 77 a.align_center, 78 !reduceMotionEnabled && a.fade_in, 79 ])} 80 onFocusOutside={evt => evt.preventDefault()} 81 onInteractOutside={evt => evt.preventDefault()} 82 onDismiss={() => { 83 // TEMP: remove when all modals are ALF'd -sfn 84 if (!isModalActive) { 85 ref.current?.onPressCancel() 86 } 87 }}> 88 <View 89 style={[ 90 styles.container, 91 !gtMobile && styles.containerMobile, 92 t.atoms.bg, 93 t.atoms.border_contrast_medium, 94 !reduceMotionEnabled && [ 95 a.zoom_fade_in, 96 {animationDelay: 0.1}, 97 {animationFillMode: 'backwards'}, 98 ], 99 ]}> 100 <ComposePost 101 cancelRef={ref} 102 replyTo={state.replyTo} 103 quote={state.quote} 104 onPost={state.onPost} 105 onPostSuccess={state.onPostSuccess} 106 mention={state.mention} 107 openEmojiPicker={onOpenPicker} 108 text={state.text} 109 imageUris={state.imageUris} 110 openGallery={state.openGallery} 111 /> 112 </View> 113 <EmojiPicker state={pickerState} close={onClosePicker} /> 114 </DismissableLayer.DismissableLayer> 115 </FocusScope.FocusScope> 116 ) 117} 118 119const styles = StyleSheet.create({ 120 container: { 121 marginTop: 50, 122 maxWidth: 600, 123 width: '100%', 124 paddingVertical: 0, 125 borderRadius: 8, 126 marginBottom: 0, 127 borderWidth: 1, 128 // @ts-expect-error web only 129 maxHeight: 'calc(100% - (40px * 2))', 130 overflow: 'hidden', 131 }, 132 containerMobile: { 133 borderRadius: 0, 134 marginBottom: BOTTOM_BAR_HEIGHT, 135 // @ts-expect-error web only 136 maxHeight: `calc(100% - ${BOTTOM_BAR_HEIGHT}px)`, 137 }, 138})