Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 195 lines 5.5 kB view raw
1/** 2 * In the Web build, we center all content so that it mirrors the 3 * mobile experience (a single narrow column). We then place a UI 4 * shell around the content if you're in desktop. 5 * 6 * Because scrolling is handled by components deep in the hierarchy, 7 * we can't just wrap the top-level element with a max width. The 8 * centering has to be done at the ScrollView. 9 * 10 * These components wrap the RN ScrollView-based components to provide 11 * consistent layout. It also provides <CenteredView> for views that 12 * need to match layout but which aren't scrolled. 13 */ 14 15import {forwardRef} from 'react' 16import { 17 type FlatList, 18 type FlatListProps, 19 type ScrollViewProps, 20 StyleSheet, 21 View, 22 type ViewProps, 23} from 'react-native' 24import Animated from 'react-native-reanimated' 25 26import {usePalette} from '#/lib/hooks/usePalette' 27import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 28import {addStyle} from '#/lib/styles' 29import {useLayoutBreakpoints} from '#/alf' 30import {useDialogContext} from '#/components/Dialog' 31import {CENTER_COLUMN_OFFSET} from '#/components/Layout' 32 33interface AddedProps { 34 desktopFixedHeight?: boolean | number 35} 36 37/** 38 * @deprecated use `Layout` components 39 */ 40export const CenteredView = forwardRef(function CenteredView( 41 { 42 style, 43 topBorder, 44 ...props 45 }: React.PropsWithChildren< 46 ViewProps & {sideBorders?: boolean; topBorder?: boolean} 47 >, 48 ref: React.Ref<View>, 49) { 50 const pal = usePalette('default') 51 const {isMobile} = useWebMediaQueries() 52 const {centerColumnOffset} = useLayoutBreakpoints() 53 const {isWithinDialog} = useDialogContext() 54 if (!isMobile) { 55 style = addStyle(style, styles.container) 56 } 57 if (centerColumnOffset && !isWithinDialog) { 58 style = addStyle(style, styles.containerOffset) 59 } 60 if (topBorder) { 61 style = addStyle(style, { 62 borderTopWidth: 1, 63 }) 64 style = addStyle(style, pal.border) 65 } 66 return <View ref={ref} style={style} {...props} /> 67}) 68 69export const FlatList_INTERNAL = forwardRef(function FlatListImpl<ItemT>( 70 { 71 contentContainerStyle, 72 style, 73 contentOffset, 74 desktopFixedHeight, 75 ...props 76 }: React.PropsWithChildren< 77 Omit<FlatListProps<ItemT>, 'CellRendererComponent'> & AddedProps 78 >, 79 ref: React.Ref<FlatList<ItemT>>, 80) { 81 const {isMobile} = useWebMediaQueries() 82 const {centerColumnOffset} = useLayoutBreakpoints() 83 const {isWithinDialog} = useDialogContext() 84 if (!isMobile) { 85 contentContainerStyle = addStyle( 86 contentContainerStyle, 87 styles.containerScroll, 88 ) 89 } 90 if (centerColumnOffset && !isWithinDialog) { 91 style = addStyle(style, styles.containerOffset) 92 } 93 if (contentOffset && contentOffset?.y !== 0) { 94 // NOTE 95 // we use paddingTop & contentOffset to space around the floating header 96 // but reactnative web puts the paddingTop on the wrong element (style instead of the contentContainer) 97 // so we manually correct it here 98 // -prf 99 style = addStyle(style, { 100 paddingTop: 0, 101 }) 102 contentContainerStyle = addStyle(contentContainerStyle, { 103 paddingTop: Math.abs(contentOffset.y), 104 }) 105 } 106 if (desktopFixedHeight) { 107 if (typeof desktopFixedHeight === 'number') { 108 // @ts-expect-error Web only -prf 109 style = addStyle(style, { 110 height: `calc(100vh - ${desktopFixedHeight}px)`, 111 }) 112 } else { 113 style = addStyle(style, styles.fixedHeight) 114 } 115 if (!isMobile) { 116 // NOTE 117 // react native web produces *three* wrapping divs 118 // the first two use the `style` prop and the innermost uses the 119 // `contentContainerStyle`. Unfortunately the stable-gutter style 120 // needs to be applied to only the "middle" of these. To hack 121 // around this, we set data-stable-gutters which can then be 122 // styled in our external CSS. 123 // -prf 124 // @ts-expect-error web only -prf 125 props.dataSet = props.dataSet || {} 126 // @ts-expect-error web only -prf 127 props.dataSet.stableGutters = '1' 128 } 129 } 130 return ( 131 <Animated.FlatList 132 ref={ref} 133 contentContainerStyle={[styles.contentContainer, contentContainerStyle]} 134 style={style} 135 contentOffset={contentOffset} 136 {...props} 137 /> 138 ) 139}) 140 141/** 142 * @deprecated use `Layout` components 143 */ 144export const ScrollView = forwardRef(function ScrollViewImpl( 145 {contentContainerStyle, ...props}: React.PropsWithChildren<ScrollViewProps>, 146 ref: React.Ref<Animated.ScrollView>, 147) { 148 const {isMobile} = useWebMediaQueries() 149 const {centerColumnOffset} = useLayoutBreakpoints() 150 if (!isMobile) { 151 contentContainerStyle = addStyle( 152 contentContainerStyle, 153 styles.containerScroll, 154 ) 155 } 156 if (centerColumnOffset) { 157 contentContainerStyle = addStyle( 158 contentContainerStyle, 159 styles.containerOffset, 160 ) 161 } 162 return ( 163 <Animated.ScrollView 164 contentContainerStyle={[styles.contentContainer, contentContainerStyle]} 165 ref={ref} 166 {...props} 167 /> 168 ) 169}) 170 171const styles = StyleSheet.create({ 172 contentContainer: { 173 // @ts-expect-error web only 174 minHeight: '100vh', 175 }, 176 container: { 177 width: '100%', 178 maxWidth: 600, 179 marginLeft: 'auto', 180 marginRight: 'auto', 181 }, 182 containerOffset: { 183 transform: [{translateX: CENTER_COLUMN_OFFSET}], 184 }, 185 containerScroll: { 186 width: '100%', 187 maxWidth: 600, 188 marginLeft: 'auto', 189 marginRight: 'auto', 190 }, 191 fixedHeight: { 192 // @ts-expect-error web only 193 height: '100vh', 194 }, 195})