Bluesky app fork with some witchin' additions ๐Ÿ’ซ witchsky.app
bluesky fork client

possible slider android crash fix #12

merged opened by whey.party targeting main

sorry react native reanimated is scary stuff

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:mn45tewwnse5btfftvd3powc/sh.tangled.repo.pull/3m6w6rruwqf22
+52 -180
Interdiff #0 โ†’ #1
+50 -176
src/components/forms/Slider.tsx
··· 1 - import {useCallback, useEffect, useRef} from 'react' // Removed useState 2 - import {type StyleProp, View, type ViewStyle} from 'react-native' 3 - import {Gesture, GestureDetector} from 'react-native-gesture-handler' 4 - import Animated, { 5 - runOnJS, 6 - useAnimatedStyle, 7 - useSharedValue, 8 - withSpring, 9 - } from 'react-native-reanimated' 1 + import {type ViewStyle} from 'react-native' 2 + import {Slider as RNSlider} from '@miblanchard/react-native-slider' 10 3 11 - import {useHaptics} from '#/lib/haptics' 12 - import {atoms as a, platform, useTheme} from '#/alf' 4 + import {useTheme} from '#/alf' 13 5 14 - export interface SliderProps { 6 + interface SliderProps { 15 7 value: number 16 8 onValueChange: (value: number) => void 17 - min?: number 18 - max?: number 9 + minimumValue?: number 10 + maximumValue?: number 19 11 step?: number 20 - label?: string 21 - accessibilityHint?: string 22 - style?: StyleProp<ViewStyle> 23 - debounce?: number 12 + trackStyle?: ViewStyle 13 + minimumTrackStyle?: ViewStyle 14 + thumbStyle?: ViewStyle 15 + thumbTouchSize?: {width: number; height: number} 24 16 } 25 17 26 18 export function Slider({ 27 19 value, 28 20 onValueChange, 29 - min = 0, 30 - max = 100, 21 + minimumValue = 0, 22 + maximumValue = 1, 31 23 step = 1, 32 - label, 33 - accessibilityHint, 34 - style, 35 - debounce, 24 + trackStyle, 25 + minimumTrackStyle, 26 + thumbStyle, 27 + thumbTouchSize = {width: 40, height: 40}, 36 28 }: SliderProps) { 37 29 const t = useTheme() 38 - const playHaptic = useHaptics() 39 - const timerRef = useRef<NodeJS.Timeout | undefined>(undefined) 40 30 41 - const width = useSharedValue(0) 42 - 43 - const progress = useSharedValue(0) 44 - const isPressed = useSharedValue(false) 45 - 46 - useEffect(() => { 47 - if (!isPressed.value) { 48 - const clamped = Math.min(Math.max(value, min), max) 49 - const normalized = (clamped - min) / (max - min) 50 - progress.value = withSpring(normalized, {overshootClamping: true}) 51 - } 52 - }, [value, min, max, progress, isPressed]) 53 - 54 - useEffect(() => { 55 - return () => { 56 - if (timerRef.current) clearTimeout(timerRef.current) 57 - } 58 - }, []) 59 - 60 - const updateValueJS = useCallback( 61 - (val: number) => { 62 - if (debounce && debounce > 0) { 63 - if (timerRef.current) { 64 - clearTimeout(timerRef.current) 65 - } 66 - timerRef.current = setTimeout(() => { 67 - onValueChange(val) 68 - }, debounce) 69 - } else { 70 - onValueChange(val) 71 - } 72 - }, 73 - [onValueChange, debounce], 74 - ) 75 - 76 - const handleValueChange = useCallback( 77 - (newProgress: number) => { 78 - 'worklet' 79 - const rawValue = min + newProgress * (max - min) 80 - 81 - const steppedValue = Math.round(rawValue / step) * step 82 - const clamped = Math.min(Math.max(steppedValue, min), max) 83 - 84 - runOnJS(updateValueJS)(clamped) 85 - }, 86 - [min, max, step, updateValueJS], 87 - ) 88 - 89 - const pan = Gesture.Pan() 90 - .onBegin(e => { 91 - isPressed.value = true 92 - 93 - // FIX 2: Access width.value instead of width 94 - if (width.value > 0) { 95 - const newProgress = Math.min(Math.max(e.x / width.value, 0), 1) 96 - progress.value = newProgress 97 - handleValueChange(newProgress) 98 - } 99 - }) 100 - .onUpdate(e => { 101 - // FIX 3: Access width.value instead of width 102 - if (width.value === 0) return 103 - const newProgress = Math.min(Math.max(e.x / width.value, 0), 1) 104 - progress.value = newProgress 105 - handleValueChange(newProgress) 106 - }) 107 - .onFinalize(() => { 108 - isPressed.value = false 109 - runOnJS(playHaptic)('Light') 110 - }) 111 - 112 - const thumbAnimatedStyle = useAnimatedStyle(() => { 113 - // FIX 4: Access width.value for calculations 114 - const translateX = progress.value * width.value 115 - return { 116 - transform: [ 117 - {translateX: translateX - 12}, 118 - {scale: isPressed.value ? 1.1 : 1}, 119 - ], 120 - } 121 - }) 122 - 123 - const trackAnimatedStyle = useAnimatedStyle(() => { 124 - return { 125 - width: `${progress.value * 100}%`, 126 - } 127 - }) 128 - 129 31 return ( 130 - <View 131 - style={[a.w_full, a.justify_center, {height: 28}, style]} 132 - accessibilityRole="adjustable" 133 - accessibilityLabel={label} 134 - accessibilityHint={accessibilityHint} 135 - accessibilityValue={{min, max, now: value}}> 136 - <GestureDetector gesture={pan}> 137 - <View 138 - style={[a.flex_1, a.justify_center, {cursor: 'pointer'}]} 139 - // @ts-ignore web-only style 140 - // FIX 5: Update width.value directly on layout 141 - onLayout={e => { 142 - width.value = e.nativeEvent.layout.width 143 - }}> 144 - <View 145 - style={[ 146 - a.w_full, 147 - a.absolute, 148 - t.atoms.bg_contrast_50, 149 - {height: 4, borderRadius: 2}, 150 - ]} 151 - /> 152 - 153 - <Animated.View 154 - style={[ 155 - a.absolute, 156 - a.rounded_full, 157 - {height: 4, backgroundColor: t.palette.primary_500}, 158 - trackAnimatedStyle, 159 - ]} 160 - /> 161 - 162 - <Animated.View 163 - style={[ 164 - a.absolute, 165 - a.rounded_full, 166 - t.atoms.bg, 167 - { 168 - left: 0, 169 - width: 24, 170 - height: 24, 171 - borderWidth: 1, 172 - borderColor: t.atoms.border_contrast_low.borderColor, 173 - }, 174 - thumbAnimatedStyle, 175 - platform({ 176 - web: { 177 - boxShadow: '0px 2px 4px 0px #0000001A', 178 - }, 179 - ios: { 180 - shadowColor: '#000', 181 - shadowOffset: {width: 0, height: 2}, 182 - shadowOpacity: 0.15, 183 - shadowRadius: 4, 184 - }, 185 - android: {elevation: 3}, 186 - }), 187 - ]} 188 - /> 189 - </View> 190 - </GestureDetector> 191 - </View> 32 + <RNSlider 33 + value={[value]} // always an array 34 + onValueChange={values => onValueChange(values[0])} 35 + minimumValue={minimumValue} 36 + maximumValue={maximumValue} 37 + step={step} 38 + trackStyle={{ 39 + height: 4, 40 + borderRadius: 2, 41 + backgroundColor: t.atoms.bg_contrast_50.backgroundColor, 42 + ...trackStyle, 43 + }} 44 + minimumTrackStyle={{ 45 + height: 4, 46 + borderRadius: 2, 47 + backgroundColor: t.palette.primary_500, 48 + ...minimumTrackStyle, 49 + }} 50 + thumbStyle={{ 51 + width: 24, 52 + height: 24, 53 + borderRadius: 12, 54 + borderWidth: 1, 55 + borderColor: t.atoms.border_contrast_low.borderColor, 56 + backgroundColor: t.atoms.bg.backgroundColor, 57 + shadowColor: '#000', 58 + shadowOffset: {width: 0, height: 2}, 59 + shadowOpacity: 0.15, 60 + shadowRadius: 4, 61 + elevation: 3, 62 + ...thumbStyle, 63 + }} 64 + thumbTouchSize={thumbTouchSize} 65 + /> 192 66 ) 193 67 }
+2 -4
src/screens/Settings/AppearanceSettings.tsx
··· 184 184 <Trans>Hue shift the colors:</Trans> 185 185 </Text> 186 186 <Slider 187 - label="Volume" 188 187 value={hue} 189 188 onValueChange={setHue} 190 - min={0} 191 - max={360} 189 + minimumValue={0} 190 + maximumValue={360} 192 191 step={1} 193 - debounce={0.3} 194 192 /> 195 193 </View> 196 194 </SettingsList.Group>

History

2 rounds 3 comments
sign up or login to add to the discussion
1 commit
expand
acf2ff25
attempt 2 of slider android crash fix
expand 3 comments

Wait dont merge it yet i think i forgot to add the dependency

nvm, go ahead, somehow the dependency i picked (miblanchard/react-native-slider) was already included in the yarn.lock and package.json

it was used in the now-removed web image cropper https://github.com/bluesky-social/social-app/commit/7916b26aadb7e003728d9dc653ab8b8deabf4076 but the package was never removed

pull request successfully merged
1 commit
expand
f702e12b
possible slider android crash fix
expand 0 comments