mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
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 React from 'react'
16import {
17 FlatList,
18 FlatListProps,
19 ScrollViewProps,
20 StyleSheet,
21 View,
22 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'
29
30interface AddedProps {
31 desktopFixedHeight?: boolean | number
32}
33
34export const CenteredView = React.forwardRef(function CenteredView(
35 {
36 style,
37 sideBorders,
38 topBorder,
39 ...props
40 }: React.PropsWithChildren<
41 ViewProps & {sideBorders?: boolean; topBorder?: boolean}
42 >,
43 ref: React.Ref<View>,
44) {
45 const pal = usePalette('default')
46 const {isMobile} = useWebMediaQueries()
47 if (!isMobile) {
48 style = addStyle(style, styles.container)
49 }
50 if (sideBorders && !isMobile) {
51 style = addStyle(style, {
52 borderLeftWidth: StyleSheet.hairlineWidth,
53 borderRightWidth: StyleSheet.hairlineWidth,
54 })
55 style = addStyle(style, pal.border)
56 }
57 if (topBorder) {
58 style = addStyle(style, {
59 borderTopWidth: 1,
60 })
61 style = addStyle(style, pal.border)
62 }
63 return <View ref={ref} style={style} {...props} />
64})
65
66export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
67 {
68 contentContainerStyle,
69 style,
70 contentOffset,
71 desktopFixedHeight,
72 ...props
73 }: React.PropsWithChildren<FlatListProps<ItemT> & AddedProps>,
74 ref: React.Ref<FlatList<ItemT>>,
75) {
76 const pal = usePalette('default')
77 const {isMobile} = useWebMediaQueries()
78 if (!isMobile) {
79 contentContainerStyle = addStyle(
80 contentContainerStyle,
81 styles.containerScroll,
82 )
83 }
84 if (contentOffset && contentOffset?.y !== 0) {
85 // NOTE
86 // we use paddingTop & contentOffset to space around the floating header
87 // but reactnative web puts the paddingTop on the wrong element (style instead of the contentContainer)
88 // so we manually correct it here
89 // -prf
90 style = addStyle(style, {
91 paddingTop: 0,
92 })
93 contentContainerStyle = addStyle(contentContainerStyle, {
94 paddingTop: Math.abs(contentOffset.y),
95 })
96 }
97 if (desktopFixedHeight) {
98 if (typeof desktopFixedHeight === 'number') {
99 // @ts-ignore Web only -prf
100 style = addStyle(style, {
101 height: `calc(100vh - ${desktopFixedHeight}px)`,
102 })
103 } else {
104 style = addStyle(style, styles.fixedHeight)
105 }
106 if (!isMobile) {
107 // NOTE
108 // react native web produces *three* wrapping divs
109 // the first two use the `style` prop and the innermost uses the
110 // `contentContainerStyle`. Unfortunately the stable-gutter style
111 // needs to be applied to only the "middle" of these. To hack
112 // around this, we set data-stable-gutters which can then be
113 // styled in our external CSS.
114 // -prf
115 // @ts-ignore web only -prf
116 props.dataSet = props.dataSet || {}
117 // @ts-ignore web only -prf
118 props.dataSet.stableGutters = '1'
119 }
120 }
121 return (
122 <Animated.FlatList
123 ref={ref}
124 contentContainerStyle={[
125 styles.contentContainer,
126 contentContainerStyle,
127 pal.border,
128 ]}
129 style={style}
130 contentOffset={contentOffset}
131 {...props}
132 />
133 )
134})
135
136export const ScrollView = React.forwardRef(function ScrollViewImpl(
137 {contentContainerStyle, ...props}: React.PropsWithChildren<ScrollViewProps>,
138 ref: React.Ref<Animated.ScrollView>,
139) {
140 const pal = usePalette('default')
141
142 const {isMobile} = useWebMediaQueries()
143 if (!isMobile) {
144 contentContainerStyle = addStyle(
145 contentContainerStyle,
146 styles.containerScroll,
147 )
148 }
149 return (
150 <Animated.ScrollView
151 contentContainerStyle={[
152 styles.contentContainer,
153 contentContainerStyle,
154 pal.border,
155 ]}
156 // @ts-ignore something is wrong with the reanimated types -prf
157 ref={ref}
158 {...props}
159 />
160 )
161})
162
163const styles = StyleSheet.create({
164 contentContainer: {
165 borderLeftWidth: StyleSheet.hairlineWidth,
166 borderRightWidth: StyleSheet.hairlineWidth,
167 // @ts-ignore web only
168 minHeight: '100vh',
169 },
170 container: {
171 width: '100%',
172 maxWidth: 600,
173 marginLeft: 'auto',
174 marginRight: 'auto',
175 },
176 containerScroll: {
177 width: '100%',
178 maxWidth: 600,
179 marginLeft: 'auto',
180 marginRight: 'auto',
181 },
182 fixedHeight: {
183 // @ts-ignore web only
184 height: '100vh',
185 },
186})