fork
Configure Feed
Select the types of activity you want to include in your feed.
mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
fork
Configure Feed
Select the types of activity you want to include in your feed.
1import React from 'react'
2import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
3import * as DropdownMenu from 'zeego/dropdown-menu'
4import {Pressable, StyleSheet, Platform, View, ViewStyle} from 'react-native'
5import {IconProp} from '@fortawesome/fontawesome-svg-core'
6import {MenuItemCommonProps} from 'zeego/lib/typescript/menu'
7import {usePalette} from 'lib/hooks/usePalette'
8import {isWeb} from 'platform/detection'
9import {useTheme} from 'lib/ThemeContext'
10import {HITSLOP_10} from 'lib/constants'
11
12// Custom Dropdown Menu Components
13// ==
14export const DropdownMenuRoot = DropdownMenu.Root
15// export const DropdownMenuTrigger = DropdownMenu.Trigger
16export const DropdownMenuContent = DropdownMenu.Content
17
18type TriggerProps = Omit<
19 React.ComponentProps<(typeof DropdownMenu)['Trigger']>,
20 'children'
21> &
22 React.PropsWithChildren<{
23 testID?: string
24 accessibilityLabel?: string
25 accessibilityHint?: string
26 }>
27export const DropdownMenuTrigger = DropdownMenu.create(
28 (props: TriggerProps) => {
29 const theme = useTheme()
30 const defaultCtrlColor = theme.palette.default.postCtrl
31 const ref = React.useRef<View>(null)
32
33 // HACK
34 // fire a click event on the keyboard press to trigger the dropdown
35 // -prf
36 const onPress = isWeb
37 ? (evt: any) => {
38 if (evt instanceof KeyboardEvent) {
39 // @ts-ignore web only -prf
40 ref.current?.click()
41 }
42 }
43 : undefined
44
45 return (
46 <Pressable
47 testID={props.testID}
48 accessibilityRole="button"
49 accessibilityLabel={props.accessibilityLabel}
50 accessibilityHint={props.accessibilityHint}
51 style={({pressed}) => [{opacity: pressed ? 0.5 : 1}]}
52 hitSlop={HITSLOP_10}
53 onPress={onPress}>
54 <DropdownMenu.Trigger action="press">
55 <View ref={ref}>
56 {props.children ? (
57 props.children
58 ) : (
59 <FontAwesomeIcon
60 icon="ellipsis"
61 size={20}
62 color={defaultCtrlColor}
63 />
64 )}
65 </View>
66 </DropdownMenu.Trigger>
67 </Pressable>
68 )
69 },
70 'Trigger',
71)
72
73type ItemProps = React.ComponentProps<(typeof DropdownMenu)['Item']>
74export const DropdownMenuItem = DropdownMenu.create(
75 (props: ItemProps & {testID?: string}) => {
76 const theme = useTheme()
77 const [focused, setFocused] = React.useState(false)
78 const backgroundColor = theme.colorScheme === 'dark' ? '#fff1' : '#0001'
79
80 return (
81 <DropdownMenu.Item
82 {...props}
83 style={[styles.item, focused && {backgroundColor: backgroundColor}]}
84 onFocus={() => {
85 setFocused(true)
86 props.onFocus && props.onFocus()
87 }}
88 onBlur={() => {
89 setFocused(false)
90 props.onBlur && props.onBlur()
91 }}
92 />
93 )
94 },
95 'Item',
96)
97
98type TitleProps = React.ComponentProps<(typeof DropdownMenu)['ItemTitle']>
99export const DropdownMenuItemTitle = DropdownMenu.create(
100 (props: TitleProps) => {
101 const pal = usePalette('default')
102 return (
103 <DropdownMenu.ItemTitle
104 {...props}
105 style={[props.style, pal.text, styles.itemTitle]}
106 />
107 )
108 },
109 'ItemTitle',
110)
111
112type IconProps = React.ComponentProps<(typeof DropdownMenu)['ItemIcon']>
113export const DropdownMenuItemIcon = DropdownMenu.create((props: IconProps) => {
114 return <DropdownMenu.ItemIcon {...props} />
115}, 'ItemIcon')
116
117type SeparatorProps = React.ComponentProps<(typeof DropdownMenu)['Separator']>
118export const DropdownMenuSeparator = DropdownMenu.create(
119 (props: SeparatorProps) => {
120 const pal = usePalette('default')
121 const theme = useTheme()
122 const {borderColor: separatorColor} =
123 theme.colorScheme === 'dark' ? pal.borderDark : pal.border
124 return (
125 <DropdownMenu.Separator
126 {...props}
127 style={[
128 props.style,
129 styles.separator,
130 {backgroundColor: separatorColor},
131 ]}
132 />
133 )
134 },
135 'Separator',
136)
137
138// Types for Dropdown Menu and Items
139export type DropdownItem = {
140 label: string | 'separator'
141 onPress?: () => void
142 testID?: string
143 icon?: {
144 ios: MenuItemCommonProps['ios']
145 android: string
146 web: IconProp
147 }
148}
149type Props = {
150 items: DropdownItem[]
151 testID?: string
152 accessibilityLabel?: string
153 accessibilityHint?: string
154 triggerStyle?: ViewStyle
155}
156
157/* The `NativeDropdown` function uses native iOS and Android dropdown menus.
158 * It also creates a animated custom dropdown for web that uses
159 * Radix UI primitives under the hood
160 * @prop {DropdownItem[]} items - An array of dropdown items
161 * @prop {React.ReactNode} children - A custom dropdown trigger
162 */
163export function NativeDropdown({
164 items,
165 children,
166 testID,
167 accessibilityLabel,
168 accessibilityHint,
169}: React.PropsWithChildren<Props>) {
170 const pal = usePalette('default')
171 const theme = useTheme()
172 const dropDownBackgroundColor =
173 theme.colorScheme === 'dark' ? pal.btn : pal.viewLight
174
175 return (
176 <DropdownMenuRoot>
177 <DropdownMenuTrigger
178 action="press"
179 testID={testID}
180 accessibilityLabel={accessibilityLabel}
181 accessibilityHint={accessibilityHint}>
182 {children}
183 </DropdownMenuTrigger>
184 <DropdownMenuContent
185 style={[styles.content, dropDownBackgroundColor]}
186 loop>
187 {items.map((item, index) => {
188 if (item.label === 'separator') {
189 return (
190 <DropdownMenuSeparator
191 key={getKey(item.label, index, item.testID)}
192 />
193 )
194 }
195 if (index > 1 && items[index - 1].label === 'separator') {
196 return (
197 <DropdownMenu.Group key={getKey(item.label, index, item.testID)}>
198 <DropdownMenuItem
199 key={getKey(item.label, index, item.testID)}
200 onSelect={item.onPress}>
201 <DropdownMenuItemTitle>{item.label}</DropdownMenuItemTitle>
202 {item.icon && (
203 <DropdownMenuItemIcon
204 ios={item.icon.ios}
205 // androidIconName={item.icon.android} TODO: Add custom android icon support, because these ones are based on https://developer.android.com/reference/android/R.drawable.html and they are ugly
206 >
207 <FontAwesomeIcon
208 icon={item.icon.web}
209 size={20}
210 style={[pal.text]}
211 />
212 </DropdownMenuItemIcon>
213 )}
214 </DropdownMenuItem>
215 </DropdownMenu.Group>
216 )
217 }
218 return (
219 <DropdownMenuItem
220 key={getKey(item.label, index, item.testID)}
221 onSelect={item.onPress}>
222 <DropdownMenuItemTitle>{item.label}</DropdownMenuItemTitle>
223 {item.icon && (
224 <DropdownMenuItemIcon
225 ios={item.icon.ios}
226 // androidIconName={item.icon.android}
227 >
228 <FontAwesomeIcon
229 icon={item.icon.web}
230 size={20}
231 style={[pal.text]}
232 />
233 </DropdownMenuItemIcon>
234 )}
235 </DropdownMenuItem>
236 )
237 })}
238 </DropdownMenuContent>
239 </DropdownMenuRoot>
240 )
241}
242
243const getKey = (label: string, index: number, id?: string) => {
244 if (id) {
245 return id
246 }
247 return `${label}_${index}`
248}
249
250const styles = StyleSheet.create({
251 separator: {
252 height: 1,
253 marginVertical: 4,
254 },
255 content: {
256 backgroundColor: '#f0f0f0',
257 borderRadius: 8,
258 paddingVertical: 4,
259 paddingHorizontal: 4,
260 marginTop: 6,
261 ...Platform.select({
262 web: {
263 animationDuration: '400ms',
264 animationTimingFunction: 'cubic-bezier(0.16, 1, 0.3, 1)',
265 willChange: 'transform, opacity',
266 animationKeyframes: {
267 '0%': {opacity: 0, transform: [{scale: 0.5}]},
268 '100%': {opacity: 1, transform: [{scale: 1}]},
269 },
270 boxShadow:
271 '0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)',
272 transformOrigin: 'var(--radix-dropdown-menu-content-transform-origin)',
273 },
274 }),
275 },
276 item: {
277 flexDirection: 'row',
278 justifyContent: 'space-between',
279 alignItems: 'center',
280 columnGap: 20,
281 // @ts-ignore -web
282 cursor: 'pointer',
283 paddingVertical: 8,
284 paddingHorizontal: 12,
285 borderRadius: 8,
286 },
287 itemTitle: {
288 fontSize: 18,
289 },
290})