mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at push-notifications 239 lines 5.3 kB view raw
1import React, {useRef} from 'react' 2import { 3 Share, 4 StyleProp, 5 StyleSheet, 6 TouchableOpacity, 7 TouchableWithoutFeedback, 8 View, 9 ViewStyle, 10} from 'react-native' 11import {IconProp} from '@fortawesome/fontawesome-svg-core' 12import RootSiblings from 'react-native-root-siblings' 13import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 14import {Text} from '../text/Text' 15import {Button, ButtonType} from './Button' 16import {colors} from '../../../lib/styles' 17import {toShareUrl} from '../../../../lib/strings' 18import {useStores} from '../../../../state' 19import {ReportPostModal, ConfirmModal} from '../../../../state/models/shell-ui' 20import {TABS_ENABLED} from '../../../../build-flags' 21 22const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10} 23 24export interface DropdownItem { 25 icon?: IconProp 26 label: string 27 onPress: () => void 28} 29 30export type DropdownButtonType = ButtonType | 'bare' 31 32export function DropdownButton({ 33 type = 'bare', 34 style, 35 items, 36 label, 37 menuWidth, 38 children, 39}: { 40 type: DropdownButtonType 41 style?: StyleProp<ViewStyle> 42 items: DropdownItem[] 43 label?: string 44 menuWidth?: number 45 children?: React.ReactNode 46}) { 47 const ref = useRef<TouchableOpacity>(null) 48 49 const onPress = () => { 50 ref.current?.measure( 51 ( 52 _x: number, 53 _y: number, 54 width: number, 55 height: number, 56 pageX: number, 57 pageY: number, 58 ) => { 59 if (!menuWidth) { 60 menuWidth = 200 61 } 62 createDropdownMenu( 63 pageX + width - menuWidth, 64 pageY + height, 65 menuWidth, 66 items, 67 ) 68 }, 69 ) 70 } 71 72 if (type === 'bare') { 73 return ( 74 <TouchableOpacity 75 style={style} 76 onPress={onPress} 77 hitSlop={HITSLOP} 78 // Fix an issue where specific references cause runtime error in jest environment 79 ref={process.env.JEST_WORKER_ID != null ? null : ref}> 80 {children} 81 </TouchableOpacity> 82 ) 83 } 84 return ( 85 <View ref={ref}> 86 <Button onPress={onPress} style={style} label={label}> 87 {children} 88 </Button> 89 </View> 90 ) 91} 92 93export function PostDropdownBtn({ 94 style, 95 children, 96 itemHref, 97 isAuthor, 98 onCopyPostText, 99 onDeletePost, 100}: { 101 style?: StyleProp<ViewStyle> 102 children?: React.ReactNode 103 itemHref: string 104 itemTitle: string 105 isAuthor: boolean 106 onCopyPostText: () => void 107 onDeletePost: () => void 108}) { 109 const store = useStores() 110 111 const dropdownItems: DropdownItem[] = [ 112 TABS_ENABLED 113 ? { 114 icon: ['far', 'clone'], 115 label: 'Open in new tab', 116 onPress() { 117 store.nav.newTab(itemHref) 118 }, 119 } 120 : undefined, 121 { 122 icon: ['far', 'paste'], 123 label: 'Copy post text', 124 onPress() { 125 onCopyPostText() 126 }, 127 }, 128 { 129 icon: 'share', 130 label: 'Share...', 131 onPress() { 132 Share.share({url: toShareUrl(itemHref)}) 133 }, 134 }, 135 { 136 icon: 'circle-exclamation', 137 label: 'Report post', 138 onPress() { 139 store.shell.openModal(new ReportPostModal(itemHref)) 140 }, 141 }, 142 isAuthor 143 ? { 144 icon: ['far', 'trash-can'], 145 label: 'Delete post', 146 onPress() { 147 store.shell.openModal( 148 new ConfirmModal( 149 'Delete this post?', 150 'Are you sure? This can not be undone.', 151 onDeletePost, 152 ), 153 ) 154 }, 155 } 156 : undefined, 157 ].filter(Boolean) as DropdownItem[] 158 159 return ( 160 <DropdownButton style={style} items={dropdownItems} menuWidth={200}> 161 {children} 162 </DropdownButton> 163 ) 164} 165 166function createDropdownMenu( 167 x: number, 168 y: number, 169 width: number, 170 items: DropdownItem[], 171): RootSiblings { 172 const onPressItem = (index: number) => { 173 sibling.destroy() 174 items[index].onPress() 175 } 176 const onOuterPress = () => sibling.destroy() 177 const sibling = new RootSiblings( 178 ( 179 <> 180 <TouchableWithoutFeedback onPress={onOuterPress}> 181 <View style={styles.bg} /> 182 </TouchableWithoutFeedback> 183 <View style={[styles.menu, {left: x, top: y, width}]}> 184 {items.map((item, index) => ( 185 <TouchableOpacity 186 key={index} 187 style={[styles.menuItem]} 188 onPress={() => onPressItem(index)}> 189 {item.icon && ( 190 <FontAwesomeIcon style={styles.icon} icon={item.icon} /> 191 )} 192 <Text style={styles.label}>{item.label}</Text> 193 </TouchableOpacity> 194 ))} 195 </View> 196 </> 197 ), 198 ) 199 return sibling 200} 201 202const styles = StyleSheet.create({ 203 bg: { 204 position: 'absolute', 205 top: 0, 206 right: 0, 207 bottom: 0, 208 left: 0, 209 backgroundColor: '#000', 210 opacity: 0.1, 211 }, 212 menu: { 213 position: 'absolute', 214 backgroundColor: '#fff', 215 borderRadius: 14, 216 opacity: 1, 217 paddingVertical: 6, 218 }, 219 menuItem: { 220 flexDirection: 'row', 221 alignItems: 'center', 222 paddingVertical: 10, 223 paddingLeft: 15, 224 paddingRight: 40, 225 }, 226 menuItemBorder: { 227 borderTopWidth: 1, 228 borderTopColor: colors.gray1, 229 marginTop: 4, 230 paddingTop: 12, 231 }, 232 icon: { 233 marginLeft: 6, 234 marginRight: 8, 235 }, 236 label: { 237 fontSize: 18, 238 }, 239})