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.

at remove-preload 290 lines 8.6 kB view raw
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})