Something went wrong. Try again.
Bluesky app fork with some witchin' additions 💫 forked from jollywhoppers.com/witchsky.app
Something went wrong. Try again.
4.9 kB · 190 lines
TSX
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191import * as React from 'react'import { Dimensions, type LayoutChangeEvent, type NativeSyntheticEvent, Platform, type StyleProp, View, type ViewStyle,} from 'react-native'import {useSafeAreaInsets} from 'react-native-safe-area-context'import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core'
import {IS_IOS} from '#/env'import { type BottomSheetState, type BottomSheetViewProps,} from './BottomSheet.types'import { BottomSheetPortalProvider, Context as PortalContext,} from './BottomSheetPortal'
const screenHeight = Dimensions.get('screen').height
const NativeView: React.ComponentType< BottomSheetViewProps & { ref: React.RefObject<any> style: StyleProp<ViewStyle> }> = requireNativeViewManager('BottomSheet')
const NativeModule = requireNativeModule('BottomSheet')
const IS_IOS15 = Platform.OS === 'ios' && // semvar - can be 3 segments, so can't use Number(Platform.Version) Number(Platform.Version.split('.').at(0)) < 16
export class BottomSheetNativeComponent extends React.Component< BottomSheetViewProps, { open: boolean viewHeight?: number }> { ref = React.createRef<any>()
static contextType = PortalContext
constructor(props: BottomSheetViewProps) { super(props) this.state = { open: false, } }
present() { this.setState({open: true}) }
dismiss() { this.ref.current?.dismiss() }
private onStateChange = ( event: NativeSyntheticEvent<{state: BottomSheetState}>, ) => { const {state} = event.nativeEvent const isOpen = state !== 'closed' this.setState({open: isOpen}) this.props.onStateChange?.(event) }
private updateLayout = () => { this.ref.current?.updateLayout() }
static dismissAll = async () => { await NativeModule.dismissAll() }
render() { const Portal = this.context as React.ContextType<typeof PortalContext> if (!Portal) { throw new Error( 'BottomSheet: You need to wrap your component tree with a <BottomSheetPortalProvider> to use the bottom sheet.', ) }
if (!this.state.open) { return null }
let extraStyles if (IS_IOS15 && this.state.viewHeight) { const {viewHeight} = this.state const cornerRadius = this.props.cornerRadius ?? 0 if (viewHeight < screenHeight / 2) { extraStyles = { height: viewHeight, marginTop: screenHeight / 2 - viewHeight, borderTopLeftRadius: cornerRadius, borderTopRightRadius: cornerRadius, } } }
return ( <Portal> <BottomSheetNativeComponentInner {...this.props} nativeViewRef={this.ref} onStateChange={this.onStateChange} extraStyles={extraStyles} onLayout={e => { if (IS_IOS15) { const {height} = e.nativeEvent.layout this.setState({viewHeight: height}) } if (Platform.OS === 'android') { // TEMP HACKFIX: I had to timebox this, but this is Bad. // On Android, if you run updateLayout() immediately, // it will take ages to actually run on the native side. // However, adding literally any delay will fix this, including // a console.log() - just sending the log to the CLI is enough. // TODO: Get to the bottom of this and fix it properly! -sfn setTimeout(() => this.updateLayout()) } else { this.updateLayout() } }} /> </Portal> ) }}
function BottomSheetNativeComponentInner({ children, backgroundColor, onLayout, onStateChange, nativeViewRef, extraStyles, ...rest}: BottomSheetViewProps & { extraStyles?: StyleProp<ViewStyle> onStateChange: ( event: NativeSyntheticEvent<{state: BottomSheetState}>, ) => void nativeViewRef: React.RefObject<View> onLayout: (event: LayoutChangeEvent) => void}) { const insets = useSafeAreaInsets() const cornerRadius = rest.cornerRadius ?? 0
const sheetHeight = IS_IOS ? screenHeight - insets.top : screenHeight
return ( <NativeView {...rest} onStateChange={onStateChange} ref={nativeViewRef} style={{ position: 'absolute', height: sheetHeight, width: '100%', }} containerBackgroundColor={backgroundColor}> <View style={[ { flex: 1, backgroundColor, }, Platform.OS === 'android' && { borderTopLeftRadius: cornerRadius, borderTopRightRadius: cornerRadius, overflow: 'hidden', }, extraStyles, ]}> <View onLayout={onLayout}> <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider> </View> </View> </NativeView> )}