Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
119
fork

Configure Feed

Select the types of activity you want to include in your feed.

at a876aae44ea07494ebea9727350aa060b81f317b 227 lines 6.0 kB view raw
1import {useCallback, useState} from 'react' 2import { 3 ActivityIndicator, 4 type GestureResponderEvent, 5 type NativeSyntheticEvent, 6 type NativeTouchEvent, 7 Pressable, 8 type PressableStateCallbackType, 9 type StyleProp, 10 StyleSheet, 11 type TextStyle, 12 View, 13 type ViewStyle, 14} from 'react-native' 15 16import {choose} from '#/lib/functions' 17import {useTheme} from '#/lib/ThemeContext' 18import {Text} from '../text/Text' 19 20export type ButtonType = 21 | 'primary' 22 | 'secondary' 23 | 'default' 24 | 'inverted' 25 | 'primary-outline' 26 | 'secondary-outline' 27 | 'primary-light' 28 | 'secondary-light' 29 | 'default-light' 30 31// Augment type for react-native-web (see https://github.com/necolas/react-native-web/issues/1684#issuecomment-766451866) 32declare module 'react-native' { 33 interface PressableStateCallbackType { 34 // @ts-ignore web only 35 hovered?: boolean 36 focused?: boolean 37 } 38} 39 40/** 41 * @deprecated use Button from `#/components/Button.tsx` instead 42 */ 43export function Button({ 44 type = 'primary', 45 label, 46 style, 47 labelContainerStyle, 48 labelStyle, 49 onPress, 50 children, 51 testID, 52 accessibilityLabel, 53 accessibilityHint, 54 accessibilityLabelledBy, 55 onAccessibilityEscape, 56 withLoading = false, 57 disabled = false, 58}: React.PropsWithChildren<{ 59 type?: ButtonType 60 label?: string 61 style?: StyleProp<ViewStyle> 62 labelContainerStyle?: StyleProp<ViewStyle> 63 labelStyle?: StyleProp<TextStyle> 64 onPress?: (e: NativeSyntheticEvent<NativeTouchEvent>) => void | Promise<void> 65 testID?: string 66 accessibilityLabel?: string 67 accessibilityHint?: string 68 accessibilityLabelledBy?: string 69 onAccessibilityEscape?: () => void 70 withLoading?: boolean 71 disabled?: boolean 72}>) { 73 const theme = useTheme() 74 const typeOuterStyle = choose<ViewStyle, Record<ButtonType, ViewStyle>>( 75 type, 76 { 77 primary: { 78 backgroundColor: theme.palette.primary.background, 79 }, 80 secondary: { 81 backgroundColor: theme.palette.secondary.background, 82 }, 83 default: { 84 backgroundColor: theme.palette.default.backgroundLight, 85 }, 86 inverted: { 87 backgroundColor: theme.palette.inverted.background, 88 }, 89 'primary-outline': { 90 backgroundColor: theme.palette.default.background, 91 borderWidth: 1, 92 borderColor: theme.palette.primary.border, 93 }, 94 'secondary-outline': { 95 backgroundColor: theme.palette.default.background, 96 borderWidth: 1, 97 borderColor: theme.palette.secondary.border, 98 }, 99 'primary-light': { 100 backgroundColor: theme.palette.default.background, 101 }, 102 'secondary-light': { 103 backgroundColor: theme.palette.default.background, 104 }, 105 'default-light': { 106 backgroundColor: theme.palette.default.background, 107 }, 108 }, 109 ) 110 const typeLabelStyle = choose<TextStyle, Record<ButtonType, TextStyle>>( 111 type, 112 { 113 primary: { 114 color: theme.palette.primary.text, 115 fontWeight: '600', 116 }, 117 secondary: { 118 color: theme.palette.secondary.text, 119 fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined, 120 }, 121 default: { 122 color: theme.palette.default.text, 123 }, 124 inverted: { 125 color: theme.palette.inverted.text, 126 fontWeight: '600', 127 }, 128 'primary-outline': { 129 color: theme.palette.primary.textInverted, 130 fontWeight: theme.palette.primary.isLowContrast ? '600' : undefined, 131 }, 132 'secondary-outline': { 133 color: theme.palette.secondary.textInverted, 134 fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined, 135 }, 136 'primary-light': { 137 color: theme.palette.primary.textInverted, 138 fontWeight: theme.palette.primary.isLowContrast ? '600' : undefined, 139 }, 140 'secondary-light': { 141 color: theme.palette.secondary.textInverted, 142 fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined, 143 }, 144 'default-light': { 145 color: theme.palette.default.text, 146 fontWeight: theme.palette.default.isLowContrast ? '600' : undefined, 147 }, 148 }, 149 ) 150 151 const [isLoading, setIsLoading] = useState(false) 152 const onPressWrapped = useCallback( 153 async (event: GestureResponderEvent) => { 154 event.stopPropagation() 155 event.preventDefault() 156 if (withLoading) setIsLoading(true) 157 await onPress?.(event) 158 if (withLoading) setIsLoading(false) 159 }, 160 [onPress, withLoading], 161 ) 162 163 const getStyle = useCallback( 164 (state: PressableStateCallbackType) => { 165 const arr = [typeOuterStyle, styles.outer, style] 166 if (state.pressed) { 167 arr.push({opacity: 0.6}) 168 } else if (state.hovered) { 169 arr.push({opacity: 0.8}) 170 } 171 return arr 172 }, 173 [typeOuterStyle, style], 174 ) 175 176 const renderChildern = useCallback(() => { 177 if (!label) { 178 return children 179 } 180 181 return ( 182 <View style={[styles.labelContainer, labelContainerStyle]}> 183 {label && withLoading && isLoading ? ( 184 <ActivityIndicator size={12} color={typeLabelStyle.color} /> 185 ) : null} 186 <Text type="button" style={[typeLabelStyle, labelStyle]}> 187 {label} 188 </Text> 189 </View> 190 ) 191 }, [ 192 children, 193 label, 194 withLoading, 195 isLoading, 196 labelContainerStyle, 197 typeLabelStyle, 198 labelStyle, 199 ]) 200 201 return ( 202 <Pressable 203 style={getStyle} 204 onPress={onPressWrapped} 205 disabled={disabled || isLoading} 206 testID={testID} 207 accessibilityRole="button" 208 accessibilityLabel={accessibilityLabel} 209 accessibilityHint={accessibilityHint} 210 accessibilityLabelledBy={accessibilityLabelledBy} 211 onAccessibilityEscape={onAccessibilityEscape}> 212 {renderChildern} 213 </Pressable> 214 ) 215} 216 217const styles = StyleSheet.create({ 218 outer: { 219 paddingHorizontal: 14, 220 paddingVertical: 8, 221 borderRadius: 24, 222 }, 223 labelContainer: { 224 flexDirection: 'row', 225 gap: 8, 226 }, 227})