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