Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at main 186 lines 4.8 kB view raw
1import * as React from 'react' 2import { 3 Dimensions, 4 type LayoutChangeEvent, 5 type NativeSyntheticEvent, 6 Platform, 7 type StyleProp, 8 useWindowDimensions, 9 View, 10 type ViewStyle, 11} from 'react-native' 12import {useSafeAreaInsets} from 'react-native-safe-area-context' 13import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core' 14 15import { 16 type BottomSheetState, 17 type BottomSheetViewProps, 18} from './BottomSheet.types' 19import { 20 BottomSheetPortalProvider, 21 Context as PortalContext, 22} from './BottomSheetPortal' 23 24const NativeView: React.ComponentType< 25 BottomSheetViewProps & { 26 ref: React.RefObject<any> 27 style: StyleProp<ViewStyle> 28 } 29> = requireNativeViewManager('BottomSheet') 30 31const NativeModule = requireNativeModule('BottomSheet') 32 33const IS_IOS15 = 34 Platform.OS === 'ios' && 35 // semvar - can be 3 segments, so can't use Number(Platform.Version) 36 Number(Platform.Version.split('.').at(0)) < 16 37// older android versions (15 and below) aren't naturally edge-to-edge 38// and behave a little differently 39const IS_NON_E2E_ANDROID = 40 Platform.OS === 'android' && Number(Platform.Version) < 35 41 42export class BottomSheetNativeComponent extends React.Component< 43 BottomSheetViewProps, 44 { 45 open: boolean 46 viewHeight?: number 47 } 48> { 49 ref = React.createRef<any>() 50 51 static contextType = PortalContext 52 53 constructor(props: BottomSheetViewProps) { 54 super(props) 55 this.state = { 56 open: false, 57 } 58 } 59 60 present() { 61 this.setState({open: true}) 62 } 63 64 dismiss() { 65 this.ref.current?.dismiss() 66 } 67 68 private onStateChange = ( 69 event: NativeSyntheticEvent<{state: BottomSheetState}>, 70 ) => { 71 const {state} = event.nativeEvent 72 const isOpen = state !== 'closed' 73 this.setState({open: isOpen}) 74 this.props.onStateChange?.(event) 75 } 76 77 static dismissAll = async () => { 78 await NativeModule.dismissAll() 79 } 80 81 render() { 82 const Portal = this.context as React.ContextType<typeof PortalContext> 83 if (!Portal) { 84 throw new Error( 85 'BottomSheet: You need to wrap your component tree with a <BottomSheetPortalProvider> to use the bottom sheet.', 86 ) 87 } 88 89 if (!this.state.open) { 90 return null 91 } 92 93 let extraStyles 94 if (IS_IOS15 && this.state.viewHeight) { 95 const screenHeight = Dimensions.get('screen').height 96 const {viewHeight} = this.state 97 const cornerRadius = this.props.cornerRadius ?? 0 98 if (viewHeight < screenHeight / 2) { 99 extraStyles = { 100 height: viewHeight, 101 marginTop: screenHeight / 2 - viewHeight, 102 borderTopLeftRadius: cornerRadius, 103 borderTopRightRadius: cornerRadius, 104 } 105 } 106 } 107 108 return ( 109 <Portal> 110 <BottomSheetNativeComponentInner 111 {...this.props} 112 nativeViewRef={this.ref} 113 onStateChange={this.onStateChange} 114 extraStyles={extraStyles} 115 onLayout={ 116 IS_IOS15 117 ? e => { 118 const {height} = e.nativeEvent.layout 119 this.setState({viewHeight: height}) 120 } 121 : undefined 122 } 123 /> 124 </Portal> 125 ) 126 } 127} 128 129function BottomSheetNativeComponentInner({ 130 children, 131 backgroundColor, 132 onLayout, 133 onStateChange, 134 nativeViewRef, 135 extraStyles, 136 ...rest 137}: BottomSheetViewProps & { 138 extraStyles?: StyleProp<ViewStyle> 139 onStateChange: ( 140 event: NativeSyntheticEvent<{state: BottomSheetState}>, 141 ) => void 142 nativeViewRef: React.RefObject<View> 143 onLayout?: (event: LayoutChangeEvent) => void 144}) { 145 const insets = useSafeAreaInsets() 146 const cornerRadius = rest.cornerRadius ?? 0 147 const {height: screenHeight} = useWindowDimensions() 148 149 // sigh... on older Android versions, screenHeight does not include safe area insets 150 // on newer Androids + iOS, it does. we need to find the inner bit + the bottom inset 151 // for the sheet content 152 const sheetHeight = IS_NON_E2E_ANDROID 153 ? screenHeight + insets.bottom 154 : screenHeight - insets.top 155 156 return ( 157 <NativeView 158 {...rest} 159 onStateChange={onStateChange} 160 ref={nativeViewRef} 161 style={{ 162 position: 'absolute', 163 height: sheetHeight, 164 width: '100%', 165 }} 166 containerBackgroundColor={backgroundColor}> 167 <View 168 style={[ 169 { 170 flex: 1, 171 backgroundColor, 172 }, 173 Platform.OS === 'android' && { 174 borderTopLeftRadius: cornerRadius, 175 borderTopRightRadius: cornerRadius, 176 overflow: 'hidden', 177 }, 178 extraStyles, 179 ]}> 180 <View onLayout={onLayout}> 181 <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider> 182 </View> 183 </View> 184 </NativeView> 185 ) 186}