mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

at rm-precision 144 lines 3.8 kB view raw
1import { 2 Children, 3 cloneElement, 4 isValidElement, 5 type ReactElement, 6 type ReactNode, 7 useCallback, 8 useEffect, 9 useMemo, 10 useRef, 11} from 'react' 12import { 13 AccessibilityInfo, 14 findNodeHandle, 15 Pressable, 16 Text, 17 View, 18} from 'react-native' 19import {msg} from '@lingui/macro' 20import {useLingui} from '@lingui/react' 21 22import {useA11y} from '#/state/a11y' 23 24/** 25 * Conditionally wraps children in a `FocusTrap` component based on whether 26 * screen reader support is enabled. THIS SHOULD BE USED SPARINGLY, only when 27 * no better option is available. 28 */ 29export function FocusScope({children}: {children: ReactNode}) { 30 const {screenReaderEnabled} = useA11y() 31 32 return screenReaderEnabled ? <FocusTrap>{children}</FocusTrap> : children 33} 34 35/** 36 * `FocusTrap` is intended as a last-ditch effort to ensure that users keep 37 * focus within a certain section of the app, like an overlay. 38 * 39 * It works by placing "guards" at the start and end of the active content. 40 * Then when the user reaches either of those guards, it will announce that 41 * they have reached the start or end of the content and tell them how to 42 * remain within the active content section. 43 */ 44function FocusTrap({children}: {children: ReactNode}) { 45 const {_} = useLingui() 46 const child = useRef<View>(null) 47 48 /* 49 * Here we add a ref to the first child of this component. This currently 50 * overrides any ref already on that first child, so we throw an error here 51 * to prevent us from ever accidentally doing this. 52 */ 53 const decoratedChildren = useMemo(() => { 54 return Children.toArray(children).map((node, i) => { 55 if (i === 0 && isValidElement(node)) { 56 const n = node as ReactElement<any> 57 if (n.props.ref !== undefined) { 58 throw new Error( 59 'FocusScope needs to override the ref on its first child.', 60 ) 61 } 62 return cloneElement(n, { 63 ...n.props, 64 ref: child, 65 }) 66 } 67 return node 68 }) 69 }, [children]) 70 71 const focusNode = useCallback((ref: View | null) => { 72 if (!ref) return 73 const node = findNodeHandle(ref) 74 if (node) { 75 AccessibilityInfo.setAccessibilityFocus(node) 76 } 77 }, []) 78 79 useEffect(() => { 80 setTimeout(() => { 81 focusNode(child.current) 82 }, 1e3) 83 }, [focusNode]) 84 85 return ( 86 <> 87 <Pressable 88 accessible 89 accessibilityLabel={_( 90 msg`You've reached the start of the active content.`, 91 )} 92 accessibilityHint={_( 93 msg`Please go back, or activate this element to return to the start of the active content.`, 94 )} 95 accessibilityActions={[{name: 'activate', label: 'activate'}]} 96 onAccessibilityAction={event => { 97 switch (event.nativeEvent.actionName) { 98 case 'activate': { 99 focusNode(child.current) 100 } 101 } 102 }}> 103 <Noop /> 104 </Pressable> 105 <View 106 /** 107 * This property traps focus effectively on iOS, but not on Android. 108 */ 109 accessibilityViewIsModal> 110 {decoratedChildren} 111 </View> 112 <Pressable 113 accessibilityLabel={_( 114 msg`You've reached the end of the active content.`, 115 )} 116 accessibilityHint={_( 117 msg`Please go back, or activate this element to return to the start of the active content.`, 118 )} 119 accessibilityActions={[{name: 'activate', label: 'activate'}]} 120 onAccessibilityAction={event => { 121 switch (event.nativeEvent.actionName) { 122 case 'activate': { 123 focusNode(child.current) 124 } 125 } 126 }}> 127 <Noop /> 128 </Pressable> 129 </> 130 ) 131} 132 133function Noop() { 134 return ( 135 <Text 136 accessible={false} 137 style={{ 138 height: 1, 139 opacity: 0, 140 }}> 141 {' '} 142 </Text> 143 ) 144}